View Javadoc
1   /*
2    *   Licensed to the Apache Software Foundation (ASF) under one
3    *   or more contributor license agreements.  See the NOTICE file
4    *   distributed with this work for additional information
5    *   regarding copyright ownership.  The ASF licenses this file
6    *   to you under the Apache License, Version 2.0 (the
7    *   "License"); you may not use this file except in compliance
8    *   with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *   Unless required by applicable law or agreed to in writing,
13   *   software distributed under the License is distributed on an
14   *   "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *   KIND, either express or implied.  See the License for the
16   *   specific language governing permissions and limitations
17   *   under the License.
18   *
19   */
20  package org.apache.directory.ldap.client.api.search;
21  
22  
23  /**
24   * A builder for constructing well formed search filters according to
25   * <a href="https://tools.ietf.org/html/rfc4515.html">RFC 4515</a>.  This 
26   * builder is most convenient when you use static imports.  For example:
27   * <pre>
28   * import static org.apache.directory.ldap.client.api.search.FilterBuilder.and;
29   * import static org.apache.directory.ldap.client.api.search.FilterBuilder.equal;
30   * import static org.apache.directory.ldap.client.api.search.FilterBuilder.or;
31   * 
32   * ...
33   * 
34   *         String filter = 
35   *                 or(
36   *                     and( 
37   *                         equal( "givenName", "kermit" ), 
38   *                         equal( "sn", "the frog" ) ),
39   *                     and( 
40   *                         equal( "givenName", "miss" ), 
41   *                         equal( "sn", "piggy" ) ) )
42   *                 .toString()
43   * </pre>
44   *
45   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
46   */
47  public class FilterBuilder
48  {
49      /** The built filter */
50      /* No qualifier */ Filter filter;
51  
52  
53      /**
54       * A private constructor that creates a new instance of a FilterBuilder
55       * containing a given filter.
56       */
57      /* No qualifier*/ FilterBuilder( Filter filter )
58      {
59          this.filter = filter;
60      }
61  
62  
63      /**
64       * Returns a new FilterBuilder that will <code>&amp;</code> together all of the 
65       * supplied filters.  For example:
66       * 
67       * <pre>
68       * and( equal( "givenName", "kermit" ), equal( "sn", "the frog" ) ).toString()
69       * </pre>
70       * would result in the string:
71       * <pre>
72       * (&amp;(givenName=kermit)(sn=the frog))
73       * </pre>
74       * 
75       * Which would match all entries with a given name of <code>kermit</code>
76       * and a surname <code>the frog</code>.
77       *
78       * @param filters The filters to and together
79       * @return A new FilterBuilder
80       */
81      public static FilterBuilder and( FilterBuilder... filters )
82      {
83          SetOfFiltersFilter filter = SetOfFiltersFilter.and();
84  
85          for ( FilterBuilder builder : filters )
86          {
87              filter.add( builder.filter );
88          }
89  
90          return new FilterBuilder( filter );
91      }
92  
93  
94      /**
95       * Returns a new FilterBuilder for testing the approximate equality of an 
96       * attribute. For example:
97       * 
98       * <pre>
99       * approximatelyEqual( "l", "san fransico" ).toString();
100      * </pre>
101      * would result in the string:
102      * <pre>
103      * (l~=san fransico)
104      * </pre>
105      * 
106      * Which <i>MIGHT</i> match results whose locality is 
107      * <code>San Francisco</code>.  The matching rule used to apply this filter
108      * is dependent on the server implementation.
109      *
110      * @param attribute The attribute 
111      * @param value The value
112      * @return A new FilterBuilder
113      */
114     public static FilterBuilder approximatelyEqual( String attribute, String value )
115     {
116         return new FilterBuilder( AttributeValueAssertionFilter.approximatelyEqual( attribute, value ) );
117     }
118 
119 
120     /**
121      * Returns a new FilterBuilder for testing equality of an attribute. For 
122      * example:
123      * 
124      * <pre>
125      * equal( "cn", "Kermit The Frog" ).toString();
126      * </pre>
127      * would result in the string:
128      * <pre>
129      * (cn&gt;=Kermit The Frog)
130      * </pre>
131      * 
132      * Which would match entries with the common name 
133      * <code>Kermit The Frog</code>.
134      *
135      * @param attribute The attribute 
136      * @param value The value
137      * @return A new FilterBuilder
138      */
139     public static FilterBuilder equal( String attribute, String value )
140     {
141         return new FilterBuilder( AttributeValueAssertionFilter.equal( attribute, value ) );
142     }
143 
144 
145     /**
146      * Creates an extensible match filter by calling 
147      * {@link #extensible(String, String) extensible(null, value)}.
148      *
149      * @param value The value to test for
150      * @return A new MatchingRuleAssertionFilterBuilder
151      */
152     public static MatchingRuleAssertionFilterBuilder extensible( String value )
153     {
154         return new MatchingRuleAssertionFilterBuilder( null, value );
155     }
156 
157 
158     /**
159      * Creates an extensible match filter.  This filter can be used to specify
160      * that dn attributes should be included in the match, which matcher to 
161      * use, or that all attributes that support a specific matcher will be
162      * checked.  For example:
163      * 
164      * <pre>
165      * extensible( "sn", "Barney Rubble" )
166      *     .useDnAttributes()
167      *     .setMatchingRule( "2.4.6.8.10" )
168      *     .toString();
169      * </pre>
170      * would result in the string:
171      * <pre>
172      * (sn:dn:2.4.6.8.10:=Barney Rubble)
173      * </pre>
174      * 
175      * Not that the specialized filter builder that is returned <b>IS</b> a 
176      * FilterBuilder so it can be chained with other filters.  For example:
177      * 
178      * <pre>
179      * and(
180      *     extensible( "sn", "Rubble" )
181      *         .useDnAttributes()
182      *         .setMatchingRule( "2.4.6.8.10" ),
183      *     equal( "givenName", "Barney" ) )
184      *     .toString();
185      * </pre>
186      *
187      * @param attribute The attribute to test
188      * @param value The value to test for
189      * @return A new MatchingRuleAssertionFilterBuilder
190      */
191     public static MatchingRuleAssertionFilterBuilder extensible( String attribute, String value )
192     {
193         return new MatchingRuleAssertionFilterBuilder( attribute, value );
194     }
195     
196     
197     /**
198      * Returns a new FilterBuilder for testing lexicographical greater than.  
199      * For example:
200      * 
201      * <pre>
202      * greaterThanOrEqual( "sn", "n" ).toString();
203      * </pre>
204      * would result in the string:
205      * <pre>
206      * (sn&gt;=n)
207      * </pre>
208      * 
209      * which would match results whose surname starts with the second half of
210      * the alphabet.  
211      *
212      * @param attribute The attribute 
213      * @param value The value
214      * @return A new FilterBuilder
215      */
216     public static FilterBuilder greaterThanOrEqual( String attribute, String value )
217     {
218         return new FilterBuilder( AttributeValueAssertionFilter.greaterThanOrEqual( attribute, value ) );
219     }
220 
221 
222     /**
223      * Returns a new FilterBuilder for testing lexicographical less than.  For
224      * example:
225      * 
226      * <pre>
227      * lessThanOrEqual( "sn", "mzzzzzz" ).toString();
228      * </pre>
229      * would result in the string:
230      * <pre>
231      * (sn&lt;=mzzzzzz)
232      * </pre>
233      * 
234      * which would match results whose surname starts with the first half of
235      * the alphabet.  <i>Note, this is not perfect, but if you know anybody with
236      * a last name that starts with an <code>m</code> followed by six
237      * <code>z</code>'s...</i>
238      *
239      * @param attribute The attribute 
240      * @param value The value
241      * @return A new FilterBuilder
242      */
243     public static FilterBuilder lessThanOrEqual( String attribute, String value )
244     {
245         return new FilterBuilder( AttributeValueAssertionFilter.lessThanOrEqual( attribute, value ) );
246     }
247 
248 
249     /**
250      * Returns a new FilterBuilder for negating another filter.  For example:
251      * 
252      * <pre>
253      * not( present( "givenName" ) ).toString();
254      * </pre>
255      * would result in the string:
256      * <pre>
257      * (!(givenName=*))
258      * </pre>
259      *
260      * @param builder The filter to negate
261      * @return A new FilterBuilder
262      */
263     public static FilterBuilder not( FilterBuilder builder )
264     {
265         return new FilterBuilder( UnaryFilter.not( builder.filter ) );
266     }
267 
268 
269     /**
270      * Returns a new FilterBuilder that will <code>|</code> together all of the 
271      * supplied filters.  For example:
272      * 
273      * <pre>
274      * or( equal( "givenName", "kermit" ), equal( "givenName", "walter" ) ).toString()
275      * </pre>
276      * would result in the string:
277      * <pre>
278      * (|(givenName=kermit)(givenName=walter))
279      * </pre>
280      * 
281      * Which would match any entry with the <code>givenName</code> of either
282      * <code>kermit</code> or <code>walter</code>.
283      *
284      * @param builders The filters to or together
285      * @return A new FilterBuilder
286      */
287     public static FilterBuilder or( FilterBuilder... builders )
288     {
289         SetOfFiltersFilter filter = SetOfFiltersFilter.or();
290 
291         for ( FilterBuilder builder : builders )
292         {
293             filter.add( builder.filter );
294         }
295 
296         return new FilterBuilder( filter );
297     }
298 
299 
300     /**
301      * Returns a new FilterBuilder for testing the presence of an attributes.  
302      * For example:
303      * 
304      * <pre>
305      * present( "givenName" ).toString();
306      * </pre>
307      * would result in the string:
308      * <pre>
309      * (givenName=*)
310      * </pre>
311      * 
312      * Which would match any entry that has a <code>givenName</code> attribute.
313      *
314      * @param attribute The attribute to test the presence of
315      * @return A new FilterBuilder
316      */
317     public static FilterBuilder present( String attribute )
318     {
319         return new FilterBuilder( AttributeDescriptionFilter.present( attribute ) );
320     }
321 
322 
323     /**
324      * Returns a new FilterBuilder that will construct a SubString filter, with an <em>initial</em> part, 
325      * and zero to N <em>any</em> part, but no <em>final</em> part.  
326      * 
327      * For instance:
328      * 
329      * <pre>
330      * startswith( "sn", "Th", "Soft", "Foun" )).toString()
331      * </pre>
332      * would result in the string:
333      * <pre>
334      * (sn=Th*Soft*Foun*)
335      * </pre>
336      * 
337      * Which would match any entry with the <code>sn</code> starting with <code>'Th'</code>, and 
338      * having a <code>Soft</code> and <code>Foun</code> strings in the middle, like 
339      * 'The Apache Software Foundation'.
340      *
341      * @param attribute The attribute to use in the filter
342      * @param parts The sub elements to use in the filter
343      * @return A new FilterBuilder
344      */
345     public static FilterBuilder startsWith( String attribute, String... parts )
346     {
347         if ( ( parts == null ) || ( parts.length == 0 ) )
348         {
349             throw new IllegalArgumentException( "An 'initial' part is needed" );
350         }
351 
352         return new FilterBuilder( SubstringFilter.startsWith( attribute, parts ) );
353     }
354 
355 
356     /**
357      * Returns a new FilterBuilder that will construct a SubString filter, with an <em>initial</em> part, 
358      * and zero to N <em>any</em> parts, but no <em>final</em> part.  
359      * 
360      * For instance:
361      * 
362      * <pre>
363      * startswith( "sn", "Th", "Soft", "Foun" ).toString()
364      * </pre>
365      * would result in the string:
366      * <pre>
367      * (sn=Th*Soft*Foun*)
368      * </pre>
369      * 
370      * Which would match any entry with the <code>sn</code> starting with <code>'Th'</code>, and 
371      * having a <code>Soft</code> and <code>Foun</code> strings in the middle, like 
372      * 'The Apache Software Foundation'.
373      *
374      * @param attribute The attribute to use in the filter
375      * @param parts The sub elements to use in the filter
376      * @return A new FilterBuilder
377      */
378     public static FilterBuilder endsWith( String attribute, String... parts )
379     {
380         if ( ( parts == null ) || ( parts.length == 0 ) )
381         {
382             throw new IllegalArgumentException( "At a 'final' part is needed" );
383         }
384 
385         return new FilterBuilder( SubstringFilter.endsWith( attribute, parts ) );
386     }
387 
388 
389     /**
390      * Returns a new FilterBuilder that will construct a SubString filter, with zero to N <em>any</em> parts, 
391      * but no <em>initial</em> or <em>final</em> parts.  
392      * 
393      * For instance:
394      * 
395      * <pre>
396      * contains( "sn", "Soft", "Foun" )).toString()
397      * </pre>
398      * would result in the string:
399      * <pre>
400      * (sn=*Soft*Foun*)
401      * </pre>
402      * 
403      * Which would match any entry with the <code>sn</code> having a <code>Soft</code> 
404      * and <code>Foun</code> strings in the middle, like 
405      * 'The Apache Software Foundation'.
406      *
407      * @param attribute The attribute to use in the filter
408      * @param parts The sub elements to use in the filter
409      * @return A new FilterBuilder
410      */
411     public static FilterBuilder contains( String attribute, String... parts )
412     {
413         if ( ( parts == null ) || ( parts.length == 0 ) )
414         {
415             throw new IllegalArgumentException( "At least one 'any' part is needed" );
416         }
417 
418         return new FilterBuilder( SubstringFilter.contains( attribute, parts ) );
419     }
420 
421 
422     /**
423      * Returns a new FilterBuilder that will construct a SubString filter, with a <em>initial</em> part, 
424      * zero to N <em>any</em> parts, and a <em>final</em> part.
425      * 
426      * For instance:
427      * 
428      * <pre>
429      * substring( "sn", "The", "Soft", "Foun", "ion" )).toString()
430      * </pre>
431      * would result in the string:
432      * <pre>
433      * (sn=The*Soft*Foun*ion)
434      * </pre>
435      * 
436      * Which would match any entry with the <code>sn</code> having a <code>Soft</code> 
437      * and <code>Foun</code> strings in the middle, starts with <code>The</code> and ends with <code>ion</code> like 
438      * 'The Apache Software Foundation'.
439      * <p>
440      * Note that if we have only two strings in the parts, they will be the <em>initial</em> and <em>final</em> ones :
441      * 
442      * <pre>
443      * substring( "sn", "The", "ion" )).toString()
444      * </pre>
445      * would result in the string:
446      * <pre>
447      * (sn=The*ion)
448      * </pre>
449      * 
450      * @param attribute The attribute to use in the filter
451      * @param parts The sub elements to use in the filter
452      * @return A new FilterBuilder
453      */
454     public static FilterBuilder substring( String attribute, String... parts )
455     {
456         if ( ( parts == null ) || ( parts.length == 0 ) )
457         {
458             throw new IllegalArgumentException( "At least one if 'initial', 'any' or 'final' part is needed" );
459         }
460 
461         return new FilterBuilder( SubstringFilter.substring( attribute, parts ) );
462     }
463 
464 
465     /**
466      * Returns the string version of the filter represented by this FilterBuilder.
467      * 
468      * @return The string representation of the filter
469      */
470     @Override
471     public String toString()
472     {
473         return filter.build().toString();
474     }
475 }