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 */
020package org.apache.directory.shared.ldap.model.subtree;
021
022
023import java.util.Collections;
024import java.util.Set;
025
026import org.apache.directory.shared.ldap.model.filter.ExprNode;
027import org.apache.directory.shared.ldap.model.name.Dn;
028
029
030/**
031 * A simple implementation of the SubtreeSpecification interface.
032 *
033 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
034 */
035public class BaseSubtreeSpecification implements SubtreeSpecification
036{
037    /** the subtree base relative to the administration point */
038    private final Dn base;
039
040    /** the set of subordinates entries and their subordinates to exclude */
041    private final Set<Dn> chopBefore;
042
043    /** the set of subordinates entries whose subordinates are to be excluded */
044    private final Set<Dn> chopAfter;
045
046    /** the minimum distance below base to start including entries */
047    private final int minBaseDistance;
048
049    /** the maximum distance from base past which entries are excluded */
050    private final int maxBaseDistance;
051
052    /**
053     * a filter using only assertions on objectClass attributes for subtree
054     * refinement
055     */
056    private final ExprNode refinement;
057
058
059    // -----------------------------------------------------------------------
060    // C O N S T R U C T O R S
061    // -----------------------------------------------------------------------
062
063    /**
064     * Creates a simple subtree whose administrative point is necessarily the
065     * base and all subordinates underneath (excluding those that are part of
066     * inner areas) are part of the the subtree.
067     */
068    @SuppressWarnings("unchecked")
069    public BaseSubtreeSpecification()
070    {
071        this.base = new Dn();
072        this.minBaseDistance = 0;
073        this.maxBaseDistance = UNBOUNDED_MAX;
074        this.chopAfter = Collections.EMPTY_SET;
075        this.chopBefore = Collections.EMPTY_SET;
076        this.refinement = null;
077    }
078
079
080    /**
081     * Creates a simple subtree refinement whose administrative point is
082     * necessarily the base and only those subordinates selected by the
083     * refinement filter are included.
084     *
085     * @param refinement the filter expression only composed of objectClass attribute
086     *  value assertions
087     */
088    @SuppressWarnings("unchecked")
089    public BaseSubtreeSpecification( ExprNode refinement )
090    {
091        this.base = new Dn();
092        this.minBaseDistance = 0;
093        this.maxBaseDistance = UNBOUNDED_MAX;
094        this.chopAfter = Collections.EMPTY_SET;
095        this.chopBefore = Collections.EMPTY_SET;
096        this.refinement = refinement;
097    }
098
099
100    /**
101     * Creates a simple subtree whose administrative point above the base and
102     * all subordinates underneath the base (excluding those that are part of
103     * inner areas) are part of the the subtree.
104     *
105     * @param base the base of the subtree relative to the administrative point
106     */
107    @SuppressWarnings("unchecked")
108    public BaseSubtreeSpecification( Dn base )
109    {
110        this.base = base;
111        this.minBaseDistance = 0;
112        this.maxBaseDistance = UNBOUNDED_MAX;
113        this.chopAfter = Collections.EMPTY_SET;
114        this.chopBefore = Collections.EMPTY_SET;
115        this.refinement = null;
116    }
117
118
119    /**
120     * Creates a subtree without a refinement filter where all other aspects can
121     * be varied.
122     *
123     * @param base the base of the subtree relative to the administrative point
124     * @param minBaseDistance the minimum distance below base to start including entries
125     * @param maxBaseDistance the maximum distance from base past which entries are excluded
126     * @param chopAfter the set of subordinates entries whose subordinates are to be
127     *  excluded
128     * @param chopBefore the set of subordinates entries and their subordinates to
129     * exclude
130     */
131    public BaseSubtreeSpecification( Dn base, int minBaseDistance, int maxBaseDistance,
132        Set<Dn> chopAfter, Set<Dn> chopBefore )
133    {
134        this( base, minBaseDistance, maxBaseDistance, chopAfter, chopBefore, null );
135    }
136
137
138    /**
139     * Creates a subtree which may be a refinement filter where all aspects of
140     * the specification can be set. If the refinement filter is null this
141     * defaults to {@link #BaseSubtreeSpecification(org.apache.directory.shared.ldap.model.name.Dn, int, int, Set, Set)}.
142     *
143     * @param base the base of the subtree relative to the administrative point
144     * @param minBaseDistance the minimum distance below base to start including entries
145     * @param maxBaseDistance the maximum distance from base past which entries are excluded
146     * @param chopAfter the set of subordinates entries whose subordinates are to be
147     * excluded
148     * @param chopBefore the set of subordinates entries and their subordinates to
149     * exclude
150     * @param refinement the filter expression only composed of objectClass attribute
151     * value assertions
152     */
153    public BaseSubtreeSpecification( Dn base, int minBaseDistance, int maxBaseDistance,
154        Set<Dn> chopAfter, Set<Dn> chopBefore, ExprNode refinement )
155    {
156        this.base = base;
157        this.minBaseDistance = minBaseDistance;
158
159        if ( maxBaseDistance < 0 )
160        {
161            this.maxBaseDistance = UNBOUNDED_MAX;
162        }
163        else
164        {
165            this.maxBaseDistance = maxBaseDistance;
166        }
167
168        this.chopAfter = chopAfter;
169        this.chopBefore = chopBefore;
170        this.refinement = refinement;
171    }
172
173
174    // -----------------------------------------------------------------------
175    // A C C E S S O R S
176    // -----------------------------------------------------------------------
177    /**
178     * @return The base
179     */
180    public Dn getBase()
181    {
182        return this.base;
183    }
184
185    /**
186     * @return The set of ChopBefore exclusions
187     */
188    public Set<Dn> getChopBeforeExclusions()
189    {
190        return this.chopBefore;
191    }
192
193
194    /**
195     * @return The set of ChopAfter exclusions
196     */
197    public Set<Dn> getChopAfterExclusions()
198    {
199        return this.chopAfter;
200    }
201
202
203    /**
204     * @return The mimimum distance from the base
205     */
206    public int getMinBaseDistance()
207    {
208        return this.minBaseDistance;
209    }
210
211
212    /**
213     * @return The maximum distance from the base
214     */
215    public int getMaxBaseDistance()
216    {
217        return this.maxBaseDistance;
218    }
219
220
221    /**
222     * @return The refinement
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 toString( StringBuilder buffer )
237    {
238        buffer.append( toString() );
239    }
240
241
242    /**
243     * @see Object#toString()
244     */
245    public String toString()
246    {
247        StringBuilder buffer = new StringBuilder();
248        boolean isFirst = true;
249        buffer.append( '{' );
250
251        // The base
252        if ( !base.isEmpty() ) 
253        {
254            buffer.append( " base \"" );
255            buffer.append( base.getName() );
256            buffer.append( '"' );
257            isFirst = false;
258        }
259
260        // The minimum
261        if ( minBaseDistance > 0 ) 
262        {
263            if ( isFirst )
264            {
265                isFirst = false;
266                buffer.append( " " );
267            }
268            else
269            {
270                buffer.append( ", " );
271            }
272            
273            buffer.append( "minimum " );
274            buffer.append( minBaseDistance );
275        }
276
277        // The maximum
278        if ( maxBaseDistance > UNBOUNDED_MAX ) 
279        {
280            if ( isFirst )
281            {
282                isFirst = false;
283                buffer.append( " " );
284            }
285            else
286            {
287                buffer.append( ", " );
288            }
289            
290            buffer.append( "maximum " );
291            buffer.append( maxBaseDistance );
292        }
293
294        // The chopBefore exclusions
295        if ( !chopBefore.isEmpty() || !chopAfter.isEmpty() )
296        {
297            if ( isFirst )
298            {
299                isFirst = false;
300                buffer.append( " " );
301            }
302            else
303            {
304                buffer.append( ", " );
305            }
306            
307            buffer.append( "specificExclusions { " );
308            
309            boolean isFirstExclusion = true;
310
311            if ( chopBefore != null )
312            {
313                for ( Dn exclusion : chopBefore )
314                {
315                    if ( isFirstExclusion )
316                    {
317                        isFirstExclusion = false;
318                    }
319                    else
320                    {
321                        buffer.append( ", " );
322                    }
323                    
324                    buffer.append( "chopBefore: \"" );
325                    buffer.append( exclusion.getName() );
326                    buffer.append( '"' );
327                }
328            }
329
330            if ( chopAfter != null )
331            {
332                for ( Dn exclusion : chopAfter )
333                {
334                    if ( isFirstExclusion )
335                    {
336                        isFirstExclusion = false;
337                    }
338                    else
339                    {
340                        buffer.append( ", " );
341                    }
342                    
343                    buffer.append( "chopAfter: \"" );
344                    buffer.append( exclusion.getName() );
345                    buffer.append( '"' );
346                }
347            }
348
349            buffer.append( " }" );
350        }
351
352        if ( refinement != null )
353        {
354            if ( isFirst )
355            {
356                isFirst = false;
357                buffer.append( " " );
358            }
359            else
360            {
361                buffer.append( ", " );
362            }
363            
364            buffer.append( "specificationFilter " );
365            buffer.append( refinement.toString() );
366        }
367
368        buffer.append( " }" );
369        
370        return buffer.toString();
371    }
372}