/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* File: $Source: /cvsroot/slrp/glitter/com.ibm.adtech.glitter/src/com/ibm/adtech/glitter/util/PolymorphicNumber.java,v $
* Created by: Lee Feigenbaum (<a href="mailto:feigenbl@us.ibm.com">feigenbl@us.ibm.com</a>)
* Created on: 10/23/06
* Revision: $Id: PolymorphicNumber.java 164 2007-07-31 14:11:09Z mroy $
*
* Contributors: IBM Corporation - initial API and implementation
* Cambridge Semantics Incorporated - Fork to Anzo
*******************************************************************************/
package org.openanzo.glitter.util;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import org.openanzo.rdf.MemTypedLiteral;
import org.openanzo.rdf.TriplePatternComponent;
import org.openanzo.rdf.TypedLiteral;
/**
* A {@link PolymorphicNumber} handles math operations on numeric types that may require type promotion and coercion.
*
* @author lee <lee@cambridgesemantics.com>
*
*/
final public class PolymorphicNumber extends Number implements Comparable<PolymorphicNumber> {
/**
* Constant 0.
*/
static final protected PolymorphicNumber ZERO = new PolymorphicNumber(0);
private static final long serialVersionUID = 3835267671359590129L;
private final Number number;
private Long numberAsLong = null;
private BigInteger numberAsBigInteger = null;
private Double numberAsDouble = null;
private BigDecimal numberAsBigDecimal = null;
private MathOps ops = null;
/**
* Create a {@link PolymorphicNumber} from a Java {@link Number}.
*
* @param n
* {@link Number} to create from
*/
public PolymorphicNumber(Number n) {
this.number = n;
}
/**
* Create a {@link PolymorphicNumber} from the numeric value of a Glitter term.
*
* @param tpc
* TriplePatternComponent to create from
*/
public PolymorphicNumber(TriplePatternComponent tpc) {
this.number = (Number) ((TypedLiteral) tpc).getNativeValue();
}
/**
* Get the value of this number in narrowest (most specific) representation for its type.
*
* @return The value of this number in narrowest (most specific) representation for its type.
*/
private Number narrowestValue() {
return getOps().narrowestValue();
}
/**
* Get the value of this number in its narrowest form wrapped as a {@link TypedLiteral}
*
* @return The value of this number in its narrowest form wrapped as a {@link TypedLiteral}.
*/
public TypedLiteral asTypedLiteral() {
return MemTypedLiteral.create(narrowestValue());
}
/**
* Polymorphic addition.
*
* @param other
* PolymorphicNumber to add to this PolymorphicNumber
* @return PolymorphicNumber that is the product of adding this PolymorphicNumber and the other PolymorphicNumber
*/
public PolymorphicNumber add(PolymorphicNumber other) {
return getOps(other).add(other);
}
/**
* Polymorphic subtraction.
*
* @param other
* PolymorphicNumber to subtract from this PolymorphicNumber
* @return PolymorphicNumber that is the product of subtracting this PolymorphicNumber and the other PolymorphicNumber
*/
public PolymorphicNumber subtract(PolymorphicNumber other) {
return getOps(other).subtract(other);
}
/**
* Polymorphic multiplication.
*
* @param other
* PolymorphicNumber to multiply with this PolymorphicNumber
* @return PolymorphicNumber that is the product of multiplying this PolymorphicNumber and the other PolymorphicNumber
*/
public PolymorphicNumber multiply(PolymorphicNumber other) {
return getOps(other).multiply(other);
}
/**
* Polymorphic division.
*
* @param other
* PolymorphicNumber to divide from this PolymorphicNumber
* @return PolymorphicNumber that is the product of dividing this PolymorphicNumber and the other PolymorphicNumber
*/
public PolymorphicNumber divide(PolymorphicNumber other) {
return getOps(other).divide(other);
}
/**
* Polymorphic numeric negation.
*
* @return PolymorphicNumber that is the negation of this PolymorphicNumber
*/
public PolymorphicNumber negate() {
return getOps().negate();
}
public int compareTo(PolymorphicNumber other) {
return getOps(other).compareTo(other);
}
@Override
public boolean equals(Object o) {
if (o instanceof PolymorphicNumber)
return compareTo((PolymorphicNumber) o) == 0;
return false;
}
@Override
public int hashCode() {
return this.number.hashCode();
}
// extraction
/**
* @return A representation of this number as a {@link BigInteger}
*/
public BigInteger bigIntegerValue() {
if (this.numberAsBigInteger == null) {
if (this.number instanceof BigInteger)
this.numberAsBigInteger = (BigInteger) this.number;
else if (this.number instanceof BigDecimal)
this.numberAsBigInteger = ((BigDecimal) this.number).toBigInteger();
else
this.numberAsBigInteger = BigInteger.valueOf(longValue());
}
return this.numberAsBigInteger;
}
/**
*
* @return A representation of this number as a {@link BigDecimal}.
*/
public BigDecimal bigDecimalValue() {
if (this.numberAsBigDecimal == null) {
if (this.number instanceof BigDecimal)
this.numberAsBigDecimal = (BigDecimal) this.number;
else if (this.number instanceof BigInteger)
this.numberAsBigDecimal = new BigDecimal((BigInteger) this.number);
else if (convertsSafelyToLong())
this.numberAsBigDecimal = BigDecimal.valueOf(longValue());
else
this.numberAsBigDecimal = BigDecimal.valueOf(doubleValue());
}
return this.numberAsBigDecimal;
}
////////////////////////////////////////////////
// java.lang.Number
//
@Override
public double doubleValue() {
if (this.numberAsDouble == null)
this.numberAsDouble = this.number.doubleValue();
return this.numberAsDouble;
}
@Override
public float floatValue() {
return (float) doubleValue();
}
@Override
public int intValue() {
return (int) longValue();
}
@Override
public long longValue() {
if (this.numberAsLong == null)
this.numberAsLong = this.number.longValue();
return this.numberAsLong;
}
//////////////////////////////////////////////////
/**
* @return Whether this number is an integer that fits into a long
*/
public boolean convertsSafelyToLong() {
return this.number instanceof Byte || this.number instanceof Short || this.number instanceof Integer || this.number instanceof Long;
}
/**
* @return Whether this number can safely fit into a double precision floating point number
*/
public boolean convertsSafelyToDouble() {
return convertsSafelyToLong() || this.number instanceof Float || this.number instanceof Double;
}
private MathOps getOps() {
if (this.ops == null) {
if (convertsSafelyToLong())
this.ops = new LongOps();
else if (convertsSafelyToDouble())
this.ops = new DoubleOps();
else if (this.number instanceof BigInteger)
this.ops = new BigIntegerOps();
else
this.ops = new BigDecimalOps();
}
return this.ops;
}
// even if they are wider, we need to return an ops
// attached to ourselves since we're the first argument
// in whatever operation is going on
private MathOps getOps(PolymorphicNumber other) {
MathOps us = getOps(), them = other.getOps();
if (us instanceof BigDecimalOps)
return us;
if (them instanceof BigDecimalOps)
return new BigDecimalOps();
if (us instanceof BigIntegerOps)
return us;
if (them instanceof BigIntegerOps)
return new BigIntegerOps();
if (us instanceof DoubleOps)
return us;
if (them instanceof DoubleOps)
return new DoubleOps();
return us;
}
//////////////////////////////////////////////////
private interface MathOps {
/**
* A specific (by type) addition operation.
*
* @param other
* PolymorphicNumber to add to this PolymorphicNumber
* @return PolymorphicNumber that is the product of adding this PolymorphicNumber and the other PolymorphicNumber
*/
public PolymorphicNumber add(PolymorphicNumber other);
/**
* A specific (by type) subtraction operation.
*
* @param other
* PolymorphicNumber to subtract from this PolymorphicNumber
* @return PolymorphicNumber that is the product of subtracting this PolymorphicNumber and the other PolymorphicNumber
*/
public PolymorphicNumber subtract(PolymorphicNumber other);
/**
* A specific (by type) multiplication operation.
*
* @param other
* PolymorphicNumber to multiply with this PolymorphicNumber
* @return PolymorphicNumber that is the product of multiplying this PolymorphicNumber and the other PolymorphicNumber
*/
public PolymorphicNumber multiply(PolymorphicNumber other);
/**
* A specific (by type) division operation.
*
* @param other
* PolymorphicNumber to divide from this PolymorphicNumber
* @return PolymorphicNumber that is the product of dividing this PolymorphicNumber and the other PolymorphicNumber
*/
public PolymorphicNumber divide(PolymorphicNumber other);
/**
* A specific (by type) mod operation.
*
* @param other
* PolymorphicNumber to mod from this PolymorphicNumber
* @return PolymorphicNumber that is the product of mod this PolymorphicNumber and the other PolymorphicNumber
*/
public PolymorphicNumber mod(PolymorphicNumber other);
/**
* A specific (by type) numeric negation operation.
*
* @return PolymorphicNumber that is the negation of this PolymorphicNumber
*/
public PolymorphicNumber negate();
/**
* A specific (by type) comparison operation.
*
* @param other
* PolymorphicNumber to compare against
* @return results of comparing to another PolymorphicNumber
*/
public int compareTo(PolymorphicNumber other);
/**
* Extract the number as the narrowest {@link Number} subclass that it requires.
*
* @return The value of this number in narrowest (most specific) representation for its type.
*/
public Number narrowestValue();
}
private class LongOps implements MathOps {
public PolymorphicNumber add(PolymorphicNumber other) {
return new PolymorphicNumber(longValue() + other.longValue());
}
public int compareTo(PolymorphicNumber other) {
return Long.valueOf(longValue()).compareTo(other.longValue());
}
public PolymorphicNumber divide(PolymorphicNumber other) {
return new PolymorphicNumber(doubleValue() / other.doubleValue());
}
public PolymorphicNumber mod(PolymorphicNumber other) {
return new PolymorphicNumber(longValue() % other.longValue());
}
public PolymorphicNumber multiply(PolymorphicNumber other) {
return new PolymorphicNumber(longValue() * other.longValue());
}
public PolymorphicNumber negate() {
return new PolymorphicNumber(-longValue());
}
public Number narrowestValue() {
return longValue();
}
public PolymorphicNumber subtract(PolymorphicNumber other) {
return new PolymorphicNumber(longValue() - other.longValue());
}
}
private class DoubleOps implements MathOps {
public PolymorphicNumber add(PolymorphicNumber other) {
return new PolymorphicNumber(doubleValue() + other.doubleValue());
}
public int compareTo(PolymorphicNumber other) {
return new Double(doubleValue()).compareTo(other.doubleValue());
}
public PolymorphicNumber divide(PolymorphicNumber other) {
return new PolymorphicNumber(doubleValue() / other.doubleValue());
}
public PolymorphicNumber mod(PolymorphicNumber other) {
return new PolymorphicNumber(doubleValue() % other.doubleValue());
}
public PolymorphicNumber multiply(PolymorphicNumber other) {
return new PolymorphicNumber(doubleValue() * other.doubleValue());
}
public PolymorphicNumber negate() {
return new PolymorphicNumber(-doubleValue());
}
public Number narrowestValue() {
return doubleValue();
}
public PolymorphicNumber subtract(PolymorphicNumber other) {
return new PolymorphicNumber(doubleValue() - other.doubleValue());
}
}
private class BigIntegerOps implements MathOps {
public PolymorphicNumber add(PolymorphicNumber other) {
return new PolymorphicNumber(bigIntegerValue().add(other.bigIntegerValue()));
}
public int compareTo(PolymorphicNumber other) {
return bigIntegerValue().compareTo(other.bigIntegerValue());
}
public PolymorphicNumber divide(PolymorphicNumber other) {
BigDecimal first = bigDecimalValue();
BigDecimal second = other.bigDecimalValue();
try {
return new PolymorphicNumber(first.divide(second));
} catch (ArithmeticException e) {
return new PolymorphicNumber(first.divide(second, 20, RoundingMode.HALF_UP));
}
}
public PolymorphicNumber mod(PolymorphicNumber other) {
return new PolymorphicNumber(bigIntegerValue().mod(other.bigIntegerValue()));
}
public PolymorphicNumber multiply(PolymorphicNumber other) {
return new PolymorphicNumber(bigIntegerValue().multiply(other.bigIntegerValue()));
}
public PolymorphicNumber negate() {
return new PolymorphicNumber(bigIntegerValue().negate());
}
public Number narrowestValue() {
return bigIntegerValue();
}
public PolymorphicNumber subtract(PolymorphicNumber other) {
return new PolymorphicNumber(bigIntegerValue().subtract(other.bigIntegerValue()));
}
}
private class BigDecimalOps implements MathOps {
public PolymorphicNumber add(PolymorphicNumber other) {
return new PolymorphicNumber(bigDecimalValue().add(other.bigDecimalValue()));
}
public int compareTo(PolymorphicNumber other) {
return bigDecimalValue().compareTo(other.bigDecimalValue());
}
public PolymorphicNumber divide(PolymorphicNumber other) {
BigDecimal first = bigDecimalValue();
BigDecimal second = other.bigDecimalValue();
try {
return new PolymorphicNumber(first.divide(second));
} catch (ArithmeticException e) {
return new PolymorphicNumber(first.divide(second, 20, RoundingMode.HALF_UP));
}
}
public PolymorphicNumber mod(PolymorphicNumber other) {
// TODO - this isn't correct
return new PolymorphicNumber(bigDecimalValue().remainder(other.bigDecimalValue()));
}
public PolymorphicNumber multiply(PolymorphicNumber other) {
return new PolymorphicNumber(bigDecimalValue().multiply(other.bigDecimalValue()));
}
public PolymorphicNumber negate() {
return new PolymorphicNumber(bigDecimalValue().negate());
}
public Number narrowestValue() {
return bigDecimalValue();
}
public PolymorphicNumber subtract(PolymorphicNumber other) {
return new PolymorphicNumber(bigDecimalValue().subtract(other.bigDecimalValue()));
}
}
}