/*
***************************************************************************************
* Copyright (C) 2006 EsperTech, Inc. All rights reserved. *
* http://www.espertech.com/esper *
* 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.collection.MultiKeyUntyped;
import com.espertech.esper.core.context.util.AgentInstanceContext;
import com.espertech.esper.pattern.MatchedEventMap;
import com.espertech.esper.util.JavaClassHelper;
import java.lang.reflect.Array;
import java.util.*;
/**
* This class represents a 'in' filter parameter in an {@link com.espertech.esper.filter.FilterSpecCompiled} filter specification.
* <p>
* The 'in' checks for a list of values.
*/
public final class FilterSpecParamIn extends FilterSpecParam {
private final List<FilterSpecParamInValue> listOfValues;
private MultiKeyUntyped inListConstantsOnly;
private boolean hasCollMapOrArray;
private InValueAdder[] adders;
private static final long serialVersionUID = 1723225284589047752L;
/**
* Ctor.
*
* @param lookupable is the event property or function
* @param filterOperator is expected to be the IN-list operator
* @param listofValues is a list of constants and event property names
* @throws IllegalArgumentException for illegal args
*/
public FilterSpecParamIn(FilterSpecLookupable lookupable,
FilterOperator filterOperator,
List<FilterSpecParamInValue> listofValues)
throws IllegalArgumentException {
super(lookupable, filterOperator);
this.listOfValues = listofValues;
for (FilterSpecParamInValue value : listofValues) {
Class returnType = value.getReturnType();
if (JavaClassHelper.isCollectionMapOrArray(returnType)) {
hasCollMapOrArray = true;
break;
}
}
if (hasCollMapOrArray) {
adders = new InValueAdder[listofValues.size()];
for (int i = 0; i < listofValues.size(); i++) {
Class returnType = listofValues.get(0).getReturnType();
if (returnType == null) {
adders[i] = InValueAdderPlain.INSTANCE;
} else if (returnType.isArray()) {
adders[i] = InValueAdderArray.INSTANCE;
} else if (JavaClassHelper.isImplementsInterface(returnType, Map.class)) {
adders[i] = InValueAdderMap.INSTANCE;
} else if (JavaClassHelper.isImplementsInterface(returnType, Collection.class)) {
adders[i] = InValueAdderColl.INSTANCE;
} else {
adders[i] = InValueAdderPlain.INSTANCE;
}
}
}
boolean isAllConstants = true;
for (FilterSpecParamInValue value : listofValues) {
if (!value.constant()) {
isAllConstants = false;
break;
}
}
if (isAllConstants) {
inListConstantsOnly = getFilterValues(null, null);
}
if ((filterOperator != FilterOperator.IN_LIST_OF_VALUES) && ((filterOperator != FilterOperator.NOT_IN_LIST_OF_VALUES))) {
throw new IllegalArgumentException("Illegal filter operator " + filterOperator + " supplied to " +
"in-values filter parameter");
}
}
public final Object getFilterValue(MatchedEventMap matchedEvents, AgentInstanceContext agentInstanceContext) {
// If the list of values consists of all-constants and no event properties, then use cached version
if (inListConstantsOnly != null) {
return inListConstantsOnly;
}
return getFilterValues(matchedEvents, agentInstanceContext);
}
/**
* Returns the list of values we are asking to match.
*
* @return list of filter values
*/
public List<FilterSpecParamInValue> getListOfValues() {
return listOfValues;
}
public final String toString() {
return super.toString() + " in=(listOfValues=" + listOfValues.toString() + ')';
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof FilterSpecParamIn)) {
return false;
}
FilterSpecParamIn other = (FilterSpecParamIn) obj;
if (!super.equals(other)) {
return false;
}
if (listOfValues.size() != other.listOfValues.size()) {
return false;
}
if (!(Arrays.deepEquals(listOfValues.toArray(), other.listOfValues.toArray()))) {
return false;
}
return true;
}
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (listOfValues != null ? listOfValues.hashCode() : 0);
return result;
}
private MultiKeyUntyped getFilterValues(MatchedEventMap matchedEvents, AgentInstanceContext agentInstanceContext) {
if (!hasCollMapOrArray) {
Object[] constants = new Object[listOfValues.size()];
int count = 0;
for (FilterSpecParamInValue valuePlaceholder : listOfValues) {
constants[count++] = valuePlaceholder.getFilterValue(matchedEvents, agentInstanceContext);
}
return new MultiKeyUntyped(constants);
}
ArrayDeque<Object> constants = new ArrayDeque<>(listOfValues.size());
int count = 0;
for (FilterSpecParamInValue valuePlaceholder : listOfValues) {
Object value = valuePlaceholder.getFilterValue(matchedEvents, agentInstanceContext);
if (value != null) {
adders[count].add(constants, value);
}
count++;
}
return new MultiKeyUntyped(constants.toArray());
}
private interface InValueAdder {
void add(Collection<Object> constants, Object value);
}
private static class InValueAdderArray implements InValueAdder {
private final static InValueAdderArray INSTANCE = new InValueAdderArray();
private InValueAdderArray() {
}
public void add(Collection<Object> constants, Object value) {
int len = Array.getLength(value);
for (int i = 0; i < len; i++) {
constants.add(Array.get(value, i));
}
}
}
private static class InValueAdderMap implements InValueAdder {
private final static InValueAdderMap INSTANCE = new InValueAdderMap();
private InValueAdderMap() {
}
public void add(Collection<Object> constants, Object value) {
Map map = (Map) value;
constants.addAll(map.keySet());
}
}
private static class InValueAdderColl implements InValueAdder {
private final static InValueAdderColl INSTANCE = new InValueAdderColl();
private InValueAdderColl() {
}
public void add(Collection<Object> constants, Object value) {
Collection coll = (Collection) value;
constants.addAll(coll);
}
}
private static class InValueAdderPlain implements InValueAdder {
private final static InValueAdderPlain INSTANCE = new InValueAdderPlain();
private InValueAdderPlain() {
}
public void add(Collection<Object> constants, Object value) {
constants.add(value);
}
}
}