/* * Copyright 1998-2014 University Corporation for Atmospheric Research/Unidata * * Portions of this software were developed by the Unidata Program at the * University Corporation for Atmospheric Research. * * Access and use of this software shall impose the following obligations * and understandings on the user. The user is granted the right, without * any fee or cost, to use, copy, modify, alter, enhance and distribute * this software, and any derivative works thereof, and its supporting * documentation for any purpose whatsoever, provided that this entire * notice appears in all copies of the software, derivative works and * supporting documentation. Further, UCAR requests that the user credit * UCAR/Unidata in any publications that result from the use of this * software or in any product that includes this software. The names UCAR * and/or Unidata, however, may not be used in any advertising or publicity * to endorse or promote any products or commercial entity unless specific * written permission is obtained from UCAR/Unidata. The user also * understands that UCAR/Unidata is not obligated to provide the user with * any support, consulting, training or assistance of any kind with regard * to the use, operation and performance of this software nor to provide * the user with any updates, revisions, new versions or "bug fixes." * * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. */ package ucar.units; import net.jcip.annotations.Immutable; /** * Provides support for a unit that is a mutiplicative factor of a reference * unit. * * Instances of this class are immutable. * * @author Steven R. Emmerson */ @Immutable public final class ScaledUnit extends UnitImpl implements DerivableUnit { private static final long serialVersionUID = 1L; /** * The multiplicative factor. * * @serial */ private final double _scale; /** * The reference unit. * * @serial */ private final Unit _unit; /** * Constructs from a multiplicative factor. Returns a dimensionless unit * whose value is the multiplicative factor rather than unity. * * @param scale * The multiplicative factor. */ public ScaledUnit(final double scale) { this(scale, DerivedUnitImpl.DIMENSIONLESS); } /** * Constructs from a multiplicative factor and a reference unit. * * @param scale * The multiplicative factor. * @param unit * The reference unit. */ public ScaledUnit(final double scale, final Unit unit) { this(scale, unit, null); } /** * Constructs from a multiplicative factor, a reference unit, and an * identifier. * * @param scale * The multiplicative factor. * @param unit * The reference unit. * @param id * The identifier for the unit. */ public ScaledUnit(final double scale, final Unit unit, final UnitName id) { super(id); if (!(unit instanceof ScaledUnit)) { _unit = unit; _scale = scale; } else { _unit = ((ScaledUnit) unit)._unit; _scale = ((ScaledUnit) unit)._scale * scale; } } static Unit getInstance(final double scale, final Unit unit) throws MultiplyException { if (scale == 0) { throw new MultiplyException(scale, unit); } return scale == 1 ? unit : new ScaledUnit(scale, unit); } /** * Returns the multiplicative factor. * * @return The multiplicative factor. */ public double getScale() { return _scale; } /** * Returns the reference unit. * * @return The reference unit. */ public Unit getUnit() { return _unit; } /* * From UnitImpl: */ /** * Clones this unit, changing the identifier. * * @param id * The new identifier. * @return A ScaledUnit with the new identifier. */ public Unit clone(final UnitName id) { return new ScaledUnit(_scale, getUnit(), id); } @Override public Unit multiplyBy(final double scale) throws MultiplyException { return getInstance(scale * _scale, _unit); } /** * Multiplies this unit by another unit. * * @param that * The other unit. * @return The product of this unit and the other unit. * @throws MultiplyException * Can't multiply these units together. */ @Override protected Unit myMultiplyBy(final Unit that) throws MultiplyException { return that instanceof ScaledUnit ? new ScaledUnit(getScale() * ((ScaledUnit) that).getScale(), getUnit().multiplyBy(((ScaledUnit) that).getUnit())) : new ScaledUnit(getScale(), getUnit().multiplyBy(that)); } /** * Divides this unit by another unit. * * @param that * The other unit. * @return The quotient of this unit divided by the other unit. * @throws OperationException * Can't divide these units. */ @Override protected Unit myDivideBy(final Unit that) throws OperationException { return that instanceof ScaledUnit ? new ScaledUnit(getScale() / ((ScaledUnit) that).getScale(), getUnit().divideBy(((ScaledUnit) that).getUnit())) : new ScaledUnit(getScale(), getUnit().divideBy(that)); } /** * Divides this unit into another unit. * * @param that * The other unit. * @return The quotient of this unit divided into the other unit. * @throws OperationException * Can't divide these units. */ @Override protected Unit myDivideInto(final Unit that) throws OperationException { return that instanceof ScaledUnit ? new ScaledUnit(((ScaledUnit) that).getScale() / getScale(), getUnit().divideInto(((ScaledUnit) that).getUnit())) : new ScaledUnit(1 / getScale(), getUnit().divideInto(that)); } /** * Raises this unit to a power. * * @param power * The power. * @return The result of raising this unit to the power. * @throws RaiseException * Can't raise this unit to a power. */ @Override protected Unit myRaiseTo(final int power) throws RaiseException { return new ScaledUnit(Math.pow(getScale(), power), getUnit().raiseTo( power)); } /** * Gets the derived unit underlying this unit. * * @return The derived unit which underlies this unit. */ public DerivedUnit getDerivedUnit() { return getUnit().getDerivedUnit(); } /** * Converts a numeric value from this unit to the underlying derived unit. * * @param amount * The numeric value in this unit. * @return The equivalent value in the underlying derived unit. * @throws ConversionException * Can't convert value to the underlying derived unit. */ public float toDerivedUnit(final float amount) throws ConversionException { return (float) toDerivedUnit((double) amount); } /** * Converts a numeric value from this unit to the underlying derived unit. * * @param amount * The numeric value in this unit. * @return The equivalent value in the underlying derived unit. * @throws ConversionException * Can't convert value to the underlying derived unit. */ public double toDerivedUnit(final double amount) throws ConversionException { if (!(_unit instanceof DerivableUnit)) { throw new ConversionException(this, getDerivedUnit()); } return ((DerivableUnit) _unit).toDerivedUnit(amount * getScale()); } /** * Converts numeric values from this unit to the underlying derived unit. * * @param input * The numeric values in this unit. * @param output * The equivalent values in the underlying derived unit. * @return <code>output</code>. * @throws ConversionException * Can't convert values to the underlying derived unit. */ public float[] toDerivedUnit(final float[] input, final float[] output) throws ConversionException { final float scale = (float) getScale(); for (int i = input.length; --i >= 0;) { output[i] = input[i] * scale; } if (!(_unit instanceof DerivableUnit)) { throw new ConversionException(this, getDerivedUnit()); } return ((DerivableUnit) getUnit()).toDerivedUnit(output, output); } /** * Converts numeric values from this unit to the underlying derived unit. * * @param input * The numeric values in this unit. * @param output * The equivalent values in the underlying derived unit. * @return <code>output</code>. * @throws ConversionException * Can't convert values to the underlying derived unit. */ public double[] toDerivedUnit(final double[] input, final double[] output) throws ConversionException { final double scale = getScale(); for (int i = input.length; --i >= 0;) { output[i] = input[i] * scale; } if (!(_unit instanceof DerivableUnit)) { throw new ConversionException(this, getDerivedUnit()); } return ((DerivableUnit) getUnit()).toDerivedUnit(output, output); } /** * Converts a numeric value from the underlying derived unit to this unit. * * @param amount * The numeric value in the underlying derived unit. * @return The equivalent value in this unit. * @throws ConversionException * Can't convert value. */ public float fromDerivedUnit(final float amount) throws ConversionException { return (float) fromDerivedUnit((double) amount); } /** * Converts a numeric value from the underlying derived unit to this unit. * * @param amount * The numeric value in the underlying derived unit. * @return The equivalent value in this unit. * @throws ConversionException * Can't convert value. */ public double fromDerivedUnit(final double amount) throws ConversionException { if (!(_unit instanceof DerivableUnit)) { throw new ConversionException(getDerivedUnit(), this); } return ((DerivableUnit) getUnit()).fromDerivedUnit(amount) / getScale(); } /** * Converts numeric values from the underlying derived unit to this unit. * * @param input * The numeric values in the underlying derived unit. * @param output * The equivalent values in this unit. * @return <code>output</code>. * @throws ConversionException * Can't convert values. */ public float[] fromDerivedUnit(final float[] input, final float[] output) throws ConversionException { if (!(_unit instanceof DerivableUnit)) { throw new ConversionException(getDerivedUnit(), this); } ((DerivableUnit) getUnit()).fromDerivedUnit(input, output); final float scale = (float) getScale(); for (int i = input.length; --i >= 0;) { output[i] /= scale; } return output; } /** * Converts numeric values from the underlying derived unit to this unit. * * @param input * The numeric values in the underlying derived unit. * @param output * The equivalent values in this unit. * @return <code>output</code>. * @throws ConversionException * Can't convert values. */ public double[] fromDerivedUnit(final double[] input, final double[] output) throws ConversionException { if (!(_unit instanceof DerivableUnit)) { throw new ConversionException(getDerivedUnit(), this); } ((DerivableUnit) getUnit()).fromDerivedUnit(input, output); final double scale = getScale(); for (int i = input.length; --i >= 0;) { output[i] /= scale; } return output; } /** * Indicates if this unit is semantically identical to an object. * * @param object * The object. * @return <code>true</code> if an only if this unit is semantically * identical to <code>object * </code>. */ public boolean equals(final Object object) { if (this == object) { return true; } if (_scale == 1) { return object.equals(_unit); } if (!(object instanceof ScaledUnit)) { return false; } ScaledUnit that = (ScaledUnit) object; if (Double.compare(that._scale, _scale) != 0) return false; if (!_unit.equals(that._unit)) return false; return true; } /** * Returns the hash code of this instance. * * @return The hash code of this instance. */ @Override public int hashCode() { return (getScale() == 1 ? 0 : Double.valueOf(getScale()).hashCode()) ^ getUnit().hashCode(); } /** * Indicates if this unit is dimensionless. A ScaledUnit is dimensionless if * and only if the reference unit is dimensionless. * * @return <code>true</code> if and only if this unit is dimensionless. */ public boolean isDimensionless() { return getUnit().isDimensionless(); } /** * Returns the string representation of this unit. * * @return The string representation of this unit. */ @Override public String toString() { final String string = super.toString(); // get symbol or name return string != null ? string : getCanonicalString(); } /** * Returns the canonical string representation of the unit. * * @return The canonical string representation. */ public String getCanonicalString() { return DerivedUnitImpl.DIMENSIONLESS.equals(_unit) ? Double.toString(getScale()) : Double.toString(getScale()) + " " + _unit.toString(); } /** * Tests this class. */ public static void main(final String[] args) throws Exception { final BaseUnit meter = BaseUnit.getOrCreate(UnitName.newUnitName( "meter", null, "m"), BaseQuantity.LENGTH); final ScaledUnit nauticalMile = new ScaledUnit(1852f, meter); System.out.println("nauticalMile.getUnit().equals(meter)=" + nauticalMile.getUnit().equals(meter)); final ScaledUnit nauticalMileMeter = (ScaledUnit) nauticalMile .multiplyBy(meter); System.out.println("nauticalMileMeter.divideBy(nauticalMile)=" + nauticalMileMeter.divideBy(nauticalMile)); System.out.println("meter.divideBy(nauticalMile)=" + meter.divideBy(nauticalMile)); System.out .println("nauticalMile.raiseTo(2)=" + nauticalMile.raiseTo(2)); System.out.println("nauticalMile.toDerivedUnit(1.)=" + nauticalMile.toDerivedUnit(1.)); System.out .println("nauticalMile.toDerivedUnit(new float[]{1,2,3}, new float[3])[1]=" + nauticalMile.toDerivedUnit(new float[] { 1, 2, 3 }, new float[3])[1]); System.out.println("nauticalMile.fromDerivedUnit(1852.)=" + nauticalMile.fromDerivedUnit(1852.)); System.out .println("nauticalMile.fromDerivedUnit(new float[]{1852},new float[1])[0]=" + nauticalMile.fromDerivedUnit(new float[] { 1852 }, new float[1])[0]); System.out.println("nauticalMile.equals(nauticalMile)=" + nauticalMile.equals(nauticalMile)); final ScaledUnit nautical2Mile = new ScaledUnit(2, nauticalMile); System.out.println("nauticalMile.equals(nautical2Mile)=" + nauticalMile.equals(nautical2Mile)); System.out.println("nauticalMile.isDimensionless()=" + nauticalMile.isDimensionless()); final BaseUnit radian = BaseUnit.getOrCreate(UnitName.newUnitName( "radian", null, "rad"), BaseQuantity.PLANE_ANGLE); final ScaledUnit degree = new ScaledUnit(3.14159 / 180, radian); System.out.println("degree.isDimensionless()=" + degree.isDimensionless()); } }