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