All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hfg.units.Quantity Maven / Gradle / Ivy

There is a newer version: 20240423
Show newest version
package com.hfg.units;


import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.hfg.exception.InvalidValueException;
import com.hfg.util.CompareUtil;
import com.hfg.util.StringBuilderPlus;
import com.hfg.util.StringUtil;
import com.hfg.util.collection.CollectionUtil;

//------------------------------------------------------------------------------
/**
 Quantifiable amount. Amount plus units.
 
@author J. Alex Taylor, hairyfatguy.com
*/ //------------------------------------------------------------------------------ // com.hfg XML/HTML Coding Library // // 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA // // J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com // [email protected] //------------------------------------------------------------------------------ public class Quantity implements Comparable { private Double mDoubleValue; private Integer mIntValue; private Long mLongValue; private Unit mUnit; private Map mScalingFactorMap; // private static Pattern sIntRegex = Pattern.compile("(-?\\d+)\\s?+([^\\d\\.]\\S+)"); private static Pattern sIntRegex = Pattern.compile("(-?\\d+)\\s?+([^\\d]+)"); private static Pattern sDoubleRegex = Pattern.compile("(-?\\d+\\.\\d+)\\s?+([^\\d]+)"); private static Pattern sScientificRegex = Pattern.compile("(-?\\d+(?:\\.\\d+)?E\\-?\\d+)\\s?+(\\S+)"); //########################################################################### // CONSTRUCTORS //########################################################################### //--------------------------------------------------------------------------- /** Convenience constructor that will call Unit.valueOf() on the specified unit string. * @param inValue the numeric amount as a double * @param inUnit the volume unit of the specified amount */ public Quantity(Double inValue, String inUnit) { this(inValue, Unit.valueOf(inUnit)); } //--------------------------------------------------------------------------- /** Convenience constructor that will call Unit.valueOf() on the specified unit string. * @param inValue the numeric amount as a float * @param inUnit the volume unit of the specified amount */ public Quantity(Float inValue, String inUnit) { this(inValue, Unit.valueOf(inUnit)); } //--------------------------------------------------------------------------- /** Convenience constructor that will call Unit.valueOf() on the specified unit string. * @param inValue the numeric amount as an integer * @param inUnit the volume unit of the specified amount */ public Quantity(Integer inValue, String inUnit) { this(inValue, Unit.valueOf(inUnit)); } //--------------------------------------------------------------------------- /** Convenience constructor that will call Unit.valueOf() on the specified unit string. * @param inValue the numeric amount as an integer * @param inUnit the volume unit of the specified amount */ public Quantity(Long inValue, String inUnit) { this(inValue, Unit.valueOf(inUnit)); } //--------------------------------------------------------------------------- /** Convenience constructor. * @param inValue the string value */ public Quantity(String inValue) { Matcher m = sIntRegex.matcher(inValue.trim()); if (m.matches()) { mIntValue = Integer.parseInt(m.group(1)); mUnit = Unit.valueOf(m.group(2)); } else { m = sDoubleRegex.matcher(inValue.trim()); if (m.matches()) { mDoubleValue = Double.parseDouble(m.group(1)); mUnit = Unit.valueOf(m.group(2)); } else { m = sScientificRegex.matcher(inValue.trim()); if (m.matches()) { mDoubleValue = Double.parseDouble(m.group(1)); mUnit = Unit.valueOf(m.group(2)); } else { throw new UnitException("Couldn't parse " + StringUtil.singleQuote(inValue) + " into a Quantity!"); } } } } //--------------------------------------------------------------------------- public Quantity(Double inValue, Unit inUnit) { nullCheckAmount(inValue); mDoubleValue = inValue; mUnit = inUnit; } //--------------------------------------------------------------------------- public Quantity(Float inValue, Unit inUnit) { nullCheckAmount(inValue); mDoubleValue = (inValue != null ? inValue.doubleValue() : null); mUnit = inUnit; } //--------------------------------------------------------------------------- public Quantity(Integer inValue, Unit inUnit) { nullCheckAmount(inValue); mIntValue = inValue; mUnit = inUnit; } //--------------------------------------------------------------------------- public Quantity(Long inValue, Unit inUnit) { nullCheckAmount(inValue); mLongValue = inValue; mUnit = inUnit; } //--------------------------------------------------------------------------- private void nullCheckAmount(Number inValue) { if (null == inValue) { throw new InvalidValueException("A non-null amount must be specified!"); } } //########################################################################### // PUBLIC METHODS //########################################################################### //--------------------------------------------------------------------------- @Override public String toString() { return toString(null); } //--------------------------------------------------------------------------- public String toString(String inFmtString) { StringBuilderPlus buffer = new StringBuilderPlus().setDelimiter(" "); Double doubleValue = doubleValue(); if (doubleValue != null) { if (doubleValue == Math.floor(doubleValue) && ! Double.isInfinite(doubleValue)) { buffer.append(doubleValue.intValue()); } else { buffer.append(StringUtil.isSet(inFmtString) ? String.format(inFmtString, doubleValue) : doubleValue + ""); } buffer.delimitedAppend(mUnit.computeUnitLabel(mScalingFactorMap)); } return buffer.toString(); } //--------------------------------------------------------------------------- @Override public boolean equals(Object inObj2) { return (inObj2 != null && inObj2 instanceof Quantity && (this == inObj2 || 0 == compareTo(inObj2))); } //--------------------------------------------------------------------------- @Override public int hashCode() { int hashCode = 0; Double doubleValue = doubleValue(); if (doubleValue != null) { hashCode += doubleValue.hashCode(); } if (getUnit() != null) { hashCode += 31 * getUnit().hashCode(); } if (CollectionUtil.hasValues(mScalingFactorMap)) { for (QuantityType quantityType : mScalingFactorMap.keySet()) { hashCode += 31 * quantityType.hashCode() + mScalingFactorMap.get(quantityType).hashCode(); } } return hashCode; } //--------------------------------------------------------------------------- public int compareTo(Object inObj2) { int result = -1; if (inObj2 != null) { if (inObj2 instanceof Quantity) { Quantity quantity2 = (Quantity) inObj2; result = CompareUtil.compare(getUnit(), quantity2.getUnit()); if (0 == result) { result = CompareUtil.compare(doubleValue(), quantity2.doubleValue()); } if (0 == result) { result = CompareUtil.compare(mScalingFactorMap, quantity2.mScalingFactorMap); } } } return result; } //--------------------------------------------------------------------------- public void scale(QuantityType inQuantityType, SI_ScalingFactor inScalingFactor) { if (null == mScalingFactorMap) { mScalingFactorMap = new HashMap<>(4); } mScalingFactorMap.put(inQuantityType, inScalingFactor); } //--------------------------------------------------------------------------- public Quantity convertTo(Unit inUnit) { Quantity convertedQuantity; if (getUnit().equals(inUnit)) { convertedQuantity = this; } else { Double doubleValue = doubleValue(); // Put the value in SI base units if (mUnit.getMeasurementSystem() != null && (mUnit.getMeasurementSystem().equals(MeasurementSystem.Metric) || mUnit.getMeasurementSystem().equals(MeasurementSystem.SI))) { Map scalingFactorMap = new HashMap<>(3); if (StringUtil.isSet(getUnit().getAbbrev()) || ! getUnit().hasSubUnits()) { scalingFactorMap.put(mUnit.getQuantityType(), SI_ScalingFactor.one); } else { for (SubUnit subUnit : mUnit.getSubUnits()) { QuantityType quantityType = subUnit.getUnit().getQuantityType(); scalingFactorMap.put(quantityType, SI_ScalingFactor.one); } } doubleValue = mUnit.computeScaledValue(doubleValue, scalingFactorMap); } else { doubleValue = mUnit.computeBaseSIValue(doubleValue); } convertedQuantity = new Quantity(inUnit.computeValueFromBaseSIValue(doubleValue), inUnit); } return convertedQuantity; } //--------------------------------------------------------------------------- public Quantity invert() { return new Quantity( - doubleValue(), getUnit()); } //--------------------------------------------------------------------------- public Quantity multiplyBy(double inValue) { return new Quantity(doubleValue() * inValue, getUnit()); } //--------------------------------------------------------------------------- public Quantity divideBy(double inValue) { return new Quantity(doubleValue() / inValue, getUnit()); } //--------------------------------------------------------------------------- public Quantity add(Quantity inValue) { testQuantityTypeEquivalence(inValue); Quantity result; if (inValue != null) { Quantity convertedValue = inValue.convertTo(getUnit()); result = new Quantity(doubleValue() + convertedValue.doubleValue(), getUnit()); } else { result = this; } return result; } //--------------------------------------------------------------------------- public Quantity subtract(Quantity inValue) { testQuantityTypeEquivalence(inValue); Quantity result; if (inValue != null) { Quantity convertedValue = inValue.convertTo(getUnit()); result = new Quantity(doubleValue() - convertedValue.doubleValue(), getUnit()); } else { result = this; } return result; } //--------------------------------------------------------------------------- public boolean lessThan(Quantity inValue) { testQuantityTypeEquivalence(inValue); boolean result = false; if (inValue != null) { Quantity convertedValue = inValue.convertTo(getUnit()); result = doubleValue() < convertedValue.doubleValue(); } return result; } //--------------------------------------------------------------------------- public boolean lessThanOrEqualTo(Quantity inValue) { testQuantityTypeEquivalence(inValue); boolean result = false; if (inValue != null) { Quantity convertedValue = inValue.convertTo(getUnit()); result = doubleValue() <= convertedValue.doubleValue(); } return result; } //--------------------------------------------------------------------------- public boolean greaterThan(Quantity inValue) { testQuantityTypeEquivalence(inValue); boolean result = false; if (inValue != null) { Quantity convertedValue = inValue.convertTo(getUnit()); result = doubleValue() > convertedValue.doubleValue(); } return result; } //--------------------------------------------------------------------------- public boolean greaterThanOrEqualTo(Quantity inValue) { testQuantityTypeEquivalence(inValue); boolean result = false; if (inValue != null) { Quantity convertedValue = inValue.convertTo(getUnit()); result = doubleValue() >= convertedValue.doubleValue(); } return result; } //--------------------------------------------------------------------------- public Unit getUnit() { return mUnit; } //--------------------------------------------------------------------------- public Double doubleValue() { Double value = mDoubleValue; if (null == value) { if (mIntValue != null) { value = mIntValue.doubleValue(); } else if (mLongValue != null) { value = mLongValue.doubleValue(); } } // Scale the value? if (value != null && CollectionUtil.hasValues(mScalingFactorMap)) { value = mUnit.computeScaledValue(value, mScalingFactorMap); } return value; } //--------------------------------------------------------------------------- public Float floatValue() { Double doubleValue = doubleValue(); return (doubleValue != null ? doubleValue.floatValue() : null); } //--------------------------------------------------------------------------- public Integer intValue() { Integer value = mIntValue; if (null == value) { if (mDoubleValue != null) { value = mDoubleValue.intValue(); } else if (mLongValue != null) { value = mLongValue.intValue(); } } // Scale the value? if (value != null && CollectionUtil.hasValues(mScalingFactorMap)) { value = (int) mUnit.computeScaledValue(value, mScalingFactorMap); } return value; } //--------------------------------------------------------------------------- public Long longValue() { Long value = mLongValue; if (null == value) { if (mDoubleValue != null) { value = mDoubleValue.longValue(); } else if (mIntValue != null) { value = mIntValue.longValue(); } } // Scale the value? if (value != null && CollectionUtil.hasValues(mScalingFactorMap)) { value = (long) mUnit.computeScaledValue(value, mScalingFactorMap); } return value; } //--------------------------------------------------------------------------- public Quantity autoScale() { return autoScale(SI_ScalingFactor.values()); } //--------------------------------------------------------------------------- public Quantity autoScale(SI_ScalingFactor[] inScalingValues) { Quantity scaledQuantity = null; double doubleValue = doubleValue(); if (doubleValue > 900 || doubleValue < 0.1) { for (int i = 1; i < inScalingValues.length; i++) { if (doubleValue < inScalingValues[i - 1].getScalingFactor() && doubleValue >= inScalingValues[i].getScalingFactor()) { if (StringUtil.isSet(getUnit().getAbbrev()) || ! getUnit().hasSubUnits()) { scaledQuantity = this.convertTo(new Unit(getUnit(), inScalingValues[i])); } else { List subUnits = new ArrayList<>(getUnit().getSubUnits()); subUnits.set(0, new SubUnit(subUnits.get(0).getUnit(), inScalingValues[i], subUnits.get(0).getPow())); scaledQuantity = this.convertTo(new Unit(subUnits)); } break; } } } return (scaledQuantity != null ? scaledQuantity : this); } //--------------------------------------------------------------------------- /** Performs a validation test to ensure that the unit is of the expected quantity type. @param inExpectedQuantityType the quantity type that is expected. @throws InvalidValueException if the unit isn't set or is not of the expected type. */ public void verifyQuantityType(QuantityType inExpectedQuantityType) { if (null == getUnit() || ! getUnit().getQuantityType().equals(inExpectedQuantityType)) { throw new InvalidValueException("The quantity " + StringUtil.singleQuote(toString()) + " isn't specified in " + inExpectedQuantityType + " units!"); } } //########################################################################### // PRIVATE METHODS //########################################################################### //--------------------------------------------------------------------------- private void testQuantityTypeEquivalence(Quantity inValue) { if (! getUnit().getQuantityType().equals(inValue.getUnit().getQuantityType())) { throw new UnitException("Quantity Type mismatch (" + getUnit().getQuantityType() + " vs. " + inValue.getUnit().getQuantityType() + ")!"); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy