/* * © 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.IntegerOverflowException; 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 Operators extends OperatorsAbstract { public static abstract class Computer { private String image; public Computer(final String im) { image = im; } public double compute(final double v1, final double v2) { throw new UnsupportedOperationException("'" + image + "' is not supported for DOUBLE"); } public boolean compute(final boolean b1, final boolean b2) { throw new UnsupportedOperationException("'" + image + "' is not supported for BOOLEAN"); } public int compute(final int v1, final int v2) throws IntegerOverflowException { throw new UnsupportedOperationException("'" + image + "' is not supported for INTEGER"); } public String compute(final String v1, final String v2) { throw new UnsupportedOperationException("'" + image + "' is not supported for STRING"); } public DateTime compute(final DateTime d1, final DateTime d2) { throw new UnsupportedOperationException("'" + image + "' is not supported for DATETIME"); } } /** * 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 { // Define the computers Computer add = new Computer("+") { @Override public int compute(final int v1, final int v2) throws IntegerOverflowException { long res = ((long) v1 + (long) v2); if (res < Integer.MIN_VALUE || Integer.MAX_VALUE < res) { throw new IntegerOverflowException(); } return (int) res; } @Override public double compute(final double v1, final double v2) { return v1 + v2; } @Override public String compute(final String v1, final String v2) { return v1.concat(v2); } }; Computer sub = new Computer("-") { @Override public int compute(final int v1, final int v2) throws IntegerOverflowException { long res = ((long) v1 - (long) v2); if (res < Integer.MIN_VALUE || Integer.MAX_VALUE < res) { throw new IntegerOverflowException(); } return (int) res; } @Override public double compute(final double v1, final double v2) { return v1 - v2; } }; Computer mul = new Computer("*") { @Override public int compute(final int v1, final int v2) throws IntegerOverflowException { long res = ((long) v1 * (long) v2); if (res < Integer.MIN_VALUE || Integer.MAX_VALUE < res) { throw new IntegerOverflowException(); } return (int) res; } @Override public double compute(final double v1, final double v2) { return v1 * v2; } }; Computer div = new Computer("/") { @Override public int compute(final int v1, final int v2) throws IntegerOverflowException { if (v1 % v2 != 0) throw new IntegerOverflowException(); return v1 / v2; } @Override public double compute(final double v1, final double v2) { return v1 / v2; } }; add(new Operators(add, "+")); add(new Operators(add, "*+")); add(new Operators(sub, "-")); add(new Operators(sub, "*-")); add(new Operators(mul, "*")); add(new Operators(mul, "**")); add(new Operators(div, "/")); add(new Operators(div, "*/")); } } private Computer computer; /** * The constructor. Operators shoud be constructed via Operator.Factory * * @param operation * @param image */ private Operators(final Computer computer, final String image) { super(image); this.computer = computer; // 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); ValueHolder ret = ValueHolder.createValueHolder(String.class, values.size()); for (String[] value : values) { ret.add(computer.compute(value[0], value[1])); } return ret; } @Override protected ValueHolder evaluateString(final FormulaContext ctx, final String s1, final String s2) { return ValueHolder.valueOf(computer.compute(s1, s2)); } // ----------- Numbers @Override protected ValueHolder evaluateNumber(final FormulaContext ctx, final ValueHolder[] params) { Collection<double[]> values = new ParameterCollectionDouble(params, isPermutative); ValueHolder ret = ValueHolder.createValueHolder(double.class, values.size()); for (double[] value : values) { ret.add(computer.compute(value[0], value[1])); } return ret; } @Override protected ValueHolder evaluateNumber(final FormulaContext ctx, final double d1, final double d2) { return ValueHolder.valueOf(computer.compute(d1, d2)); } // ----------- Integers @Override protected ValueHolder evaluateInt(final FormulaContext ctx, final ValueHolder[] params) { Collection<int[]> values = new ParameterCollectionInt(params, isPermutative); ValueHolder ret = ValueHolder.createValueHolder(int.class, values.size()); for (int[] value : values) { try { ret.add(computer.compute(value[0], value[1])); } catch (IntegerOverflowException e) { ret.add(computer.compute((double) value[0], (double) value[1])); } } return ret; } @Override protected ValueHolder evaluateInt(final FormulaContext ctx, final int i1, final int i2) { try { return ValueHolder.valueOf(computer.compute(i1, i2)); } catch (IntegerOverflowException e) { return ValueHolder.valueOf(computer.compute((double) i1, (double) i2)); } } // ----------- DateTimes @Override protected ValueHolder evaluateDateTime(final FormulaContext ctx, final ValueHolder[] params) { Collection<DateTime[]> values = new ParameterCollectionObject<DateTime>(params, DateTime.class, isPermutative); ValueHolder ret = ValueHolder.createValueHolder(DateTime.class, values.size()); for (DateTime[] value : values) { ret.add(computer.compute(value[0], value[1])); } return ret; } @Override protected ValueHolder evaluateDateTime(final FormulaContext ctx, final DateTime dt1, final DateTime dt2) { return ValueHolder.valueOf(computer.compute(dt1, dt2)); } // ----------- Numbers @Override protected ValueHolder evaluateBoolean(final FormulaContext ctx, final ValueHolder[] params) { Collection<boolean[]> values = new ParameterCollectionBoolean(params, isPermutative); ValueHolder ret = ValueHolder.createValueHolder(boolean.class, values.size()); for (boolean[] value : values) { ret.add(computer.compute(value[0], value[1])); } return ret; } @Override protected ValueHolder evaluateBoolean(final FormulaContext ctx, final boolean b1, final boolean b2) { return ValueHolder.valueOf(computer.compute(b1, b2)); } }