/**
* The contents of this file are subject to the Open Software License
* Version 3.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.opensource.org/licenses/osl-3.0.txt
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*/
package org.mulgara.query.filter.arithmetic;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.mulgara.query.QueryException;
import org.mulgara.query.Variable;
import org.mulgara.query.filter.value.NumericExpression;
/**
* Represents a binary arithmetic operation.
*
* @created Mar 13, 2008
* @author Paula Gearon
* @copyright © 2008 <a href="http://www.topazproject.org/">The Topaz Project</a>
* @licence <a href="{@docRoot}/../../LICENCE.txt">Open Software License v3.0</a>
*/
public abstract class BinaryOperation extends AbstractNumericOperation implements NumericExpression {
/** Serialization ID */
private static final long serialVersionUID = -8435619400443937913L;
/** The first operand */
protected NumericExpression lhs;
/** The second operand */
protected NumericExpression rhs;
/**
* Creates an operation between two terms
* @param lhs The left side of the operation
* @param rhs The right side of the operation
*/
BinaryOperation(NumericExpression lhs, NumericExpression rhs) {
this.lhs = lhs;
this.rhs = rhs;
lhs.setContextOwner(this);
rhs.setContextOwner(this);
}
/** {@inheritDoc} */
public boolean isGrounded() throws QueryException {
return lhs.isGrounded() && rhs.isGrounded();
}
/** @see org.mulgara.query.filter.RDFTerm#getVariables() */
public Set<Variable> getVariables() {
Set<Variable> result = new HashSet<Variable>(lhs.getVariables());
result.addAll(rhs.getVariables());
return result;
}
// Not using generics in NumberOps as we can't know the types at this stage, but they are handy
// for defining the classes correctly
/**
* Calculate the result of this operation, returning it as a normal number.
* @throws QueryException The values of one of the operands could not be resolved.
* @see org.mulgara.query.filter.value.NumericExpression#getNumber()
*/
public Number getNumber() throws QueryException {
Number left = lhs.getNumber();
Number right = rhs.getNumber();
@SuppressWarnings("unchecked")
NumberOps<Number, Number> op = (NumberOps<Number, Number>)opMap.get(new ClassPair(left, right));
if (op == null) throw new AssertionError("Missing entry in operation map");
return doOperation(op, left, right);
}
/**
* Perform the operation specific to the implementing class
* @param ops The operations to use for the current parameters
* @param left The first operand
* @param right The second operand
* @return The arithmetic result of applying the operation to the parameters
*/
abstract <L extends Number, R extends Number> Number doOperation(NumberOps<L,R> ops, L left, R right);
/** Stores classes as an integrated pair for mapping pairs of Numbers to their appropriate functions */
static class ClassPair {
private Class<? extends Number> left;
private Class<? extends Number> right;
/**
* Create the pair using two classes
* @param left The left side of the pair
* @param right The right side of the pair
*/
public ClassPair(Class<? extends Number> left, Class<? extends Number> right) {
this.left = left;
this.right = right;
}
/**
* Create the pair using two <em>instance</em> of classes
* @param left The left side instance for the pair
* @param right The right side instance for the pair
*/
public ClassPair(Number left, Number right) {
this.left = left.getClass();
this.right = right.getClass();
}
/** @return The left side of the pair */
public Class<? extends Number> getLeft() { return left; }
/** @return The right side of the pair */
public Class<? extends Number> getRight() { return right; }
/** @return a hashcode determined by mising the hashcodes of the contributing classes */
public int hashCode() { return left.hashCode() ^ ~right.hashCode(); }
/** @return <code>true</code> iff o is a class pair containing equal elements */
public boolean equals(Object o) {
if (o == null || (!(o instanceof ClassPair))) return false;
return left == ((ClassPair)o).left && right == ((ClassPair)o).right;
}
}
/** Defines a binary function that takes two numbers and returns a new one */
interface NumberOps<LT extends Number,RT extends Number> {
/**
* Get a ClassPair that represents this operation
* @return a new ClassPair for this operation type
*/
public ClassPair getClassPair();
/**
* Perform a multiplication on arguments of the specified types.
* @param left The first multiplicands
* @param right The second multiplicands
* @return The product as a number of the type defined in this class.
*/
public Number product(LT left, RT right);
/**
* Perform a sum on arguments of the specified types.
* @param left The first addend
* @param right The second addend
* @return The sum as a number of the type defined in this class.
*/
public Number sum(LT left, RT right);
/**
* Perform a subtraction on arguments of the specified types.
* @param left The minuend
* @param right The subtrahend
* @return The difference as a number of the type defined in this class.
*/
public Number subtract(LT left, RT right);
/**
* Perform a multiplication on arguments of the specified types.
* @param left The dividend
* @param right The divisor
* @return The quotient as a number of the type defined in this class.
*/
public Number divide(LT left, RT right);
}
/** A map of types to the functions that multiply them with correct type promotions */
protected static Map<ClassPair,NumberOps<? extends Number,? extends Number>> opMap = new HashMap<ClassPair,NumberOps<? extends Number,? extends Number>>();
/** A utility to add a number function to the promotion map */
private static void addType(NumberOps<?,?> nf) { opMap.put(nf.getClassPair(), nf); }
// The following population of the operation map was generated with this ruby script:
/*
* types = [ "Double", "Float", "Long", "Integer", "Short", "Byte" ]
* puts " static {"
* types.each do |l|
* types.each do |r|
* puts " addType(new #{l[0].chr}#{r[0].chr}());"
* end
* end
* puts " }"
* puts
* types.each do |l|
* types.each do |r|
* puts " private static class #{l[0].chr}#{r[0].chr} implements NumberOps<#{l},#{r}> {"
* puts " public ClassPair getClassPair() { return new ClassPair(#{l}.class, #{r}.class); }"
* puts " public Number product(#{l} left, #{r} right) { return left * right; }"
* puts " public Number sum(#{l} left, #{r} right) { return left + right; }"
* puts " public Number subtract(#{l} left, #{r} right) { return left - right; }"
* puts " public Number divide(#{l} left, #{r} right) { return left / right; }"
* puts " }"
* puts
* end
* end
*/
static {
addType(new DD());
addType(new DF());
addType(new DL());
addType(new DI());
addType(new DS());
addType(new DB());
addType(new FD());
addType(new FF());
addType(new FL());
addType(new FI());
addType(new FS());
addType(new FB());
addType(new LD());
addType(new LF());
addType(new LL());
addType(new LI());
addType(new LS());
addType(new LB());
addType(new ID());
addType(new IF());
addType(new IL());
addType(new II());
addType(new IS());
addType(new IB());
addType(new SD());
addType(new SF());
addType(new SL());
addType(new SI());
addType(new SS());
addType(new SB());
addType(new BD());
addType(new BF());
addType(new BL());
addType(new BI());
addType(new BS());
addType(new BB());
}
private static class DD implements NumberOps<Double,Double> {
public ClassPair getClassPair() { return new ClassPair(Double.class, Double.class); }
public Number product(Double left, Double right) { return left * right; }
public Number sum(Double left, Double right) { return left + right; }
public Number subtract(Double left, Double right) { return left - right; }
public Number divide(Double left, Double right) { return left / right; }
}
private static class DF implements NumberOps<Double,Float> {
public ClassPair getClassPair() { return new ClassPair(Double.class, Float.class); }
public Number product(Double left, Float right) { return left * right; }
public Number sum(Double left, Float right) { return left + right; }
public Number subtract(Double left, Float right) { return left - right; }
public Number divide(Double left, Float right) { return left / right; }
}
private static class DL implements NumberOps<Double,Long> {
public ClassPair getClassPair() { return new ClassPair(Double.class, Long.class); }
public Number product(Double left, Long right) { return left * right; }
public Number sum(Double left, Long right) { return left + right; }
public Number subtract(Double left, Long right) { return left - right; }
public Number divide(Double left, Long right) { return left / right; }
}
private static class DI implements NumberOps<Double,Integer> {
public ClassPair getClassPair() { return new ClassPair(Double.class, Integer.class); }
public Number product(Double left, Integer right) { return left * right; }
public Number sum(Double left, Integer right) { return left + right; }
public Number subtract(Double left, Integer right) { return left - right; }
public Number divide(Double left, Integer right) { return left / right; }
}
private static class DS implements NumberOps<Double,Short> {
public ClassPair getClassPair() { return new ClassPair(Double.class, Short.class); }
public Number product(Double left, Short right) { return left * right; }
public Number sum(Double left, Short right) { return left + right; }
public Number subtract(Double left, Short right) { return left - right; }
public Number divide(Double left, Short right) { return left / right; }
}
private static class DB implements NumberOps<Double,Byte> {
public ClassPair getClassPair() { return new ClassPair(Double.class, Byte.class); }
public Number product(Double left, Byte right) { return left * right; }
public Number sum(Double left, Byte right) { return left + right; }
public Number subtract(Double left, Byte right) { return left - right; }
public Number divide(Double left, Byte right) { return left / right; }
}
private static class FD implements NumberOps<Float,Double> {
public ClassPair getClassPair() { return new ClassPair(Float.class, Double.class); }
public Number product(Float left, Double right) { return left * right; }
public Number sum(Float left, Double right) { return left + right; }
public Number subtract(Float left, Double right) { return left - right; }
public Number divide(Float left, Double right) { return left / right; }
}
private static class FF implements NumberOps<Float,Float> {
public ClassPair getClassPair() { return new ClassPair(Float.class, Float.class); }
public Number product(Float left, Float right) { return left * right; }
public Number sum(Float left, Float right) { return left + right; }
public Number subtract(Float left, Float right) { return left - right; }
public Number divide(Float left, Float right) { return left / right; }
}
private static class FL implements NumberOps<Float,Long> {
public ClassPair getClassPair() { return new ClassPair(Float.class, Long.class); }
public Number product(Float left, Long right) { return left * right; }
public Number sum(Float left, Long right) { return left + right; }
public Number subtract(Float left, Long right) { return left - right; }
public Number divide(Float left, Long right) { return left / right; }
}
private static class FI implements NumberOps<Float,Integer> {
public ClassPair getClassPair() { return new ClassPair(Float.class, Integer.class); }
public Number product(Float left, Integer right) { return left * right; }
public Number sum(Float left, Integer right) { return left + right; }
public Number subtract(Float left, Integer right) { return left - right; }
public Number divide(Float left, Integer right) { return left / right; }
}
private static class FS implements NumberOps<Float,Short> {
public ClassPair getClassPair() { return new ClassPair(Float.class, Short.class); }
public Number product(Float left, Short right) { return left * right; }
public Number sum(Float left, Short right) { return left + right; }
public Number subtract(Float left, Short right) { return left - right; }
public Number divide(Float left, Short right) { return left / right; }
}
private static class FB implements NumberOps<Float,Byte> {
public ClassPair getClassPair() { return new ClassPair(Float.class, Byte.class); }
public Number product(Float left, Byte right) { return left * right; }
public Number sum(Float left, Byte right) { return left + right; }
public Number subtract(Float left, Byte right) { return left - right; }
public Number divide(Float left, Byte right) { return left / right; }
}
private static class LD implements NumberOps<Long,Double> {
public ClassPair getClassPair() { return new ClassPair(Long.class, Double.class); }
public Number product(Long left, Double right) { return left * right; }
public Number sum(Long left, Double right) { return left + right; }
public Number subtract(Long left, Double right) { return left - right; }
public Number divide(Long left, Double right) { return left / right; }
}
private static class LF implements NumberOps<Long,Float> {
public ClassPair getClassPair() { return new ClassPair(Long.class, Float.class); }
public Number product(Long left, Float right) { return left * right; }
public Number sum(Long left, Float right) { return left + right; }
public Number subtract(Long left, Float right) { return left - right; }
public Number divide(Long left, Float right) { return left / right; }
}
private static class LL implements NumberOps<Long,Long> {
public ClassPair getClassPair() { return new ClassPair(Long.class, Long.class); }
public Number product(Long left, Long right) { return left * right; }
public Number sum(Long left, Long right) { return left + right; }
public Number subtract(Long left, Long right) { return left - right; }
public Number divide(Long left, Long right) { return left / right; }
}
private static class LI implements NumberOps<Long,Integer> {
public ClassPair getClassPair() { return new ClassPair(Long.class, Integer.class); }
public Number product(Long left, Integer right) { return left * right; }
public Number sum(Long left, Integer right) { return left + right; }
public Number subtract(Long left, Integer right) { return left - right; }
public Number divide(Long left, Integer right) { return left / right; }
}
private static class LS implements NumberOps<Long,Short> {
public ClassPair getClassPair() { return new ClassPair(Long.class, Short.class); }
public Number product(Long left, Short right) { return left * right; }
public Number sum(Long left, Short right) { return left + right; }
public Number subtract(Long left, Short right) { return left - right; }
public Number divide(Long left, Short right) { return left / right; }
}
private static class LB implements NumberOps<Long,Byte> {
public ClassPair getClassPair() { return new ClassPair(Long.class, Byte.class); }
public Number product(Long left, Byte right) { return left * right; }
public Number sum(Long left, Byte right) { return left + right; }
public Number subtract(Long left, Byte right) { return left - right; }
public Number divide(Long left, Byte right) { return left / right; }
}
private static class ID implements NumberOps<Integer,Double> {
public ClassPair getClassPair() { return new ClassPair(Integer.class, Double.class); }
public Number product(Integer left, Double right) { return left * right; }
public Number sum(Integer left, Double right) { return left + right; }
public Number subtract(Integer left, Double right) { return left - right; }
public Number divide(Integer left, Double right) { return left / right; }
}
private static class IF implements NumberOps<Integer,Float> {
public ClassPair getClassPair() { return new ClassPair(Integer.class, Float.class); }
public Number product(Integer left, Float right) { return left * right; }
public Number sum(Integer left, Float right) { return left + right; }
public Number subtract(Integer left, Float right) { return left - right; }
public Number divide(Integer left, Float right) { return left / right; }
}
private static class IL implements NumberOps<Integer,Long> {
public ClassPair getClassPair() { return new ClassPair(Integer.class, Long.class); }
public Number product(Integer left, Long right) { return left * right; }
public Number sum(Integer left, Long right) { return left + right; }
public Number subtract(Integer left, Long right) { return left - right; }
public Number divide(Integer left, Long right) { return left / right; }
}
private static class II implements NumberOps<Integer,Integer> {
public ClassPair getClassPair() { return new ClassPair(Integer.class, Integer.class); }
public Number product(Integer left, Integer right) { return left * right; }
public Number sum(Integer left, Integer right) { return left + right; }
public Number subtract(Integer left, Integer right) { return left - right; }
public Number divide(Integer left, Integer right) { return left / right; }
}
private static class IS implements NumberOps<Integer,Short> {
public ClassPair getClassPair() { return new ClassPair(Integer.class, Short.class); }
public Number product(Integer left, Short right) { return left * right; }
public Number sum(Integer left, Short right) { return left + right; }
public Number subtract(Integer left, Short right) { return left - right; }
public Number divide(Integer left, Short right) { return left / right; }
}
private static class IB implements NumberOps<Integer,Byte> {
public ClassPair getClassPair() { return new ClassPair(Integer.class, Byte.class); }
public Number product(Integer left, Byte right) { return left * right; }
public Number sum(Integer left, Byte right) { return left + right; }
public Number subtract(Integer left, Byte right) { return left - right; }
public Number divide(Integer left, Byte right) { return left / right; }
}
private static class SD implements NumberOps<Short,Double> {
public ClassPair getClassPair() { return new ClassPair(Short.class, Double.class); }
public Number product(Short left, Double right) { return left * right; }
public Number sum(Short left, Double right) { return left + right; }
public Number subtract(Short left, Double right) { return left - right; }
public Number divide(Short left, Double right) { return left / right; }
}
private static class SF implements NumberOps<Short,Float> {
public ClassPair getClassPair() { return new ClassPair(Short.class, Float.class); }
public Number product(Short left, Float right) { return left * right; }
public Number sum(Short left, Float right) { return left + right; }
public Number subtract(Short left, Float right) { return left - right; }
public Number divide(Short left, Float right) { return left / right; }
}
private static class SL implements NumberOps<Short,Long> {
public ClassPair getClassPair() { return new ClassPair(Short.class, Long.class); }
public Number product(Short left, Long right) { return left * right; }
public Number sum(Short left, Long right) { return left + right; }
public Number subtract(Short left, Long right) { return left - right; }
public Number divide(Short left, Long right) { return left / right; }
}
private static class SI implements NumberOps<Short,Integer> {
public ClassPair getClassPair() { return new ClassPair(Short.class, Integer.class); }
public Number product(Short left, Integer right) { return left * right; }
public Number sum(Short left, Integer right) { return left + right; }
public Number subtract(Short left, Integer right) { return left - right; }
public Number divide(Short left, Integer right) { return left / right; }
}
private static class SS implements NumberOps<Short,Short> {
public ClassPair getClassPair() { return new ClassPair(Short.class, Short.class); }
public Number product(Short left, Short right) { return left * right; }
public Number sum(Short left, Short right) { return left + right; }
public Number subtract(Short left, Short right) { return left - right; }
public Number divide(Short left, Short right) { return left / right; }
}
private static class SB implements NumberOps<Short,Byte> {
public ClassPair getClassPair() { return new ClassPair(Short.class, Byte.class); }
public Number product(Short left, Byte right) { return left * right; }
public Number sum(Short left, Byte right) { return left + right; }
public Number subtract(Short left, Byte right) { return left - right; }
public Number divide(Short left, Byte right) { return left / right; }
}
private static class BD implements NumberOps<Byte,Double> {
public ClassPair getClassPair() { return new ClassPair(Byte.class, Double.class); }
public Number product(Byte left, Double right) { return left * right; }
public Number sum(Byte left, Double right) { return left + right; }
public Number subtract(Byte left, Double right) { return left - right; }
public Number divide(Byte left, Double right) { return left / right; }
}
private static class BF implements NumberOps<Byte,Float> {
public ClassPair getClassPair() { return new ClassPair(Byte.class, Float.class); }
public Number product(Byte left, Float right) { return left * right; }
public Number sum(Byte left, Float right) { return left + right; }
public Number subtract(Byte left, Float right) { return left - right; }
public Number divide(Byte left, Float right) { return left / right; }
}
private static class BL implements NumberOps<Byte,Long> {
public ClassPair getClassPair() { return new ClassPair(Byte.class, Long.class); }
public Number product(Byte left, Long right) { return left * right; }
public Number sum(Byte left, Long right) { return left + right; }
public Number subtract(Byte left, Long right) { return left - right; }
public Number divide(Byte left, Long right) { return left / right; }
}
private static class BI implements NumberOps<Byte,Integer> {
public ClassPair getClassPair() { return new ClassPair(Byte.class, Integer.class); }
public Number product(Byte left, Integer right) { return left * right; }
public Number sum(Byte left, Integer right) { return left + right; }
public Number subtract(Byte left, Integer right) { return left - right; }
public Number divide(Byte left, Integer right) { return left / right; }
}
private static class BS implements NumberOps<Byte,Short> {
public ClassPair getClassPair() { return new ClassPair(Byte.class, Short.class); }
public Number product(Byte left, Short right) { return left * right; }
public Number sum(Byte left, Short right) { return left + right; }
public Number subtract(Byte left, Short right) { return left - right; }
public Number divide(Byte left, Short right) { return left / right; }
}
private static class BB implements NumberOps<Byte,Byte> {
public ClassPair getClassPair() { return new ClassPair(Byte.class, Byte.class); }
public Number product(Byte left, Byte right) { return left * right; }
public Number sum(Byte left, Byte right) { return left + right; }
public Number subtract(Byte left, Byte right) { return left - right; }
public Number divide(Byte left, Byte right) { return left / right; }
}
}