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.ldap.client.api.search; 021 022import org.apache.directory.api.i18n.I18n; 023 024/** 025 * A builder for constructing well formed search filters according to 026 * <a href="https://tools.ietf.org/html/rfc4515.html">RFC 4515</a>. This 027 * builder is most convenient when you use static imports. For example: 028 * <pre> 029 * import static org.apache.directory.ldap.client.api.search.FilterBuilder.and; 030 * import static org.apache.directory.ldap.client.api.search.FilterBuilder.equal; 031 * import static org.apache.directory.ldap.client.api.search.FilterBuilder.or; 032 * 033 * ... 034 * 035 * String filter = 036 * or( 037 * and( 038 * equal( "givenName", "kermit" ), 039 * equal( "sn", "the frog" ) ), 040 * and( 041 * equal( "givenName", "miss" ), 042 * equal( "sn", "piggy" ) ) ) 043 * .toString() 044 * </pre> 045 * 046 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 047 */ 048public class FilterBuilder 049{ 050 /** The built filter */ 051 /* No qualifier */ Filter filter; 052 053 054 /** 055 * A private constructor that creates a new instance of a FilterBuilder 056 * containing a given filter. 057 * 058 * @param filter The filter 059 */ 060 /* No qualifier*/ FilterBuilder( Filter filter ) 061 { 062 this.filter = filter; 063 } 064 065 066 /** 067 * Returns a new FilterBuilder that will <code>&</code> together all of the 068 * supplied filters. For example: 069 * 070 * <pre> 071 * and( equal( "givenName", "kermit" ), equal( "sn", "the frog" ) ).toString() 072 * </pre> 073 * would result in the string: 074 * <pre> 075 * (&(givenName=kermit)(sn=the frog)) 076 * </pre> 077 * 078 * Which would match all entries with a given name of <code>kermit</code> 079 * and a surname <code>the frog</code>. 080 * 081 * @param filters The filters to and together 082 * @return A new FilterBuilder 083 */ 084 public static FilterBuilder and( FilterBuilder... filters ) 085 { 086 SetOfFiltersFilter filter = SetOfFiltersFilter.and(); 087 088 for ( FilterBuilder builder : filters ) 089 { 090 filter.add( builder.filter ); 091 } 092 093 return new FilterBuilder( filter ); 094 } 095 096 097 /** 098 * Returns a new FilterBuilder for testing the approximate equality of an 099 * attribute. For example: 100 * 101 * <pre> 102 * approximatelyEqual( "l", "san fransico" ).toString(); 103 * </pre> 104 * would result in the string: 105 * <pre> 106 * (l~=san fransico) 107 * </pre> 108 * 109 * Which <i>MIGHT</i> match results whose locality is 110 * <code>San Francisco</code>. The matching rule used to apply this filter 111 * is dependent on the server implementation. 112 * 113 * @param attribute The attribute 114 * @param value The value 115 * @return A new FilterBuilder 116 */ 117 public static FilterBuilder approximatelyEqual( String attribute, String value ) 118 { 119 return new FilterBuilder( AttributeValueAssertionFilter.approximatelyEqual( attribute, value ) ); 120 } 121 122 123 /** 124 * Returns a new FilterBuilder for testing equality of an attribute. For 125 * example: 126 * 127 * <pre> 128 * equal( "cn", "Kermit The Frog" ).toString(); 129 * </pre> 130 * would result in the string: 131 * <pre> 132 * (cn>=Kermit The Frog) 133 * </pre> 134 * 135 * Which would match entries with the common name 136 * <code>Kermit The Frog</code>. 137 * 138 * @param attribute The attribute 139 * @param value The value 140 * @return A new FilterBuilder 141 */ 142 public static FilterBuilder equal( String attribute, String value ) 143 { 144 return new FilterBuilder( AttributeValueAssertionFilter.equal( attribute, value ) ); 145 } 146 147 148 /** 149 * Creates an extensible match filter by calling 150 * {@link #extensible(String, String) extensible(null, value)}. 151 * 152 * @param value The value to test for 153 * @return A new MatchingRuleAssertionFilterBuilder 154 */ 155 public static MatchingRuleAssertionFilterBuilder extensible( String value ) 156 { 157 return new MatchingRuleAssertionFilterBuilder( null, value ); 158 } 159 160 161 /** 162 * Creates an extensible match filter. This filter can be used to specify 163 * that dn attributes should be included in the match, which matcher to 164 * use, or that all attributes that support a specific matcher will be 165 * checked. For example: 166 * 167 * <pre> 168 * extensible( "sn", "Barney Rubble" ) 169 * .useDnAttributes() 170 * .setMatchingRule( "2.4.6.8.10" ) 171 * .toString(); 172 * </pre> 173 * would result in the string: 174 * <pre> 175 * (sn:dn:2.4.6.8.10:=Barney Rubble) 176 * </pre> 177 * 178 * Not that the specialized filter builder that is returned <b>IS</b> a 179 * FilterBuilder so it can be chained with other filters. For example: 180 * 181 * <pre> 182 * and( 183 * extensible( "sn", "Rubble" ) 184 * .useDnAttributes() 185 * .setMatchingRule( "2.4.6.8.10" ), 186 * equal( "givenName", "Barney" ) ) 187 * .toString(); 188 * </pre> 189 * 190 * @param attribute The attribute to test 191 * @param value The value to test for 192 * @return A new MatchingRuleAssertionFilterBuilder 193 */ 194 public static MatchingRuleAssertionFilterBuilder extensible( String attribute, String value ) 195 { 196 return new MatchingRuleAssertionFilterBuilder( attribute, value ); 197 } 198 199 200 /** 201 * Returns a new FilterBuilder for testing lexicographical greater than. 202 * For example: 203 * 204 * <pre> 205 * greaterThanOrEqual( "sn", "n" ).toString(); 206 * </pre> 207 * would result in the string: 208 * <pre> 209 * (sn>=n) 210 * </pre> 211 * 212 * which would match results whose surname starts with the second half of 213 * the alphabet. 214 * 215 * @param attribute The attribute 216 * @param value The value 217 * @return A new FilterBuilder 218 */ 219 public static FilterBuilder greaterThanOrEqual( String attribute, String value ) 220 { 221 return new FilterBuilder( AttributeValueAssertionFilter.greaterThanOrEqual( attribute, value ) ); 222 } 223 224 225 /** 226 * Returns a new FilterBuilder for testing lexicographical less than. For 227 * example: 228 * 229 * <pre> 230 * lessThanOrEqual( "sn", "mzzzzzz" ).toString(); 231 * </pre> 232 * would result in the string: 233 * <pre> 234 * (sn<=mzzzzzz) 235 * </pre> 236 * 237 * which would match results whose surname starts with the first half of 238 * the alphabet. <i>Note, this is not perfect, but if you know anybody with 239 * a last name that starts with an <code>m</code> followed by six 240 * <code>z</code>'s...</i> 241 * 242 * @param attribute The attribute 243 * @param value The value 244 * @return A new FilterBuilder 245 */ 246 public static FilterBuilder lessThanOrEqual( String attribute, String value ) 247 { 248 return new FilterBuilder( AttributeValueAssertionFilter.lessThanOrEqual( attribute, value ) ); 249 } 250 251 252 /** 253 * Returns a new FilterBuilder for negating another filter. For example: 254 * 255 * <pre> 256 * not( present( "givenName" ) ).toString(); 257 * </pre> 258 * would result in the string: 259 * <pre> 260 * (!(givenName=*)) 261 * </pre> 262 * 263 * @param builder The filter to negate 264 * @return A new FilterBuilder 265 */ 266 public static FilterBuilder not( FilterBuilder builder ) 267 { 268 return new FilterBuilder( UnaryFilter.not( builder.filter ) ); 269 } 270 271 272 /** 273 * Returns a new FilterBuilder that will <code>|</code> together all of the 274 * supplied filters. For example: 275 * 276 * <pre> 277 * or( equal( "givenName", "kermit" ), equal( "givenName", "walter" ) ).toString() 278 * </pre> 279 * would result in the string: 280 * <pre> 281 * (|(givenName=kermit)(givenName=walter)) 282 * </pre> 283 * 284 * Which would match any entry with the <code>givenName</code> of either 285 * <code>kermit</code> or <code>walter</code>. 286 * 287 * @param builders The filters to or together 288 * @return A new FilterBuilder 289 */ 290 public static FilterBuilder or( FilterBuilder... builders ) 291 { 292 SetOfFiltersFilter filter = SetOfFiltersFilter.or(); 293 294 for ( FilterBuilder builder : builders ) 295 { 296 filter.add( builder.filter ); 297 } 298 299 return new FilterBuilder( filter ); 300 } 301 302 303 /** 304 * Returns a new FilterBuilder for testing the presence of an attributes. 305 * For example: 306 * 307 * <pre> 308 * present( "givenName" ).toString(); 309 * </pre> 310 * would result in the string: 311 * <pre> 312 * (givenName=*) 313 * </pre> 314 * 315 * Which would match any entry that has a <code>givenName</code> attribute. 316 * 317 * @param attribute The attribute to test the presence of 318 * @return A new FilterBuilder 319 */ 320 public static FilterBuilder present( String attribute ) 321 { 322 return new FilterBuilder( AttributeDescriptionFilter.present( attribute ) ); 323 } 324 325 326 /** 327 * Returns a new FilterBuilder that will construct a SubString filter, with an <em>initial</em> part, 328 * and zero to N <em>any</em> part, but no <em>final</em> part. 329 * 330 * For instance: 331 * 332 * <pre> 333 * startswith( "sn", "Th", "Soft", "Foun" )).toString() 334 * </pre> 335 * would result in the string: 336 * <pre> 337 * (sn=Th*Soft*Foun*) 338 * </pre> 339 * 340 * Which would match any entry with the <code>sn</code> starting with <code>'Th'</code>, and 341 * having a <code>Soft</code> and <code>Foun</code> strings in the middle, like 342 * 'The Apache Software Foundation'. 343 * 344 * @param attribute The attribute to use in the filter 345 * @param parts The sub elements to use in the filter 346 * @return A new FilterBuilder 347 */ 348 public static FilterBuilder startsWith( String attribute, String... parts ) 349 { 350 if ( ( parts == null ) || ( parts.length == 0 ) ) 351 { 352 throw new IllegalArgumentException( I18n.err( I18n.ERR_04162_INITIAL_PART_NEEDED ) ); 353 } 354 355 return new FilterBuilder( SubstringFilter.startsWith( attribute, parts ) ); 356 } 357 358 359 /** 360 * Returns a new FilterBuilder that will construct a SubString filter, with an <em>initial</em> part, 361 * and zero to N <em>any</em> parts, but no <em>final</em> part. 362 * 363 * For instance: 364 * 365 * <pre> 366 * startswith( "sn", "Th", "Soft", "Foun" ).toString() 367 * </pre> 368 * would result in the string: 369 * <pre> 370 * (sn=Th*Soft*Foun*) 371 * </pre> 372 * 373 * Which would match any entry with the <code>sn</code> starting with <code>'Th'</code>, and 374 * having a <code>Soft</code> and <code>Foun</code> strings in the middle, like 375 * 'The Apache Software Foundation'. 376 * 377 * @param attribute The attribute to use in the filter 378 * @param parts The sub elements to use in the filter 379 * @return A new FilterBuilder 380 */ 381 public static FilterBuilder endsWith( String attribute, String... parts ) 382 { 383 if ( ( parts == null ) || ( parts.length == 0 ) ) 384 { 385 throw new IllegalArgumentException( I18n.err( I18n.ERR_04163_FINAL_PART_NEEDED ) ); 386 } 387 388 return new FilterBuilder( SubstringFilter.endsWith( attribute, parts ) ); 389 } 390 391 392 /** 393 * Returns a new FilterBuilder that will construct a SubString filter, with zero to N <em>any</em> parts, 394 * but no <em>initial</em> or <em>final</em> parts. 395 * 396 * For instance: 397 * 398 * <pre> 399 * contains( "sn", "Soft", "Foun" )).toString() 400 * </pre> 401 * would result in the string: 402 * <pre> 403 * (sn=*Soft*Foun*) 404 * </pre> 405 * 406 * Which would match any entry with the <code>sn</code> having a <code>Soft</code> 407 * and <code>Foun</code> strings in the middle, like 408 * 'The Apache Software Foundation'. 409 * 410 * @param attribute The attribute to use in the filter 411 * @param parts The sub elements to use in the filter 412 * @return A new FilterBuilder 413 */ 414 public static FilterBuilder contains( String attribute, String... parts ) 415 { 416 if ( ( parts == null ) || ( parts.length == 0 ) ) 417 { 418 throw new IllegalArgumentException( I18n.err( I18n.ERR_04164_ANY_PART_NEEDED ) ); 419 } 420 421 return new FilterBuilder( SubstringFilter.contains( attribute, parts ) ); 422 } 423 424 425 /** 426 * Returns a new FilterBuilder that will construct a SubString filter, with a <em>initial</em> part, 427 * zero to N <em>any</em> parts, and a <em>final</em> part. 428 * 429 * For instance: 430 * 431 * <pre> 432 * substring( "sn", "The", "Soft", "Foun", "ion" )).toString() 433 * </pre> 434 * would result in the string: 435 * <pre> 436 * (sn=The*Soft*Foun*ion) 437 * </pre> 438 * 439 * Which would match any entry with the <code>sn</code> having a <code>Soft</code> 440 * and <code>Foun</code> strings in the middle, starts with <code>The</code> and ends with <code>ion</code> like 441 * 'The Apache Software Foundation'. 442 * <p> 443 * Note that if we have only two strings in the parts, they will be the <em>initial</em> and <em>final</em> ones : 444 * 445 * <pre> 446 * substring( "sn", "The", "ion" )).toString() 447 * </pre> 448 * would result in the string: 449 * <pre> 450 * (sn=The*ion) 451 * </pre> 452 * 453 * @param attribute The attribute to use in the filter 454 * @param parts The sub elements to use in the filter 455 * @return A new FilterBuilder 456 */ 457 public static FilterBuilder substring( String attribute, String... parts ) 458 { 459 if ( ( parts == null ) || ( parts.length == 0 ) ) 460 { 461 throw new IllegalArgumentException( I18n.err( I18n.ERR_04165_INITIAL_ANY_FINAL_PART_NEEDED ) ); 462 } 463 464 return new FilterBuilder( SubstringFilter.substring( attribute, parts ) ); 465 } 466 467 468 /** 469 * Returns the string version of the filter represented by this FilterBuilder. 470 * 471 * @return The string representation of the filter 472 */ 473 @Override 474 public String toString() 475 { 476 return filter.build().toString(); 477 } 478}