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.api.subtree;
21  
22  
23  import org.apache.directory.api.ldap.model.entry.Entry;
24  import org.apache.directory.api.ldap.model.exception.LdapException;
25  import org.apache.directory.api.ldap.model.name.Dn;
26  import org.apache.directory.api.ldap.model.schema.SchemaManager;
27  import org.apache.directory.api.ldap.model.subtree.SubtreeSpecification;
28  import org.apache.directory.server.core.api.event.Evaluator;
29  import org.apache.directory.server.core.api.event.ExpressionEvaluator;
30  
31  
32  /**
33   * An evaluator used to determine if an entry is included in the collection
34   * represented by a subtree specification.
35   *
36   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
37   */
38  public class SubtreeEvaluator
39  {
40      /** A refinement filter evaluator */
41      private final Evaluator 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 schemaManager The server schemaManager
49       */
50      public SubtreeEvaluator( SchemaManager schemaManager )
51      {
52          evaluator = new ExpressionEvaluator( schemaManager );
53      }
54  
55  
56      /**
57       * Determines if an entry is selected by a subtree specification.
58       *
59       * @param subtree the subtree specification
60       * @param apDn the distinguished name of the administrative point containing the subentry
61       * @param entryDn the distinguished name of the candidate entry
62       * @param entry The entry to evaluate
63       * @return true if the entry is selected by the specification, false if it is not
64       * @throws LdapException if errors are encountered while evaluating selection
65       */
66      public boolean evaluate( SubtreeSpecification subtree, Dn apDn, Dn entryDn, Entry entry )
67          throws LdapException
68      {
69          /* =====================================================================
70           * NOTE: Regarding the overall approach, we try to narrow down the
71           * possibilities by slowly pruning relative names off of the entryDn.
72           * For example we check first if the entry is a descendant of the AP.
73           * If so we use the relative name thereafter to calculate if it is
74           * a descendant of the base. This means shorter names to compare and
75           * less work to do while we continue to deduce inclusion by the subtree
76           * specification.
77           * =====================================================================
78           */
79          // First construct the subtree base, which is the concatenation of the
80          // AP Dn and the subentry base
81          Dn subentryBaseDn = apDn;
82          subentryBaseDn = subentryBaseDn.add( subtree.getBase() );
83  
84          if ( !entryDn.isDescendantOf( subentryBaseDn ) )
85          {
86              // The entry Dn is not part of the subtree specification, get out
87              return false;
88          }
89  
90          /*
91           * Evaluate based on minimum and maximum chop values.  Here we simply
92           * need to compare the distances respectively with the size of the
93           * baseRelativeRdn.  For the max distance entries with a baseRelativeRdn
94           * size greater than the max distance are rejected.  For the min distance
95           * entries with a baseRelativeRdn size less than the minimum distance
96           * are rejected.
97           */
98          int entryRelativeDnSize = entryDn.size() - subentryBaseDn.size();
99  
100         if ( ( subtree.getMaxBaseDistance() != SubtreeSpecification.UNBOUNDED_MAX )
101             && ( entryRelativeDnSize > subtree.getMaxBaseDistance() ) )
102         {
103             return false;
104         }
105 
106         if ( ( subtree.getMinBaseDistance() > 0 ) && ( entryRelativeDnSize < subtree.getMinBaseDistance() ) )
107         {
108             return false;
109         }
110 
111         /*
112          * For specific exclusions we must iterate through the set and check
113          * if the baseRelativeRdn is a descendant of the exclusion.  The
114          * isDescendant() function will return true if the compared names
115          * are equal so for chopAfter exclusions we must check for equality
116          * as well and reject if the relative names are equal.
117          */
118         // Now, get the entry's relative part
119 
120         if ( !subtree.getChopBeforeExclusions().isEmpty() || !subtree.getChopAfterExclusions().isEmpty() )
121         {
122             Dn entryRelativeDn = entryDn.getDescendantOf( apDn ).getDescendantOf( subtree.getBase() );
123 
124             for ( Dn chopBeforeDn : subtree.getChopBeforeExclusions() )
125             {
126                 if ( entryRelativeDn.isDescendantOf( chopBeforeDn ) )
127                 {
128                     return false;
129                 }
130             }
131 
132             for ( Dn chopAfterDn : subtree.getChopAfterExclusions() )
133             {
134                 if ( entryRelativeDn.isDescendantOf( chopAfterDn ) && !chopAfterDn.equals( entryRelativeDn ) )
135                 {
136                     return false;
137                 }
138             }
139         }
140 
141         /*
142          * The last remaining step is to check and see if the refinement filter
143          * selects the entry candidate based on objectClass attribute values.
144          * To do this we invoke the refinement evaluator members evaluate() method.
145          */
146         if ( subtree.getRefinement() != null )
147         {
148             return evaluator.evaluate( subtree.getRefinement(), entryDn, entry );
149         }
150 
151         /*
152          * If nothing has rejected the candidate entry and there is no refinement
153          * filter then the entry is included in the collection represented by the
154          * subtree specification so we return true.
155          */
156         return true;
157     }
158 }