View Javadoc

1   /*
2    *   Copyright 2004 The Apache Software Foundation
3    *
4    *   Licensed under the Apache License, Version 2.0 (the "License");
5    *   you may not use this file except in compliance with the License.
6    *   You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *   Unless required by applicable law or agreed to in writing, software
11   *   distributed under the License is distributed on an "AS IS" BASIS,
12   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *   See the License for the specific language governing permissions and
14   *   limitations under the License.
15   *
16   */
17  package org.apache.ldap.server.subtree;
18  
19  
20  import org.apache.ldap.common.util.NamespaceTools;
21  import org.apache.ldap.common.name.LdapName;
22  import org.apache.ldap.common.subtree.SubtreeSpecification;
23  import org.apache.ldap.server.schema.OidRegistry;
24  
25  import javax.naming.Name;
26  import javax.naming.NamingException;
27  import javax.naming.directory.Attribute;
28  import java.util.Iterator;
29  
30  
31  /***
32   * An evaluator used to determine if an entry is included in the collection
33   * represented by a subtree specification.
34   *
35   * @author <a href="mailto:directory-dev@incubator.apache.org">Apache Directory Project</a>
36   * @version $Rev$
37   */
38  public class SubtreeEvaluator
39  {
40      /*** A refinement filter evaluator */
41      private final RefinementEvaluator evaluator;
42  
43  
44      /***
45       * Creates a subtreeSpecification evaluatior which can be used to determine
46       * if an entry is included within the collection of a subtree.
47       *
48       * @param registry a registry used to lookup objectClass names for OIDs
49       */
50      public SubtreeEvaluator( OidRegistry registry )
51      {
52          RefinementLeafEvaluator leafEvaluator = new RefinementLeafEvaluator( registry );
53          evaluator = new RefinementEvaluator( leafEvaluator );
54      }
55  
56  
57      /***
58       * Determines if an entry is selected by a subtree specification.
59       *
60       * @param subtree the subtree specification
61       * @param apDn the distinguished name of the administrative point containing the subentry
62       * @param entryDn the distinguished name of the candidate entry
63       * @param objectClasses the objectClasses of the candidate entry
64       * @return true if the entry is selected by the specification, false if it is not
65       * @throws javax.naming.NamingException if errors are encountered while evaluating selection
66       */
67      public boolean evaluate( SubtreeSpecification subtree, Name apDn, Name entryDn, Attribute objectClasses )
68              throws NamingException
69      {
70         /* =====================================================================
71          * NOTE: Regarding the overall approach, we try to narrow down the
72          * possibilities by slowly pruning relative names off of the entryDn.
73          * For example we check first if the entry is a descendant of the AP.
74          * If so we use the relative name thereafter to calculate if it is
75          * a descendant of the base.  This means shorter names to compare and
76          * less work to do while we continue to deduce inclusion by the subtree
77          * specification.
78          * =====================================================================
79          */
80  
81         /*
82          * First we simply check if the candidate entry is a descendant of the
83          * administrative point.  In the process we calculate the relative
84          * distinguished name relative to the administrative point.
85          */
86          Name apRelativeRdn;
87          if ( ! NamespaceTools.isDescendant( apDn, entryDn ) )
88          {
89              return false;
90          }
91          else if ( apDn.equals( entryDn ) )
92          {
93              apRelativeRdn = new LdapName();
94          }
95          else
96          {
97              apRelativeRdn = NamespaceTools.getRelativeName( apDn, entryDn );
98          }
99  
100        /*
101         * We do the same thing with the base as we did with the administrative
102         * point: check if the entry is a descendant of the base and find the
103         * relative name of the entry with respect to the base rdn.  With the
104         * baseRelativeRdn we can later make comparisons with specific exclusions.
105         */
106         Name baseRelativeRdn;
107         if ( subtree.getBase() != null && subtree.getBase().size() == 0 )
108         {
109             baseRelativeRdn = apRelativeRdn;
110         }
111         else if ( apRelativeRdn.equals( subtree.getBase() ) )
112         {
113             baseRelativeRdn = new LdapName();
114         }
115         else if ( ! NamespaceTools.isDescendant( subtree.getBase(), apRelativeRdn ) )
116         {
117             return false;
118         }
119         else
120         {
121             baseRelativeRdn = NamespaceTools.getRelativeName( subtree.getBase(), apRelativeRdn );
122         }
123 
124         /*
125          * Evaluate based on minimum and maximum chop values.  Here we simply
126          * need to compare the distances respectively with the size of the
127          * baseRelativeRdn.  For the max distance entries with a baseRelativeRdn
128          * size greater than the max distance are rejected.  For the min distance
129          * entries with a baseRelativeRdn size less than the minimum distance
130          * are rejected.
131          */
132         if ( subtree.getMaxBaseDistance() != SubtreeSpecification.UNBOUNDED_MAX )
133         {
134             if ( subtree.getMaxBaseDistance() < baseRelativeRdn.size() )
135             {
136                 return false;
137             }
138         }
139 
140         if ( subtree.getMinBaseDistance() > 0 )
141         {
142             if ( baseRelativeRdn.size() < subtree.getMinBaseDistance() )
143             {
144                 return false;
145             }
146         }
147 
148         /*
149          * For specific exclusions we must iterate through the set and check
150          * if the baseRelativeRdn is a descendant of the exclusion.  The
151          * isDescendant() function will return true if the compared names
152          * are equal so for chopAfter exclusions we must check for equality
153          * as well and reject if the relative names are equal.
154          */
155         Iterator list = subtree.getChopBeforeExclusions().iterator();
156         while ( list.hasNext() )
157         {
158             Name chopBefore = ( Name ) list.next();
159             if ( NamespaceTools.isDescendant( chopBefore, baseRelativeRdn ) )
160             {
161                 return false;
162             }
163         }
164 
165         list = subtree.getChopAfterExclusions().iterator();
166         while ( list.hasNext() )
167         {
168             Name chopAfter = ( Name ) list.next();
169             if ( NamespaceTools.isDescendant( chopAfter, baseRelativeRdn ) && ! chopAfter.equals( baseRelativeRdn ) )
170             {
171                 return false;
172             }
173         }
174 
175         /*
176          * The last remaining step is to check and see if the refinement filter
177          * selects the entry candidate based on objectClass attribute values.
178          * To do this we invoke the refinement evaluator members evaluate() method.
179          */
180         if ( subtree.getRefinement() != null )
181         {
182             return evaluator.evaluate( subtree.getRefinement(), objectClasses );
183         }
184 
185         /*
186          * If nothing has rejected the candidate entry and there is no refinement
187          * filter then the entry is included in the collection represented by the
188          * subtree specification so we return true.
189          */
190         return true;
191     }
192 }