package com.insightfullogic.honest_profiler.core.aggregation.filter; import static java.util.regex.Pattern.compile; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; /** * Enumeration of the comparisons supported by filtering. The comparisons each provide the {@link Predicate} though * their {@link #getPredicate(Object)} method, which applies the comparison to an item and specified value, and returns * whether the item fulfills the condition. * <p> * It would have been nice to be able to parametrize the Enum, it might have been possible to make the internal factory * methods lighter. May be revisited if <a href="http://openjdk.java.net/jeps/301">JEP 301</a> gets implemented. */ public enum Comparison { // Numerical comparisons EQUALS_NR("="), GT(">"), LT("<"), GE(">="), LE("<="), // String comparisons EQUALS_STR("Equals"), STARTS_WITH("Starts With"), ENDS_WITH("Ends With"), CONTAINS("Contains"), NOT_STARTS_WITH("Doesn't Start With"), NOT_ENDS_WITH("Doesn't End With"), NOT_CONTAINS("Doesn't Contain"), MATCHES("Matches"); // Regexp Matching // Class Properties // A bit hacky : This is for use by ValueType, but it can't be defined there before it actually is needed. // Enums can be sooo tricky :( So it got placed here semi-arbitrarily... public static final Function<String, ?> PCT_INTERPRETER = str -> Double.parseDouble(str) / 100.; /** * List of Comparisons which can be applied to {@link Number}s which are available in the front-end. */ public static final Comparison[] NUMBER_COMPARISONS = new Comparison[] { EQUALS_NR, GT, LT, GE, LE }; /** * List of Comparisons which can be applied to {@link String}s which are available in the front-end. */ public static final Comparison[] STRING_COMPARISONS = new Comparison[] { EQUALS_STR, STARTS_WITH, ENDS_WITH, CONTAINS, MATCHES }; // Class Methods /** * Internal {@link Predicate} factory for applying comparisons to {@link Double}s. * <p> * @param comparison the comparison for which the {@link Predicate} is constructed * @param value the value the comparison will compare against * @return a {@link Predicate} which can compare {@link Double}s against the specified value */ private static Predicate<Double> getPredicate(Comparison comparison, Double value) { switch (comparison) { case EQUALS_NR: return nr -> nr.doubleValue() == value.doubleValue(); case GT: return nr -> nr > value; case LT: return nr -> nr < value; case GE: return nr -> nr >= value; case LE: return nr -> nr <= value; default: break; } throw new RuntimeException( "Comparison type " + comparison + " is not compatible with the value type Double."); } /** * Internal {@link Predicate} factory for applying comparisons to {@link Integer}s. * <p> * @param comparison the comparison for which the {@link Predicate} is constructed * @param value the value the comparison will compare against * @return a {@link Predicate} which can compare {@link Integer}s against the specified value */ private static Predicate<Integer> getPredicate(Comparison comparison, Integer value) { switch (comparison) { case EQUALS_NR: return nr -> nr.intValue() == value.intValue(); case GT: return nr -> nr > value; case LT: return nr -> nr < value; case GE: return nr -> nr >= value; case LE: return nr -> nr <= value; default: break; } throw new RuntimeException( "Comparison type " + comparison + " is not compatible with the value type Integer."); } /** * Internal {@link Predicate} factory for applying comparisons to {@link Long}s. * <p> * @param comparison the comparison for which the {@link Predicate} is constructed * @param value the value the comparison will compare against * @return a {@link Predicate} which can compare {@link Long}s against the specified value */ private static Predicate<Long> getPredicate(Comparison comparison, Long value) { switch (comparison) { case EQUALS_NR: return nr -> nr.longValue() == value.longValue(); case GT: return nr -> nr > value; case LT: return nr -> nr < value; case GE: return nr -> nr >= value; case LE: return nr -> nr <= value; default: break; } throw new RuntimeException( "Comparison type " + comparison + " is not compatible with the value type Long."); } /** * Internal {@link Predicate} factory for applying comparisons to {@link String}s. * <p> * @param comparison the comparison for which the {@link Predicate} is constructed * @param value the value the comparison will compare against * @return a {@link Predicate} which can compare {@link String}s against the specified value */ private static Predicate<String> getPredicate(Comparison comparison, String value) { switch (comparison) { case EQUALS_STR: return str -> str.equals(value); case STARTS_WITH: return str -> str.startsWith(value); case ENDS_WITH: return str -> str.endsWith(value); case CONTAINS: return str -> str.contains(value); case NOT_STARTS_WITH: return str -> !str.startsWith(value); case NOT_ENDS_WITH: return str -> !str.endsWith(value); case NOT_CONTAINS: return str -> !str.contains(value); // For the MATCHES comparison the value is interpreted as a regular expression. case MATCHES: Pattern pattern = compile(value); return str -> pattern.matcher(str).matches(); default: break; } throw new RuntimeException( "Comparison type " + comparison + " is not compatible with the value type String."); } // Instance Properties private String name; // Instance Constructors /** * Private constructor, setting the name which can be used for displaying the Comparison. * <p> * @param name the display name */ private Comparison(String name) { this.name = name; } // Instance Methods /** * Returns a {@link Predicate} which will evaluate this comparison against the provided value. * <p> * @param <T> the type of the {@link Object} tested by the {@link Predicate} * @param value the value the {@link Predicate} will compare against * @return a {@link Predicate} which compares input to the provided value */ @SuppressWarnings("unchecked") public <T> Predicate<T> getPredicate(T value) { if (value instanceof Double) { return (Predicate<T>)getPredicate(this, (Double)value); } if (value instanceof Integer) { return (Predicate<T>)getPredicate(this, (Integer)value); } if (value instanceof Long) { return (Predicate<T>)getPredicate(this, (Long)value); } if (value instanceof String) { return (Predicate<T>)getPredicate(this, (String)value); } throw new RuntimeException( "Comparison type " + this + " is not compatible with the value type String."); } @Override public String toString() { return name; } }