/*
* 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 concrete implementation of derived units.
*
* @author Steven R. Emmerson
*/
// @Immutable
public class DerivedUnitImpl extends UnitImpl implements DerivedUnit, DerivableUnit {
private static final long serialVersionUID = 1L;
/**
* The dimensionless derived unit.
*/
public static final DerivedUnitImpl DIMENSIONLESS = new DerivedUnitImpl();
/**
* The dimension of this derived unit.
*
* @serial
*/
private /* final */ UnitDimension dimension;
/**
* Constructs a dimensionless derived unit from nothing.
*/
protected DerivedUnitImpl() {
// dimensionless derived unit
this(new UnitDimension(), dimensionlessID());
}
/**
* Returns the identifiers associated with the dimensionless, derived unit.
*
* @return The identifiers of the dimensionless, derived unit.
*/
private static UnitName dimensionlessID() {
UnitName id;
try {
id = UnitName.newUnitName("1", "1", "1");
}
catch (final NameException e) {
id = null;
}
return id;
}
/**
* Constructs from a unit dimension. This is a trusted constructor for use
* by subclasses only.
*
* @param dimension
* The unit dimension.
*/
protected DerivedUnitImpl(final UnitDimension dimension) {
this(dimension, null);
}
/**
* Constructs from identifiers. This is a trusted constructor for use by
* subclasses only.
*
* @param id
* The identifiers for the unit.
*/
protected DerivedUnitImpl(final UnitName id) {
this(null, id);
}
/**
* Constructs from a unit dimension and identifiers. This is a trusted
* constructor for use by subclasses only.
*
* @param dimension
* The unit dimension.
* @param id
* The identifiers for the unit.
*/
protected DerivedUnitImpl(final UnitDimension dimension, final UnitName id) {
super(id);
this.dimension = dimension;
}
/**
* Sets the unit dimension of this derived unit. This is a trusted method
* for use by subclasses only and should be called only once immediately
* after construction of this superinstance.
*
* @param dimension
* The unit dimension.
*/
protected void setDimension(final UnitDimension dimension) {
this.dimension = dimension;
}
/**
* Returns the unit dimension of this derived unit.
*
* @return The unit dimension of this derived unit.
*/
public final UnitDimension getDimension() {
return dimension;
}
/**
* Returns the quantity dimension of this derived unit.
*
* @return The quantity dimension of this derived unit.
*/
public final QuantityDimension getQuantityDimension() {
return getDimension().getQuantityDimension();
}
/*
* From DerivedUnit:
*/
/**
* Indicates if this derived unit is the reciprocal of another derived unit
* (e.g. "second" and "hertz").
*
* @param that
* The other, derived unit.
*/
public final boolean isReciprocalOf(final DerivedUnit that) {
return dimension.isReciprocalOf(that.getDimension());
}
/*
* From UnitImpl:
*/
/**
* Returns the derived unit that is convertible with this unit. Obviously,
* the method returns this derived unit.
*
* @return <code>this</code>.
*/
public final DerivedUnit getDerivedUnit() {
return this;
}
/**
* Clones the derived unit changing the identifiers.
*
* @param id
* The identifiers for the new unit.
* @return The new unit.
*/
public final Unit clone(final UnitName id) {
return new DerivedUnitImpl(dimension, id);
}
/**
* Multiplies this derived unit by another.
*
* @param that
* The other unit.
* @return The product of the two units.
* @throws MultiplyException
* Can't multiply these units.
*/
@Override
protected Unit myMultiplyBy(final Unit that) throws MultiplyException {
Unit result;
if (dimension.getRank() == 0) {
result = that;
}
else {
if (!(that instanceof DerivedUnit)) {
result = that.multiplyBy(this);
}
else {
final UnitDimension thatDimension = ((DerivedUnit) that)
.getDimension();
result = thatDimension.getRank() == 0
? this
: new DerivedUnitImpl(dimension
.multiplyBy(thatDimension));
}
}
return result;
}
/**
* Divides this derived unit by another.
*
* @param that
* The other unit.
* @return The quotient of the two units.
* @throws OperationException
* Can't divide these units.
*/
@Override
protected Unit myDivideBy(final Unit that) throws OperationException {
Unit result;
if (dimension.getRank() == 0) {
result = that.raiseTo(-1);
}
else {
if (!(that instanceof DerivedUnit)) {
result = that.divideInto(this);
}
else {
final UnitDimension thatDimension = ((DerivedUnit) that)
.getDimension();
result = thatDimension.getRank() == 0
? this
: new DerivedUnitImpl(dimension.divideBy(thatDimension));
}
}
return result;
}
/**
* Divides this derived unit into another.
*
* @param that
* The other unit.
* @return The quotient of the two units.
* @throws OperationException
* Can't divide these units.
*/
@Override
protected Unit myDivideInto(final Unit that) throws OperationException {
return that.divideBy(this);
}
/**
* Raises this derived unit to a power.
*
* @param power
* The power.
* @return This derived unit raised to the given power.
*/
@Override
protected Unit myRaiseTo(final int power) {
return power == 1
? this
: new DerivedUnitImpl(dimension.raiseTo(power));
}
/**
* Converts a numerical value from this unit to the derived unit. Obviously,
* the numerical value is unchanged.
*
* @param amount
* The numerical values in this unit.
* @return The numerical value in the derived unit.
*/
public final float toDerivedUnit(final float amount) {
return amount;
}
/**
* Converts a numerical value from this unit to the derived unit. Obviously,
* the numerical value is unchanged.
*
* @param amount
* The numerical values in this unit.
* @return The numerical value in the derived unit.
*/
public final double toDerivedUnit(final double amount) {
return amount;
}
/**
* Converts numerical values from this unit to the derived unit. Obviously,
* the numerical values are unchanged.
*
* @param input
* The numerical values in this unit.
* @param output
* The numerical values in the derived unit. May be the same
* array as <code>input</code>.
* @return <code>output</code>.
*/
public final float[] toDerivedUnit(final float[] input, final float[] output) {
if (input != output) {
System.arraycopy(input, 0, output, 0, input.length);
}
return output;
}
/**
* Converts numerical values from this unit to the derived unit. Obviously,
* the numerical values are unchanged.
*
* @param input
* The numerical values in this unit.
* @param output
* The numerical values in the derived unit. May be the same
* array as <code>input</code>.
* @return <code>output</code>.
*/
public final double[] toDerivedUnit(final double[] input,
final double[] output) {
if (input != output) {
System.arraycopy(input, 0, output, 0, input.length);
}
return output;
}
/**
* Converts a numerical value to this unit from the derived unit. Obviously,
* the numerical value is unchanged.
*
* @param amount
* The numerical values in the derived unit.
* @return The numerical value in this unit.
*/
public final float fromDerivedUnit(final float amount) {
return amount;
}
/**
* Converts a numerical value to this unit from the derived unit. Obviously,
* the numerical value is unchanged.
*
* @param amount
* The numerical values in the derived unit.
* @return The numerical value in this unit.
*/
public final double fromDerivedUnit(final double amount) {
return amount;
}
/**
* Converts numerical values to this unit from the derived unit. Obviously,
* the numerical values are unchanged.
*
* @param input
* The numerical values in the derived unit.
* @param output
* The numerical values in this unit. May be the same array as
* <code>input</code>.
* @return <code>output</code>.
*/
public final float[] fromDerivedUnit(final float[] input,
final float[] output) {
return toDerivedUnit(input, output);
}
/**
* Converts numerical values to this unit from the derived unit. Obviously,
* the numerical values are unchanged.
*
* @param input
* The numerical values in the derived unit.
* @param output
* The numerical values in this unit. May be the same array as
* <code>input</code>.
* @return <code>output</code>.
*/
public final double[] fromDerivedUnit(final double[] input,
final double[] output) {
return toDerivedUnit(input, output);
}
/**
* Indicates if values in this unit are convertible with another unit.
*
* @param that
* The other unit.
* @return <code>true</code> if and only if values in this unit are
* convertible to values in <code>
* that</code>.
*/
@Override
public final boolean isCompatible(final Unit that) {
final DerivedUnit unit = that.getDerivedUnit();
return equals(unit) || isReciprocalOf(unit);
}
/**
* Indicates if this derived unit is semantically identical to an object.
*
* @param object
* The object
* @return <code>true</code> if and only if this derived unit is
* semantically identical to <code>
* object</code>.
*/
@Override
public boolean equals(final Object object) {
if (this == object) {
return true;
}
if (!(object instanceof DerivedUnit)) {
return false;
}
final DerivedUnit that = (DerivedUnit) object;
return (this instanceof BaseUnit && that instanceof BaseUnit)
? false
: dimension.equals(that.getDimension());
}
/**
* Returns the hash code of this instance.
*
* @return The hash code of this instance.
*/
@Override
public int hashCode() {
return this instanceof BaseUnit
? System.identityHashCode(this)
: dimension.hashCode();
}
/**
* Indicates if this derived unit is dimensionless.
*
* @return <code>true</code> if and only if this derived unit is
* dimensionless.
*/
public boolean isDimensionless() {
return dimension.isDimensionless();
}
/**
* Returns a string representation of this unit. If the symbol or name is
* available, then that is returned; otherwise, the corresponding expression
* in base units is returned.
*
* @return The string expression for this derived 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 dimension.toString();
}
/**
* Tests this class.
*/
public static void main(final String[] args) throws Exception {
final BaseUnit second = BaseUnit.getOrCreate(UnitName.newUnitName(
"second", null, "s"), BaseQuantity.TIME);
System.out.println("second = \"" + second + '"');
final BaseUnit meter = BaseUnit.getOrCreate(UnitName.newUnitName(
"meter", null, "m"), BaseQuantity.LENGTH);
System.out.println("meter = \"" + meter + '"');
final DerivedUnitImpl meterSecond = (DerivedUnitImpl) meter
.myMultiplyBy(second);
System.out.println("meterSecond = \"" + meterSecond + '"');
final DerivedUnitImpl meterPerSecond = (DerivedUnitImpl) meter
.myDivideBy(second);
System.out.println("meterPerSecond = \"" + meterPerSecond + '"');
final DerivedUnitImpl secondPerMeter = (DerivedUnitImpl) second
.myDivideBy(meter);
System.out.println("secondPerMeter = \"" + secondPerMeter + '"');
System.out.println("meterPerSecond.isReciprocalOf(secondPerMeter)="
+ meterPerSecond.isReciprocalOf(secondPerMeter));
System.out.println("meter.toDerivedUnit(1.0)="
+ meter.toDerivedUnit(1.0));
System.out
.println("meter.toDerivedUnit(new double[] {1,2,3}, new double[3])[1]="
+ meter.toDerivedUnit(new double[] { 1, 2, 3 },
new double[3])[1]);
System.out.println("meter.fromDerivedUnit(1.0)="
+ meter.fromDerivedUnit(1.0));
System.out
.println("meter.fromDerivedUnit(new double[] {1,2,3}, new double[3])[2]="
+ meter.fromDerivedUnit(new double[] { 1, 2, 3 },
new double[3])[2]);
System.out.println("meter.isCompatible(meter)="
+ meter.isCompatible(meter));
System.out.println("meter.isCompatible(second)="
+ meter.isCompatible(second));
System.out.println("meter.equals(meter)=" + meter.equals(meter));
System.out.println("meter.equals(second)=" + meter.equals(second));
System.out
.println("meter.isDimensionless()=" + meter.isDimensionless());
final Unit sPerS = second.myDivideBy(second);
System.out.println("sPerS = \"" + sPerS + '"');
System.out
.println("sPerS.isDimensionless()=" + sPerS.isDimensionless());
meterPerSecond.raiseTo(2);
meter.myDivideBy(meterPerSecond);
}
}