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

com.github.dm.jrt.function.PredicateWrapper Maven / Gradle / Ivy

There is a newer version: 5.9.0
Show newest version
/*
 * Licensed 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
 *
 * http://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 com.github.dm.jrt.function;

import com.github.dm.jrt.util.Reflection;

import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.List;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

/**
 * Class wrapping a predicate instance.
 * 

* Created by davide-maestroni on 10/16/2015. * * @param the input data type. */ public class PredicateWrapper implements Predicate { private static final LogicalPredicate AND_PREDICATE = new LogicalPredicate(); private static final LogicalPredicate CLOSE_PREDICATE = new LogicalPredicate(); private static final LogicalPredicate NEGATE_PREDICATE = new LogicalPredicate(); private static final LogicalPredicate OPEN_PREDICATE = new LogicalPredicate(); private static final LogicalPredicate OR_PREDICATE = new LogicalPredicate(); private final Predicate mPredicate; private final List> mPredicates; /** * Constructor. * * @param predicate the core predicate. * @param predicates the list of wrapped predicates. */ @SuppressWarnings("ConstantConditions") PredicateWrapper(@NotNull final Predicate predicate, @NotNull final List> predicates) { if (predicate == null) { throw new NullPointerException("the predicate must not be null"); } if (predicates.isEmpty()) { throw new IllegalArgumentException("the list of predicates must not be empty"); } mPredicate = predicate; mPredicates = predicates; } /** * Returns a composed predicate wrapper that represents a short-circuiting logical AND of this * predicate and another. * * @param other a predicate that will be logically-ANDed with this predicate. * @return the composed predicate. */ @NotNull @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "class comparison with == is done") public PredicateWrapper and(@NotNull final Predicate other) { final Class otherClass = other.getClass(); final List> predicates = mPredicates; final ArrayList> newPredicates = new ArrayList>(predicates.size() + 4); newPredicates.add(OPEN_PREDICATE); newPredicates.addAll(predicates); newPredicates.add(AND_PREDICATE); if (otherClass == PredicateWrapper.class) { newPredicates.addAll(((PredicateWrapper) other).mPredicates); } else { newPredicates.add(other); } newPredicates.add(CLOSE_PREDICATE); return new PredicateWrapper(new AndPredicate(mPredicate, other), newPredicates); } /** * Checks if the predicates wrapped by this instance have a static context. * * @return whether the predicates have a static context. */ public boolean hasStaticContext() { for (final Predicate predicate : mPredicates) { if (!Reflection.hasStaticContext(predicate.getClass())) { return false; } } return true; } @Override public int hashCode() { int result = 0; for (final Predicate predicate : mPredicates) { result = 31 * result + predicate.getClass().hashCode(); } return result; } @Override public boolean equals(final Object o) { if (this == o) { return true; } if ((o == null) || (getClass() != o.getClass())) { return false; } final PredicateWrapper that = (PredicateWrapper) o; final List> thisPredicates = mPredicates; final List> thatPredicates = that.mPredicates; final int size = thisPredicates.size(); if (size != thatPredicates.size()) { return false; } for (int i = 0; i < size; ++i) { if (thisPredicates.get(i).getClass() != thatPredicates.get(i).getClass()) { return false; } } return true; } /** * Returns a predicate wrapper that represents the logical negation of this predicate. * * @return the negated predicate. */ @NotNull @SuppressWarnings("unchecked") public PredicateWrapper negate() { final List> predicates = mPredicates; final int size = predicates.size(); final ArrayList> newPredicates = new ArrayList>(size + 1); if (size == 1) { newPredicates.add(NEGATE_PREDICATE); newPredicates.add(predicates.get(0)); } else { final Predicate first = predicates.get(0); if (first == NEGATE_PREDICATE) { newPredicates.add(predicates.get(1)); } else { newPredicates.add(first); for (int i = 1; i < size; ++i) { final Predicate predicate = predicates.get(i); if (predicate == NEGATE_PREDICATE) { ++i; } else if (predicate == OR_PREDICATE) { newPredicates.add(AND_PREDICATE); } else if (predicate == AND_PREDICATE) { newPredicates.add(OR_PREDICATE); } else { if ((predicate != OPEN_PREDICATE) && (predicate != CLOSE_PREDICATE)) { newPredicates.add(NEGATE_PREDICATE); } newPredicates.add(predicate); } } } } final Predicate predicate = mPredicate; if (predicate instanceof NegatePredicate) { return new PredicateWrapper(((NegatePredicate) predicate).mPredicate, newPredicates); } return new PredicateWrapper(new NegatePredicate(predicate), newPredicates); } /** * Returns a composed predicate wrapper that represents a short-circuiting logical OR of this * predicate and another. * * @param other a predicate that will be logically-ORed with this predicate. * @return the composed predicate. */ @NotNull @SuppressFBWarnings(value = "BC_UNCONFIRMED_CAST", justification = "class comparison with == is done") public PredicateWrapper or(@NotNull final Predicate other) { final Class otherClass = other.getClass(); final List> predicates = mPredicates; final ArrayList> newPredicates = new ArrayList>(predicates.size() + 4); newPredicates.add(OPEN_PREDICATE); newPredicates.addAll(predicates); newPredicates.add(OR_PREDICATE); if (otherClass == PredicateWrapper.class) { newPredicates.addAll(((PredicateWrapper) other).mPredicates); } else { newPredicates.add(other); } newPredicates.add(CLOSE_PREDICATE); return new PredicateWrapper(new OrPredicate(mPredicate, other), newPredicates); } /** * Predicate implementation logically-ANDing the wrapped ones. * * @param the input data type. */ private static final class AndPredicate implements Predicate { private final Predicate mOther; private final Predicate mPredicate; /** * Constructor. * * @param predicate the wrapped predicate. * @param other the other predicate to be logically-ANDed. */ private AndPredicate(@NotNull final Predicate predicate, @NotNull final Predicate other) { mPredicate = predicate; mOther = other; } public boolean test(final IN in) { return mPredicate.test(in) && mOther.test(in); } } /** * Class indicating a logical operation (like AND and OR). */ private static class LogicalPredicate implements Predicate { public boolean test(final Object o) { throw new UnsupportedOperationException("should never be called"); } } /** * Predicate implementation negating the wrapped one. * * @param the input data type. */ private static final class NegatePredicate implements Predicate { private final Predicate mPredicate; /** * Constructor. * * @param predicate the wrapped predicate. */ private NegatePredicate(@NotNull final Predicate predicate) { mPredicate = predicate; } public boolean test(final IN in) { return !mPredicate.test(in); } } /** * Predicate implementation logically-ORing the wrapped ones. * * @param the input data type. */ private static final class OrPredicate implements Predicate { private final Predicate mOther; private final Predicate mPredicate; /** * Constructor. * * @param predicate the wrapped predicate. * @param other the other predicate to be logically-ORed. */ private OrPredicate(@NotNull final Predicate predicate, @NotNull final Predicate other) { mPredicate = predicate; mOther = other; } public boolean test(final IN in) { return mPredicate.test(in) || mOther.test(in); } } public boolean test(final IN in) { return mPredicate.test(in); } }