/**
* AnalyzerBeans
* Copyright (C) 2014 Neopost - Customer Information Management
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.eobjects.analyzer.beans.filter;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Inject;
import org.apache.metamodel.query.FilterItem;
import org.apache.metamodel.query.OperatorType;
import org.apache.metamodel.query.Query;
import org.apache.metamodel.query.SelectItem;
import org.apache.metamodel.schema.Column;
import org.eobjects.analyzer.beans.api.Alias;
import org.eobjects.analyzer.beans.api.Categorized;
import org.eobjects.analyzer.beans.api.Configured;
import org.eobjects.analyzer.beans.api.Description;
import org.eobjects.analyzer.beans.api.Distributed;
import org.eobjects.analyzer.beans.api.FilterBean;
import org.eobjects.analyzer.beans.api.Initialize;
import org.eobjects.analyzer.beans.api.QueryOptimizedFilter;
import org.eobjects.analyzer.beans.api.Validate;
import org.eobjects.analyzer.beans.categories.FilterCategory;
import org.eobjects.analyzer.beans.convert.ConvertToBooleanTransformer;
import org.eobjects.analyzer.beans.convert.ConvertToDateTransformer;
import org.eobjects.analyzer.beans.convert.ConvertToNumberTransformer;
import org.eobjects.analyzer.beans.convert.ConvertToStringTransformer;
import org.eobjects.analyzer.data.InputColumn;
import org.eobjects.analyzer.data.InputRow;
import org.eobjects.analyzer.util.HasLabelAdvice;
import org.eobjects.analyzer.util.ReflectionUtils;
import com.google.common.base.Joiner;
@FilterBean("Equals")
@Description("A filter that excludes values that are not equal (=) to specific set of valid values")
@Categorized(FilterCategory.class)
@Distributed(true)
public class EqualsFilter implements QueryOptimizedFilter<ValidationCategory>, HasLabelAdvice {
@Inject
@Configured(order = 1)
@Alias("Column")
@Description("The column to compare values of")
InputColumn<?> inputColumn;
@Inject
@Configured(order = 21, required = false)
@Alias("Values")
@Description("Value(s) to compare with")
String[] compareValues = new String[1];
@Inject
@Configured(order = 22, required = false)
@Description("Column holding value to compare with")
InputColumn<?> compareColumn;
private Object[] operands;
private boolean number = false;
public EqualsFilter() {
}
public EqualsFilter(String[] values, InputColumn<?> column) {
this();
this.compareValues = values;
this.inputColumn = column;
init();
}
public EqualsFilter(InputColumn<?> inputColumn, InputColumn<?> valueColumn) {
this();
this.inputColumn = inputColumn;
this.compareColumn = valueColumn;
init();
}
public void setValues(String[] values) {
this.compareValues = values;
}
public void setValueColumn(InputColumn<?> valueColumn) {
this.compareColumn = valueColumn;
}
@Validate
public void validate() {
if (compareColumn == null) {
if (compareValues == null || compareValues.length == 0) {
throw new IllegalStateException("Either 'Values' or 'Value column' needs to be specified.");
}
}
}
@Initialize
public void init() {
Class<?> dataType = inputColumn.getDataType();
if (ReflectionUtils.isNumber(dataType)) {
number = true;
}
List<Object> operandList = new ArrayList<Object>();
if (compareColumn != null) {
operandList.add(null);
}
if (compareValues != null) {
for (int i = 0; i < compareValues.length; i++) {
final String value = compareValues[i];
final Object operand = toOperand(value);
operandList.add(operand);
}
}
operands = operandList.toArray();
}
@Override
public String getSuggestedLabel() {
if (inputColumn == null) {
return null;
}
if (compareColumn != null) {
return inputColumn.getName() + " = " + compareColumn.getName();
}
if (compareValues == null || compareValues.length == 0) {
return null;
}
if (compareValues.length == 1) {
return inputColumn.getName() + " = " + compareValues[0];
}
return inputColumn.getName() + " IN (" + Joiner.on(',').join(compareValues) + ")";
}
private Object toOperand(Object value) {
Class<?> dataType = inputColumn.getDataType();
if (ReflectionUtils.isBoolean(dataType)) {
return ConvertToBooleanTransformer.transformValue(value, ConvertToBooleanTransformer.DEFAULT_TRUE_TOKENS,
ConvertToBooleanTransformer.DEFAULT_FALSE_TOKENS);
} else if (ReflectionUtils.isDate(dataType)) {
return ConvertToDateTransformer.getInternalInstance().transformValue(value);
} else if (ReflectionUtils.isNumber(dataType)) {
return ConvertToNumberTransformer.transformValue(value);
} else if (ReflectionUtils.isString(dataType)) {
return ConvertToStringTransformer.transformValue(value);
} else {
return value;
}
}
@Override
public ValidationCategory categorize(InputRow inputRow) {
Object inputValue = inputRow.getValue(inputColumn);
final Object compareValue;
if (compareColumn == null) {
compareValue = null;
} else {
compareValue = inputRow.getValue(compareColumn);
}
return filter(inputValue, compareValue);
}
public ValidationCategory filter(final Object v) {
return filter(v, null);
}
public ValidationCategory filter(final Object v, final Object compareValue) {
if (compareColumn != null) {
operands[0] = toOperand(compareValue);
}
if (v == null) {
for (Object obj : operands) {
if (obj == null) {
return ValidationCategory.VALID;
}
}
return ValidationCategory.INVALID;
} else {
for (Object operand : operands) {
if (operand != null) {
if (number) {
Number n1 = (Number) operand;
Number n2 = (Number) v;
if (equals(n1, n2)) {
return ValidationCategory.VALID;
}
}
if (operand.equals(v)) {
return ValidationCategory.VALID;
}
}
}
}
return ValidationCategory.INVALID;
}
private boolean equals(Number n1, Number n2) {
if (n1 instanceof Short || n1 instanceof Integer || n1 instanceof Long || n2 instanceof Short
|| n2 instanceof Integer || n2 instanceof Long) {
// use long comparision
return n1.longValue() == n2.longValue();
}
return n1.doubleValue() == n2.doubleValue();
}
@Override
public boolean isOptimizable(ValidationCategory category) {
if (compareColumn != null && compareValues != null && compareValues.length > 0) {
boolean hasCompareValues = false;
for (String compareValue : compareValues) {
if (compareValue != null) {
hasCompareValues = true;
}
}
return !hasCompareValues;
}
return true;
}
@Override
public Query optimizeQuery(Query q, ValidationCategory category) {
final Column inputPhysicalColumn = inputColumn.getPhysicalColumn();
if (category == ValidationCategory.VALID) {
if (compareColumn == null) {
final SelectItem selectItem = new SelectItem(inputPhysicalColumn);
final List<FilterItem> filterItems = new ArrayList<FilterItem>();
for (Object operand : operands) {
filterItems.add(new FilterItem(selectItem, OperatorType.EQUALS_TO, operand));
}
q.where(new FilterItem(filterItems.toArray(new FilterItem[filterItems.size()])));
} else {
final Column valuePhysicalColumn = compareColumn.getPhysicalColumn();
q.where(inputPhysicalColumn, OperatorType.EQUALS_TO, valuePhysicalColumn);
}
} else {
if (compareColumn == null) {
for (Object operand : operands) {
q.where(inputColumn.getPhysicalColumn(), OperatorType.DIFFERENT_FROM, operand);
}
} else {
final Column valuePhysicalColumn = compareColumn.getPhysicalColumn();
q.where(inputPhysicalColumn, OperatorType.DIFFERENT_FROM, valuePhysicalColumn);
}
}
return q;
}
}