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.event;
021
022
023 import java.util.Comparator;
024
025 import org.apache.directory.server.i18n.I18n;
026 import org.apache.directory.shared.ldap.NotImplementedException;
027 import org.apache.directory.shared.ldap.entry.StringValue;
028 import org.apache.directory.shared.ldap.entry.EntryAttribute;
029 import org.apache.directory.shared.ldap.entry.ServerEntry;
030 import org.apache.directory.shared.ldap.entry.Value;
031 import org.apache.directory.shared.ldap.exception.LdapException;
032 import org.apache.directory.shared.ldap.exception.LdapInvalidSearchFilterException;
033 import org.apache.directory.shared.ldap.filter.ApproximateNode;
034 import org.apache.directory.shared.ldap.filter.EqualityNode;
035 import org.apache.directory.shared.ldap.filter.ExprNode;
036 import org.apache.directory.shared.ldap.filter.ExtensibleNode;
037 import org.apache.directory.shared.ldap.filter.GreaterEqNode;
038 import org.apache.directory.shared.ldap.filter.LessEqNode;
039 import org.apache.directory.shared.ldap.filter.PresenceNode;
040 import org.apache.directory.shared.ldap.filter.ScopeNode;
041 import org.apache.directory.shared.ldap.filter.SimpleNode;
042 import org.apache.directory.shared.ldap.filter.SubstringNode;
043 import org.apache.directory.shared.ldap.schema.AttributeType;
044 import org.apache.directory.shared.ldap.schema.LdapComparator;
045 import org.apache.directory.shared.ldap.schema.MatchingRule;
046 import org.apache.directory.shared.ldap.schema.Normalizer;
047 import org.apache.directory.shared.ldap.schema.SchemaManager;
048
049
050 /**
051 * Evaluates LeafNode assertions on candidates using a database.
052 *
053 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
054 * @version $Rev: 928945 $
055 */
056 public class LeafEvaluator implements Evaluator
057 {
058 /** equality matching type constant */
059 private static final int EQUALITY_MATCH = 0;
060
061 /** ordering matching type constant */
062 private static final int ORDERING_MATCH = 1;
063
064 /** substring matching type constant */
065 private static final int SUBSTRING_MATCH = 3;
066
067 /** SchemaManager needed for normalizing and comparing values */
068 private SchemaManager schemaManager;
069
070 /** Substring node evaluator we depend on */
071 private SubstringEvaluator substringEvaluator;
072
073 /** ScopeNode evaluator we depend on */
074 private ScopeEvaluator scopeEvaluator;
075
076 /** Constants used for comparisons */
077 private static final boolean COMPARE_GREATER = true;
078 private static final boolean COMPARE_LESSER = false;
079
080
081 /**
082 * Creates a leaf expression node evaluator.
083 *
084 * @param substringEvaluator
085 */
086 public LeafEvaluator( SchemaManager schemaManager,
087 SubstringEvaluator substringEvaluator )
088 {
089 this.schemaManager = schemaManager;
090 this.scopeEvaluator = new ScopeEvaluator();
091 this.substringEvaluator = substringEvaluator;
092 }
093
094
095 public ScopeEvaluator getScopeEvaluator()
096 {
097 return scopeEvaluator;
098 }
099
100
101 public SubstringEvaluator getSubstringEvaluator()
102 {
103 return substringEvaluator;
104 }
105
106
107 /**
108 * @see Evaluator#evaluate(ExprNode, String, ServerEntry)
109 */
110 public boolean evaluate( ExprNode node, String dn, ServerEntry entry ) throws LdapException
111 {
112 if ( node instanceof ScopeNode )
113 {
114 return scopeEvaluator.evaluate( node, dn, entry );
115 }
116
117 if ( node instanceof PresenceNode )
118 {
119 String attrId = ( ( PresenceNode ) node ).getAttribute();
120 return evalPresence( attrId, entry );
121 }
122 else if ( ( node instanceof EqualityNode ) || ( node instanceof ApproximateNode ) )
123 {
124 return evalEquality( ( EqualityNode<?> ) node, entry );
125 }
126 else if ( node instanceof GreaterEqNode )
127 {
128 return evalGreaterOrLesser( ( GreaterEqNode<?> ) node, entry, COMPARE_GREATER );
129 }
130 else if ( node instanceof LessEqNode )
131 {
132 return evalGreaterOrLesser( ( LessEqNode<?> ) node, entry, COMPARE_LESSER );
133 }
134 else if ( node instanceof SubstringNode )
135 {
136 return substringEvaluator.evaluate( node, dn, entry );
137 }
138 else if ( node instanceof ExtensibleNode )
139 {
140 throw new NotImplementedException();
141 }
142 else
143 {
144 throw new LdapInvalidSearchFilterException( I18n.err( I18n.ERR_245, node ) );
145 }
146 }
147
148
149 /**
150 * Evaluates a simple greater than or less than attribute value assertion on
151 * a perspective candidate.
152 *
153 * @param node the greater than or less than node to evaluate
154 * @param entry the perspective candidate
155 * @param isGreater true if it is a greater than or equal to comparison,
156 * false if it is a less than or equal to comparison.
157 * @return the ava evaluation on the perspective candidate
158 * @throws LdapException if there is a database access failure
159 */
160 @SuppressWarnings("unchecked")
161 private boolean evalGreaterOrLesser( SimpleNode<?> node, ServerEntry entry, boolean isGreaterOrLesser )
162 throws LdapException
163 {
164 String attrId = node.getAttribute();
165
166 // get the attribute associated with the node
167 AttributeType type = schemaManager.lookupAttributeTypeRegistry( attrId );
168 EntryAttribute attr = entry.get( type );
169
170 // If we do not have the attribute just return false
171 if ( null == attr )
172 {
173 return false;
174 }
175
176 /*
177 * We need to iterate through all values and for each value we normalize
178 * and use the comparator to determine if a match exists.
179 */
180 Normalizer normalizer = getNormalizer( attrId );
181 Comparator comparator = getComparator( attrId );
182 Object filterValue = normalizer.normalize( node.getValue() );
183
184 /*
185 * Cheaper to not check isGreater in one loop - better to separate
186 * out into two loops which you choose to execute based on isGreater
187 */
188 if ( isGreaterOrLesser == COMPARE_GREATER )
189 {
190 for ( Value<?> value : attr )
191 {
192 Object normValue = normalizer.normalize( value );
193
194 // Found a value that is greater than or equal to the ava value
195 if ( 0 >= comparator.compare( normValue, filterValue ) )
196 {
197 return true;
198 }
199 }
200 }
201 else
202 {
203 for ( Value<?> value : attr )
204 {
205 Object normValue = normalizer.normalize( value );
206
207 // Found a value that is less than or equal to the ava value
208 if ( 0 <= comparator.compare( normValue, filterValue ) )
209 {
210 return true;
211 }
212 }
213 }
214
215 // no match so return false
216 return false;
217 }
218
219
220 /**
221 * Evaluates a simple presence attribute value assertion on a perspective
222 * candidate.
223 *
224 * @param attrId the name of the attribute tested for presence
225 * @param entry the perspective candidate
226 * @return the ava evaluation on the perspective candidate
227 */
228 private boolean evalPresence( String attrId, ServerEntry entry ) throws LdapException
229 {
230 if ( entry == null )
231 {
232 return false;
233 }
234
235 return null != entry.get( attrId );
236 }
237
238
239 /**
240 * Evaluates a simple equality attribute value assertion on a perspective
241 * candidate.
242 *
243 * @param node the equality node to evaluate
244 * @param entry the perspective candidate
245 * @return the ava evaluation on the perspective candidate
246 * @throws LdapException if there is a database access failure
247 */
248 @SuppressWarnings("unchecked")
249 private boolean evalEquality( EqualityNode<?> node, ServerEntry entry ) throws LdapException
250 {
251 Normalizer normalizer = getNormalizer( node.getAttribute() );
252 Comparator comparator = getComparator( node.getAttribute() );
253
254 // get the attribute associated with the node
255 EntryAttribute attr = entry.get( node.getAttribute() );
256
257 // If we do not have the attribute just return false
258 if ( null == attr )
259 {
260 return false;
261 }
262
263 // check if AVA value exists in attribute
264 AttributeType at = schemaManager.lookupAttributeTypeRegistry( node.getAttribute() );
265 Value<?> value = null;
266
267 if ( at.getSyntax().isHumanReadable() )
268 {
269 if ( node.getValue().isBinary() )
270 {
271 value = new StringValue( node.getValue().getString() );
272 }
273 else
274 {
275 value = node.getValue();
276 }
277 }
278 else
279 {
280 value = node.getValue();
281 }
282
283 if ( attr.contains( value ) )
284 {
285 return true;
286 }
287
288 // get the normalized AVA filter value
289 Value<?> filterValue = normalizer.normalize( value );
290
291 // check if the normalized value is present
292 if ( attr.contains( filterValue ) )
293 {
294 return true;
295 }
296
297 /*
298 * We need to now iterate through all values because we could not get
299 * a lookup to work. For each value we normalize and use the comparator
300 * to determine if a match exists.
301 */
302 for ( Value<?> val : attr )
303 {
304 Value<?> normValue = normalizer.normalize( val );
305
306 if ( 0 == comparator.compare( normValue.get(), filterValue.get() ) )
307 {
308 return true;
309 }
310 }
311
312 // no match so return false
313 return false;
314 }
315
316
317 /**
318 * Gets the comparator for equality matching.
319 *
320 * @param attrId the attribute identifier
321 * @return the comparator for equality matching
322 * @throws LdapException if there is a failure
323 */
324 private LdapComparator<? super Object> getComparator( String attrId ) throws LdapException
325 {
326 MatchingRule mrule = getMatchingRule( attrId, EQUALITY_MATCH );
327 return mrule.getLdapComparator();
328 }
329
330
331 /**
332 * Gets the normalizer for equality matching.
333 *
334 * @param attrId the attribute identifier
335 * @return the normalizer for equality matching
336 * @throws LdapException if there is a failure
337 */
338 private Normalizer getNormalizer( String attrId ) throws LdapException
339 {
340 MatchingRule mrule = getMatchingRule( attrId, EQUALITY_MATCH );
341 return mrule.getNormalizer();
342 }
343
344
345 /**
346 * Gets the matching rule for an attributeType.
347 *
348 * @param attrId the attribute identifier
349 * @return the matching rule
350 * @throws LdapException if there is a failure
351 */
352 private MatchingRule getMatchingRule( String attrId, int matchType ) throws LdapException
353 {
354 MatchingRule mrule = null;
355 AttributeType type = schemaManager.lookupAttributeTypeRegistry( attrId );
356
357 switch ( matchType )
358 {
359 case ( EQUALITY_MATCH ):
360 mrule = type.getEquality();
361 break;
362
363 case ( SUBSTRING_MATCH ):
364 mrule = type.getSubstring();
365 break;
366
367 case ( ORDERING_MATCH ):
368 mrule = type.getOrdering();
369 break;
370
371 default:
372 throw new LdapException( I18n.err( I18n.ERR_246, matchType ) );
373 }
374
375 if ( ( mrule == null ) && ( matchType != EQUALITY_MATCH ) )
376 {
377 mrule = type.getEquality();
378 }
379
380 return mrule;
381 }
382 }