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 */ 020package org.apache.directory.server.xdbm.search.evaluator; 021 022 023import java.util.Iterator; 024import java.util.regex.Pattern; 025 026import org.apache.directory.api.ldap.model.entry.Attribute; 027import org.apache.directory.api.ldap.model.entry.Entry; 028import org.apache.directory.api.ldap.model.entry.Value; 029import org.apache.directory.api.ldap.model.exception.LdapException; 030import org.apache.directory.api.ldap.model.filter.SubstringNode; 031import org.apache.directory.api.ldap.model.schema.AttributeType; 032import org.apache.directory.api.ldap.model.schema.MatchingRule; 033import org.apache.directory.api.ldap.model.schema.Normalizer; 034import org.apache.directory.api.ldap.model.schema.SchemaManager; 035import org.apache.directory.api.ldap.model.schema.normalizers.NoOpNormalizer; 036import org.apache.directory.server.core.api.partition.PartitionTxn; 037import org.apache.directory.server.xdbm.IndexEntry; 038import org.apache.directory.server.xdbm.Store; 039import org.apache.directory.server.xdbm.search.Evaluator; 040 041 042/** 043 * Evaluates substring filter assertions on an entry. 044 * 045 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 046 */ 047public class SubstringEvaluator implements Evaluator<SubstringNode> 048{ 049 /** Database used while evaluating candidates */ 050 private final Store db; 051 052 /** Reference to the SchemaManager */ 053 private final SchemaManager schemaManager; 054 055 /** The Substring expression */ 056 private final SubstringNode node; 057 058 /** The regular expression generated for the SubstringNode pattern */ 059 private final Pattern regex; 060 061 /** The AttributeType we will use for the evaluation */ 062 private final AttributeType attributeType; 063 064 /** The associated normalizer */ 065 private final Normalizer normalizer; 066 067 068 /** 069 * Creates a new SubstringEvaluator for substring expressions. 070 * 071 * @param node the substring expression node 072 * @param db the database this evaluator uses 073 * @param schemaManager the schema manager 074 * @throws LdapException if there are failures accessing resources and the db 075 */ 076 public SubstringEvaluator( SubstringNode node, Store db, SchemaManager schemaManager ) throws LdapException 077 { 078 this.db = db; 079 this.node = node; 080 this.schemaManager = schemaManager; 081 this.attributeType = node.getAttributeType(); 082 083 MatchingRule rule = attributeType.getSubstring(); 084 085 if ( rule == null ) 086 { 087 rule = attributeType.getEquality(); 088 } 089 090 if ( rule != null ) 091 { 092 normalizer = rule.getNormalizer(); 093 } 094 else 095 { 096 normalizer = new NoOpNormalizer( attributeType.getSyntaxOid() ); 097 } 098 099 // compile the regular expression to search for a matching attribute 100 // if the attributeType is humanReadable 101 if ( attributeType.getSyntax().isHumanReadable() ) 102 { 103 regex = node.getRegex( normalizer ); 104 } 105 else 106 { 107 regex = null; 108 } 109 } 110 111 112 /** 113 * {@inheritDoc} 114 */ 115 @SuppressWarnings("unchecked") 116 @Override 117 public boolean evaluate( PartitionTxn partitionTxn, IndexEntry<?, String> indexEntryQM ) throws LdapException 118 { 119 IndexEntry<String, String> indexEntry = ( IndexEntry<String, String> ) indexEntryQM; 120 121 Entry entry = indexEntry.getEntry(); 122 123 // resuscitate the entry if it has not been and set entry in IndexEntry 124 if ( null == entry ) 125 { 126 entry = db.fetch( partitionTxn, indexEntry.getId() ); 127 128 if ( null == entry ) 129 { 130 // The entry is not anymore present : get out 131 return false; 132 } 133 134 indexEntry.setEntry( entry ); 135 } 136 137 /* 138 * Don't make a call here to evaluateWithoutIndex( Entry ) for 139 * code reuse since we do want to set the value on the indexEntry on 140 * matches. 141 */ 142 143 // get the attribute 144 Attribute attr = entry.get( attributeType ); 145 146 // if the attribute exists and the pattern matches return true 147 if ( attr != null ) 148 { 149 /* 150 * Cycle through the attribute values testing normalized version 151 * obtained from using the substring matching rule's normalizer. 152 * The test uses the comparator obtained from the appropriate 153 * substring matching rule. 154 */ 155 if ( attr.isHumanReadable() ) 156 { 157 for ( Value value : attr ) 158 { 159 String strValue = value.getString(); 160 String normalizedValue = attr.getAttributeType().getEquality().getNormalizer().normalize( strValue ); 161 162 // Once match is found cleanup and return true 163 if ( regex.matcher( normalizedValue ).matches() ) 164 { 165 // before returning we set the normalized value 166 indexEntry.setKey( strValue ); 167 return true; 168 } 169 } 170 } 171 else 172 { 173 // Slightly more complex. We won't be able to use a regex to check 174 // the value. 175 for ( Value value : attr ) 176 { 177 byte[] byteValue = value.getBytes(); 178 179 // Once match is found cleanup and return true 180 // @TODO : implement this check. 181 /* 182 if ( check( byteValue ) ) 183 { 184 // before returning we set the normalized value 185 indexEntry.setKey( byteValue ); 186 return true; 187 } 188 */ 189 } 190 } 191 192 // Fall through as we didn't find any matching value for this attribute. 193 // We will have to check in the potential descendant, if any. 194 } 195 196 // If we do not have the attribute, loop through the descendant 197 // May be the node Attribute has descendant ? 198 if ( schemaManager.getAttributeTypeRegistry().hasDescendants( attributeType ) ) 199 { 200 // TODO check to see if descendant handling is necessary for the 201 // index so we can match properly even when for example a name 202 // attribute is used instead of more specific commonName 203 Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants( attributeType ); 204 205 while ( descendants.hasNext() ) 206 { 207 AttributeType descendant = descendants.next(); 208 209 attr = entry.get( descendant ); 210 211 if ( null != attr ) 212 { 213 214 /* 215 * Cycle through the attribute values testing normalized version 216 * obtained from using the substring matching rule's normalizer. 217 * The test uses the comparator obtained from the appropriate 218 * substring matching rule. 219 */ 220 for ( Value value : attr ) 221 { 222 String strValue = value.getString(); 223 String normalizedValue = attr.getAttributeType().getEquality().getNormalizer().normalize( strValue ); 224 225 // Once match is found cleanup and return true 226 if ( regex.matcher( normalizedValue ).matches() ) 227 { 228 // before returning we set the normalized value 229 indexEntry.setKey( strValue ); 230 return true; 231 } 232 } 233 } 234 } 235 } 236 237 // we fell through so a match was not found - assertion was false. 238 return false; 239 } 240 241 242 /** 243 * {@inheritDoc} 244 */ 245 @Override 246 public boolean evaluate( Entry entry ) throws LdapException 247 { 248 // get the attribute 249 Attribute attr = entry.get( attributeType ); 250 251 // if the attribute exists and the pattern matches return true 252 if ( attr != null ) 253 { 254 /* 255 * Cycle through the attribute values testing normalized version 256 * obtained from using the substring matching rule's normalizer. 257 * The test uses the comparator obtained from the appropriate 258 * substring matching rule. 259 */ 260 for ( Value value : attr ) 261 { 262 String strValue = value.getString(); 263 264 // Once match is found cleanup and return true 265 if ( regex.matcher( strValue ).matches() ) 266 { 267 return true; 268 } 269 } 270 271 // Fall through as we didn't find any matching value for this attribute. 272 // We will have to check in the potential descendant, if any. 273 } 274 275 // If we do not have the attribute, loop through the descendant 276 // May be the node Attribute has descendant ? 277 if ( schemaManager.getAttributeTypeRegistry().hasDescendants( attributeType ) ) 278 { 279 // TODO check to see if descendant handling is necessary for the 280 // index so we can match properly even when for example a name 281 // attribute is used instead of more specific commonName 282 Iterator<AttributeType> descendants = schemaManager.getAttributeTypeRegistry().descendants( attributeType ); 283 284 while ( descendants.hasNext() ) 285 { 286 AttributeType descendant = descendants.next(); 287 288 attr = entry.get( descendant ); 289 290 if ( null != attr ) 291 { 292 293 /* 294 * Cycle through the attribute values testing normalized version 295 * obtained from using the substring matching rule's normalizer. 296 * The test uses the comparator obtained from the appropriate 297 * substring matching rule. 298 */ 299 for ( Value value : attr ) 300 { 301 String strValue = value.getString(); 302 303 // Once match is found cleanup and return true 304 if ( regex.matcher( strValue ).matches() ) 305 { 306 return true; 307 } 308 } 309 } 310 } 311 } 312 313 // we fell through so a match was not found - assertion was false. 314 return false; 315 } 316 317 318 public Pattern getPattern() 319 { 320 return regex; 321 } 322 323 324 /** 325 * {@inheritDoc} 326 */ 327 @Override 328 public SubstringNode getExpression() 329 { 330 return node; 331 } 332 333 334 /** 335 * @see Object#toString() 336 */ 337 @Override 338 public String toString( String tabs ) 339 { 340 StringBuilder sb = new StringBuilder(); 341 342 sb.append( tabs ).append( "SubstringEvaluator : " ).append( node ).append( "\n" ); 343 344 return sb.toString(); 345 } 346 347 348 /** 349 * @see Object#toString() 350 */ 351 @Override 352 public String toString() 353 { 354 return toString( "" ); 355 } 356}