001    /*
002     *  Licensed to the Apache Software Foundation (ASF) under one
003     *  or more contributor license agreements.  See the NOTICE file
004     *  distributed with this work for additional information
005     *  regarding copyright ownership.  The ASF licenses this file
006     *  to you under the Apache License, Version 2.0 (the
007     *  "License"); you may not use this file except in compliance
008     *  with the License.  You may obtain a copy of the License at
009     *
010     *    http://www.apache.org/licenses/LICENSE-2.0
011     *
012     *  Unless required by applicable law or agreed to in writing,
013     *  software distributed under the License is distributed on an
014     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     *  KIND, either express or implied.  See the License for the
016     *  specific language governing permissions and limitations
017     *  under the License.
018     *
019     */
020    package org.apache.directory.shared.ldap.subtree;
021    
022    
023    import org.apache.directory.shared.ldap.filter.ExprNode;
024    import org.apache.directory.shared.ldap.name.DN;
025    
026    import java.util.Iterator;
027    import java.util.Set;
028    import java.util.Collections;
029    
030    
031    /**
032     * A simple implementation of the SubtreeSpecification interface.
033     *
034     * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
035     * @version $Rev: 918756 $
036     */
037    public class BaseSubtreeSpecification implements SubtreeSpecification
038    {
039        /** the subtree base relative to the administration point */
040        private final DN base;
041    
042        /** the set of subordinates entries and their subordinates to exclude */
043        private final Set<DN> chopBefore;
044    
045        /** the set of subordinates entries whose subordinates are to be excluded */
046        private final Set<DN> chopAfter;
047    
048        /** the minimum distance below base to start including entries */
049        private final int minBaseDistance;
050    
051        /** the maximum distance from base past which entries are excluded */
052        private final int maxBaseDistance;
053    
054        /**
055         * a filter using only assertions on objectClass attributes for subtree
056         * refinement
057         */
058        private final ExprNode refinement;
059    
060    
061        // -----------------------------------------------------------------------
062        // C O N S T R U C T O R S
063        // -----------------------------------------------------------------------
064    
065        /**
066         * Creates a simple subtree whose administrative point is necessarily the
067         * base and all subordinates underneath (excluding those that are part of
068         * inner areas) are part of the the subtree.
069         */
070        @SuppressWarnings("unchecked")
071        public BaseSubtreeSpecification()
072        {
073            this.base = new DN();
074            this.minBaseDistance = 0;
075            this.maxBaseDistance = UNBOUNDED_MAX;
076            this.chopAfter = Collections.EMPTY_SET;
077            this.chopBefore = Collections.EMPTY_SET;
078            this.refinement = null;
079        }
080    
081    
082        /**
083         * Creates a simple subtree refinement whose administrative point is
084         * necessarily the base and only those subordinates selected by the
085         * refinement filter are included.
086         *
087         * @param refinement
088         *            the filter expression only composed of objectClass attribute
089         *            value assertions
090         */
091        @SuppressWarnings("unchecked")
092        public BaseSubtreeSpecification(ExprNode refinement)
093        {
094            this.base = new DN();
095            this.minBaseDistance = 0;
096            this.maxBaseDistance = UNBOUNDED_MAX;
097            this.chopAfter = Collections.EMPTY_SET;
098            this.chopBefore = Collections.EMPTY_SET;
099            this.refinement = refinement;
100        }
101    
102    
103        /**
104         * Creates a simple subtree whose administrative point above the base and
105         * all subordinates underneath the base (excluding those that are part of
106         * inner areas) are part of the the subtree.
107         *
108         * @param base
109         *            the base of the subtree relative to the administrative point
110         */
111        @SuppressWarnings("unchecked")
112        public BaseSubtreeSpecification( DN base )
113        {
114            this.base = base;
115            this.minBaseDistance = 0;
116            this.maxBaseDistance = UNBOUNDED_MAX;
117            this.chopAfter = Collections.EMPTY_SET;
118            this.chopBefore = Collections.EMPTY_SET;
119            this.refinement = null;
120        }
121    
122    
123        /**
124         * Creates a subtree without a refinement filter where all other aspects can
125         * be varied.
126         *
127         * @param base
128         *            the base of the subtree relative to the administrative point
129         * @param minBaseDistance
130         *            the minimum distance below base to start including entries
131         * @param maxBaseDistance
132         *            the maximum distance from base past which entries are excluded
133         * @param chopAfter
134         *            the set of subordinates entries whose subordinates are to be
135         *            excluded
136         * @param chopBefore
137         *            the set of subordinates entries and their subordinates to
138         *            exclude
139         */
140        public BaseSubtreeSpecification( DN base, int minBaseDistance, int maxBaseDistance, 
141            Set<DN> chopAfter, Set<DN> chopBefore )
142        {
143            this( base, minBaseDistance, maxBaseDistance, chopAfter, chopBefore, null );
144        }
145    
146    
147        /**
148         * Creates a subtree which may be a refinement filter where all aspects of
149         * the specification can be set. If the refinement filter is null this
150         * defaults to {@link #BaseSubtreeSpecification(DN, int, int, Set, Set)}.
151         *
152         * @param base
153         *            the base of the subtree relative to the administrative point
154         * @param minBaseDistance
155         *            the minimum distance below base to start including entries
156         * @param maxBaseDistance
157         *            the maximum distance from base past which entries are excluded
158         * @param chopAfter
159         *            the set of subordinates entries whose subordinates are to be
160         *            excluded
161         * @param chopBefore
162         *            the set of subordinates entries and their subordinates to
163         *            exclude
164         * @param refinement
165         *            the filter expression only composed of objectClass attribute
166         *            value assertions
167         */
168        public BaseSubtreeSpecification( DN base, int minBaseDistance, int maxBaseDistance, 
169            Set<DN> chopAfter, Set<DN> chopBefore, ExprNode refinement )
170        {
171            this.base = base;
172            this.minBaseDistance = minBaseDistance;
173    
174            if ( maxBaseDistance < 0 )
175            {
176                this.maxBaseDistance = UNBOUNDED_MAX;
177            }
178            else
179            {
180                this.maxBaseDistance = maxBaseDistance;
181            }
182    
183            this.chopAfter = chopAfter;
184            this.chopBefore = chopBefore;
185            this.refinement = refinement;
186        }
187    
188    
189        // -----------------------------------------------------------------------
190        // A C C E S S O R S
191        // -----------------------------------------------------------------------
192    
193    
194        public DN getBase()
195        {
196            return this.base;
197        }
198    
199    
200        public Set<DN> getChopBeforeExclusions()
201        {
202            return this.chopBefore;
203        }
204    
205    
206        public Set<DN> getChopAfterExclusions()
207        {
208            return this.chopAfter;
209        }
210    
211    
212        public int getMinBaseDistance()
213        {
214            return this.minBaseDistance;
215        }
216    
217    
218        public int getMaxBaseDistance()
219        {
220            return this.maxBaseDistance;
221        }
222    
223    
224        public ExprNode getRefinement()
225        {
226            return this.refinement;
227        }
228    
229    
230        /**
231         * Converts this item into its string representation as stored
232         * in directory.
233         *
234         * @param buffer the string buffer
235         */
236        public void printToBuffer( StringBuilder buffer )
237        {
238            buffer.append( '{' );
239    
240            if(!base.isEmpty()) {
241                buffer.append( ' ' );
242                buffer.append( "base" );
243                buffer.append( ' ' );
244                buffer.append( '"' );
245                buffer.append( base.getName() );
246                buffer.append( '"' );
247                buffer.append( ',' );
248            }
249    
250            if(minBaseDistance > 0) {
251                buffer.append( ' ' );
252                buffer.append( "minimum" );
253                buffer.append( ' ' );
254                buffer.append( minBaseDistance );
255                buffer.append( ',' );
256            }
257    
258            if(maxBaseDistance > UNBOUNDED_MAX) {
259                buffer.append( ' ' );
260                buffer.append( "maximum" );
261                buffer.append( ' ' );
262                buffer.append( maxBaseDistance );
263                buffer.append( ',' );
264            }
265    
266            if ( !chopBefore.isEmpty() || !chopAfter.isEmpty() )
267            {
268                buffer.append( ' ' );
269                buffer.append( "specificExclusions" );
270                buffer.append( ' ' );
271                buffer.append( '{' );
272    
273                for ( Iterator<DN> it = chopBefore.iterator(); it.hasNext(); )
274                {
275                    DN dn = it.next();
276                    buffer.append( ' ' );
277                    buffer.append( "chopBefore" );
278                    buffer.append( ':' );
279                    buffer.append( ' ' );
280                    buffer.append( '"' );
281                    buffer.append( dn.getName() );
282                    buffer.append( '"' );
283    
284                    if(it.hasNext())
285                    {
286                        buffer.append( ',' );
287                        buffer.append( ' ' );
288                    }
289                }
290    
291                if ( !chopBefore.isEmpty() && !chopAfter.isEmpty() )
292                {
293                    buffer.append( ',' );
294                    buffer.append( ' ' );
295                }
296    
297                for ( Iterator<DN> it = chopAfter.iterator(); it.hasNext(); )
298                {
299                    DN dn = it.next();
300                    buffer.append( ' ' );
301                    buffer.append( "chopAfter" );
302                    buffer.append( ':' );
303                    buffer.append( ' ' );
304                    buffer.append( '"' );
305                    buffer.append( dn.getName() );
306                    buffer.append( '"' );
307    
308                    if(it.hasNext())
309                    {
310                        buffer.append( ',' );
311                        buffer.append( ' ' );
312                    }
313                }
314    
315                buffer.append( ' ' );
316                buffer.append( '}' );
317    
318                buffer.append( ',' );
319            }
320    
321            if ( refinement != null )
322            {
323                buffer.append( ' ' );
324                buffer.append( "specificationFilter" );
325                buffer.append( ' ' );
326    
327                // The ExprNode could represent both, a refinement
328                // or a filter. First we try to print the ExprNode
329                // as refinement. If that fails it is printed as
330                // LDAP filter.
331                try
332                {
333                    // Must use a tempBuffer here because the 
334                    // exception could occur after some characters
335                    // were added to the buffer.
336                    StringBuilder tempBuffer = new StringBuilder();
337                    refinement.printRefinementToBuffer( tempBuffer );
338                    buffer.append( tempBuffer );
339                }
340                catch ( UnsupportedOperationException e )
341                {
342                    buffer.append( refinement.toString() );
343                }
344    
345                buffer.append( ',' );
346            }
347    
348            if(buffer.charAt( buffer.length()-1 ) == ',') {
349                buffer.deleteCharAt( buffer.length()-1 );
350            }
351    
352            buffer.append( ' ' );
353            buffer.append( '}' );
354        }
355    
356    
357    }