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

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

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


import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.*;
import java.util.regex.Pattern;

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

// NOTE: Add new unit classes to the /META-DATA/services/com.hfg.units.Unit file
//       for them to be properly discoverable!!!
//------------------------------------------------------------------------------
/**
 Container for 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 Unit implements Comparable { private static MathContext sDefaultMathContext = new MathContext(16, RoundingMode.HALF_UP); private static Set sInstances = new OrderedSet<>(50); private static Map sLookupMap = new HashMap<>(); private static boolean sInitialized = false; private MeasurementSystem mMeasurementSystem; private String mName; private Set mAlternateNames; private String mPlural; private String mAbbrev; private String mPluralAbbrev; private QuantityType mQuantityType; private Integer mPow; private BaseSIUnitConverter mConversionToBaseSIUnit; private List mSubUnits; private MathContext mMathContext; //--------------------------------------------------------------------------- protected Unit(QuantityType inQuantityType, String inName, String inAbbrev, Integer inPow) { this(null, inQuantityType, inName, inAbbrev, inPow, null); } //--------------------------------------------------------------------------- protected Unit(MeasurementSystem inSystem, QuantityType inQuantityType, String inName, String inAbbrev, Integer inPow) { this(inSystem, inQuantityType, inName, inAbbrev, inPow, null); } //--------------------------------------------------------------------------- protected Unit(QuantityType inQuantityType, String inName, String inAbbrev, Integer inPow, BaseSIUnitConverter inConversionToBaseSIUnit) { this(null, inQuantityType, inName, inAbbrev, inPow, inConversionToBaseSIUnit); } //--------------------------------------------------------------------------- protected Unit(MeasurementSystem inSystem, QuantityType inQuantityType, String inName, String inAbbrev, Integer inPow, BaseSIUnitConverter inConversionToBaseSIUnit) { mMeasurementSystem = inSystem; mQuantityType = inQuantityType; mName = inName; mAbbrev = inAbbrev; mPow = inPow; mConversionToBaseSIUnit = inConversionToBaseSIUnit; addToMaps(this); } //--------------------------------------------------------------------------- protected Unit(MeasurementSystem inSystem, QuantityType inQuantityType, String inName, String inAbbrev, Integer inPow, BaseSIUnitConverter inConversionToBaseSIUnit, SubUnit[] inSubUnits) { mMeasurementSystem = inSystem; mQuantityType = inQuantityType; mName = inName; mAbbrev = inAbbrev; mPow = inPow; mConversionToBaseSIUnit = inConversionToBaseSIUnit; if (inSubUnits != null && inSubUnits.length > 0) { mSubUnits = new ArrayList<>(Arrays.asList(inSubUnits)); } addToMaps(this); } //--------------------------------------------------------------------------- protected Unit(MeasurementSystem inSystem, QuantityType inQuantityType, SubUnit inSubUnit) { mMeasurementSystem = inSystem; mQuantityType = inQuantityType; mSubUnits = new ArrayList<>(1); mSubUnits.add(inSubUnit); addToMaps(this); } //--------------------------------------------------------------------------- protected Unit(Unit inSubUnit, SI_ScalingFactor inScalingFactor) { this(new SubUnit(inSubUnit, inScalingFactor)); } //--------------------------------------------------------------------------- protected Unit(SubUnit... inSubUnits) { if (null == inSubUnits || 0 == inSubUnits.length) { throw new RuntimeException("No SubUnits specified!"); } initFromSubUnits(Arrays.asList(inSubUnits)); } //--------------------------------------------------------------------------- protected Unit(List inSubUnits) { initFromSubUnits(inSubUnits); } //--------------------------------------------------------------------------- protected Unit(MeasurementSystem inSystem, QuantityType inQuantityType, String inName, String inAbbrev, List inSubUnits) { mMeasurementSystem = inSystem; mQuantityType = inQuantityType; mName = inName; mAbbrev = inAbbrev; mSubUnits = inSubUnits; addToMaps(this); } //--------------------------------------------------------------------------- protected Unit(MeasurementSystem inSystem, QuantityType inQuantityType, String inName, String inAbbrev, SubUnit... inSubUnits) { mMeasurementSystem = inSystem; mQuantityType = inQuantityType; mName = inName; mAbbrev = inAbbrev; if (inSubUnits != null && inSubUnits.length > 0) { mSubUnits = new ArrayList<>(Arrays.asList(inSubUnits)); } addToMaps(this); } //--------------------------------------------------------------------------- private void initFromSubUnits(List inSubUnits) { mSubUnits = inSubUnits; mMeasurementSystem = mSubUnits.get(0).getUnit().getMeasurementSystem(); // Add to the lookup map but not to the list of instances since these are not "official" String name = name(); if (! sLookupMap.containsKey(name) // Don't override an existing mapping && (getSubUnits().size() > 1 || null == getSubUnits().get(0).getScalingFactor())) // Don't add scaled simple base units { sLookupMap.put(name, this); } } //--------------------------------------------------------------------------- private void addToMaps(Unit inValue) { sInstances.add(inValue); sLookupMap.put(inValue.name(), inValue); } //########################################################################### // PUBLIC METHODS //########################################################################### //--------------------------------------------------------------------------- /** Sets the default MathContext to specify precision and rounding during operations. * @param inValue the MathContext to use */ public static void setDefaultMathContext(MathContext inValue) { sDefaultMathContext = inValue; } //--------------------------------------------------------------------------- /** Sets the MathContext to specify precision and rounding during operations with this Unit. * @param inValue the MathContext to use */ public Unit setMathContext(MathContext inValue) { mMathContext = inValue; return this; } //--------------------------------------------------------------------------- public MathContext getMathContext() { return mMathContext != null ? mMathContext : sDefaultMathContext; } //--------------------------------------------------------------------------- public static Collection values() { ensureExtendingClassesAreInitialized(); return Collections.unmodifiableCollection(sInstances); } //--------------------------------------------------------------------------- public static Unit valueOf(SubUnit inSubUnit) { List subUnits = new ArrayList<>(1); subUnits.add(inSubUnit); return valueOf(subUnits); } //--------------------------------------------------------------------------- public static Unit valueOf(List inSubUnits) { Unit unit = null; if (CollectionUtil.hasValues(inSubUnits)) { for (Unit existingUnit : sInstances) { if (0 == existingUnit.compareTo(inSubUnits)) { unit = existingUnit; break; } } if (null == unit) { unit = new Unit(inSubUnits); } } return unit; } //--------------------------------------------------------------------------- public static Unit valueOf(String inString) { ensureExtendingClassesAreInitialized(); Unit unit = null; if (StringUtil.isSet(inString)) { String string = inString.trim(); List subUnits = new ArrayList<>(5); if (string.contains("/")) { String[] pieces = string.split("/"); if (pieces.length > 2) { throw new UnitParseException("Multiple '/' operators in a unit string is ambiguous!"); } // Special nasty case: '% v/v' if (string.equalsIgnoreCase(ConcentrationUnit.pct_by_volume.getAbbrev()) || ConcentrationUnit.pct_by_volume.getAlternateNames().contains(string)) { subUnits.addAll(ConcentrationUnit.pct_by_volume.getSubUnits()); } // Special nasty case: '% w/w' else if (string.equalsIgnoreCase(ConcentrationUnit.pct_by_weight.getAbbrev()) || ConcentrationUnit.pct_by_weight.getAlternateNames().contains(string)) { subUnits.addAll(ConcentrationUnit.pct_by_weight.getSubUnits()); } else { // Numerator subUnits.addAll(parseNumeratorBlockOfUnits(pieces[0].trim())); // Denominator subUnits.addAll(parseDenominatorBlockOfUnits(pieces[1].trim())); } } else { subUnits.addAll(parseNumeratorBlockOfUnits(string)); } // Does the configuration of subunits match a defined unit? for (Unit stdUnit : sInstances) { if (stdUnit.hasSubUnits() && 0 == CompareUtil.compare(stdUnit.getSubUnits(), subUnits)) { unit = stdUnit; break; } } if (null == unit && 1 == subUnits.size()) { SubUnit subUnit = subUnits.get(0); for (Unit stdUnit : sInstances) { if (stdUnit.equals(subUnit.getUnit()) && null == subUnit.getPow() && null == subUnit.getScalingFactor()) { unit = stdUnit; break; } } } if (null == unit) { // If all the subunits are of the same type, use that type as the constructor. Set quantityTypes = new HashSet<>(2); for (SubUnit subUnit : subUnits) { quantityTypes.add(subUnit.getUnit().getQuantityType()); } if (1 == quantityTypes.size()) { QuantityType quantityType = quantityTypes.iterator().next(); if (quantityType.equals(QuantityType.TIME)) { unit = new TimeUnit(subUnits); } else if (quantityType.equals(QuantityType.LENGTH)) { unit = new LengthUnit(subUnits); } else if (quantityType.equals(QuantityType.MASS)) { unit = new MassUnit(subUnits); } else if (quantityType.equals(QuantityType.VOLUME)) { unit = new VolumeUnit(subUnits); } else if (quantityType.equals(QuantityType.CONCENTRATION)) { unit = new ConcentrationUnit(subUnits); } else if (quantityType.equals(QuantityType.AREA)) { unit = new AreaUnit(subUnits); } else if (quantityType.equals(QuantityType.BIOLOGICAL_POTENCY)) { unit = new BiologicalPotencyUnit(subUnits); } else if (quantityType.equals(QuantityType.CATALYTIC_ACTIVITY)) { unit = new CatalyticActivityUnit(subUnits); } else if (quantityType.equals(QuantityType.ELECTRIC_CURRENT)) { unit = new ElectricCurrentUnit(subUnits); } } // As a last resort just create a Unit from the SubUnits. if (null == unit) { unit = new Unit(subUnits); } } } return unit; } //--------------------------------------------------------------------------- @Override public String toString() { StringBuilderPlus buffer = new StringBuilderPlus().setDelimiter(" "); if (StringUtil.isSet(getAbbrev())) { buffer.append(getAbbrev()); if (getPow() != null) { buffer.append(StringUtil.toSuperscript(getPow())); } } else if (hasSubUnits()) { for (SubUnit subUnit : getSubUnits()) { if (subUnit.getPow() != null && subUnit.getPow().equals(-1) && 2 == getSubUnits().size() && 1 == getSubUnits().indexOf(subUnit) && (null == getSubUnits().get(0).getPow() || getSubUnits().get(0).getPow() > 0)) { buffer.append("/"); if (subUnit.getScalingFactor() != null) { buffer.append(subUnit.getScalingFactor().getSymbol()); } buffer.append(subUnit.toStringMinusPow()); } else { buffer.delimitedAppend(subUnit); } } } else { throw new ProgrammingException("Unit with no abbreviation or SubUnits!"); } return buffer.toString(); } //--------------------------------------------------------------------------- @Override public boolean equals(Object inObj2) { return (inObj2 != null && inObj2 instanceof Unit && (this == inObj2 || 0 == compareTo(inObj2))); } //--------------------------------------------------------------------------- @Override public int hashCode() { int hashCode = 0; if (getMeasurementSystem() != null) { hashCode += 31 * getMeasurementSystem().hashCode(); } if (getQuantityType() != null) { hashCode += 31 * getQuantityType().hashCode(); } if (getPow() != null) { hashCode += 31 * getPow().hashCode(); } if (hasSubUnits()) { for (SubUnit subUnit : getSubUnits()) { hashCode += 31 * subUnit.hashCode(); } } else { if (name() != null) { hashCode += 31 * name().hashCode(); } } return hashCode; } //--------------------------------------------------------------------------- public int compareTo(Object inObj2) { int result = -1; if (inObj2 != null) { if (inObj2 instanceof Unit) { Unit unit2 = (Unit) inObj2; result = CompareUtil.compare(getMeasurementSystem(), unit2.getMeasurementSystem()); /* if (0 == result) { result = CompareUtil.compare(name(), unit2.name()); } */ if (0 == result) { result = CompareUtil.compare(getQuantityType(), unit2.getQuantityType()); } if (0 == result) { result = CompareUtil.compare(getPow(), unit2.getPow()); } if (0 == result) { if (hasSubUnits()) { result = CompareUtil.compare(getSubUnits(), unit2.getSubUnits()); } else { result = CompareUtil.compare(name(), unit2.name()); } } } } return result; } //--------------------------------------------------------------------------- public int compareTo(List inSubUnits) { int result = -1; if (inSubUnits != null) { if (1 == inSubUnits.size()) { // Does this unit equal the specified subunit? SubUnit subUnit = inSubUnits.get(0); result = CompareUtil.compare(this, subUnit.getUnit()); if (0 == result) { result = CompareUtil.compare(getPow(), subUnit.getPow()); } if (0 == result && subUnit.getScalingFactor() != null) { result = -1; } } if (result != 0 && hasSubUnits()) { result = CompareUtil.compare(getSubUnits(), inSubUnits); if (0 == result) { if (getPow() != null) { result = 1; } } } } return result; } //--------------------------------------------------------------------------- public String name() { if (null == mName && CollectionUtil.hasValues(mSubUnits)) { StringBuilderPlus name = new StringBuilderPlus().setDelimiter(" "); for (SubUnit subUnit : mSubUnits) { String subUnitName = subUnit.getUnit().name(); if (null == subUnitName) { name = null; break; } if (subUnit.getScalingFactor() != null) { subUnitName = subUnit.getScalingFactor().getPrefix() + subUnitName; } if (subUnit.getPow() != null) { subUnitName += StringUtil.toSuperscript(subUnit.getPow()); } name.delimitedAppend(subUnitName); } if (StringUtil.isSet(name)) { mName = name.toString(); } } return mName; } //--------------------------------------------------------------------------- public String getAbbrev() { if (null == mAbbrev && CollectionUtil.hasValues(mSubUnits)) { StringBuilderPlus abbrev = new StringBuilderPlus().setDelimiter(" "); for (int i = 0; i < mSubUnits.size(); i++) { SubUnit subUnit = mSubUnits.get(i); String subUnitAbbrev = subUnit.getUnit().getAbbrev(); if (null == subUnitAbbrev) { abbrev = null; break; } String powString = ""; if (subUnit.getPow() != null) { if (1 == i && -1 == subUnit.getPow() && (null == mSubUnits.get(0).getPow() || mSubUnits.get(0).getPow() > 1)) { abbrev.append("/"); } else { if (i > 0) { abbrev.append(" "); } powString = StringUtil.toSuperscript(subUnit.getPow()); } } if (subUnit.getScalingFactor() != null) { subUnitAbbrev = subUnit.getScalingFactor().getSymbol() + subUnitAbbrev; } subUnitAbbrev += powString; abbrev.append(subUnitAbbrev); } if (StringUtil.isSet(abbrev)) { mAbbrev = abbrev.toString(); } } return mAbbrev; } //--------------------------------------------------------------------------- public Unit addAlternateName(String inValue) { if (null == mAlternateNames) { mAlternateNames = new HashSet<>(4); } sLookupMap.put(inValue, this); return this; } //--------------------------------------------------------------------------- public Set getAlternateNames() { return mAlternateNames; } //--------------------------------------------------------------------------- public Unit setPlural(String inValue) { mPlural = inValue; sLookupMap.put(mPlural, this); return this; } //--------------------------------------------------------------------------- public String getPlural() { return mPlural; } //--------------------------------------------------------------------------- public Unit setPluralAbbrev(String inValue) { mPluralAbbrev = inValue; sLookupMap.put(mPluralAbbrev, this); return this; } //--------------------------------------------------------------------------- public String getPluralAbbrev() { return mPluralAbbrev; } //--------------------------------------------------------------------------- public Unit setMeasurementSystem(MeasurementSystem inValue) { mMeasurementSystem = inValue; return this; } //--------------------------------------------------------------------------- public MeasurementSystem getMeasurementSystem() { return mMeasurementSystem; } //--------------------------------------------------------------------------- public QuantityType getQuantityType() { if (null == mQuantityType && CollectionUtil.hasValues(getSubUnits()) && 1 == getSubUnits().size()) { mQuantityType = getSubUnits().get(0).getUnit().getQuantityType(); } return mQuantityType; } //--------------------------------------------------------------------------- public Integer getPow() { if (null == mPow && CollectionUtil.hasValues(getSubUnits()) && 1 == getSubUnits().size()) { mPow = getSubUnits().get(0).getUnit().getPow(); } return mPow; } //--------------------------------------------------------------------------- public boolean hasSubUnits() { return CollectionUtil.hasValues(mSubUnits); } //--------------------------------------------------------------------------- public List getSubUnits() { return mSubUnits; } //--------------------------------------------------------------------------- public double computeBaseSIValue(double inValue) { double value = inValue; if (mConversionToBaseSIUnit != null) { value = mConversionToBaseSIUnit.apply(value); } else if (hasSubUnits() && 1 == getSubUnits().size() && getSubUnits().get(0).getUnit().mConversionToBaseSIUnit != null) { // The unit has a converter but was wrapped by another unit value = getSubUnits().get(0).getUnit().mConversionToBaseSIUnit.apply(value); } else if (getMeasurementSystem() != null && (getMeasurementSystem().equals(MeasurementSystem.Metric) || getMeasurementSystem().equals(MeasurementSystem.SI))) { // Put the value in SI base units by scaling Map scalingFactorMap = new HashMap<>(3); if (getQuantityType() != null || ! hasSubUnits()) { scalingFactorMap.put(getQuantityType(), SI_ScalingFactor.one); } else { for (SubUnit subUnit : getSubUnits()) { QuantityType quantityType = subUnit.getUnit().getQuantityType(); scalingFactorMap.put(quantityType, SI_ScalingFactor.one); } } value = computeScaledValue(value, scalingFactorMap); } else { throw new UnitException("Couldn't convert " + name() + " to base SI units! No converter specified."); } return value; } //--------------------------------------------------------------------------- public double computeValueFromBaseSIValue(double inValue) { double value = inValue; if (CollectionUtil.hasValues(getSubUnits())) { for (SubUnit subUnit : getSubUnits()) { if (subUnit.getScalingFactor() != null) { if (subUnit.getPow() != null && subUnit.getPow() < 0) { value *= subUnit.getScalingFactor().getScalingFactor(); } else { value = value / subUnit.getScalingFactor().getScalingFactor(); } } } } if (mConversionToBaseSIUnit != null) { value = mConversionToBaseSIUnit.reverse(value); } return value; } //--------------------------------------------------------------------------- public double computeScaledValue(double inValue, Map inScalingFactorMap) { BigDecimal value = allocateBigDecimal(inValue); if (CollectionUtil.hasValues(getSubUnits())) { for (SubUnit subUnit : getSubUnits()) { SI_ScalingFactor scalingFactor = inScalingFactorMap.get(subUnit.getUnit().getQuantityType()); if (scalingFactor != null) { BigDecimal adjustedScalingFactor = allocateBigDecimal(scalingFactor.getScalingFactor()) .multiply(allocateBigDecimal(subUnit.getScalingFactor() != null ? subUnit.getScalingFactor().getScalingFactor() : 1d)); if (subUnit.getPow() != null && subUnit.getPow() < 0) { value = value.divide(adjustedScalingFactor, getMathContext()); } else { value = value.multiply(adjustedScalingFactor); } } } } else { SI_ScalingFactor scalingFactor = inScalingFactorMap.get(getQuantityType()); if (scalingFactor != null) { if (getPow() != null && getPow() < 0) { value = value.multiply(allocateBigDecimal(scalingFactor.getScalingFactor())); } else { value = value.divide(allocateBigDecimal(scalingFactor.getScalingFactor()), getMathContext()); } } } return value.doubleValue(); } //--------------------------------------------------------------------------- public String computeUnitLabel(Map inScalingFactorMap, boolean isPlural) { StringBuilderPlus buffer = new StringBuilderPlus().setDelimiter(" "); if (! CollectionUtil.hasValues(inScalingFactorMap) && StringUtil.isSet(getAbbrev())) { buffer.delimitedAppend(isPlural && StringUtil.isSet(getPluralAbbrev()) ? getPluralAbbrev() : getAbbrev()); } else // if (StringUtil.isSet(getAbbrev()) // || ! hasSubUnits()) if (! hasSubUnits() // || (1 == getSubUnits().size())) || getQuantityType() != null) { String prefix = ""; if (CollectionUtil.hasValues(inScalingFactorMap)) { SI_ScalingFactor scalingFactor = inScalingFactorMap.get(getQuantityType()); if (scalingFactor != null) { prefix = scalingFactor.getSymbol(); } } buffer.delimitedAppend(prefix + toString()); if (getPow() != null) { buffer.append(StringUtil.toSuperscript(getPow())); } } else { for (SubUnit subUnit : getSubUnits()) { String prefix = ""; boolean prefixApplied = false; if (inScalingFactorMap != null) { SI_ScalingFactor scalingFactor = inScalingFactorMap.get(subUnit.getUnit().getQuantityType()); if (scalingFactor != null) { prefix = scalingFactor.getSymbol(); prefixApplied = true; } } if (subUnit.getPow() != null && subUnit.getPow().equals(-1) && 2 == getSubUnits().size() && 1 == getSubUnits().indexOf(subUnit) && (null == getSubUnits().get(0).getPow() || getSubUnits().get(0).getPow() > 0)) { buffer.append("/" + prefix); if (! prefixApplied && subUnit.getScalingFactor() != null) { buffer.append(subUnit.getScalingFactor().getSymbol()); } buffer.append(subUnit.getUnit().getAbbrev()); } else { buffer.delimitedAppend(prefix); if (! prefixApplied && subUnit.getScalingFactor() != null) { buffer.append(subUnit.getScalingFactor().getSymbol()); } buffer.append(subUnit.getUnit().getAbbrev()); if (subUnit.getPow() != null) { buffer.append(StringUtil.toSuperscript(subUnit.getPow())); } } } } return buffer.toString(); } //--------------------------------------------------------------------------- public Unit getNumerator() { Unit numerator = null; if (CollectionUtil.hasValues(getSubUnits())) { List numeratorSubUnits = new ArrayList<>(4); for (SubUnit subUnit : getSubUnits()) { if (null == subUnit.getPow() || subUnit.getPow() > 0) { if (subUnit.getUnit().hasSubUnits() && ! subUnit.getUnit().getQuantityType().equals(QuantityType.VOLUME)) // Don't unroll L to dm3 { for (SubUnit subSubUnit : subUnit.getUnit().getSubUnits()) { if (null == subSubUnit.getPow() || subSubUnit.getPow() > 0) { numeratorSubUnits.add(new SubUnit(subSubUnit.getUnit(), subUnit.getScalingFactor(), subUnit.getPow())); } } } else { numeratorSubUnits.add(subUnit); } } } if (CollectionUtil.hasValues(numeratorSubUnits)) { numerator = Unit.valueOf(numeratorSubUnits); } } else if (null == getPow() || getPow() > 0) { numerator = this; } return numerator; } //--------------------------------------------------------------------------- public Unit getDenominator() { Unit denominator = null; if (CollectionUtil.hasValues(getSubUnits())) { List denominatorSubUnits = new ArrayList<>(4); for (SubUnit subUnit : getSubUnits()) { if (subUnit.getUnit().hasSubUnits() && subUnit.getUnit().getSubUnits().size() > 1) // or L will unroll into dm3 { for (SubUnit subSubUnit : subUnit.getUnit().getSubUnits()) { if (subSubUnit.getPow() != null && subSubUnit.getPow() < 0) { // Don't use the scaling factor of the parent subunit denominatorSubUnits.add(new SubUnit(subSubUnit.getUnit(), subSubUnit.getScalingFactor(), subSubUnit.getPow().equals(-1) ? null : -subSubUnit.getPow())); } } } else if (subUnit.getPow() != null && subUnit.getPow() < 0) { denominatorSubUnits.add(new SubUnit(subUnit.getUnit(), subUnit.getScalingFactor(), subUnit.getPow().equals(-1) ? null : -subUnit.getPow())); } } if (CollectionUtil.hasValues(denominatorSubUnits)) { denominator = Unit.valueOf(denominatorSubUnits); } } else if (getPow() != null && getPow() < 0) { if (getPow().equals(-1)) { denominator = Unit.valueOf(getAbbrev()); } else { denominator = Unit.valueOf(new SubUnit(this, -getPow())); } } return denominator; } //########################################################################### // PRIVATE METHODS //########################################################################### //--------------------------------------------------------------------------- private BigDecimal allocateBigDecimal(double inValue) { return new BigDecimal(Double.toString(inValue), getMathContext()); } //--------------------------------------------------------------------------- // This is done to ensure that calls to values() and valueOf() have registered values to work with. // The file /META-DATA/services/com.hfg.units.Unit holds the names of the classes to be initialized. // (Had to create a custom ServiceLoader that doesn't try to instantiate the properties but just // calls Class.forName() to initialize the static definitions.) private static void ensureExtendingClassesAreInitialized() { if (! sInitialized) { try { new UnitServiceLoader().load(); } catch (Exception e) { // Ignore } sInitialized = true; } } //--------------------------------------------------------------------------- private static List parseNumeratorBlockOfUnits(String inString) { List subUnits = new ArrayList<>(4); String[] pieces = inString.split("\\s+"); String unparsableBit = null; for (String piece : pieces) { if (StringUtil.isSet(unparsableBit)) { piece = unparsableBit + " " + piece; } SubUnit subUnit = parseSubUnit(piece); if (null == subUnit) { // Not recognized? Perhaps it's part of a multi-word unit? unparsableBit = piece; } else { unparsableBit = null; // Special nasty case: 'fl oz' can get misinterpreted as femto liters and ounce. if (subUnit.getUnit().equals(MassUnit.ounce) && subUnits.size() > 0 && subUnits.get(subUnits.size() - 1).getUnit().equals(VolumeUnit.liter) && subUnits.get(subUnits.size() - 1).getScalingFactor().equals(SI_ScalingFactor.femto)) { subUnits.remove(subUnits.size() - 1); subUnit = new SubUnit(VolumeUnit.fluid_ounce, subUnit.getPow()); } subUnits.add(subUnit); } } if (StringUtil.isSet(unparsableBit)) { // Special nasty case: "mole equivalent" if (unparsableBit.equalsIgnoreCase("equivalent") && 1 == subUnits.size() && subUnits.get(0).equals(AmountOfSubstanceUnit.mole)) { subUnits.set(0, new SubUnit(AmountOfSubstanceUnit.mole_equivalent)); } // Special nasty case: "fluid dram". 'fl' can get misinterpreted as femto liters. else if (unparsableBit.equalsIgnoreCase("dr") && 1 == subUnits.size() && subUnits.get(0).getUnit().equals(VolumeUnit.liter) && subUnits.get(0).getScalingFactor().equals(SI_ScalingFactor.femto)) { subUnits.set(0, new SubUnit(VolumeUnit.fluid_dram)); } else { throw new UnitParseException("Couldn't match " + StringUtil.singleQuote(unparsableBit) + " to a unit!"); } } return subUnits; } //--------------------------------------------------------------------------- private static List parseDenominatorBlockOfUnits(String inString) { List subUnits = new ArrayList<>(4); String[] pieces = inString.split("\\s+"); String unparsableBit = null; for (String piece : pieces) { if (StringUtil.isSet(unparsableBit)) { piece = unparsableBit + " " + piece; } SubUnit subUnit = parseSubUnit(piece); if (null == subUnit) { // Not recognized? Perhaps it's part of a multi-word unit? unparsableBit = piece; } else { unparsableBit = null; // Special nasty case: 'fl oz' can get misinterpreted as femto liters and ounce. if (subUnit.getUnit().equals(MassUnit.ounce) && subUnits.size() > 0 && subUnits.get(subUnits.size() - 1).getUnit().equals(VolumeUnit.liter) && subUnits.get(subUnits.size() - 1).getScalingFactor().equals(SI_ScalingFactor.femto)) { subUnits.remove(subUnits.size() - 1); subUnit = new SubUnit(VolumeUnit.fluid_ounce, subUnit.getPow()); } Integer pow = subUnit.getPow(); if (pow != null) { pow = - pow; } else { pow = -1; } subUnits.add(new SubUnit(subUnit.getUnit(), subUnit.getScalingFactor(), pow)); } } if (StringUtil.isSet(unparsableBit)) { throw new UnitParseException("Couldn't match " + StringUtil.singleQuote(unparsableBit) + " to a unit!"); } return subUnits; } //--------------------------------------------------------------------------- private static SubUnit parseSubUnit(String inString) { SubUnit subUnit = null; String string = inString; Integer pow = extractPow(string); Unit unit = null; SI_ScalingFactor scalingFactor = null; if (pow != null) { // Remove the pow from the end of the string string = string.substring(0, string.length() - pow.toString().length()); } // Attempt to match using unit abbreviations for (Unit unitValue : sInstances) { if (StringUtil.isSet(unitValue.getAbbrev()) && string.endsWith(unitValue.getAbbrev())) { // Is there a prefix? int endIndex = string.length() - unitValue.getAbbrev().length(); if (endIndex > 0) { if (unitValue.getMeasurementSystem().equals(MeasurementSystem.SI) || unitValue.getMeasurementSystem().equals(MeasurementSystem.Metric)) { String prefix = string.substring(0, endIndex); SI_ScalingFactor matchingScalingFactor = SI_ScalingFactor.valueOf(prefix); if (matchingScalingFactor != null) { // We found a match unit = unitValue; scalingFactor = matchingScalingFactor; break; } } } else { // We found a match that has no scaling factor unit = unitValue; break; } } } if (null == unit) { // Attempt to match using unit names for (String key : sLookupMap.keySet()) { if (StringUtil.isSet(key)) { // Does the string end with the unit name? if (Pattern.compile(key + "$", Pattern.CASE_INSENSITIVE).matcher(string).find()) { Unit unitValue = sLookupMap.get(key); // Is there a prefix? int endIndex = string.length() - key.length(); if (endIndex > 0) { String prefix = string.substring(0, endIndex); SI_ScalingFactor matchingScalingFactor = SI_ScalingFactor.valueOf(prefix); if (matchingScalingFactor != null) { // We found a match unit = unitValue; scalingFactor = matchingScalingFactor; break; } } else { // We found a match that has no scaling factor unit = unitValue; break; } } } } } if (null == unit && inString.endsWith("s")) { // The units may have been pluralized subUnit = parseSubUnit(inString.substring(0, inString.length() - 1)); } if (null == unit && inString.endsWith(".")) { // The units may have had a period at the end of its abbreviation subUnit = parseSubUnit(inString.substring(0, inString.length() - 1)); } return (subUnit != null ? subUnit : unit != null ? new SubUnit(unit, scalingFactor, pow) : null); } //--------------------------------------------------------------------------- private static Integer extractPow(String inString) { StringBuilder buffer = new StringBuilder(); for (int i = inString.length() - 1; i > 0; i--) { Character superscriptChar = null; switch (inString.charAt(i)) { case '\u207B': superscriptChar = '-'; break; case '\u2070': superscriptChar = '0'; break; case 0xB9: superscriptChar = '1'; break; case 0xB2: superscriptChar = '2'; break; case 0xB3: superscriptChar = '3'; break; case '\u2074': superscriptChar = '4'; break; case '\u2075': superscriptChar = '5'; break; case '\u2076': superscriptChar = '6'; break; case '\u2077': superscriptChar = '7'; break; case '\u2078': superscriptChar = '8'; break; case '\u2079': superscriptChar = '9'; break; } if (superscriptChar != null) { buffer.insert(0, superscriptChar); } else { break; } } return (buffer.length() > 0 ? Integer.parseInt(buffer.toString()) : null); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy