[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]