/*
* ====================
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 2008-2009 Sun Microsystems, Inc. All rights reserved.
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License("CDDL") (the "License"). You may not use this file
* except in compliance with the License.
*
* You can obtain a copy of the License at
* http://opensource.org/licenses/cddl1.php
* See the License for the specific language governing permissions and limitations
* under the License.
*
* When distributing the Covered Code, include this CDDL Header Notice in each file
* and include the License file at http://opensource.org/licenses/cddl1.php.
* If applicable, add the following below this CDDL Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
* ====================
* Portions Copyrighted 2014 ForgeRock AS.
*/
package org.identityconnectors.framework.common.objects.filter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import org.identityconnectors.framework.common.objects.Attribute;
import org.identityconnectors.framework.common.objects.ConnectorObject;
/**
* FilterBuilder creates a {@linkplain Filter filter} that will
* {@linkplain Filter#accept match} any {@code ConnectorObject} that satisfies
* all of the selection criteria that were specified using this builder.
*
* @author Will Droste
* @since 1.0
*/
public final class FilterBuilder {
private FilterBuilder() {
}
/**
* Select only an input <code>ConnectorObject</code> with a value for the
* specified <code>Attribute</code> that
* <em>contains as a final substring</em> the value of the specified
* <code>Attribute</code>.
* <p>
* For example, if the specified <code>Attribute</code> were
* <code>{"hairColor": "d"}</code>, <br>
* this would match any <code>ConnectorObject</code> with a value such as <br>
* <code>{"hairColor": "red"}</code> or <br>
* <code>{"hairColor": "blond"}</code> <br>
* but would <em>not</em> match any <code>ConnectorObject</code> that
* contains only values such as <br>
* <code>{"hairColor": "blonde"}</code> or <br>
* <code>{"hairColor": "auburn"}</code>. <br>
* This also would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <code>{"hairColor": null}</code> <br>
* or that lacks the attribute <code>"hairColor"</code>.
*
* @param attr
* <code>Attribute</code> <em>containing exactly one value</em>
* to test against each value of the corresponding
* <code>ConnectorObject</code> attribute.
* @return an instance of <code>Filter</code> whose <code>accept()</code>
* method will return <code>true</code> if at least one value of the
* corresponding attribute of the <code>ConnectorObject</code>
* <em>contains as its last part</em> the value of the specified
* <code>Attribute</code>; otherwise <code>false</code>.
*/
public static Filter endsWith(final Attribute attr) {
return new EndsWithFilter(attr);
}
/**
* Select only an input <code>ConnectorObject</code> with a value for the
* specified <code>Attribute</code> that contains as an
* <em>initial substring</em> the value of the specified
* <code>Attribute</code>.
* <p>
* For example, if the specified <code>Attribute</code> were
* <code>{"hairColor": "b"}</code>, <br>
* this would match any <code>ConnectorObject</code> with a value such as <br>
* <code>{"hairColor": "brown"}</code> or <br>
* <code>{"hairColor": "blond"}</code> <br>
* but would <em>not</em> match any <code>ConnectorObject</code> that
* contains only values such as <br>
* <code>{"hairColor": "red"}</code> or <br>
* <code>{"hairColor": "auburn"}</code>. <br>
* This also would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <code>{"hairColor": null}</code> <br>
* or that lacks the attribute <code>"hairColor"</code>.
*
* @param attr
* <code>Attribute</code> <em>containing exactly one value</em>
* to test against each value of the corresponding
* <code>ConnectorObject</code> attribute.
* @return an instance of <code>Filter</code> whose <code>accept()</code>
* method will return <code>true</code> if at least one value of the
* corresponding attribute of the <code>ConnectorObject</code>
* <em>contains as its first part</em> the value of the specified
* <code>Attribute</code>; otherwise <code>false</code>.
*/
public static Filter startsWith(final Attribute attr) {
return new StartsWithFilter(attr);
}
/**
* Select only an input <code>ConnectorObject</code> with a value for the
* specified <code>Attribute</code> that <em>contains as any substring</em>
* the value of the specified <code>Attribute</code>.
* <p>
* For example, if the specified <code>Attribute</code> were
* <code>{"hairColor": "a"}</code>, <br>
* this would match any <code>ConnectorObject</code> with a value such as <br>
* <code>{"hairColor": "auburn"}</code> or <br>
* <code>{"hairColor": "gray"}</code> <br>
* but would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <br>
* <code>{"hairColor": "red"}</code> or <br>
* <code>{"hairColor": "grey"}</code>. <br>
* This also would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <code>{"hairColor": null}</code> <br>
* or that lacks the attribute <code>"hairColor"</code>.
*
* @param attr
* <code>Attribute</code> <em>containing exactly one</em> value
* to test against each value of the corresponding
* <code>ConnectorObject</code> attribute.
* @return an instance of <code>Filter</code> whose <code>accept()</code>
* method will return <code>true</code> if at least one value of the
* corresponding attribute of the <code>ConnectorObject</code>
* <em>contains anywhere within it</em> the value of the specified
* <code>Attribute</code>; otherwise <code>false</code>.
*/
public static Filter contains(final Attribute attr) {
return new ContainsFilter(attr);
}
/**
* Select only an input <code>ConnectorObject</code> with a value for the
* specified <code>Attribute</code> that is <em>lexically equal to</em> the
* value of the specified <code>Attribute</code>.
* <p>
* <b>NOTE: Is comparison case-sensitive?</b>
* <p>
* For example, if the specified <code>Attribute</code> were
* <code>{"hairColor": "brown"}</code>, <br>
* this would match any <code>ConnectorObject</code> with a value such as <br>
* <code>{"hairColor": "brown"}</code> or <br>
* <code>{"hairColor": "BROWN"}</code> <br>
* but would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <br>
* <code>{"hairColor": "brownish-gray"}</code> or <br>
* <code>{"hairColor": "auburn"}</code>. <br>
* This also would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <code>{"hairColor": null}</code> <br>
* or that lacks the attribute <code>"hairColor"</code>.
* <p>
* <b>NOTE:</b> <i>Lexical</i> comparison of two string values compares the
* characters of each value, even if the string values could be interpreted
* as numeric. The values <code>"01"</code> and <code>"1"</code> are unequal
* lexically, although they would be equivalent arithmetically.
* <p>
* Two attributes with binary syntax are equal if and only if their
* constituent bytes match.
*
* @param attr
* <code>Attribute</code> <em>containing exactly one value</em>
* to test against each value of the corresponding
* <code>ConnectorObject</code> attribute.
* @return an instance of <code>Filter</code> whose <code>accept()</code>
* method will return <code>true</code> if at least one value of the
* corresponding attribute of the <code>ConnectorObject</code>
* <em>matches lexically</em> the value of the specified
* <code>Attribute</code>; otherwise <code>false</code>.
*/
public static Filter equalTo(final Attribute attr) {
return new EqualsFilter(attr);
}
/**
* Select only an input <code>ConnectorObject</code> with a value for the
* specified <code>Attribute</code> that is
* <em>lexically greater than or equal to</em> the value of the specified
* <code>Attribute</code>.
* <p>
* <b>NOTE: Is comparison case-sensitive?</b>
* <p>
* For example, if the specified <code>Attribute</code> were
* <code>{"hairColor": "brown"}</code>, <br>
* this would match any <code>ConnectorObject</code> with a value such as <br>
* <code>{"hairColor": "brown"}</code> or <br>
* <code>{"hairColor": "brownish-gray"}</code> or <br>
* <code>{"hairColor": "red"}</code> <br>
* but would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <br>
* <code>{"hairColor": "black"}</code> or <br>
* <code>{"hairColor": "blond"}</code> or <br>
* <code>{"hairColor": "auburn"}</code>. <br>
* This also would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <code>{"hairColor": null}</code> <br>
* or that lacks the attribute <code>"hairColor"</code>.
* <p>
* <b>NOTE:</b> <i>Lexical</i> comparison of two string values compares the
* characters of each value, even if the string values could be interpreted
* as numeric. <br>
* When compared lexically, <code>"99"</code> is greater than
* <code>"123"</code>. <br>
* When compared arithmetically, <code>99</code> is less than
* <code>123</code>.
*
* @param attr
* <code>Attribute</code> <em>containing exactly one value</em>
* to test against each value of the corresponding
* <code>ConnectorObject</code> attribute.
* @return an instance of <code>Filter</code> whose <code>accept()</code>
* method will return <code>true</code> if at least one value of the
* corresponding attribute of the <code>ConnectorObject</code>
* <em>matches or sorts alphabetically after</em> the value of the
* specified <code>Attribute</code>; otherwise <code>false</code>.
*/
public static Filter greaterThanOrEqualTo(final Attribute attr) {
return new GreaterThanOrEqualFilter(attr);
}
/**
* Select only an input <code>ConnectorObject</code> with a value for the
* specified <code>Attribute</code> that is
* <em>lexically less than or equal to</em> the value of the specified
* <code>Attribute</code>.
* <p>
* <b>NOTE: Is comparison case-sensitive?</b>
* <p>
* For example, if the specified <code>Attribute</code> were
* <code>{"hairColor": "brown"}</code>, <br>
* this would match any <code>ConnectorObject</code> with a value such as <br>
* <code>{"hairColor": "brown"}</code> or <br>
* <code>{"hairColor": "black"}</code> or <br>
* <code>{"hairColor": "blond"}</code> or <br>
* <code>{"hairColor": "auburn"}</code> <br>
* but would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <br>
* <code>{"hairColor": "brownish-gray"}</code> or <br>
* <code>{"hairColor": "red"}</code> <br>
* This also would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <code>{"hairColor": null}</code> <br>
* or that lacks the attribute <code>"hairColor"</code>.
* <p>
* <b>NOTE:</b> <i>Lexical</i> comparison of two string values compares the
* characters of each value, even if the string values could be interpreted
* as numeric. <br>
* When compared lexically, <code>"99"</code> is greater than
* <code>"123"</code>. <br>
* When compared arithmetically, <code>99</code> is less than
* <code>123</code>.
*
* @param attr
* <code>Attribute</code> <em>containing exactly one value</em>
* to test against each value of the corresponding
* <code>ConnectorObject</code> attribute.
* @return an instance of <code>Filter</code> whose <code>accept()</code>
* method will return <code>true</code> if at least one value of the
* corresponding attribute of the <code>ConnectorObject</code>
* <em>matches or sorts alphabetically before</em> the value of the
* specified <code>Attribute</code>; otherwise <code>false</code>.
*/
public static Filter lessThanOrEqualTo(final Attribute attr) {
return new LessThanOrEqualFilter(attr);
}
/**
* Select only an input <code>ConnectorObject</code> with a value for the
* specified <code>Attribute</code> that is <em>lexically less than</em> the
* value of the specified <code>Attribute</code>.
* <p>
* <b>NOTE: Is comparison case-sensitive?</b>
* <p>
* For example, if the specified <code>Attribute</code> were
* <code>{"hairColor": "brown"}</code>, <br>
* this would match any <code>ConnectorObject</code> with a value such as <br>
* <code>{"hairColor": "black"}</code> or <br>
* <code>{"hairColor": "blond"}</code> or <br>
* <code>{"hairColor": "auburn"}</code> <br>
* but would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <br>
* <code>{"hairColor": "brown"}</code> or <br>
* <code>{"hairColor": "brownish-gray"}</code> or <br>
* <code>{"hairColor": "red"}</code> <br>
* This also would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <code>{"hairColor": null}</code> <br>
* or that lacks the attribute <code>"hairColor"</code>.
* <p>
* <b>NOTE:</b> <i>Lexical</i> comparison of two string values compares the
* characters of each value, even if the string values could be interpreted
* as numeric. <br>
* When compared lexically, <code>"99"</code> is greater than
* <code>"123"</code>. <br>
* When compared arithmetically, <code>99</code> is less than
* <code>123</code>.
*
* @param attr
* <code>Attribute</code> <em>containing exactly one value</em>
* to test against each value of the corresponding
* <code>ConnectorObject</code> attribute.
* @return an instance of <code>Filter</code> whose <code>accept()</code>
* method will return <code>true</code> if at least one value of the
* corresponding attribute of the <code>ConnectorObject</code>
* <em>sorts alphabetically before</em> the value of the specified
* <code>Attribute</code>; otherwise <code>false</code>.
*/
public static Filter lessThan(final Attribute attr) {
return new LessThanFilter(attr);
}
/**
* Select only an input <code>ConnectorObject</code> with a value for the
* specified <code>Attribute</code> that is <em>lexically greater than</em>
* the value of the specified <code>Attribute</code>.
* <p>
* <b>NOTE: Is comparison case-sensitive?</b>
* <p>
* For example, if the specified <code>Attribute</code> were
* <code>{"hairColor": "brown"}</code>, <br>
* this would match any <code>ConnectorObject</code> with a value such as <br>
* <code>{"hairColor": "brownish-gray"}</code> or <br>
* <code>{"hairColor": "red"}</code> <br>
* but would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <br>
* <code>{"hairColor": "brown"}</code> or <br>
* <code>{"hairColor": "black"}</code> or <br>
* <code>{"hairColor": "blond"}</code> or <br>
* <code>{"hairColor": "auburn"}</code>. <br>
* This also would <em>not</em> match any <code>ConnectorObject</code> that
* contains only <code>{"hairColor": null}</code> <br>
* or that lacks the attribute <code>"hairColor"</code>.
* <p>
* <b>NOTE:</b> <i>Lexical</i> comparison of two string values compares the
* characters of each value, even if the string values could be interpreted
* as numeric. <br>
* When compared lexically, <code>"99"</code> is greater than
* <code>"123"</code>. <br>
* When compared arithmetically, <code>99</code> is less than
* <code>123</code>.
*
* @param attr
* <code>Attribute</code> <em>containing exactly one value</em>
* to test against each value of the corresponding
* <code>ConnectorObject</code> attribute.
* @return an instance of <code>Filter</code> whose <code>accept()</code>
* method will return <code>true</code> if at least one value of the
* corresponding attribute of the <code>ConnectorObject</code>
* <em>sorts alphabetically after</em> the value of the specified
* <code>Attribute</code>; otherwise <code>false</code>.
*/
public static Filter greaterThan(final Attribute attr) {
return new GreaterThanFilter(attr);
}
/**
* Logically "ANDs" together the two specified instances of {@link Filter}.
* The resulting <i>conjunct</i> <code>Filter</code> is true if and only if
* both of the specified filters are true.
*
* @param leftHandSide
* left-hand-side filter.
* @param rightHandSide
* right-hand-side filter.
* @return the result of
* <code>(leftHandSide && rightHandSide)</code>
*/
public static Filter and(final Filter leftHandSide, final Filter rightHandSide) {
return new AndFilter(leftHandSide, rightHandSide);
}
/**
* Creates a new "AND" filter using the provided list of sub-filters.
* <p>
* Creating a new "AND" filter with a {@code null} or empty list of
* sub-filters is equivalent to calling "alwaysTrue".
*
* @param subFilters
* The list of sub-filters, may be empty or {@code null}.
* @return The newly created "AND" filter.
*/
public static Filter and(final Collection<Filter> subFilters) {
switch (subFilters.size()) {
case 0:
return null;
case 1:
return subFilters.iterator().next();
default:
return new AndFilter(new ArrayList<Filter>(subFilters));
}
}
/**
* Creates a new "AND" filter using the provided list of sub-filters.
* <p>
* Creating a new "AND" filter with a {@code null} or empty list of
* sub-filters is equivalent to calling "alwaysTrue".
*
* @param subFilters
* The list of sub-filters, may be empty or {@code null}.
* @return The newly created "AND" filter.
*/
public static Filter and(final Filter... subFilters) {
return and(Arrays.asList(subFilters));
}
/**
* Logically "OR" together the two specified instances of {@link Filter}.
* The resulting <i>disjunct</i> <code>Filter</code> is true if and only if
* at least one of the specified filters is true.
*
* @param leftHandSide
* left-hand-side filter.
* @param rightHandSide
* right-hand-side filter.
* @return the result of <code>(leftHandSide || rightHandSide)</code>
*/
public static Filter or(final Filter leftHandSide, final Filter rightHandSide) {
return new OrFilter(leftHandSide, rightHandSide);
}
/**
* Creates a new "OR" filter using the provided list of sub-filters.
* <p>
* Creating a new "OR" filter with a {@code null} or empty list of
* sub-filters is equivalent to "alwaysTrue".
*
* @param subFilters
* The list of sub-filters, may be empty or {@code null}.
* @return The newly created {@code or} filter.
*/
public static Filter or(final Collection<Filter> subFilters) {
switch (subFilters.size()) {
case 0:
return null;
case 1:
return subFilters.iterator().next();
default:
return new OrFilter(new ArrayList<Filter>(subFilters));
}
}
/**
* Creates a new "OR" filter using the provided list of sub-filters.
* <p>
* Creating a new "OR" filter with a {@code null} or empty list of
* sub-filters is equivalent to "alwaysTrue".
*
* @param subFilters
* The list of sub-filters, may be empty or {@code null}.
* @return The newly created {@code or} filter.
*/
public static Filter or(final Filter... subFilters) {
return or(Arrays.asList(subFilters));
}
/**
* Logically negate the specified {@link Filter}. The resulting
* <code>Filter</code> is true if and only if the specified filter is false.
*
* @param filter
* the <code>Filter</code> to negate.
* @return the result of <code>(!filter)</code>.
*/
public static Filter not(final Filter filter) {
return new NotFilter(filter);
}
/**
* Select only an input {@link ConnectorObject} with a value for the
* specified <code>Attribute</code> that contains all the values from the
* specified <code>Attribute</code>.
* <p>
* For example, if the specified {@link Attribute} were
* <code>{"hairColor": "brown", "red"}</code>, <br>
* this would match any <code>ConnectorObject</code> with values such as
* <ul>
* <li><code>{"hairColor": "black", "brown", "red"}</code></li>
* <li><code>{"hairColor": "blond", "brown", "red"}</code></li>
* <li><code>{"hairColor": "auburn", "brown", "red"}</code></li>
* </ul>
* but would <em>not</em> match any {@link ConnectorObject} that contains
* only
* <ul>
* <li><code>{"hairColor": "brown"}</code></li>
* <li><code>{"hairColor": "brownish-gray"}</code></li>
* <li><code>{"hairColor": "red"}</code></li>
* </ul>
* This also would <em>not</em> match any {@link ConnectorObject} that
* contains only <code>{"hairColor": null}</code> <br>
* or that lacks the attribute <code>"hairColor"</code>.
* <p>
* <b>NOTE:</b> <i>Lexical</i> comparison of two string values compares the
* characters of each value, even if the string values could be interpreted
* as numeric. <br>
*
* @param attr
* {@link Attribute} <em>containing exactly one value</em> to
* test against each value of the corresponding
* <code>ConnectorObject</code> attribute.
* @return an instance of {@link Filter} whose <code>accept()</code> method
* will return <code>true</code> if at least one value of the
* corresponding attribute of the {@link ConnectorObject}
* <em>sorts alphabetically before</em> the value of the specified
* {@link Attribute}; otherwise <code>false</code>.
*/
public static Filter containsAllValues(final Attribute attr) {
return new ContainsAllValuesFilter(attr);
}
}