/**
* Copyright 2010 Archfirst
*
* 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.archfirst.common.quantity;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import javax.persistence.Embeddable;
import javax.persistence.Transient;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/**
* A decimal quantity.
*
* This is almost an immutable object - I tried to remove the setter and use
* field-based access, but then Hibernate gives following error:
*
* org.hibernate.PropertyNotFoundException: Could not find a setter for
* property value in class org.archfirst.util.quantity.DecimalQuantity
*
* For now, just don't use the setter.
*
* @author Naresh Bhatia
*/
@XmlJavaTypeAdapter(DecimalQuantityAdapter.class)
@Embeddable
public class DecimalQuantity implements Comparable<DecimalQuantity>, Serializable {
private static final long serialVersionUID = 1L;
public static final DecimalQuantity ZERO = new DecimalQuantity();
/** The rounding mode used for calculations */
private static RoundingMode roundingMode = RoundingMode.HALF_UP;
private BigDecimal value;
// ----- Constructors -----
public DecimalQuantity() {
this.value = BigDecimal.ZERO;
}
public DecimalQuantity(String value) {
this.value = new BigDecimal(value);
}
public DecimalQuantity(BigDecimal value) {
this.value = value;
}
public DecimalQuantity(long value) {
this.value = new BigDecimal(value);
}
public DecimalQuantity(double value) {
// BigDecimal(String) is the preferred constructor
this.value = new BigDecimal(Double.toString(value));
}
// ----- Operations -----
public DecimalQuantity plus(DecimalQuantity that) {
return new DecimalQuantity(value.add(that.getValue()));
}
public DecimalQuantity minus(DecimalQuantity that) {
return new DecimalQuantity(value.subtract(that.getValue()));
}
public DecimalQuantity multiply(long multiplicand) {
return new DecimalQuantity(value.multiply(new BigDecimal(multiplicand)));
}
public DecimalQuantity multiply(BigDecimal multiplicand) {
return new DecimalQuantity(value.multiply(multiplicand));
}
public DecimalQuantity divide(long divisor) {
BigDecimal bdDivisor = new BigDecimal(divisor);
BigDecimal newValue = value.divide(bdDivisor, roundingMode);
return new DecimalQuantity(newValue);
}
public DecimalQuantity divide(BigDecimal divisor) {
BigDecimal newValue = value.divide(divisor, roundingMode);
return new DecimalQuantity(newValue);
}
public DecimalQuantity abs() {
return new DecimalQuantity(value.abs());
}
public DecimalQuantity negate() {
return this.multiply(-1L);
}
public DecimalQuantity setScale(int newScale) {
return new DecimalQuantity(
value.stripTrailingZeros().setScale(newScale, roundingMode));
}
// ----- Getters and Setters -----
public BigDecimal getValue() {
return value;
}
private void setValue(BigDecimal value) {
this.value = value;
}
// ----- Read-Only Operations -----
public long longValue() {
return value.longValue();
}
public double doubleValue() {
return value.doubleValue();
}
public DecimalQuantity min(DecimalQuantity that) {
return (this.compareTo(that) < 0) ? this : that;
}
public DecimalQuantity max(DecimalQuantity that) {
return (this.compareTo(that) > 0) ? this : that;
}
/** Considers two quantities as equal only if they are equal in value and scale */
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (!(object instanceof DecimalQuantity)) {
return false;
}
final DecimalQuantity that = (DecimalQuantity)object;
return this.value.equals(that.getValue());
}
@Override
public int hashCode() {
return value.hashCode();
}
/** Return true only if the quantity is positive. */
@Transient
public boolean isPlus() {
return value.compareTo(BigDecimal.ZERO) > 0;
}
/** Return true only if the quantity is negative. */
@Transient
public boolean isMinus() {
return value.compareTo(BigDecimal.ZERO) < 0;
}
/** Return true only if the quantity is zero. */
@Transient
public boolean isZero() {
return value.compareTo(BigDecimal.ZERO) == 0;
}
/**
* Considers two quantities as equal if they are equal in value regardless
* of scale (unlike equal() which requires the scale to be the same too)
*/
public boolean eq(DecimalQuantity that) {
return this.value.compareTo(that.getValue()) == 0;
}
/**
* Return true only if 'this' quantity is greater than 'that' quantity.
*/
public boolean gt(DecimalQuantity that) {
return this.value.compareTo(that.getValue()) > 0;
}
/**
* Return true only if 'this' quantity is greater than or equal to
* 'that' quantity.
*/
public boolean gteq(DecimalQuantity that) {
return this.value.compareTo(that.getValue()) >= 0;
}
/**
* Return true only if 'this' quantity is less than 'that' quantity.
*/
public boolean lt(DecimalQuantity that) {
return this.value.compareTo(that.getValue()) < 0;
}
/**
* Return true only if 'this' quantity is less than or equal to
* 'that' quantity.
*/
public boolean lteq(DecimalQuantity that) {
return this.value.compareTo(that.getValue()) <= 0;
}
/**
* Considers two quantities as equal if they are equal in value
* regardless of scale
*/
@Override
public int compareTo(DecimalQuantity that) {
return this.value.compareTo(that.getValue());
}
@Override
public String toString() {
return this.value.toString();
}
}