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 */
020 package org.apache.directory.server.core.subtree;
021
022
023 import java.util.Iterator;
024
025 import org.apache.directory.server.core.event.Evaluator;
026 import org.apache.directory.server.core.event.ExpressionEvaluator;
027 import org.apache.directory.shared.ldap.entry.ServerEntry;
028 import org.apache.directory.shared.ldap.exception.LdapException;
029 import org.apache.directory.shared.ldap.name.DN;
030 import org.apache.directory.shared.ldap.schema.SchemaManager;
031 import org.apache.directory.shared.ldap.schema.registries.OidRegistry;
032 import org.apache.directory.shared.ldap.subtree.SubtreeSpecification;
033 import org.apache.directory.shared.ldap.util.NamespaceTools;
034
035
036 /**
037 * An evaluator used to determine if an entry is included in the collection
038 * represented by a subtree specification.
039 *
040 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
041 * @version $Rev: 927146 $
042 */
043 public class SubtreeEvaluator
044 {
045 /** A refinement filter evaluator */
046 private final Evaluator evaluator;
047
048
049 /**
050 * Creates a subtreeSpecification evaluatior which can be used to determine
051 * if an entry is included within the collection of a subtree.
052 *
053 * @param oidRegistry a registry used to lookup objectClass names for OIDs
054 * @param attrRegistry registry to be looked up
055 */
056 public SubtreeEvaluator( OidRegistry oidRegistry, SchemaManager schemaManager )
057 {
058 evaluator = new ExpressionEvaluator( oidRegistry, schemaManager );
059 }
060
061
062 /**
063 * Determines if an entry is selected by a subtree specification.
064 *
065 * @param subtree the subtree specification
066 * @param apDn the distinguished name of the administrative point containing the subentry
067 * @param entryDn the distinguished name of the candidate entry
068 * @return true if the entry is selected by the specification, false if it is not
069 * @throws LdapException if errors are encountered while evaluating selection
070 */
071 public boolean evaluate( SubtreeSpecification subtree, DN apDn, DN entryDn, ServerEntry entry )
072 throws LdapException
073 {
074 // TODO: Try to make this cast unnecessary.
075 DN dnEntryDn = (DN) entryDn;
076
077 /* =====================================================================
078 * NOTE: Regarding the overall approach, we try to narrow down the
079 * possibilities by slowly pruning relative names off of the entryDn.
080 * For example we check first if the entry is a descendant of the AP.
081 * If so we use the relative name thereafter to calculate if it is
082 * a descendant of the base. This means shorter names to compare and
083 * less work to do while we continue to deduce inclusion by the subtree
084 * specification.
085 * =====================================================================
086 */
087
088 /*
089 * First we simply check if the candidate entry is a descendant of the
090 * administrative point. In the process we calculate the relative
091 * distinguished name relative to the administrative point.
092 */
093 DN apRelativeRdn;
094
095 if ( !NamespaceTools.isDescendant( apDn, entryDn ) )
096 {
097 return false;
098 }
099 else if ( apDn.equals( entryDn ) )
100 {
101 apRelativeRdn = new DN();
102 }
103 else
104 {
105 apRelativeRdn = NamespaceTools.getRelativeName( apDn, entryDn );
106 }
107
108 /*
109 * We do the same thing with the base as we did with the administrative
110 * point: check if the entry is a descendant of the base and find the
111 * relative name of the entry with respect to the base rdn. With the
112 * baseRelativeRdn we can later make comparisons with specific exclusions.
113 */
114 DN baseRelativeRdn;
115
116 if ( subtree.getBase() != null && subtree.getBase().size() == 0 )
117 {
118 baseRelativeRdn = apRelativeRdn;
119 }
120 else if ( apRelativeRdn.equals( subtree.getBase() ) )
121 {
122 baseRelativeRdn = new DN();
123 }
124 else if ( !NamespaceTools.isDescendant( subtree.getBase(), apRelativeRdn ) )
125 {
126 return false;
127 }
128 else
129 {
130 baseRelativeRdn = NamespaceTools.getRelativeName( subtree.getBase(), apRelativeRdn );
131 }
132
133 /*
134 * Evaluate based on minimum and maximum chop values. Here we simply
135 * need to compare the distances respectively with the size of the
136 * baseRelativeRdn. For the max distance entries with a baseRelativeRdn
137 * size greater than the max distance are rejected. For the min distance
138 * entries with a baseRelativeRdn size less than the minimum distance
139 * are rejected.
140 */
141 if ( subtree.getMaxBaseDistance() != SubtreeSpecification.UNBOUNDED_MAX )
142 {
143 if ( subtree.getMaxBaseDistance() < baseRelativeRdn.size() )
144 {
145 return false;
146 }
147 }
148
149 if ( subtree.getMinBaseDistance() > 0 )
150 {
151 if ( baseRelativeRdn.size() < subtree.getMinBaseDistance() )
152 {
153 return false;
154 }
155 }
156
157 /*
158 * For specific exclusions we must iterate through the set and check
159 * if the baseRelativeRdn is a descendant of the exclusion. The
160 * isDescendant() function will return true if the compared names
161 * are equal so for chopAfter exclusions we must check for equality
162 * as well and reject if the relative names are equal.
163 */
164 Iterator list = subtree.getChopBeforeExclusions().iterator();
165
166 while ( list.hasNext() )
167 {
168 DN chopBefore = ( DN ) list.next();
169
170 if ( NamespaceTools.isDescendant( chopBefore, baseRelativeRdn ) )
171 {
172 return false;
173 }
174 }
175
176 list = subtree.getChopAfterExclusions().iterator();
177
178 while ( list.hasNext() )
179 {
180 DN chopAfter = ( DN ) list.next();
181
182 if ( NamespaceTools.isDescendant( chopAfter, baseRelativeRdn ) && !chopAfter.equals( baseRelativeRdn ) )
183 {
184 return false;
185 }
186 }
187
188 /*
189 * The last remaining step is to check and see if the refinement filter
190 * selects the entry candidate based on objectClass attribute values.
191 * To do this we invoke the refinement evaluator members evaluate() method.
192 */
193 if ( subtree.getRefinement() != null )
194 {
195 return evaluator.evaluate( subtree.getRefinement(), dnEntryDn.getNormName(), entry );
196 }
197
198 /*
199 * If nothing has rejected the candidate entry and there is no refinement
200 * filter then the entry is included in the collection represented by the
201 * subtree specification so we return true.
202 */
203 return true;
204 }
205 }