com.github.dm.jrt.function.PredicateWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jroutine Show documentation
Show all versions of jroutine Show documentation
Parallel programming on the go
/*
* 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 super IN> mPredicate;
private final List> mPredicates;
/**
* Constructor.
*
* @param predicate the core predicate.
* @param predicates the list of wrapped predicates.
*/
@SuppressWarnings("ConstantConditions")
PredicateWrapper(@NotNull final Predicate super IN> 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 super IN> other) {
final Class extends Predicate> 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 super IN> predicate = mPredicate;
if (predicate instanceof NegatePredicate) {
return new PredicateWrapper(((NegatePredicate super IN>) 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 super IN> other) {
final Class extends Predicate> 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 super IN> mOther;
private final Predicate super IN> mPredicate;
/**
* Constructor.
*
* @param predicate the wrapped predicate.
* @param other the other predicate to be logically-ANDed.
*/
private AndPredicate(@NotNull final Predicate super IN> predicate,
@NotNull final Predicate super IN> 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