blob: 7553789963e0deebc01b32aaea0e180eab16b20b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
package org.apache.directory.ldap.client.api.search;
import org.apache.directory.api.i18n.I18n;
/**
* A builder for constructing well formed search filters according to
* <a href="https://tools.ietf.org/html/rfc4515.html">RFC 4515</a>. This
* builder is most convenient when you use static imports. For example:
* <pre>
* import static org.apache.directory.ldap.client.api.search.FilterBuilder.and;
* import static org.apache.directory.ldap.client.api.search.FilterBuilder.equal;
* import static org.apache.directory.ldap.client.api.search.FilterBuilder.or;
*
* ...
*
* String filter =
* or(
* and(
* equal( "givenName", "kermit" ),
* equal( "sn", "the frog" ) ),
* and(
* equal( "givenName", "miss" ),
* equal( "sn", "piggy" ) ) )
* .toString()
* </pre>
*
* @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
*/
public class FilterBuilder
{
/** The built filter */
/* No qualifier */ Filter filter;
/**
* A private constructor that creates a new instance of a FilterBuilder
* containing a given filter.
*
* @param filter The filter
*/
/* No qualifier*/ FilterBuilder( Filter filter )
{
this.filter = filter;
}
/**
* Returns a new FilterBuilder that will <code>&amp;</code> together all of the
* supplied filters. For example:
*
* <pre>
* and( equal( "givenName", "kermit" ), equal( "sn", "the frog" ) ).toString()
* </pre>
* would result in the string:
* <pre>
* (&amp;(givenName=kermit)(sn=the frog))
* </pre>
*
* Which would match all entries with a given name of <code>kermit</code>
* and a surname <code>the frog</code>.
*
* @param filters The filters to and together
* @return A new FilterBuilder
*/
public static FilterBuilder and( FilterBuilder... filters )
{
SetOfFiltersFilter filter = SetOfFiltersFilter.and();
for ( FilterBuilder builder : filters )
{
filter.add( builder.filter );
}
return new FilterBuilder( filter );
}
/**
* Returns a new FilterBuilder for testing the approximate equality of an
* attribute. For example:
*
* <pre>
* approximatelyEqual( "l", "san fransico" ).toString();
* </pre>
* would result in the string:
* <pre>
* (l~=san fransico)
* </pre>
*
* Which <i>MIGHT</i> match results whose locality is
* <code>San Francisco</code>. The matching rule used to apply this filter
* is dependent on the server implementation.
*
* @param attribute The attribute
* @param value The value
* @return A new FilterBuilder
*/
public static FilterBuilder approximatelyEqual( String attribute, String value )
{
return new FilterBuilder( AttributeValueAssertionFilter.approximatelyEqual( attribute, value ) );
}
/**
* Returns a new FilterBuilder for testing equality of an attribute. For
* example:
*
* <pre>
* equal( "cn", "Kermit The Frog" ).toString();
* </pre>
* would result in the string:
* <pre>
* (cn&gt;=Kermit The Frog)
* </pre>
*
* Which would match entries with the common name
* <code>Kermit The Frog</code>.
*
* @param attribute The attribute
* @param value The value
* @return A new FilterBuilder
*/
public static FilterBuilder equal( String attribute, String value )
{
return new FilterBuilder( AttributeValueAssertionFilter.equal( attribute, value ) );
}
/**
* Creates an extensible match filter by calling
* {@link #extensible(String, String) extensible(null, value)}.
*
* @param value The value to test for
* @return A new MatchingRuleAssertionFilterBuilder
*/
public static MatchingRuleAssertionFilterBuilder extensible( String value )
{
return new MatchingRuleAssertionFilterBuilder( null, value );
}
/**
* Creates an extensible match filter. This filter can be used to specify
* that dn attributes should be included in the match, which matcher to
* use, or that all attributes that support a specific matcher will be
* checked. For example:
*
* <pre>
* extensible( "sn", "Barney Rubble" )
* .useDnAttributes()
* .setMatchingRule( "2.4.6.8.10" )
* .toString();
* </pre>
* would result in the string:
* <pre>
* (sn:dn:2.4.6.8.10:=Barney Rubble)
* </pre>
*
* Not that the specialized filter builder that is returned <b>IS</b> a
* FilterBuilder so it can be chained with other filters. For example:
*
* <pre>
* and(
* extensible( "sn", "Rubble" )
* .useDnAttributes()
* .setMatchingRule( "2.4.6.8.10" ),
* equal( "givenName", "Barney" ) )
* .toString();
* </pre>
*
* @param attribute The attribute to test
* @param value The value to test for
* @return A new MatchingRuleAssertionFilterBuilder
*/
public static MatchingRuleAssertionFilterBuilder extensible( String attribute, String value )
{
return new MatchingRuleAssertionFilterBuilder( attribute, value );
}
/**
* Returns a new FilterBuilder for testing lexicographical greater than.
* For example:
*
* <pre>
* greaterThanOrEqual( "sn", "n" ).toString();
* </pre>
* would result in the string:
* <pre>
* (sn&gt;=n)
* </pre>
*
* which would match results whose surname starts with the second half of
* the alphabet.
*
* @param attribute The attribute
* @param value The value
* @return A new FilterBuilder
*/
public static FilterBuilder greaterThanOrEqual( String attribute, String value )
{
return new FilterBuilder( AttributeValueAssertionFilter.greaterThanOrEqual( attribute, value ) );
}
/**
* Returns a new FilterBuilder for testing lexicographical less than. For
* example:
*
* <pre>
* lessThanOrEqual( "sn", "mzzzzzz" ).toString();
* </pre>
* would result in the string:
* <pre>
* (sn&lt;=mzzzzzz)
* </pre>
*
* which would match results whose surname starts with the first half of
* the alphabet. <i>Note, this is not perfect, but if you know anybody with
* a last name that starts with an <code>m</code> followed by six
* <code>z</code>'s...</i>
*
* @param attribute The attribute
* @param value The value
* @return A new FilterBuilder
*/
public static FilterBuilder lessThanOrEqual( String attribute, String value )
{
return new FilterBuilder( AttributeValueAssertionFilter.lessThanOrEqual( attribute, value ) );
}
/**
* Returns a new FilterBuilder for negating another filter. For example:
*
* <pre>
* not( present( "givenName" ) ).toString();
* </pre>
* would result in the string:
* <pre>
* (!(givenName=*))
* </pre>
*
* @param builder The filter to negate
* @return A new FilterBuilder
*/
public static FilterBuilder not( FilterBuilder builder )
{
return new FilterBuilder( UnaryFilter.not( builder.filter ) );
}
/**
* Returns a new FilterBuilder that will <code>|</code> together all of the
* supplied filters. For example:
*
* <pre>
* or( equal( "givenName", "kermit" ), equal( "givenName", "walter" ) ).toString()
* </pre>
* would result in the string:
* <pre>
* (|(givenName=kermit)(givenName=walter))
* </pre>
*
* Which would match any entry with the <code>givenName</code> of either
* <code>kermit</code> or <code>walter</code>.
*
* @param builders The filters to or together
* @return A new FilterBuilder
*/
public static FilterBuilder or( FilterBuilder... builders )
{
SetOfFiltersFilter filter = SetOfFiltersFilter.or();
for ( FilterBuilder builder : builders )
{
filter.add( builder.filter );
}
return new FilterBuilder( filter );
}
/**
* Returns a new FilterBuilder for testing the presence of an attributes.
* For example:
*
* <pre>
* present( "givenName" ).toString();
* </pre>
* would result in the string:
* <pre>
* (givenName=*)
* </pre>
*
* Which would match any entry that has a <code>givenName</code> attribute.
*
* @param attribute The attribute to test the presence of
* @return A new FilterBuilder
*/
public static FilterBuilder present( String attribute )
{
return new FilterBuilder( AttributeDescriptionFilter.present( attribute ) );
}
/**
* Returns a new FilterBuilder that will construct a SubString filter, with an <em>initial</em> part,
* and zero to N <em>any</em> part, but no <em>final</em> part.
*
* For instance:
*
* <pre>
* startswith( "sn", "Th", "Soft", "Foun" )).toString()
* </pre>
* would result in the string:
* <pre>
* (sn=Th*Soft*Foun*)
* </pre>
*
* Which would match any entry with the <code>sn</code> starting with <code>'Th'</code>, and
* having a <code>Soft</code> and <code>Foun</code> strings in the middle, like
* 'The Apache Software Foundation'.
*
* @param attribute The attribute to use in the filter
* @param parts The sub elements to use in the filter
* @return A new FilterBuilder
*/
public static FilterBuilder startsWith( String attribute, String... parts )
{
if ( ( parts == null ) || ( parts.length == 0 ) )
{
throw new IllegalArgumentException( I18n.err( I18n.ERR_04162_INITIAL_PART_NEEDED ) );
}
return new FilterBuilder( SubstringFilter.startsWith( attribute, parts ) );
}
/**
* Returns a new FilterBuilder that will construct a SubString filter, with an <em>initial</em> part,
* and zero to N <em>any</em> parts, but no <em>final</em> part.
*
* For instance:
*
* <pre>
* startswith( "sn", "Th", "Soft", "Foun" ).toString()
* </pre>
* would result in the string:
* <pre>
* (sn=Th*Soft*Foun*)
* </pre>
*
* Which would match any entry with the <code>sn</code> starting with <code>'Th'</code>, and
* having a <code>Soft</code> and <code>Foun</code> strings in the middle, like
* 'The Apache Software Foundation'.
*
* @param attribute The attribute to use in the filter
* @param parts The sub elements to use in the filter
* @return A new FilterBuilder
*/
public static FilterBuilder endsWith( String attribute, String... parts )
{
if ( ( parts == null ) || ( parts.length == 0 ) )
{
throw new IllegalArgumentException( I18n.err( I18n.ERR_04163_FINAL_PART_NEEDED ) );
}
return new FilterBuilder( SubstringFilter.endsWith( attribute, parts ) );
}
/**
* Returns a new FilterBuilder that will construct a SubString filter, with zero to N <em>any</em> parts,
* but no <em>initial</em> or <em>final</em> parts.
*
* For instance:
*
* <pre>
* contains( "sn", "Soft", "Foun" )).toString()
* </pre>
* would result in the string:
* <pre>
* (sn=*Soft*Foun*)
* </pre>
*
* Which would match any entry with the <code>sn</code> having a <code>Soft</code>
* and <code>Foun</code> strings in the middle, like
* 'The Apache Software Foundation'.
*
* @param attribute The attribute to use in the filter
* @param parts The sub elements to use in the filter
* @return A new FilterBuilder
*/
public static FilterBuilder contains( String attribute, String... parts )
{
if ( ( parts == null ) || ( parts.length == 0 ) )
{
throw new IllegalArgumentException( I18n.err( I18n.ERR_04164_ANY_PART_NEEDED ) );
}
return new FilterBuilder( SubstringFilter.contains( attribute, parts ) );
}
/**
* Returns a new FilterBuilder that will construct a SubString filter, with a <em>initial</em> part,
* zero to N <em>any</em> parts, and a <em>final</em> part.
*
* For instance:
*
* <pre>
* substring( "sn", "The", "Soft", "Foun", "ion" )).toString()
* </pre>
* would result in the string:
* <pre>
* (sn=The*Soft*Foun*ion)
* </pre>
*
* Which would match any entry with the <code>sn</code> having a <code>Soft</code>
* and <code>Foun</code> strings in the middle, starts with <code>The</code> and ends with <code>ion</code> like
* 'The Apache Software Foundation'.
* <p>
* Note that if we have only two strings in the parts, they will be the <em>initial</em> and <em>final</em> ones :
*
* <pre>
* substring( "sn", "The", "ion" )).toString()
* </pre>
* would result in the string:
* <pre>
* (sn=The*ion)
* </pre>
*
* @param attribute The attribute to use in the filter
* @param parts The sub elements to use in the filter
* @return A new FilterBuilder
*/
public static FilterBuilder substring( String attribute, String... parts )
{
if ( ( parts == null ) || ( parts.length == 0 ) )
{
throw new IllegalArgumentException( I18n.err( I18n.ERR_04165_INITIAL_ANY_FINAL_PART_NEEDED ) );
}
return new FilterBuilder( SubstringFilter.substring( attribute, parts ) );
}
/**
* Returns the string version of the filter represented by this FilterBuilder.
*
* @return The string representation of the filter
*/
@Override
public String toString()
{
return filter.build().toString();
}
}