package com.insightfullogic.honest_profiler.core.aggregation.filter; import static com.insightfullogic.honest_profiler.core.aggregation.filter.Comparison.CONTAINS; import static com.insightfullogic.honest_profiler.core.aggregation.filter.Comparison.NOT_STARTS_WITH; import static com.insightfullogic.honest_profiler.core.aggregation.filter.Target.KEY; import static java.util.Collections.emptyList; import java.util.List; import java.util.function.Predicate; import com.insightfullogic.honest_profiler.core.aggregation.result.ItemType; /** * This class describes a filter composed of several {@link FilterItem}s. The resulting filter will only accept inputs * which satisfy the conditions of all the {@link FilterItem}s. * <p> * Additionally it contains a boolean which generates an extra {@link Predicate} for filtering out "error frames" * (frames in the profile which do not correspond to proper Java methods), and a quick-filter String generates an extra * {@link Predicate} for filtering the key. * <p> * @param <T> the type of the input items which can be filtered */ public class FilterSpecification<T> { // Instance Properties private ItemType type; private boolean hideErrors; private String quickFilter; private List<FilterItem<T, ?>> filters; // Instance Constructors /** * Constructor for an empty {@link FilterSpecification} which specifies a filter for filtering items of the * specified {@link ItemType}. * <p> * @param type the type of items the filter can filter */ public FilterSpecification(ItemType type) { super(); this.type = type; filters = emptyList(); } /** * Constructor for an empty {@link FilterSpecification} which specifies a filter for filtering items of the * specified {@link ItemType}. * <p> * @param type the type of items the filter can filter * @param hideErrors a boolean specifying if error frames should be filtered out * @param filters a {@link List} of the contained {@link FilterItem}s */ public FilterSpecification(ItemType type, boolean hideErrors, List<FilterItem<T, ?>> filters) { super(); this.type = type; this.hideErrors = hideErrors; this.filters = filters; } // Instance Accessors /** * Returns a boolean indicating whether the resulting filter will filter out error frames. * <p> * @return a boolean indicating whether the resulting filter will filter out error frames. */ public boolean isHideErrors() { return hideErrors; } /** * Returns a boolean indicating whether the FilterSpecification is non-trivial, i.e. whether it actually has any * filters defined. The quickfilter is not taken into account. * <p> * The reason is that this is used by the front-end to indicate to the user whether currently a filter has been * defined by the user. The filter is specified separately from the quickfilter condition. * <p> * @return a boolean indicating whether the FilterSpecification is non-trivial */ public boolean isFiltering() { return hideErrors || filters.size() > 0; } /** * Sets the quickfilter String, which when not empty will generate an extra {@link Predicate} for filtering the key. * <p> * @param value the value used for filtering */ public void setQuickFilter(String value) { this.quickFilter = value; } // Filter Construction Methods /** * Generates a {@link Predicate} which accepts items of type T if they are accepted by all of the filters from all * contained {@link FilterItem}s, and optionally if they do not contain errors and/or if the key contains the String * specified by the quickfilter String. * <p> * @return a {@link Predicate} implementing the {@link FilterSpecification} */ public Predicate<T> getFilter() { Predicate<T> result = hideErrors ? errorFilter() : null; if (quickFilter != null && !quickFilter.isEmpty()) { result = result == null ? quickFilter() : result.and(quickFilter()); } if (filters.size() > 0) { result = result == null ? filter() : result.and(filter()); } return result == null ? str -> true : result; } // Internal Filter Factory Methods /** * Create a {@link Predicate} which filters as specified by the {@link FilterItem}s in this FilterSpecification. * <p> * @return a {@link Predicate} corresponding to the contained {@link FilterItem}s */ private Predicate<T> filter() { return filters.stream().map(item -> item.toFilter(type)).reduce(Predicate::and).get(); } /** * Create a {@link Predicate} for filtering the key using the quickfilter. * <p> * @return a {@link Predicate} for filtering the key */ private Predicate<T> quickFilter() { return new FilterPredicate<T, String>(type, KEY, CONTAINS, quickFilter); } /** * Create a {@link Predicate} for filtering out error frames. * <p> * @return a {@link Predicate} for filtering out error frames */ private final Predicate<T> errorFilter() { return new FilterPredicate<T, String>(type, KEY, NOT_STARTS_WITH, "AGCT.").and( new FilterPredicate<T, String>(type, KEY, NOT_STARTS_WITH, "Unknown <")); } }