[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]
Subject: jsr-275 take on CountedItems
/*
* JScience - Java Tools and Libraries for the Advancement of Sciences Copyright
* (c) 2005-2009, JScience (http://jscience.org/) All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package javax.measure.unit;
import java.io.Serializable;
import java.math.BigInteger;
import java.text.ParsePosition;
import java.util.HashMap;
import javax.measure.converter.AddConverter;
import javax.measure.converter.ConversionException;
import javax.measure.converter.LinearConverter;
import javax.measure.converter.MultiplyConverter;
import javax.measure.converter.RationalConverter;
import javax.measure.converter.UnitConverter;
import javax.measure.quantity.Dimensionless;
import javax.measure.quantity.Quantity;
/**
* <p> This class represents a determinate {@link javax.measure.quantity.Quantity
* quantity} (as of length, time, heat, or value) adopted as a standard of
* measurement.</p>
*
* <p> It is helpful to think of instances of this class as recording the history by
* which they are created. Thus, for example, the string "g/kg" (which is a
* dimensionless unit) would result from invoking the method toString() on a
* unit that was created by dividing a gram unit by a kilogram unit. Yet, "kg"
* divided by "kg" returns {@link #ONE} and not "kg/kg" due to automatic unit
* factorization.</p>
*
* <p> This class supports the multiplication of offsets units. The result is
* usually a unit not convertible to its {@link #toSI standard unit}. Such units
* may appear in derivative quantities. For example Cel/m is an unit of gradient,
* which is common in atmospheric and oceanographic research.</p>
*
* <p> Units raised at rational powers are also supported. For example the cubic
* root of liter is a unit compatible with meter.</p>
*
* <p> Units specializations can only be defined by sub-classing either
* {@link BaseUnit}, {@link DerivedUnit} or {@link AnnotatedUnit} (the unit
* constructor is package private).For example:[code]
* public LengthUnit extends AnnotatedUnit<Length> {
* public static LengthUnit METER = new LengthUnit(SI.METER, "myOwnUnitType"); // Equivalent to SI.METER
* LengthUnit(Unit<Length> realUnit, String annotation) { super(realUnit, annotation); }
* }[/code]</p>
*
* <p> Instances of this class and sub-classes are immutable.</p>
*
* @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
* @author <a href="mailto:steve@unidata.ucar.edu">Steve Emmerson</a>
* @author Martin Desruisseaux
* @version 1.0, April 15, 2009
* @see <a href="http://en.wikipedia.org/wiki/Units_of_measurement"> Wikipedia:
* Units of measurement</a>
*/
public abstract class Unit<Q extends Quantity> implements Serializable {
/**
* Holds the dimensionless unit <code>ONE</code>.
*/
public static final Unit<Dimensionless> ONE = new ProductUnit<Dimensionless>();
/**
* Holds the unique symbols collection (base unit or alternate units).
*/
static final HashMap<String, Unit<?>> SYMBOL_TO_UNIT = new HashMap<String, Unit<?>>();
/**
* Default constructor. Package private visibility. Subclasses should derive
* from either BaseUnit, DerivedUnit or AnnotatedUnit.
*/
Unit() {
}
// ////////////////////////////////////////////////////
// Contract methods (for sub-classes to implement). //
// ////////////////////////////////////////////////////
/**
* Returns the standard unit from which this unit is derived. The SI unit
* identifies the "type" of {@link javax.measure.quantity.Quantity quantity}
* for which this unit is employed. For example:[code]
* boolean isAngularVelocity(Unit<?> u) {
* return u.toSI().equals(RADIAN.divide(SECOND));
* }
* assert(REVOLUTION.divide(MINUTE).isAngularVelocity());
* [/code]
* <p><i> Note: Having the same SI unit is not sufficient to ensure that a
* converter exists between the two units (e.g. °C/m and K/m).</i></p>
*
* @return the system unit this unit is derived from.
*/
public abstract Unit<Q> toSI();
/**
* Returns the hash code for this unit.
*
* @return this unit hashcode value.
*/
@Override
public abstract int hashCode();
/**
* Indicates if the specified unit can be considered equals to the one
* specified.
*
* @param that the object to compare to.
* @return <code>true</code> if this unit is considered equal to that unit;
* <code>false</code> otherwise.
*/
@Override
public abstract boolean equals(Object that);
/**
* Indicates if this unit is a standard unit (base units and alternate units
* are standard units). The standard unit identifies the "type" of
* {@link javax.measure.quantity.Quantity quantity} for which the unit is
* employed.
*
* @return <code>this.toSI().equals(this)</code>
*/
public boolean isSI() {
return toSI().equals(this);
}
/**
* Indicates if this unit is compatible with the unit specified. Units don't
* need to be equals to be compatible. For example:[code]
* RADIAN.equals(ONE) == false
* RADIAN.isCompatible(ONE) == true
* [/code]
*
* @param that the other unit.
* @return <code>this.getDimension().equals(that.getDimension())</code>
* @see #getDimension()
*/
public final boolean isCompatible(Unit<?> that) {
return (this == that) || this.toSI().equals(that.toSI())
|| this.getDimension().equals(that.getDimension());
}
/**
* Casts this unit to a parameterized unit of specified nature or throw a
* <code>ClassCastException</code> if the dimension of the specified
* quantity and this unit's dimension do not match. For example:[code]
* Unit<Length> LIGHT_YEAR = NonSI.C.times(NonSI.YEAR).asType(Length.class);
* [/code]
*
* @param type the quantity class identifying the nature of the unit.
* @return this unit parameterized with the specified type.
* @throws ClassCastException if the dimension of this unit is different
* from the specified quantity dimension.
* @throws UnsupportedOperationException if the specified quantity class
* does not have a public static field named "UNIT" holding the SI unit for
* the quantity.
*/
@SuppressWarnings("unchecked")
public final <T extends Quantity> Unit<T> asType(Class<T> type)
throws ClassCastException {
Dimension thisDimension = this.getDimension();
Unit<T> quantityUnit = null;
try {
quantityUnit = (Unit<T>) type.getField("UNIT").get(null);
} catch (Exception e) {
throw new UnsupportedOperationException(
"The quantity class "
+ type
+ " does not have a public static field UNIT holding the SI unit "
+ " for the quantity.");
}
Dimension quantityDimension = quantityUnit.getDimension();
if (!thisDimension.equals(quantityDimension))
throw new ClassCastException("Dimension of " + this + " is "
+ thisDimension
+ ", but the dimension of quantity of type "
+ type.getName() + " is " + quantityDimension);
return (Unit<T>) this;
}
/**
* Returns the dimension of this unit (depends upon the current dimensional
* {@link Dimension.Model model}).
*
* @return the dimension of this unit for the current model.
*/
public final Dimension getDimension() {
Unit<?> systemUnit = this.toSI();
if (systemUnit instanceof BaseUnit<?>)
return Dimension.getModel().getDimension((BaseUnit<?>) systemUnit);
if (systemUnit instanceof AlternateUnit<?>)
return ((AlternateUnit<?>) systemUnit).getParent().getDimension();
// Product of units.
ProductUnit<?> productUnit = (ProductUnit<?>) systemUnit;
Dimension dimension = Dimension.NONE;
for (int i = 0; i < productUnit.getUnitCount(); i++) {
Unit<?> unit = productUnit.getUnit(i);
Dimension d = unit.getDimension().pow(productUnit.getUnitPow(i))
.root(productUnit.getUnitRoot(i));
dimension = dimension.times(d);
}
return dimension;
}
/**
* Returns a converter of numeric values from this unit to another unit. As
* a minimum unit sub-classes should be able to provide a unit converter to
* their associated {@link #toSI() standard} unit.
*
* @param that the unit to which to convert the numeric values.
* @return the converter from this unit to <code>that</code> unit.
* @throws ConversionException if the converter cannot be constructed (e.g.
* <code>!this.isCompatible(that)</code>).
*/
public UnitConverter getConverterTo(Unit<Q> that)
throws ConversionException {
return ((this == that) || this.equals(that)) ? UnitConverter.IDENTITY
: searchConverterTo(that);
}
private UnitConverter searchConverterTo(Unit<Q> that)
throws ConversionException {
Unit<Q> thisSI = this.toSI();
Unit<Q> thatSI = that.toSI();
if (thisSI.equals(thatSI))
return that.getConverterTo(thatSI).inverse().concatenate(
this.getConverterTo(thisSI));
// Use dimensional transforms.
if (!thisSI.getDimension().equals(thatSI.getDimension()))
throw new ConversionException(this + " is not compatible with "
+ that);
// Transform between SystemUnit and BaseUnits is Identity.
UnitConverter thisTransform = this.getConverterTo(thisSI).concatenate(
transformOf(this.toBaseUnits()));
UnitConverter thatTransform = that.getConverterTo(thatSI).concatenate(
transformOf(that.toBaseUnits()));
return thatTransform.inverse().concatenate(thisTransform);
}
private Unit<?> toBaseUnits() {
Unit<?> systemUnit = this.toSI();
if (systemUnit instanceof BaseUnit<?>)
return systemUnit;
if (systemUnit instanceof AlternateUnit<?>)
return ((AlternateUnit<?>) systemUnit).getParent().toBaseUnits();
if (systemUnit instanceof ProductUnit<?>) {
ProductUnit<?> productUnit = (ProductUnit<?>) systemUnit;
Unit<?> baseUnits = ONE;
for (int i = 0; i < productUnit.getUnitCount(); i++) {
Unit<?> unit = productUnit.getUnit(i).toBaseUnits();
unit = unit.pow(productUnit.getUnitPow(i));
unit = unit.root(productUnit.getUnitRoot(i));
baseUnits = baseUnits.times(unit);
}
return baseUnits;
} else
throw new InternalError("System Unit cannot be an instance of "
+ this.getClass());
}
private static UnitConverter transformOf(Unit<?> baseUnits) {
if (baseUnits instanceof BaseUnit<?>)
return Dimension.getModel().getTransform((BaseUnit<?>) baseUnits);
// Product of units.
ProductUnit<?> productUnit = (ProductUnit<?>) baseUnits;
UnitConverter converter = UnitConverter.IDENTITY;
for (int i = 0; i < productUnit.getUnitCount(); i++) {
Unit<?> unit = productUnit.getUnit(i);
UnitConverter cvtr = transformOf(unit);
if (!(cvtr instanceof LinearConverter))
throw new ConversionException(baseUnits
+ " is non-linear, cannot convert");
if (productUnit.getUnitRoot(i) != 1)
throw new ConversionException(productUnit
+ " holds a base unit with fractional exponent");
int pow = productUnit.getUnitPow(i);
if (pow < 0) { // Negative power.
pow = -pow;
cvtr = cvtr.inverse();
}
for (int j = 0; j < pow; j++) {
converter = converter.concatenate(cvtr);
}
}
return converter;
}
/**
* Returns a unit equivalent to this unit but used in expressions to
* distinguish between quantities of a different nature but of the same
* dimensions.
*
* <p> Examples of alternate units:[code]
* Unit<Angle> RADIAN = ONE.alternate("rad");
* Unit<Force> NEWTON = METRE.times(KILOGRAM).divide(SECOND.pow(2)).alternate("N");
* Unit<Pressure> PASCAL = NEWTON.divide(METRE.pow(2)).alternate("Pa");
* [/code]
* </p>
*
* @param symbol the new symbol for the alternate unit.
* @return the alternate unit.
* @throws UnsupportedOperationException if this unit is not a standard
* unit.
* @throws IllegalArgumentException if the specified symbol is already
* associated to a different unit.
*/
public final <A extends Quantity> AlternateUnit<A> alternate(String symbol) {
return new AlternateUnit<A>(symbol, this);
}
/**
* Returns the combination of this unit with the specified sub-unit.
* Compound units are typically used for formatting purpose. Examples of
* compound units:[code]
* Unit<Length> FOOT_INCH = FOOT.compound(INCH);
* Unit<Duration> HOUR_MINUTE_SECOND = HOUR.compound(MINUTE).compound(SECOND);
* [/code]
*
* @param that the least significant unit to combine with this unit.
* @return the corresponding compound unit.
*/
public final CompoundUnit<Q> compound(Unit<Q> that) {
return new CompoundUnit<Q>(this, that);
}
/**
* Returns the unit derived from this unit using the specified converter.
* The converter does not need to be linear. For example:[code]
* Unit<Dimensionless> DECIBEL = Unit.ONE.transform(
* new LogConverter(10).inverse().concatenate(
* new RationalConverter(1, 10)));
* [/code]
*
* @param operation the converter from the transformed unit to this unit.
* @return the unit after the specified transformation.
*/
public final Unit<Q> transform(UnitConverter operation) {
if (this instanceof TransformedUnit<?>) {
TransformedUnit<Q> tf = (TransformedUnit<Q>) this;
Unit<Q> parent = tf.getParentUnit();
UnitConverter toParent = tf.toParentUnit().concatenate(operation);
if (toParent == UnitConverter.IDENTITY)
return parent;
return new TransformedUnit<Q>(parent, toParent);
}
if (operation == UnitConverter.IDENTITY)
return this;
return new TransformedUnit<Q>(this, operation);
}
/**
* Returns the result of adding an offset to this unit. The returned unit is
* convertible with all units that are convertible with this unit.
*
* @param offset the offset added (expressed in this unit, e.g.
* <code>CELSIUS = KELVIN.plus(273.15)</code>).
* @return <code>this.transform(new AddConverter(offset))</code>
*/
public final Unit<Q> plus(double offset) {
if (offset == 0)
return this;
return transform(new AddConverter(offset));
}
/**
* Returns the result of multiplying this unit by an exact factor.
*
* @param factor the exact scale factor (e.g.
* <code>KILOMETRE = METRE.times(1000)</code>).
* @return <code>this.transform(new RationalConverter(factor, 1))</code>
*/
public final Unit<Q> times(long factor) {
if (factor == 1)
return this;
return transform(new RationalConverter(BigInteger.valueOf(factor),
BigInteger.ONE));
}
/**
* Returns the result of multiplying this unit by a an approximate factor.
*
* @param factor the approximate factor (e.g.
* <code>ELECTRON_MASS = KILOGRAM.times(9.10938188e-31)</code>).
* @return <code>this.transform(new MultiplyConverter(factor))</code>
*/
public final Unit<Q> times(double factor) {
if (factor == 1)
return this;
return transform(new MultiplyConverter(factor));
}
/**
* Returns the product of this unit with the one specified.
*
* @param that the unit multiplicand.
* @return <code>this * that</code>
*/
@SuppressWarnings("unchecked")
public final Unit<?> times(Unit<?> that) {
if (this.equals(ONE))
return that;
if (that.equals(ONE))
return this;
if (this.isRationalFactor())
return that.transform(this.getConverterTo((Unit) ONE));
if (that.isRationalFactor())
return this.transform(that.getConverterTo((Unit) ONE));
return ProductUnit.getProductInstance(this, that);
}
private boolean isRationalFactor() {
if (!(this instanceof TransformedUnit<?>))
return false;
TransformedUnit<Q> tu = (TransformedUnit<Q>) this;
return tu.getParentUnit().equals(ONE)
&& (tu.getConverterTo(tu.toSI()) instanceof RationalConverter);
}
/**
* Returns the inverse of this unit.
*
* @return <code>1 / this</code>
*/
@SuppressWarnings("unchecked")
public final Unit<?> inverse() {
if (this.equals(ONE))
return this;
if (this.isRationalFactor())
return this.transform(this.getConverterTo((Unit) ONE).inverse());
return ProductUnit.getQuotientInstance(ONE, this);
}
/**
* Returns the result of dividing this unit by an exact divisor.
*
* @param divisor the exact divisor. (e.g.
* <code>QUART = GALLON_LIQUID_US.divide(4)</code>).
* @return <code>this.transform(new RationalConverter(1 , divisor))</code>
*/
public final Unit<Q> divide(long divisor) {
if (divisor == 1)
return this;
return transform(new RationalConverter(BigInteger.ONE, BigInteger
.valueOf(divisor)));
}
/**
* Returns the result of dividing this unit by an approximate divisor.
*
* @param divisor the approximate divisor.
* @return <code>this.transform(new MultiplyConverter(1.0 / divisor))</code>
*/
public final Unit<Q> divide(double divisor) {
if (divisor == 1)
return this;
return transform(new MultiplyConverter(1.0 / divisor));
}
/**
* Returns the quotient of this unit with the one specified.
*
* @param that the unit divisor.
* @return <code>this / that</code>
*/
public final Unit<?> divide(Unit<?> that) {
return this.times(that.inverse());
}
/**
* Returns a unit equals to the given root of this unit.
*
* @param n the root's order.
* @return the result of taking the given root of this unit.
* @throws ArithmeticException if <code>n == 0</code>.
*/
public final Unit<?> root(int n) {
if (n > 0)
return ProductUnit.getRootInstance(this, n);
else if (n == 0)
throw new ArithmeticException("Root's order of zero");
else
// n < 0
return ONE.divide(this.root(-n));
}
/**
* Returns a unit equals to this unit raised to an exponent.
*
* @param n the exponent.
* @return the result of raising this unit to the exponent.
*/
public final Unit<?> pow(int n) {
if (n > 0)
return this.times(this.pow(n - 1));
else if (n == 0)
return ONE;
else
// n < 0
return ONE.divide(this.pow(-n));
}
/**
* Returns a unit instance that is defined from the specified character
* sequence using the {@link UnitFormat#getStandard standard} unit format
* (<a href="http://unitsofmeasure.org/">UCUM</a> based). This method is
* capable of parsing any units representations produced by
* {@link #toString()}. Locale-sensitive unit formatting and parsing are
* handled by the {@link UnitFormat} class and its subclasses.
*
* <p>
* This method can be used to parse dimensionless units.[code]
* Unit<Dimensionless> PERCENT = Unit.valueOf("100").inverse().asType(Dimensionless.class); [/code]
*
* @param csq the character sequence to parse.
* @return
* <code>UnitFormat.getStandard().parse(csq, new ParsePosition(0))</code>
* @throws IllegalArgumentException if the specified character sequence
* cannot be correctly parsed (e.g. not UCUM compliant).
*/
public static Unit<?> valueOf(CharSequence csq) {
return UnitFormat.getStandard().parse(csq, new ParsePosition(0));
}
// ////////////////////
// GENERAL CONTRACT //
// ////////////////////
/**
* Returns the international <code>String</code> representation of this unit
* (<a href="http://unitsofmeasure.org/">UCUM</a> based). The string
* produced for a given unit is always the same; it is not affected by the
* locale. This means that it can be used as a canonical string
* representation for exchanging units, or as a key for a Hashtable, etc.
* Locale-sensitive unit formatting and parsing is handled by the
* {@link UnitFormat} class and its subclasses.
*
* <p>Custom units types should override this method as they will not be
* recognized by the UCUM format.</p>
*
*
* @return <code>UnitFormat.getStandard().format(this)</code>
*/
@Override
public String toString() {
return UnitFormat.getStandard().format(this);
}
private static final long serialVersionUID = 1L;
}
/*
* JScience - Java Tools and Libraries for the Advancement of Sciences
* Copyright (c) 2005-2009, JScience (http://jscience.org/)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "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 THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package javax.measure.quantity;
import javax.measure.unit.Unit;
/**
* This interface represents a dimensionless quantity.
*
* @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
* @version 1.0, April 15, 2009
*/
public interface Dimensionless extends Quantity {
/**
* Holds the SI unit (Système International d'Unités) for this quantity.
*/
public final static Unit<Dimensionless> UNIT = Unit.ONE;
}
[Date Prev] | [Thread Prev] | [Thread Next] | [Date Next] -- [Date Index] | [Thread Index] | [List Home]