/**************************************************************************************
* Copyright (C) 2008 EsperTech, Inc. All rights reserved. *
* http://esper.codehaus.org *
* http://www.espertech.com *
* ---------------------------------------------------------------------------------- *
* The software in this package is published under the terms of the GPL license *
* a copy of which has been included with this distribution in the license.txt file. *
**************************************************************************************/
package com.espertech.esper.filter;
import com.espertech.esper.client.EventType;
import com.espertech.esper.epl.expression.ExprEvaluatorContext;
import com.espertech.esper.epl.property.PropertyEvaluator;
import com.espertech.esper.pattern.MatchedEventMap;
import java.util.*;
/**
* Contains the filter criteria to sift through events. The filter criteria are the event class to look for and
* a set of parameters (attribute names, operators and constant/range values).
*/
public final class FilterSpecCompiled
{
private final static ArrayDeque<FilterSpecParam> EMPTY_LIST = new ArrayDeque<FilterSpecParam>();
private final static FilterSpecParamComparator COMPARATOR_PARAMETERS = new FilterSpecParamComparator();
private final EventType filterForEventType;
private final String filterForEventTypeName;
private final ArrayDeque<FilterSpecParam> parameters;
private final PropertyEvaluator optionalPropertyEvaluator;
/**
* Constructor - validates parameter list against event type, throws exception if invalid
* property names or mismatcing filter operators are found.
* @param eventType is the event type
* @param filterParameters is a list of filter parameters
* @param eventTypeName is the name of the event type
* @param optionalPropertyEvaluator optional if evaluating properties returned by filtered events
* @throws IllegalArgumentException if validation invalid
*/
public FilterSpecCompiled(EventType eventType, String eventTypeName, List<FilterSpecParam> filterParameters,
PropertyEvaluator optionalPropertyEvaluator)
{
this.filterForEventType = eventType;
this.filterForEventTypeName = eventTypeName;
this.parameters = sortRemoveDups(filterParameters);
this.optionalPropertyEvaluator = optionalPropertyEvaluator;
}
/**
* Returns type of event to filter for.
* @return event type
*/
public final EventType getFilterForEventType()
{
return filterForEventType;
}
/**
* Returns list of filter parameters.
* @return list of filter params
*/
public final ArrayDeque<FilterSpecParam> getParameters()
{
return parameters;
}
/**
* Returns the event type name.
* @return event type name
*/
public String getFilterForEventTypeName()
{
return filterForEventTypeName;
}
/**
* Return the evaluator for property value if any is attached, or none if none attached.
* @return property evaluator
*/
public PropertyEvaluator getOptionalPropertyEvaluator()
{
return optionalPropertyEvaluator;
}
/**
* Returns the result event type of the filter specification.
* @return event type
*/
public EventType getResultEventType()
{
if (optionalPropertyEvaluator != null)
{
return optionalPropertyEvaluator.getFragmentEventType();
}
else
{
return filterForEventType;
}
}
/**
* Returns the values for the filter, using the supplied result events to ask filter parameters
* for the value to filter for.
* @param matchedEvents contains the result events to use for determining filter values
* @return filter values
*/
public FilterValueSet getValueSet(MatchedEventMap matchedEvents, ExprEvaluatorContext evaluatorContext, List<FilterValueSetParam> addendum)
{
ArrayDeque<FilterValueSetParam> valueList;
if (addendum != null) {
valueList = new ArrayDeque<FilterValueSetParam>(parameters.size() + addendum.size());
valueList.addAll(addendum);
}
else {
valueList = new ArrayDeque<FilterValueSetParam>(parameters.size());
}
populateValueSet(valueList, matchedEvents, evaluatorContext);
return new FilterValueSetImpl(filterForEventType, valueList);
}
private void populateValueSet(ArrayDeque<FilterValueSetParam> valueList, MatchedEventMap matchedEvents, ExprEvaluatorContext exprEvaluatorContext) {
// Ask each filter specification parameter for the actual value to filter for
for (FilterSpecParam specParam : parameters)
{
Object filterForValue = specParam.getFilterValue(matchedEvents, exprEvaluatorContext);
FilterValueSetParam valueParam = new FilterValueSetParamImpl(specParam.getLookupable(), specParam.getFilterOperator(), filterForValue);
valueList.add(valueParam);
}
}
@SuppressWarnings({"StringConcatenationInsideStringBufferAppend"})
public final String toString()
{
StringBuilder buffer = new StringBuilder();
buffer.append("FilterSpecCompiled type=" + this.filterForEventType);
buffer.append(" parameters=" + Arrays.toString(parameters.toArray()));
return buffer.toString();
}
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof FilterSpecCompiled))
{
return false;
}
FilterSpecCompiled other = (FilterSpecCompiled) obj;
if (!equalsTypeAndFilter(other))
{
return false;
}
if ((this.optionalPropertyEvaluator == null) && (other.optionalPropertyEvaluator == null))
{
return true;
}
if ((this.optionalPropertyEvaluator != null) && (other.optionalPropertyEvaluator == null))
{
return false;
}
if ((this.optionalPropertyEvaluator == null) && (other.optionalPropertyEvaluator != null))
{
return false;
}
return this.optionalPropertyEvaluator.compareTo(other.optionalPropertyEvaluator);
}
/**
* Compares only the type and filter portion and not the property evaluation portion.
* @param other filter to compare
* @return true if same
*/
public boolean equalsTypeAndFilter(FilterSpecCompiled other)
{
if (this.filterForEventType != other.filterForEventType)
{
return false;
}
if (this.parameters.size() != other.parameters.size())
{
return false;
}
Iterator<FilterSpecParam> iterOne = parameters.iterator();
Iterator<FilterSpecParam> iterOther = other.parameters.iterator();
while (iterOne.hasNext())
{
if (!iterOne.next().equals(iterOther.next()))
{
return false;
}
}
return true;
}
public int hashCode()
{
int hashCode = filterForEventType.hashCode();
for (FilterSpecParam param : parameters)
{
hashCode ^= 31 * param.hashCode();
}
return hashCode;
}
protected static ArrayDeque<FilterSpecParam> sortRemoveDups(List<FilterSpecParam> parameters) {
if (parameters.isEmpty()) {
return EMPTY_LIST;
}
ArrayDeque<FilterSpecParam> result = new ArrayDeque<FilterSpecParam>();
if (parameters.size() == 1) {
result.addAll(parameters);
return result;
}
TreeMap<FilterOperator, List<FilterSpecParam>> map = new TreeMap<FilterOperator, List<FilterSpecParam>>(COMPARATOR_PARAMETERS);
for (FilterSpecParam parameter : parameters) {
List<FilterSpecParam> list = map.get(parameter.getFilterOperator());
if (list == null) {
list = new ArrayList<FilterSpecParam>();
map.put(parameter.getFilterOperator(), list);
}
boolean hasDuplicate = false;
for (FilterSpecParam existing : list) {
if (existing.getLookupable().equals(parameter.getLookupable())) {
hasDuplicate = true;
break;
}
}
if (hasDuplicate) {
continue;
}
list.add(parameter);
}
for (Map.Entry<FilterOperator, List<FilterSpecParam>> entry : map.entrySet()) {
result.addAll(entry.getValue());
}
return result;
}
}