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 * https://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.api.ldap.model.filter; 021 022 023import java.util.ArrayList; 024import java.util.List; 025import java.util.regex.Pattern; 026 027import org.apache.directory.api.ldap.model.exception.LdapException; 028import org.apache.directory.api.ldap.model.schema.AttributeType; 029import org.apache.directory.api.ldap.model.schema.Normalizer; 030import org.apache.directory.api.ldap.model.schema.PrepareString; 031 032 033/** 034 * Filter expression tree node used to represent a substring assertion. 035 * 036 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 037 */ 038public class SubstringNode extends LeafNode 039{ 040 /** The initial fragment before any wildcard */ 041 private String initialPattern; 042 043 /** The end fragment after wildcard */ 044 private String finalPattern; 045 046 /** List of fragments between wildcard */ 047 private List<String> anyPattern; 048 049 050 /** 051 * Creates a new SubstringNode object with only one wildcard and no internal 052 * any fragments between wildcards. 053 * 054 * @param attributeType the name of the attributeType to substring assert 055 * @param initialPattern the initial fragment 056 * @param finalPattern the final fragment 057 */ 058 public SubstringNode( AttributeType attributeType, String initialPattern, String finalPattern ) 059 { 060 super( attributeType, AssertionType.SUBSTRING ); 061 062 anyPattern = new ArrayList<>( 2 ); 063 this.finalPattern = finalPattern; 064 this.initialPattern = initialPattern; 065 } 066 067 068 /** 069 * Creates a new SubstringNode object with only one wildcard and no internal 070 * any fragments between wildcards. 071 * 072 * @param attribute the name of the attribute to substring assert 073 * @param initialPattern the initial fragment 074 * @param finalPattern the final fragment 075 */ 076 public SubstringNode( String attribute, String initialPattern, String finalPattern ) 077 { 078 super( attribute, AssertionType.SUBSTRING ); 079 080 anyPattern = new ArrayList<>( 2 ); 081 this.finalPattern = finalPattern; 082 this.initialPattern = initialPattern; 083 } 084 085 086 /** 087 * Creates a new SubstringNode object without any value 088 * 089 * @param attribute the name of the attribute to substring assert 090 */ 091 public SubstringNode( AttributeType attribute ) 092 { 093 super( attribute, AssertionType.SUBSTRING ); 094 095 anyPattern = new ArrayList<>( 2 ); 096 this.finalPattern = null; 097 this.initialPattern = null; 098 } 099 100 101 /** 102 * Creates a new SubstringNode object without any value 103 * 104 * @param attributeType the attributeType to substring assert 105 */ 106 public SubstringNode( String attributeType ) 107 { 108 super( attributeType, AssertionType.SUBSTRING ); 109 110 anyPattern = new ArrayList<>( 2 ); 111 this.finalPattern = null; 112 this.initialPattern = null; 113 } 114 115 116 /** 117 * Creates a new SubstringNode object more than one wildcard and an any 118 * list. 119 * 120 * @param anyPattern list of internal fragments between wildcards 121 * @param attributeType the attributeType to substring assert 122 * @param initialPattern the initial fragment 123 * @param finalPattern the final fragment 124 */ 125 public SubstringNode( List<String> anyPattern, AttributeType attributeType, String initialPattern, 126 String finalPattern ) 127 { 128 super( attributeType, AssertionType.SUBSTRING ); 129 130 this.anyPattern = anyPattern; 131 this.finalPattern = finalPattern; 132 this.initialPattern = initialPattern; 133 } 134 135 136 /** 137 * Creates a new SubstringNode object more than one wildcard and an any 138 * list. 139 * 140 * @param anyPattern list of internal fragments between wildcards 141 * @param attribute the name of the attribute to substring assert 142 * @param initialPattern the initial fragment 143 * @param finalPattern the final fragment 144 */ 145 public SubstringNode( List<String> anyPattern, String attribute, String initialPattern, String finalPattern ) 146 { 147 super( attribute, AssertionType.SUBSTRING ); 148 149 this.anyPattern = anyPattern; 150 this.finalPattern = finalPattern; 151 this.initialPattern = initialPattern; 152 } 153 154 155 /** 156 * Creates a regular expression from an LDAP substring assertion filter 157 * specification. 158 * 159 * @param initialPattern 160 * the initial fragment before wildcards 161 * @param anyPattern 162 * fragments surrounded by wildcards if any 163 * @param finalPattern 164 * the final fragment after last wildcard if any 165 * @return the regular expression for the substring match filter 166 * @throws java.util.regex.PatternSyntaxException 167 * if a syntactically correct regular expression cannot be 168 * compiled 169 */ 170 public static Pattern getRegex( String initialPattern, String[] anyPattern, String finalPattern ) 171 { 172 StringBuilder buf = new StringBuilder(); 173 174 if ( initialPattern != null ) 175 { 176 buf.append( '^' ).append( Pattern.quote( initialPattern ) ); 177 } 178 179 if ( anyPattern != null ) 180 { 181 for ( int i = 0; i < anyPattern.length; i++ ) 182 { 183 buf.append( ".*" ).append( Pattern.quote( anyPattern[i] ) ); 184 } 185 } 186 187 if ( finalPattern != null ) 188 { 189 buf.append( ".*" ).append( Pattern.quote( finalPattern ) ); 190 } 191 else 192 { 193 buf.append( ".*" ); 194 } 195 196 return Pattern.compile( buf.toString() ); 197 } 198 199 200 /** 201 * Clone the Node 202 */ 203 @Override 204 public ExprNode clone() 205 { 206 ExprNode clone = super.clone(); 207 208 if ( anyPattern != null ) 209 { 210 ( ( SubstringNode ) clone ).anyPattern = new ArrayList<>(); 211 212 for ( String any : anyPattern ) 213 { 214 ( ( SubstringNode ) clone ).anyPattern.add( any ); 215 } 216 } 217 218 return clone; 219 } 220 221 222 /** 223 * Gets the initial fragment. 224 * 225 * @return the initial prefix 226 */ 227 public final String getInitial() 228 { 229 return initialPattern; 230 } 231 232 233 /** 234 * Set the initial pattern 235 * @param initialPattern The initial pattern 236 */ 237 public void setInitial( String initialPattern ) 238 { 239 this.initialPattern = initialPattern; 240 } 241 242 243 /** 244 * Gets the final fragment or suffix. 245 * 246 * @return the suffix 247 */ 248 public final String getFinal() 249 { 250 return finalPattern; 251 } 252 253 254 /** 255 * Set the final pattern 256 * @param finalPattern The final pattern 257 */ 258 public void setFinal( String finalPattern ) 259 { 260 this.finalPattern = finalPattern; 261 } 262 263 264 /** 265 * Gets the list of wildcard surrounded any fragments. 266 * 267 * @return the any fragments 268 */ 269 public final List<String> getAny() 270 { 271 return anyPattern; 272 } 273 274 275 /** 276 * Set the any patterns 277 * @param anyPattern The any patterns 278 */ 279 public void setAny( List<String> anyPattern ) 280 { 281 this.anyPattern = anyPattern; 282 } 283 284 285 /** 286 * Add an any pattern 287 * @param anyPattern The any pattern 288 */ 289 public void addAny( String anyPattern ) 290 { 291 this.anyPattern.add( anyPattern ); 292 } 293 294 295 /** 296 * Gets the compiled regular expression for the substring expression. 297 * 298 * @param normalizer the normalizer to use for pattern component normalization 299 * @return the equivalent compiled regular expression 300 * @throws LdapException if there are problems while normalizing 301 */ 302 public final Pattern getRegex( Normalizer normalizer ) throws LdapException 303 { 304 if ( ( anyPattern != null ) && ( !anyPattern.isEmpty() ) ) 305 { 306 String[] any = new String[anyPattern.size()]; 307 308 for ( int i = 0; i < any.length; i++ ) 309 { 310 any[i] = normalizer.normalize( anyPattern.get( i ), PrepareString.AssertionType.SUBSTRING_ANY ); 311 312 if ( any[i].length() == 0 ) 313 { 314 any[i] = " "; 315 } 316 } 317 318 String initialStr = null; 319 320 if ( initialPattern != null ) 321 { 322 initialStr = normalizer.normalize( initialPattern, PrepareString.AssertionType.SUBSTRING_INITIAL ); 323 } 324 325 String finalStr = null; 326 327 if ( finalPattern != null ) 328 { 329 finalStr = normalizer.normalize( finalPattern, PrepareString.AssertionType.SUBSTRING_FINAL ); 330 } 331 332 return getRegex( initialStr, any, finalStr ); 333 } 334 335 String initialStr = null; 336 337 if ( initialPattern != null ) 338 { 339 initialStr = normalizer.normalize( initialPattern, PrepareString.AssertionType.SUBSTRING_INITIAL ); 340 } 341 342 String finalStr = null; 343 344 if ( finalPattern != null ) 345 { 346 finalStr = normalizer.normalize( finalPattern, PrepareString.AssertionType.SUBSTRING_FINAL ); 347 } 348 349 return getRegex( initialStr, null, finalStr ); 350 } 351 352 353 /** 354 * {@inheritDoc} 355 */ 356 @Override 357 public boolean equals( Object obj ) 358 { 359 if ( obj == this ) 360 { 361 return true; 362 } 363 364 if ( !( obj instanceof SubstringNode ) ) 365 { 366 return false; 367 } 368 369 SubstringNode that = ( SubstringNode ) obj; 370 371 if ( initialPattern == null ) 372 { 373 if ( that.initialPattern != null ) 374 { 375 return false; 376 } 377 } 378 else 379 { 380 if ( !initialPattern.equals( that.initialPattern ) ) 381 { 382 return false; 383 } 384 } 385 386 if ( finalPattern == null ) 387 { 388 if ( that.finalPattern != null ) 389 { 390 return false; 391 } 392 } 393 else 394 { 395 if ( !finalPattern.equals( that.finalPattern ) ) 396 { 397 return false; 398 } 399 } 400 401 return super.equals( obj ); 402 } 403 404 405 /** 406 * @see Object#hashCode() 407 * @return the instance's hash code 408 */ 409 @Override 410 public int hashCode() 411 { 412 int h = 37; 413 414 h = h * 17 + super.hashCode(); 415 h = h * 17 + ( initialPattern != null ? initialPattern.hashCode() : 0 ); 416 417 if ( anyPattern != null ) 418 { 419 for ( String pattern : anyPattern ) 420 { 421 h = h * 17 + pattern.hashCode(); 422 } 423 } 424 425 h = h * 17 + ( finalPattern != null ? finalPattern.hashCode() : 0 ); 426 427 return h; 428 } 429 430 431 /** 432 * @see java.lang.Object#toString() 433 * @return A string representing the AndNode 434 */ 435 @Override 436 public String toString() 437 { 438 StringBuilder buf = new StringBuilder(); 439 440 buf.append( '(' ); 441 442 if ( attributeType != null ) 443 { 444 buf.append( attributeType.getName() ); 445 } 446 else 447 { 448 buf.append( attribute ); 449 } 450 451 buf.append( '=' ); 452 453 if ( null != initialPattern ) 454 { 455 buf.append( escapeFilterValue( initialPattern ) ).append( '*' ); 456 } 457 else 458 { 459 buf.append( '*' ); 460 } 461 462 if ( null != anyPattern ) 463 { 464 for ( String any : anyPattern ) 465 { 466 buf.append( escapeFilterValue( any ) ); 467 buf.append( '*' ); 468 } 469 } 470 471 if ( null != finalPattern ) 472 { 473 buf.append( escapeFilterValue( finalPattern ) ); 474 } 475 476 buf.append( super.toString() ); 477 478 buf.append( ')' ); 479 480 return buf.toString(); 481 } 482}