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.api.ldap.model.subtree;
021
022
023import java.util.Collections;
024import java.util.Set;
025
026import org.apache.directory.api.ldap.model.filter.ExprNode;
027import org.apache.directory.api.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.api.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    /**
187     * @return The set of ChopBefore exclusions
188     */
189    public Set<Dn> getChopBeforeExclusions()
190    {
191        return this.chopBefore;
192    }
193
194
195    /**
196     * @return The set of ChopAfter exclusions
197     */
198    public Set<Dn> getChopAfterExclusions()
199    {
200        return this.chopAfter;
201    }
202
203
204    /**
205     * @return The mimimum distance from the base
206     */
207    public int getMinBaseDistance()
208    {
209        return this.minBaseDistance;
210    }
211
212
213    /**
214     * @return The maximum distance from the base
215     */
216    public int getMaxBaseDistance()
217    {
218        return this.maxBaseDistance;
219    }
220
221
222    /**
223     * @return The refinement
224     */
225    public ExprNode getRefinement()
226    {
227        return this.refinement;
228    }
229
230
231    /**
232     * Converts this item into its string representation as stored
233     * in directory.
234     *
235     * @param buffer the string buffer
236     */
237    public void toString( StringBuilder buffer )
238    {
239        buffer.append( toString() );
240    }
241
242
243    /**
244     * @see Object#toString()
245     */
246    public String toString()
247    {
248        StringBuilder buffer = new StringBuilder();
249        boolean isFirst = true;
250        buffer.append( '{' );
251
252        // The base
253        if ( !base.isEmpty() )
254        {
255            buffer.append( " base \"" );
256            buffer.append( base.getName() );
257            buffer.append( '"' );
258            isFirst = false;
259        }
260
261        // The minimum
262        if ( minBaseDistance > 0 )
263        {
264            if ( isFirst )
265            {
266                isFirst = false;
267                buffer.append( " " );
268            }
269            else
270            {
271                buffer.append( ", " );
272            }
273
274            buffer.append( "minimum " );
275            buffer.append( minBaseDistance );
276        }
277
278        // The maximum
279        if ( maxBaseDistance > UNBOUNDED_MAX )
280        {
281            if ( isFirst )
282            {
283                isFirst = false;
284                buffer.append( " " );
285            }
286            else
287            {
288                buffer.append( ", " );
289            }
290
291            buffer.append( "maximum " );
292            buffer.append( maxBaseDistance );
293        }
294
295        // The chopBefore exclusions
296        if ( !chopBefore.isEmpty() || !chopAfter.isEmpty() )
297        {
298            if ( isFirst )
299            {
300                isFirst = false;
301                buffer.append( " " );
302            }
303            else
304            {
305                buffer.append( ", " );
306            }
307
308            buffer.append( "specificExclusions { " );
309
310            boolean isFirstExclusion = true;
311
312            if ( chopBefore != null )
313            {
314                for ( Dn exclusion : chopBefore )
315                {
316                    if ( isFirstExclusion )
317                    {
318                        isFirstExclusion = false;
319                    }
320                    else
321                    {
322                        buffer.append( ", " );
323                    }
324
325                    buffer.append( "chopBefore: \"" );
326                    buffer.append( exclusion.getName() );
327                    buffer.append( '"' );
328                }
329            }
330
331            if ( chopAfter != null )
332            {
333                for ( Dn exclusion : chopAfter )
334                {
335                    if ( isFirstExclusion )
336                    {
337                        isFirstExclusion = false;
338                    }
339                    else
340                    {
341                        buffer.append( ", " );
342                    }
343
344                    buffer.append( "chopAfter: \"" );
345                    buffer.append( exclusion.getName() );
346                    buffer.append( '"' );
347                }
348            }
349
350            buffer.append( " }" );
351        }
352
353        if ( refinement != null )
354        {
355            if ( isFirst )
356            {
357                isFirst = false;
358                buffer.append( " " );
359            }
360            else
361            {
362                buffer.append( ", " );
363            }
364
365            buffer.append( "specificationFilter " );
366            buffer.append( refinement.toString() );
367        }
368
369        buffer.append( " }" );
370
371        return buffer.toString();
372    }
373}