All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.servicerocket.confluence.randombits.filtering.core.criteria.GroupCriteria Maven / Gradle / Ivy

There is a newer version: 2.5.12
Show newest version
/*
 * Copyright (c) 2006, David Peterson
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of "randombits.org" nor the names of its contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

package com.servicerocket.confluence.randombits.filtering.core.criteria;

import java.util.*;

/**
 * Groups together griteria, either requiring that any or
 * all criteria match the specified object being compared.
 *
 * @author David Peterson
 */
public class GroupCriteria implements Criteria, Iterable, SourceCriterion {

    private static final SourceCriterionComparator COMPARATOR = new SourceCriterionComparator();

    private List> sourceCriteria = new ArrayList>();

    private List regularCriteria = new ArrayList();

    private boolean matchAll;

    private Weight weight = Weight.LIGHT;

    /**
     * Constructs the group criterion.
     *
     * @param matchAll If true, all criteria must match, otherwise
     *                 any criteria may match.
     * @param criteria One or more criteria to be matched against.
     */
    public GroupCriteria( boolean matchAll, Criterion... criteria ) {
        this.matchAll = matchAll;
        for ( Criterion criterion : criteria ) {
            addCriterion( criterion );
        }
    }

    public void addCriterion( Criterion criterion ) {
        if ( criterion instanceof SourceCriterion ) {
            SourceCriterion source = (SourceCriterion) criterion;
            sourceCriteria.add( source );
            weight = weight.compareTo( source.getWeight() ) <= 0 ? weight : source.getWeight();
            Collections.sort( sourceCriteria, COMPARATOR );
        } else if ( criterion != null ) {
            regularCriteria.add( criterion );
        }
    }

    public Collection getCriteria() {
        List allCriterion = new ArrayList();
        allCriterion.addAll( sourceCriteria );
        allCriterion.addAll( regularCriteria );
        return allCriterion;
    }

    /**
     * Checks to see if the object matches all or any of the criteria specified
     * for this matcher.
     *
     * @param object The object to match.
     * @return true if the object matches all/any of the
     *         criteria.
     */
    public boolean matches( Object object ) {
        return matchesAll( object, sourceCriteria ) && matchesAll( object, regularCriteria );
    }

    private boolean matchesAll( Object object, List criteria ) {
        for ( Criterion c : criteria ) {
            if ( c.matches( object ) ) {
                if ( !matchAll )
                    return true;
            } else if ( matchAll ) {
                return false;
            }
        }

        // If we are matching all, getting here means we succeeded.
        // If we are matching any, getting here means we failed, since we should
        // have returned true on the first success.
        // However, if there are no criteria at all, return true regardless...
        return matchAll || criteria.size() == 0;
    }

    public boolean isMatchAll() {
        return matchAll;
    }

    public Iterator iterator() {
        return getCriteria().iterator();
    }

    public Collection getMatchingValues() {
        Set values = null;
        AndCriteria nullMatchers = null;

        // It's impossible to fully limit the options if it's an 'or' and not
        // all criteria can provide source values, so just bail now.
        if ( !matchAll && regularCriteria.size() > 0 )
            return null;

        for ( SourceCriterion source : sourceCriteria ) {
            Collection matches = source.getMatchingValues();
            if ( matches == null ) {
                if ( matchAll ) {
                    // Add the check to the list to check on later.
                    nullMatchers = addCriterion( nullMatchers, source );
                } else {
                    // if we're an 'or' and a sub-criteria returns a null,
                    // all bets are off.
                    return null;
                }
            } else {
                if ( values == null ) {
                    // Create the value set.
                    values = new HashSet( matches );
                } else if ( matchAll ) {
                    // it's an 'and', so we only want the intersect.
                    values.retainAll( matches );
                    if ( values.size() == 0 ) {
                        // No point continuing if there are no results left
                        // after the intersect.
                        // Because it's an 'and', no future results will be
                        // able to expand the results
                        return values;
                    }
                } else {
                    // it's an 'or', so just add all options.
                    values.addAll( matches );
                }
            }
        }

        if ( values != null && nullMatchers != null ) {
            // Re-filter the contents over any 'null'-returning criteria, since
            // some may not have provided a any values, but may still have
            // checks to make on other returned values.
            CriteriaUtils.filterCollectionContents( values, nullMatchers );
        }

        return values;
    }

    private AndCriteria addCriterion( AndCriteria nullMatchers, Criterion source ) {
        if ( nullMatchers == null )
            nullMatchers = new AndCriteria( source );
        else
            nullMatchers.addCriterion( source );
        return nullMatchers;
    }

    @Override
    public String toString() {
        String join = null;
        StringBuilder out = new StringBuilder();

        out.append( "( " );

        join = toStringCriteria( out, join, sourceCriteria );
        join = toStringCriteria( out, join, regularCriteria );

        out.append( " )" );
        return out.toString();
    }

    private String toStringCriteria( StringBuilder out, String join, List criteria ) {
        for ( Criterion c : criteria ) {
            if ( join == null )
                join = matchAll ? " & " : " | ";
            else
                out.append( join );

            out.append( c );
        }
        return join;
    }

    public SourceCriterion.Weight getWeight() {
        return weight;
    }

}