//
// BaseUnit.java
//

/*
 VisAD system for interactive analysis and visualization of numerical
 data.  Copyright (C) 1996 - 2017 Bill Hibbard, Curtis Rueden, Tom
 Rink, Dave Glowacki, Steve Emmerson, Tom Whittaker, Don Murray, and
 Tommy Jasmin.

 This library is free software; you can redistribute it and/or
 modify it under the terms of the GNU Library General Public
 License as published by the Free Software Foundation; either
 version 2 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
 Library General Public License for more details.

 You should have received a copy of the GNU Library 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
 */

package visad;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Vector;

/**
 * A class that represents the base units of a system of units.
 * 
 * Instances are immutable.
 * 
 * @author Steven R. Emmerson
 * 
 *         This is part of Steve Emerson's Unit package that has been
 *         incorporated into VisAD.
 */
public final class BaseUnit extends Unit implements Serializable {
    private static final long               serialVersionUID    = 1L;

    /**
     * Name of the unit (e.g. "meter").
     */
    private final String                    unitName;

    /**
     * Quantity of the unit (e.g. "Length").
     */
    private final String                    quantityName;

    /**
     * Derived unit associated with base unit (for computational efficiency).
     */
    final DerivedUnit                       derivedUnit;

    final boolean                           isDimless;

    /**
     * Global database of base units (to prevent multiple base units for the
     * same quantity).
     */
    private static final Vector   baseUnits           = new Vector(
                                                                        9);

    /**
     * Constructs a base unit from the names for the quantity and unit, a the
     * unit abbreviation, and whether or not the unit is dimensionless.
     * 
     * @param unitName
     *            Name of the unit (e.g. "meter").
     * @param abbreviation
     *            The abbreviation for the unit (e.g. "m").
     * @param quantityName
     *            Name of the quantity (e.g. "Length").
     * @param isDimless
     *            Whether or not the unit is dimensionless.
     * @throws UnitException
     *             Name, abbreviation, or quantity name is 
     *                          null
     *             .
     */
    private BaseUnit(final String unitName, final String abbreviation,
            final String quantityName, final boolean isDimless)
            throws UnitException {
        super(abbreviation);
        if (unitName == null || abbreviation == null || quantityName == null) {
            throw new UnitException(
                    "Base unit name, abbreviation, or quantity name is null");
        }
        this.unitName = unitName;
        this.quantityName = quantityName;
        baseUnits.addElement(this);
        derivedUnit = new DerivedUnit(this);
        this.isDimless = isDimless;
    }

