/* ===========================================================
* TradeManager : a application to trade strategies for the Java(tm) platform
* ===========================================================
*
* (C) Copyright 2011-2011, by Simon Allen and Contributors.
*
* Project Info: org.trade
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library 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 library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Oracle, Inc.
* in the United States and other countries.]
*
* (C) Copyright 2011-2011, by Simon Allen and Contributors.
*
* Original Author: Simon Allen;
* Contributor(s): -;
*
* Changes
* -------
*
*/
package org.trade.core.valuetype;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Comparator;
import org.trade.core.conversion.JavaTypeTranslator;
import org.trade.core.message.IMessageFactory;
import org.trade.core.util.CoreUtils;
import org.trade.core.validator.DecimalValidator;
import org.trade.core.validator.ExceptionMessageListener;
import org.trade.core.validator.Validator;
/**
*/
public class Decimal extends ValueType implements Comparator<Decimal>, Comparable<Decimal> {
private static final long serialVersionUID = 4937298768811778585L;
public final static Decimal ZERO = new Decimal(0L, 0);
protected static Boolean m_ascending = new Boolean(true);
static {
// Register the appropriate converters
JavaTypeTranslator.registerDynamicTypeConverter(new ObjectToDecimal());
JavaTypeTranslator.registerDynamicTypeConverter(new DecimalToObject());
}
private static String MULTIPLIER = "1";
public static String DECIMAL_POSITIVE_7_SCALE = "#######";
public static String DECIMAL_NONNEGATIVE_8_SCALE = "########";
public static String DECIMAL_POSITIVE_10_SCALE = "##########";
public static String DECIMAL_NONNEGATIVE_11_SCALE = "###########";
private static int SCALE = 0;
private String m_format = DECIMAL_NONNEGATIVE_11_SCALE;
private BigDecimal m_value = null;
private String m_invalidValue = null; // This will be null if there were
/**
* Default Constructor. Create an object and initialize it to empty.
*/
public Decimal(int scale) {
SCALE = scale;
MULTIPLIER = String.valueOf(Math.pow(10, scale));
if (scale > 0) {
DECIMAL_POSITIVE_7_SCALE = "#######.";
DECIMAL_NONNEGATIVE_8_SCALE = "########.";
DECIMAL_POSITIVE_10_SCALE = "##########.";
DECIMAL_NONNEGATIVE_11_SCALE = "###########.";
for (int i = 0; i < scale; i++) {
DECIMAL_POSITIVE_7_SCALE = DECIMAL_POSITIVE_7_SCALE + "#";
DECIMAL_NONNEGATIVE_8_SCALE = DECIMAL_NONNEGATIVE_8_SCALE + "#";
DECIMAL_POSITIVE_10_SCALE = DECIMAL_POSITIVE_10_SCALE + "#";
DECIMAL_NONNEGATIVE_11_SCALE = DECIMAL_NONNEGATIVE_11_SCALE + "#";
}
}
}
/**
* Default Constructor. Create an object and initialize it to empty.
*
* @param deciamlString
* String
*/
public Decimal(String deciamlString, int scale) {
this(scale);
if ((null != deciamlString) && (deciamlString.length() != 0)) {
// This is necessary because Java will parse strings with multiple
// dashes
if (deciamlString.indexOf("-") != deciamlString.lastIndexOf("-")) {
m_invalidValue = deciamlString;
} else {
try {
setBigDecimal(new BigDecimal(deciamlString));
} catch (NumberFormatException e) {
m_invalidValue = deciamlString;
}
}
}
}
/**
* Constructor
*
*
* @param d
* double
*/
public Decimal(double d, int scale) {
this(scale);
setBigDecimal(new BigDecimal(d));
}
/**
* Constructor
*
*
* @param d
* Double
*/
public Decimal(Double d, int scale) {
this(scale);
setBigDecimal(new BigDecimal(d.doubleValue()));
}
/**
* Constructor
*
*
* @param bd
* BigDecimal
*/
public Decimal(BigDecimal bd, int scale) {
this(scale);
setBigDecimal(bd);
}
/**
* Constructor for Decimal.
*
* @param decimal
* Decimal
*/
public Decimal(Decimal decimal, int scale) {
this(scale);
m_value = decimal.m_value;
m_format = decimal.m_format;
m_invalidValue = decimal.m_invalidValue;
}
/**
* Constructor
*
*
*
* @param nonDecimalAmount
* long
* @param decimalAmount
* int
*/
public Decimal(long nonDecimalAmount, int decimalAmount, int scale) {
this(scale);
// Set up the default constraints for IP's basic Money values
BigDecimal val = new BigDecimal((nonDecimalAmount * 100) + decimalAmount);
setBigDecimal(val.movePointLeft(SCALE));
}
/**
* Provides the format used for determining if this object is valid. The
* format should be one of the format constants on this class. The default
* format is NORMAL_11_2.
*
* @param format
* String
*/
public void setFormat(String format) {
m_format = format;
}
/**
* Method getFormat.
*
* @return String
*/
public String getFormat() {
return m_format;
}
/**
* This maximum length includes the decimal point and digits to both sides.
*
* @return int
*/
public int getMaxLength() {
int maxLength = 14;
if (getFormat().equals(DECIMAL_NONNEGATIVE_8_SCALE)) {
maxLength = DECIMAL_NONNEGATIVE_8_SCALE.length();
} else if (getFormat().equals(DECIMAL_POSITIVE_10_SCALE)) {
maxLength = DECIMAL_POSITIVE_10_SCALE.length();
} else if (getFormat().equals(DECIMAL_POSITIVE_7_SCALE)) {
maxLength = DECIMAL_POSITIVE_7_SCALE.length();
}
return maxLength;
}
/**
* This indicates whether zero is an acceptable value for this instance.
* Currently this is determined by the format returned by getFormat().
*
* @return boolean
*/
public boolean canBeZero() {
boolean zero = true;
if (getFormat().equals(DECIMAL_POSITIVE_7_SCALE)) {
zero = false;
} else if (getFormat().equals(DECIMAL_POSITIVE_10_SCALE)) {
zero = false;
}
return zero;
}
/**
* This indicates whether zero is an acceptable value for this instance.
* Currently this is determined by the format returned by getFormat().
*
* @return boolean
*/
public boolean canBeNegative() {
boolean negative = true;
return negative;
}
/**
* Method isNegative.
*
* @return boolean
*/
public boolean isNegative() {
assertDefined();
boolean negative = false;
if (m_value.compareTo(new BigDecimal(0)) < 0) {
negative = true;
}
return negative;
}
/**
* Method isEmpty.
*
* @return boolean
*/
public boolean isEmpty() {
boolean empty = false;
if ((null == m_value) || (null != m_invalidValue)) {
empty = true;
}
return empty;
}
/**
*
* @return The value before the decimal point in the money value.
*/
public long getNonDecimalAmount() {
assertDefined();
long nonDecimalAmount = 0;
if (null != m_value) {
nonDecimalAmount = m_value.longValue();
}
return nonDecimalAmount;
}
/**
* See description of superclass method. Overrode functionality to return
* the BigDecimal this object is using intrnally.
*
* @return Object
*/
public Object getSQLObject() {
return (getBigDecimalValue());
}
/**
*
* @return The value after the decimal point in the money value.
*/
public int getDecimalAmount() {
assertDefined();
int decimalAmount = 0;
if (null != m_value) {
BigInteger tot = (m_value.movePointRight(SCALE)).toBigInteger();
BigInteger sub = m_value.toBigInteger();
sub = sub.multiply(new BigInteger(MULTIPLIER));
BigInteger res = tot.subtract(sub);
decimalAmount = res.intValue();
}
return decimalAmount;
}
/**
* Will throw a <code>NullPointerException</code> if this valuetype is
* empty.
*
*
* @return A BigDecimal representing the monetary value.
*/
public BigDecimal getBigDecimalValue() {
assertDefined();
return m_value;
}
/**
* Method toString.
*
* @return String
*/
public String toString() {
if (null != m_value) {
return (m_value.toString());
} else if (null != m_invalidValue) {
return m_invalidValue;
} else {
return "";
}
}
/**
* Method setValue.
*
* @param value
* Object
* @throws ValueTypeException
*/
public void setValue(Object value) throws ValueTypeException {
if (value instanceof Decimal) {
setBigDecimal(((Decimal) value).m_value);
} else {
try {
setBigDecimal(((Decimal) JavaTypeTranslator.convert(Decimal.class, value)).getBigDecimalValue());
} catch (Exception ex) {
throw new ValueTypeException(ex);
}
}
}
/**
* Adds two Money objects
*
*
*
*
* @param decimal
* Decimal
* @return Money the result
*/
public Decimal add(Decimal decimal) {
assertDefined();
if (null == m_value) {
if (null == decimal.getBigDecimalValue()) {
return new Decimal(SCALE);
} else {
return new Decimal(decimal.getBigDecimalValue(), SCALE);
}
}
BigDecimal value = m_value.add(decimal.getBigDecimalValue());
return new Decimal(value, SCALE);
}
/**
* Subtracts two decimal objects
*
*
*
*
* @param decimal
* Decimal
* @return Money the result
*/
public Decimal subtract(Decimal decimal) {
assertDefined();
if (null == m_value) {
return (decimal);
}
BigDecimal value = m_value.subtract(decimal.getBigDecimalValue());
return new Decimal(value, SCALE);
}
/**
* Compares two Money objects.
*
*
*
*
* @param decimal
* Decimal
* @return boolean result.
*/
public boolean isLessThan(Decimal decimal) {
assertDefined();
BigDecimal thisValue = notNull(this);
BigDecimal parameter = notNull(decimal);
return (thisValue.compareTo(parameter) < 0);
}
/**
* Compares two Money objects.
*
*
*
*
* @param decimal
* Decimal
* @return boolean result.
*/
public boolean isLessThanOrEqualTo(Decimal decimal) {
assertDefined();
BigDecimal thisValue = notNull(this);
BigDecimal parameter = notNull(decimal);
return (thisValue.compareTo(parameter) <= 0);
}
/**
* Compares two Money objects.
*
*
*
*
* @param decimal
* Decimal
* @return boolean result.
*/
public boolean isGreaterThan(Decimal decimal) {
assertDefined();
BigDecimal thisValue = notNull(this);
BigDecimal parameter = notNull(decimal);
return (thisValue.compareTo(parameter) > 0);
}
/**
* Compares two Money objects.
*
*
*
*
* @param decimal
* Decimal
* @return boolean result.
*/
public boolean isGreaterThanOrEqualTo(Decimal decimal) {
assertDefined();
BigDecimal thisValue = notNull(this);
BigDecimal parameter = notNull(decimal);
return (thisValue.compareTo(parameter) >= 0);
}
/**
* Method isValid.
*
* @param validator
* Validator
* @param receiver
* ExceptionMessageListener
* @return boolean
*/
public boolean isValid(Validator validator, ExceptionMessageListener receiver) {
return validator.isValid(m_value, m_invalidValue, null, receiver);
}
/**
* Method getDefaultValidator.
*
* @param messageFactory
* IMessageFactory
* @param isMandatory
* boolean
* @return Validator
*/
public Validator getDefaultValidator(IMessageFactory messageFactory, boolean isMandatory) {
// This allow non-negative 11.2
return new DecimalValidator(messageFactory, false, true, 11, 2, isMandatory);
}
/**
* Will throw a <code>NullPointerException</code> if this valuetype is
* empty.
*
*
* @return A double representing the monetary value.
*/
public double doubleValue() {
assertDefined();
return m_value.doubleValue();
}
/**
* Overrides Cloneable
*
*
*
*
*
*
* @return Object
* @exception *
* @see
*/
public Object clone() {
try {
Decimal other = (Decimal) super.clone();
return other;
} catch (CloneNotSupportedException e) {
// will never happen
return null;
}
}
/**
* Method compareTo.
*
* @param other
* Decimal
* @return int
*/
public int compareTo(final Decimal other) {
return CoreUtils.nullSafeComparator(this.getBigDecimalValue(), other.getBigDecimalValue());
}
/**
* Method compare.
*
* @param o1
* Decimal
* @param o2
* Decimal
* @return int
*/
public int compare(Decimal o1, Decimal o2) {
int returnVal = CoreUtils.nullSafeComparator(o1.getBigDecimalValue(), o2.getBigDecimalValue());
if (m_ascending.equals(Boolean.FALSE)) {
returnVal = returnVal * -1;
}
return returnVal;
}
/**
* Method equals.
*
* @param objectToCompare
* Object
* @return boolean
*/
public boolean equals(Object objectToCompare) {
if (super.equals(objectToCompare))
return true;
if (objectToCompare instanceof Decimal) {
if (CoreUtils.nullSafeComparator(((Decimal) objectToCompare).getBigDecimalValue(),
this.getBigDecimalValue()) == 0)
return true;
}
return false;
}
//
// Private Methods
//
/**
* Method setBigDecimal.
*
* @param value
* BigDecimal
*/
private void setBigDecimal(BigDecimal value) {
if (value == null) {
m_value = new BigDecimal(0.0);
} else {
// m_value = value;
m_value = value.setScale(SCALE, BigDecimal.ROUND_HALF_EVEN);
}
// Clear any invalid values
m_invalidValue = null;
}
/**
* Method notNull.
*
* @param value
* Decimal
* @return BigDecimal
*/
private BigDecimal notNull(Decimal value) {
if (null == value) {
return (new BigDecimal(0.0D));
} else {
return (value.getBigDecimalValue());
}
}
private void assertDefined() {
if (null != m_invalidValue) {
throw new NumberFormatException(
"Attempting to use a Money that was not properly initialized. Invalid value is: "
+ m_invalidValue);
}
}
}