/* ===========================================================
* 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.ExceptionMessageListener;
import org.trade.core.validator.PercentValidator;
import org.trade.core.validator.Validator;
/**
*/
public class Percent extends ValueType implements Comparator<Percent>, Comparable<Percent> {
/**
*
*/
private static final long serialVersionUID = 6356086072126179279L;
public final static String PERCENT_POSITIVE_7_2 = "($)#(,)###(,)###(.##)";
public final static String PERCENT_NONNEGATIVE_8_2 = "($)##(,)###(,)###(.##)";
public final static String PERCENT_POSITIVE_10_2 = "($)#(,)###(,)###(,)###(.##)";
public final static String PERCENT_NONNEGATIVE_11_2 = "($)##(,)###(,)###(,)###(.##)";
public final static Percent ZERO = new Percent(0L, 0);
protected static Boolean m_ascending = new Boolean(true);
static {
// Register the appropriate converters
JavaTypeTranslator.registerDynamicTypeConverter(new ObjectToPercent());
JavaTypeTranslator.registerDynamicTypeConverter(new PercentToObject());
}
//
// Private Attributes
//
private BigDecimal m_value = null;
private String m_format = PERCENT_NONNEGATIVE_11_2;
private String m_invalidValue = null; // This will be null if there were
// no conversion errors
private final static int SCALE = 6;
private final static String MULTIPLIER = "100";
//
// Public Methods
//
/**
* Default Constructor. Create an object and initialize it to empty.
*/
public Percent() {
}
/**
* Default Constructor. Create an object and initialize it to empty.
*
* @param PercentString
* String
*/
public Percent(String PercentString) {
if ((null != PercentString) && (PercentString.length() != 0)) {
// This is necessary because Java will parse strings with multiple
// dashes
if (PercentString.indexOf("-") != PercentString.lastIndexOf("-")) {
m_invalidValue = PercentString;
} else {
try {
setBigDecimal(new BigDecimal(PercentString));
} catch (NumberFormatException e) {
m_invalidValue = PercentString;
}
}
}
}
/**
* Constructor
*
*
* @param d
* Integer
*/
public Percent(Integer d) {
setBigDecimal(d);
}
/**
* Constructor
*
*
* @param d
* double
*/
public Percent(double d) {
setBigDecimal(new BigDecimal(d));
}
/**
* Constructor
*
*
* @param d
* Double
*/
public Percent(Double d) {
setBigDecimal(new BigDecimal(d.doubleValue()));
}
/**
* Constructor
*
*
* @param bd
* BigDecimal
*/
public Percent(BigDecimal bd) {
setBigDecimal(bd);
}
/**
* Constructor for Percent.
*
* @param Percent
* Percent
*/
public Percent(Percent Percent) {
m_value = Percent.m_value;
m_format = Percent.m_format;
m_invalidValue = Percent.m_invalidValue;
}
/**
* Constructor
*
*
*
* @param nonDecimalAmount
* long
* @param decimalAmount
* int
*/
public Percent(long nonDecimalAmount, int decimalAmount) {
// Set up the default constraints for basic Percent 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(PERCENT_NONNEGATIVE_8_2)) {
maxLength = 11;
} else if (getFormat().equals(PERCENT_POSITIVE_10_2)) {
maxLength = 13;
} else if (getFormat().equals(PERCENT_POSITIVE_7_2)) {
maxLength = 10;
}
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(PERCENT_POSITIVE_7_2)) {
zero = false;
} else if (getFormat().equals(PERCENT_POSITIVE_10_2)) {
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 = false;
// Currently all formats prohibit negative numbers.
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 Percent 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 Percent 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 Percent) {
setBigDecimal(((Percent) value).m_value);
} else {
try {
setBigDecimal(((Percent) JavaTypeTranslator.convert(Percent.class, value)).getBigDecimalValue());
} catch (Exception ex) {
throw new ValueTypeException(ex);
}
}
}
/**
* Adds two Percent objects
*
* @param Percent
* the Percent object to be added
*
*
* @return Percent the result
*/
public Percent add(Percent Percent) {
assertDefined();
if (null == m_value) {
if (null == Percent.getBigDecimalValue()) {
return new Percent();
} else {
return new Percent(Percent.getBigDecimalValue());
}
}
BigDecimal value = m_value.add(Percent.getBigDecimalValue());
return new Percent(value);
}
/**
* Subtracts two Percent objects
*
* @param Percent
* the Percent object to be subtracted
*
*
* @return Percent the result
*/
public Percent subtract(Percent Percent) {
assertDefined();
if (null == m_value) {
return (Percent);
}
BigDecimal value = m_value.subtract(Percent.getBigDecimalValue());
return new Percent(value);
}
/**
* Compares two Percent objects.
*
* @param Percent
* the Percent object to compare with.
*
*
* @return boolean result.
*/
public boolean isLessThen(Percent Percent) {
assertDefined();
BigDecimal thisValue = notNull(this);
BigDecimal parameter = notNull(Percent);
return (thisValue.compareTo(parameter) < 0);
}
/**
* Compares two Percent objects.
*
* @param Percent
* the Percent object to compare with.
*
*
* @return boolean result.
*/
public boolean isLessThenOrEqualTo(Percent Percent) {
assertDefined();
BigDecimal thisValue = notNull(this);
BigDecimal parameter = notNull(Percent);
return (thisValue.compareTo(parameter) <= 0);
}
/**
* Compares two Percent objects.
*
* @param Percent
* the Percent object to compare with.
*
*
* @return boolean result.
*/
public boolean isGreaterThen(Percent Percent) {
assertDefined();
BigDecimal thisValue = notNull(this);
BigDecimal parameter = notNull(Percent);
return (thisValue.compareTo(parameter) > 0);
}
/**
* Compares two Percent objects.
*
* @param Percent
* the Percent object to compare with.
*
*
* @return boolean result.
*/
public boolean isGreaterThenOrEqualTo(Percent Percent) {
assertDefined();
BigDecimal thisValue = notNull(this);
BigDecimal parameter = notNull(Percent);
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 PercentValidator(messageFactory, false, true, 7, 5, isMandatory);
}
/**
* @deprecated Use the new validator method instead.
* @return boolean
*/
public boolean isValid() {
boolean valid = false;
String error = getError();
if (null == error) {
valid = true;
}
return valid;
}
/**
* @deprecated Use the new validator method instead.
* @return String
*/
public String getError() {
String error = null;
if (!isEmpty()) {
long nonDecimalLength = Long.toString(getNonDecimalAmount()).length();
// Note that the decimal length will be 1 for 00-09.
long decimalLength = Long.toString(getDecimalAmount()).length();
// Allow only 2 decimal places.
if (decimalLength > 2) {
error = "only two decimal places are allowed";
}
// Add three to account for the decimal portion and decimal point.
if ((nonDecimalLength + 3) > getMaxLength()) {
error = "length of digits and decimal point should not exceed " + getMaxLength();
}
// Disallow zero for certain formats
if (!canBeZero() && (getBigDecimalValue().doubleValue() == 0)) {
error = "amount cannot be zero";
}
// Disallow negative numbers
if (!canBeNegative() && (getBigDecimalValue().doubleValue() < 0)) {
error = "amount cannot be negative";
}
}
return error;
}
/**
* 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 {
Percent other = (Percent) super.clone();
return other;
} catch (CloneNotSupportedException e) {
// will never happen
return null;
}
}
/**
* Method compareTo.
*
* @param other
* Percent
* @return int
*/
public int compareTo(final Percent other) {
return CoreUtils.nullSafeComparator(this.getBigDecimalValue(), other.getBigDecimalValue());
}
/**
* Method compare.
*
* @param o1
* Percent
* @param o2
* Percent
* @return int
*/
public int compare(Percent o1, Percent 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 Percent) {
if (CoreUtils.nullSafeComparator(((Percent) objectToCompare).getBigDecimalValue(),
this.getBigDecimalValue()) == 0)
return true;
}
return false;
}
//
// Private Methods
//
/**
* Method setBigDecimal.
*
* @param value
* Integer
*/
private void setBigDecimal(Integer value) {
if (null == value) {
m_value = new BigDecimal(0.0);
} else {
// m_value = value;
m_value = (new BigDecimal(value)).setScale(SCALE, BigDecimal.ROUND_HALF_EVEN);
}
// Clear any invalid values
m_invalidValue = null;
}
/**
* Method setBigDecimal.
*
* @param value
* BigDecimal
*/
private void setBigDecimal(BigDecimal value) {
if (null == value) {
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
* Percent
* @return BigDecimal
*/
private BigDecimal notNull(Percent 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 Percent that was not properly initialized. Invalid value is: "
+ m_invalidValue);
}
}
}