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 }