View Javadoc

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