/*
* © Copyright FOCONIS AG, 2014
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
*/
package org.openntf.formula.function;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.openntf.formula.DateTime;
import org.openntf.formula.FormulaContext;
import org.openntf.formula.Function;
import org.openntf.formula.FunctionSet;
import org.openntf.formula.ValueHolder;
import org.openntf.formula.impl.ParameterCollectionBoolean;
import org.openntf.formula.impl.ParameterCollectionDouble;
import org.openntf.formula.impl.ParameterCollectionInt;
import org.openntf.formula.impl.ParameterCollectionObject;
/**
* This class implements the default arithmetic, boolean and compare operators.
*
* As Operations are used at most, this is implemented to be (or to try to be) as fast as possible
*
* @author Roland Praml, Foconis AG
*
*/
public class Comparators extends OperatorsAbstract {
public static abstract class Matcher {
public abstract boolean match(int delta);
}
/**
* The Factory that returns a set of operators
*/
public static class Functions extends FunctionSet {
private static final Map<String, Function> functionSet = new HashMap<String, Function>(16);
private static void add(final Function f) {
functionSet.put(f.getImage().toLowerCase(), f);
}
@Override
public Map<String, Function> getFunctions() {
return functionSet;
}
static {
Matcher cmpEq = new Matcher() {
@Override
public boolean match(final int delta) {
return delta == 0;
}
};
add(new Comparators(cmpEq, "="));
add(new Comparators(cmpEq, "*="));
Matcher cmpNe = new Matcher() {
@Override
public boolean match(final int delta) {
return delta != 0;
}
};
add(new Comparators(cmpNe, "<>"));
add(new Comparators(cmpNe, "*<>"));
Matcher cmpLt = new Matcher() {
@Override
public boolean match(final int delta) {
return delta < 0;
}
};
add(new Comparators(cmpLt, "<"));
add(new Comparators(cmpLt, "*<"));
Matcher cmpGt = new Matcher() {
@Override
public boolean match(final int delta) {
return delta > 0;
}
};
add(new Comparators(cmpGt, ">"));
add(new Comparators(cmpGt, "*>"));
Matcher cmpLte = new Matcher() {
@Override
public boolean match(final int delta) {
return delta <= 0;
}
};
add(new Comparators(cmpLte, "<="));
add(new Comparators(cmpLte, "*<="));
Matcher cmpGte = new Matcher() {
@Override
public boolean match(final int delta) {
return delta >= 0;
}
};
add(new Comparators(cmpGte, ">="));
add(new Comparators(cmpGte, "*>="));
}
}
private Matcher matcher;
/**
* The constructor. Operators shoud be constructed via Operator.Factory
*
* @param operation
* @param image
*/
private Comparators(final Matcher matcher, final String image) {
super(image);
this.matcher = matcher;
// Autodetect if the operation is permutative
this.isPermutative = (image.charAt(0) == '*' && image.length() > 1);
}
// ----------- Strings
@Override
protected ValueHolder evaluateString(final FormulaContext ctx, final ValueHolder[] params) {
Collection<String[]> values = new ParameterCollectionObject<String>(params, String.class, isPermutative);
for (String[] value : values) {
int delta = value[0].compareToIgnoreCase(value[1]);
if (matcher.match(delta)) {
return ctx.TRUE;
}
}
return ctx.FALSE;
}
@Override
protected ValueHolder evaluateString(final FormulaContext ctx, final String s1, final String s2) {
int delta = s1.compareToIgnoreCase(s2);
if (matcher.match(delta)) {
return ctx.TRUE;
}
return ctx.FALSE;
}
// ----------- Numbers
@Override
protected ValueHolder evaluateNumber(final FormulaContext ctx, final ValueHolder[] params) {
Collection<double[]> values = new ParameterCollectionDouble(params, isPermutative);
for (double[] value : values) {
int delta = Double.compare(value[0], value[1]);
if (matcher.match(delta)) {
return ctx.TRUE;
}
}
return ctx.FALSE;
}
@Override
protected ValueHolder evaluateNumber(final FormulaContext ctx, final double d1, final double d2) {
int delta = Double.compare(d1, d2);
if (matcher.match(delta)) {
return ctx.TRUE;
}
return ctx.FALSE;
}
// ----------- Integers
@Override
protected ValueHolder evaluateInt(final FormulaContext ctx, final ValueHolder[] params) {
Collection<int[]> values = new ParameterCollectionInt(params, isPermutative);
for (int[] value : values) {
int delta = value[0] - value[1];
if (matcher.match(delta)) {
return ctx.TRUE;
}
}
return ctx.FALSE;
}
@Override
protected ValueHolder evaluateInt(final FormulaContext ctx, final int i1, final int i2) {
int delta = i1 - i2;
if (matcher.match(delta)) {
return ctx.TRUE;
}
return ctx.FALSE;
}
// ----------- DateTimes
@Override
protected ValueHolder evaluateDateTime(final FormulaContext ctx, final ValueHolder[] params) {
Collection<DateTime[]> values = new ParameterCollectionObject<DateTime>(params, DateTime.class, isPermutative);
for (DateTime[] value : values) {
int delta = value[0].compare(value[0], value[1]);
if (matcher.match(delta)) {
return ctx.TRUE;
}
}
return ctx.FALSE;
}
@Override
protected ValueHolder evaluateDateTime(final FormulaContext ctx, final DateTime dt1, final DateTime dt2) {
int delta = dt1.compare(dt1, dt2);
if (matcher.match(delta)) {
return ctx.TRUE;
}
return ctx.FALSE;
}
// ----------- Integers
@Override
protected ValueHolder evaluateBoolean(final FormulaContext ctx, final ValueHolder[] params) {
Collection<boolean[]> values = new ParameterCollectionBoolean(params, isPermutative);
for (boolean[] value : values) {
int delta = (value[0] ? 1 : 0) - (value[1] ? 1 : 0);
if (matcher.match(delta)) {
return ctx.TRUE;
}
}
return ctx.FALSE;
}
@Override
protected ValueHolder evaluateBoolean(final FormulaContext ctx, final boolean b1, final boolean b2) {
int delta = (b1 ? 1 : 0) - (b2 ? 1 : 0);
if (matcher.match(delta)) {
return ctx.TRUE;
}
return ctx.FALSE;
}
}