    /**
     * 

* Indicates if this instance is dimensionless. A unit is dimensionless if * it is a measure of a dimensionless quantity like angle or concentration. * Examples of dimensionless base units include radian, degree, and * steradian. *

* * @return True if an only if this unit is dimensionless. */ @Override public boolean isDimensionless() { return isDimless; } @Override public Unit scale(final double amount) throws UnitException { return ScaledUnit.getInstance(amount, this); } @Override public Unit shift(final double offset) throws UnitException { return OffsetUnit.getInstance(offset, this); } @Override public Unit log(final double base) { return derivedUnit.log(base); } /** * Raise a base unit to a power. * * @param power * The power to raise this unit by. * @return The unit resulting from raising this unit to power. * @promise This unit has not been modified. */ @Override public Unit pow(final int power) { return derivedUnit.pow(power); } /** * Raise a unit to a power. * * @param power * The power to raise this unit by. The value must be integral or * reciprocal integral. * @return The unit resulting from raising this unit to power. * @throws IllegalArgumentException * power has a non-integral or non-reciprocal * integral value. * @promise The unit has not been modified. */ @Override public Unit pow(final double power) throws IllegalArgumentException { return derivedUnit.pow(power); } /** * Returns the N-th root of this unit. * * @param root * The root to take (e.g. 2 means square root). May not be zero. * @return The unit corresponding to the root-th root of this * unit. * @promise This unit has not been modified. * @throws IllegalArgumentException * The root value is zero or the resulting unit would have a * non-integral unit dimension. */ @Override public Unit root(final int root) throws IllegalArgumentException { return derivedUnit.root(root); } /** * Return the name of this unit. * * @return The name of this unit (e.g. "meter"). */ public String unitName() { return unitName; } /** * Return the symbol of this unit. This is the same as the identifier. * * @return The symbol of this unit (e.g. "m"). */ public String unitSymbol() { return getIdentifier(); } /** * Return the name of the quantity associated with this unit. * * @return The name this units quantity (e.g. "Length"). */ public String quantityName() { return quantityName; } /** * Create a new base unit from the name of a quantity and the name of a * unit. The unit abbreviation will be the same as the unit name. The unit * will not be dimensionless. * * @param quantityName * The name of the associated quantity (e.g. "Length"). * @param unitName * The name for the unit (e.g. "meter"). * @return A new base unit or the previously created one with the same * names. * @require The arguments are non-null. The quantity name has not been used * before or the unit name is the same as before. * @promise The new quantity and unit has been added to the database. * @throws UnitException * Name, abbreviation, or quantity name is * null * or attempt to redefine the base unit associated with * quantityName. */ public static BaseUnit addBaseUnit(final String quantityName, final String unitName) throws UnitException { return addBaseUnit(quantityName, unitName, unitName); } /** * Create a new base unit from from the name of a quantity, the name of a * unit, and the unit's abbreviation. The unit will not be dimensionless. * * @param quantityName * The name of the associated quantity (e.g. "Length"). * @param unitName * The name for the unit (e.g. "meter"). * @param abbreviation * The abbreviation for the unit (e.g. "m"). * @return A new base unit or the previously created one with the same * names. * @require The arguments are non-null. The quantity name has not been used * before or the unit name is the same as before. * @promise The new quantity and unit has been added to the database. * @throws UnitException * Name, abbreviation, or quantity name is * null * or attempt to redefine the base unit associated with * quantityName. */ public static synchronized BaseUnit addBaseUnit(final String quantityName, final String unitName, final String abbreviation) throws UnitException { return addBaseUnit(quantityName, unitName, abbreviation, false); } /** * Create a new base unit from from the name of a quantity, the name of a * unit, the unit's abbreviation, and whether or not the unit is * dimensionless. * * @param quantityName * The name of the associated quantity (e.g. "Length"). * @param unitName * The name for the unit (e.g. "meter"). * @param abbreviation * The abbreviation for the unit (e.g. "m"). * @param isDimless * Whether or not the unit is dimensionless. * @return A new base unit or the previously created one with the same * names. * @require The arguments are non-null. The quantity name has not been used * before or the unit name is the same as before. * @promise The new quantity and unit has been added to the database. * @throws UnitException * Name, abbreviation, or quantity name is * null * or attempt to redefine the base unit associated with * quantityName. */ public static synchronized BaseUnit addBaseUnit(final String quantityName, final String unitName, final String abbreviation, final boolean isDimless) throws UnitException { final BaseUnit baseUnit = quantityNameToUnit(quantityName); if (baseUnit == null) { return new BaseUnit(unitName, abbreviation, quantityName, isDimless); } if (baseUnit.unitName.equals(unitName) && baseUnit.getIdentifier().equals(abbreviation) && baseUnit.isDimless == isDimless) { return baseUnit; } throw new UnitException("Attempt to redefine quantity \"" + quantityName + "\" base unit from \"" + baseUnit.unitName + "(" + baseUnit.getIdentifier() + ")" + "\" to \"" + unitName + "(" + abbreviation + ")" + "\""); } /** * Find the base unit with the given name. * * @param unitName * The name of the unit (e.g. "meter"). * @return The existing base unit with the given name or null * if no such units exists. * @require The argument is non-null. */ public static synchronized BaseUnit unitNameToUnit(final String unitName) { for (int i = 0; i < baseUnits.size(); ++i) { final BaseUnit baseUnit = baseUnits.elementAt(i); if (baseUnit.unitName.equals(unitName)) { return baseUnit; } } return null; } /** * Find the base unit for the given quantity. * * @param quantityName * The name of the quantity (e.g. "Length"). * @return The existing base unit for the given quantity or * null if no such unit exists. * @require The argument is non-null. */ public static synchronized BaseUnit quantityNameToUnit( final String quantityName) { for (int i = 0; i < baseUnits.size(); ++i) { final BaseUnit baseUnit = baseUnits.elementAt(i); if (baseUnit.quantityName.equals(quantityName)) { return baseUnit; } } return null; } private static void myAssert(final boolean assertion) { if (!assertion) { throw new AssertionError(); } } private static void myAssert(final String have, final String expect) { if (!have.equals(expect)) { throw new AssertionError(have + " != " + expect); } } private static void myAssert(final Unit have, final Unit expect) { if (!have.equals(expect)) { throw new AssertionError(have.toString() + " != " + expect); } } private static void myAssert(final double have, final double expect) { if (have != expect) { throw new AssertionError("" + have + " != " + expect); } } private static void myAssert(final double[] have, final double[] expect) { if (!Arrays.equals(have, expect)) { throw new AssertionError("" + have + " != " + expect); } } /** * Test this class. * * @param args * Arguments (ignored). * @throws UnitException * A problem occurred. */ public static void main(final String[] args) throws UnitException { final BaseUnit meter = BaseUnit.addBaseUnit("Length", "meter", "m"); myAssert(meter, meter); myAssert(meter.isConvertible(meter)); myAssert(meter.toString(), "m"); myAssert(meter.pow(2), meter.multiply(meter)); myAssert(meter.pow(2).sqrt(), meter); final BaseUnit second = BaseUnit.addBaseUnit("Time", "second", "s"); myAssert(!meter.equals(second)); myAssert(!meter.isConvertible(second)); Unit unit = meter.multiply(second); myAssert(unit, second.multiply(meter)); unit = meter.divide(second); myAssert(unit, second.divide(meter).pow(-1)); myAssert(!unit.equals(meter)); myAssert(!unit.equals(second)); myAssert(meter.toThis(5, meter), 5); myAssert(meter.toThat(5, meter), 5); final double[] values = { 1, 2 }; myAssert(meter.toThis(values, meter), values); myAssert(meter.toThat(values, meter), values); System.out.println("Checking exceptions:"); try { meter.toThis(5, second); throw new AssertionError(); } catch (final UnitException e) { System.out.println(e.getMessage()); } try { meter.toThat(5, second); throw new AssertionError(); } catch (final UnitException e) { System.out.println(e.getMessage()); } try { BaseUnit.addBaseUnit("Length", "foot", "ft"); throw new AssertionError(); } catch (final UnitException e) { System.out.println(e.getMessage()); } System.out.println("Done"); } /** * Convert values to this unit from another unit. * * @param values * The values to be converted. * @param that * The unit of values. * @return The converted values in units of this unit. * @require The units are convertible. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ double[] toThis(final double[] values, final BaseUnit that) throws UnitException { return toThis(values, that, true); } /** * Convert values to this unit from another unit. * * @param values * The values to be converted. * @param that * The unit of values. * @return The converted values in units of this unit. * @require The units are convertible. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ float[] toThis(final float[] values, final BaseUnit that) throws UnitException { return toThis(values, that, true); } /** * Convert values to this unit from a base unit. * * @param values * The values to be converted. * @param that * The unit of values. * @param copy * if false and that equals this, return * values, else return a new array * @return The converted values in units of this unit. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ double[] toThis(final double[] values, final BaseUnit that, final boolean copy) throws UnitException { if (equals(that)) { final double[] newValues = (copy) ? (double[]) values.clone() : values; return newValues; } throw new UnitException("Attempt to convert from unit \"" + that + "\" to unit \"" + this + "\""); } /** * Convert values to this unit from a base unit. * * @param values * The values to be converted. * @param that * The unit of values. * @param copy * if false and that equals this, return * values, else return a new array * @return The converted values in units of this unit. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ float[] toThis(final float[] values, final BaseUnit that, final boolean copy) throws UnitException { if (equals(that)) { final float[] newValues = (copy) ? (float[]) values.clone() : values; return newValues; } throw new UnitException("Attempt to convert from unit \"" + that + "\" to unit \"" + this + "\""); } /** * Convert values to this unit from another unit. * * @param values * The values to be converted. * @param that * The unit of values. * @return The converted values in units of this unit. * @require The units are convertible. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ @Override public double[] toThis(final double[] values, final Unit that) throws UnitException { return toThis(values, that, true); } /** * Convert values to this unit from another unit. * * @param values * The values to be converted. * @param that * The unit of values. * @return The converted values in units of this unit. * @require The units are convertible. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ @Override public float[] toThis(final float[] values, final Unit that) throws UnitException { return toThis(values, that, true); } /** * Convert values to this unit from another unit. * * @param values * The values to be converted. * @param that * The unit of values. * @param copy * if false and that equals this, return * values, else return a new array * @return The converted values in units of this unit. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ @Override public double[] toThis(final double[] values, final Unit that, final boolean copy) throws UnitException { return that.toThat(values, derivedUnit, copy); } /** * Convert values to this unit from another unit. * * @param values * The values to be converted. * @param that * The unit of values. * @param copy * if false and that equals this, return * values, else return a new array * @return The converted values in units of this unit. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ @Override public float[] toThis(final float[] values, final Unit that, final boolean copy) throws UnitException { return that.toThat(values, derivedUnit, copy); } /** * Convert values from this unit to a base unit. * * @param values * The values to be converted in units of this unit. * @param that * The unit to which to convert the values. * @return The converted values. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ double[] toThat(final double[] values, final BaseUnit that) throws UnitException { return toThat(values, that, true); } /** * Convert values from this unit to a base unit. * * @param values * The values to be converted in units of this unit. * @param that * The unit to which to convert the values. * @return The converted values. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ float[] toThat(final float[] values, final BaseUnit that) throws UnitException { return toThat(values, that, true); } /** * Convert values from this unit to a base unit. * * @param values * The values to be converted in units of this unit. * @param that * The unit to which to convert the values. * @param copy * if false and that equals this, return * values, else return a new array * @return The converted values. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ double[] toThat(final double[] values, final BaseUnit that, final boolean copy) throws UnitException { if (equals(that)) { final double[] newValues = (copy) ? (double[]) values.clone() : values; return newValues; } throw new UnitException("Attempt to convert from unit \"" + this + "\" to unit \"" + that + "\""); } /** * Convert values from this unit to a base unit. * * @param values * The values to be converted in units of this unit. * @param that * The unit to which to convert the values. * @param copy * if false and that equals this, return * values, else return a new array * @return The converted values. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ float[] toThat(final float[] values, final BaseUnit that, final boolean copy) throws UnitException { if (equals(that)) { final float[] newValues = (copy) ? (float[]) values.clone() : values; return newValues; } throw new UnitException("Attempt to convert from unit \"" + this + "\" to unit \"" + that + "\""); } /** * Convert values from this unit to another unit. * * @param values * The values to be converted in units of this unit. * @param that * The unit to which to convert the values. * @return The converted values. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ @Override public double[] toThat(final double[] values, final Unit that) throws UnitException { return toThat(values, that, true); } /** * Convert values from this unit to another unit. * * @param values * The values to be converted in units of this unit. * @param that * The unit to which to convert the values. * @return The converted values. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ @Override public float[] toThat(final float[] values, final Unit that) throws UnitException { return toThat(values, that, true); } /** * Convert values from this unit to another unit. * * @param values * The values to be converted in units of this unit. * @param that * The unit to which to convert the values. * @param copy * if false and that equals this, return * values, else return a new array * @return The converted values. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ @Override public double[] toThat(final double[] values, final Unit that, final boolean copy) throws UnitException { return that.toThis(values, derivedUnit, copy); } /** * Convert values from this unit to another unit. * * @param values * The values to be converted in units of this unit. * @param that * The unit to which to convert the values. * @param copy * if false and that equals this, return * values, else return a new array * @return The converted values. * @require The units are identical. * @promise Neither unit has been modified. * @throws UnitException * The units are not convertible. */ @Override public float[] toThat(final float[] values, final Unit that, final boolean copy) throws UnitException { return that.toThis(values, derivedUnit, copy); } /** * Returns the definition of this unit. The definition of a BaseUnit is the * same as the BaseUnit's identifier. * * @return The definition of this unit. Won't be null * * but may be empty. */ @Override public String getDefinition() { return getIdentifier(); } /** * Clones this unit, changing the identifier. This method always throws an * exception because base units may not be cloned. * * @param identifier * The name or abbreviation for the cloned unit. May be * null or empty. * @return A unit equal this this instance but with the given identifier. * @throws UnitException * Base units may not be cloned. Always thrown. */ @Override protected Unit protectedClone(final String identifier) throws UnitException { throw new UnitException("Base units may not be cloned"); } /** * Indicates whether or not this instance equals a unit. * * @param unit * A unit. * @return true if and only if this instance is equal to the * unit. */ @Override public boolean equals(final Unit unit) { if (this == unit) { return true; } if (!(unit instanceof BaseUnit)) { return derivedUnit.equals(unit); } final BaseUnit that = (BaseUnit) unit; return unitName.equals(that.unitName) && quantityName.equals(that.quantityName) && isDimless == that.isDimless; } /** * Returns the hash code of this instance. {@link Object#hashCode()} should * be overridden whenever {@link Object#equals(Object)} is. * * @return The hash code of this instance (includes the values). */ @Override public int hashCode() { if (hashCode == 0) { hashCode = unitName.hashCode() ^ quantityName.hashCode() ^ Boolean.valueOf(isDimless).hashCode(); } return hashCode; } /** * Multiply this unit by another unit. * * @param that * The unit with which to multiply this unit. * @return The product of the two units. * @promise Neither unit has been modified. * @throws UnitException * Meaningless operation. */ @Override public Unit multiply(final Unit that) throws UnitException { return derivedUnit.multiply(that); } /** * Divide this unit by another unit. * * @param that * The unit to divide into this unit. * @return The quotient of the two units. * @promise Neither unit has been modified. * @throws UnitException * Meaningless operation. */ @Override public Unit divide(final Unit that) throws UnitException { return derivedUnit.divide(that); } /** * Divide this unit into another unit. * * @param that * The unit to divided this unit. * @return The quotient of the two units. * @promise Neither unit has been modified. * @throws UnitException * Meaningless operation. */ @Override protected Unit divideInto(final Unit that) throws UnitException { return derivedUnit.divideInto(that); } /** * Indicate whether this unit is convertible with another unit. If one unit * is convertible with another, then the toThis(...)/ and * toThat(...) methods will not throw a UnitException. Unit A * is convertible with unit B if and only if unit B is convertible with unit * A; hence, calling-order is irrelevant. * * @param unit * The other unit. * @return True if and only if this unit is convertible with the other unit. */ @Override public boolean isConvertible(final Unit unit) { return unit == null ? false : derivedUnit.isConvertible(unit); } @Override public DerivedUnit getDerivedUnit() { return derivedUnit; } }

Ads help maintain this website.