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 this / that
+ */
+ public final Unit> divide(Unit> that) {
+ return this.multiply(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 n == 0
or if this operation
+ * would result in an unit with a fractional exponent.
+ */
+ 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.multiply(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 (text) using the {@linkplain UnitFormat#getInstance default} unit format
+ * (UCUM 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.
+ *
+ *
+ * This method can be used to parse dimensionless units.[code]
+ * Unit PERCENT = Unit.valueOf("100").inverse().asType(Dimensionless.class); [/code]
+ *
+ * @param charSequence the character sequence to parse.
+ * @return
+ * UnitFormat.getInstance().parse(csq, new ParsePosition(0))
+ * @throws IllegalArgumentException if the specified character sequence
+ * cannot be correctly parsed (e.g. not UCUM compliant).
+ */
+ public static Unit> valueOf(CharSequence charSequence) {
+ return UnitFormat.getInstance().parse(charSequence, new ParsePosition(0));
+ }
+
+ // ////////////////////
+ // GENERAL CONTRACT //
+ // ////////////////////
+ /**
+ * Returns the international String
representation of this unit
+ * (UCUM 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
+ * {@link UnitFormat} class and its subclasses.
+ *
+ * @return UnitFormat.getInstance().format(this)
+ */
+ @Override
+ public String toString() {
+ return UnitFormat.getInstance().format(this);
+ }
+}
diff --git a/src/main/java/javax/measure/unit/UnitConverter.java b/src/main/java/javax/measure/unit/UnitConverter.java
new file mode 100644
index 0000000..926e476
--- /dev/null
+++ b/src/main/java/javax/measure/unit/UnitConverter.java
@@ -0,0 +1,288 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+package javax.measure.unit;
+
+import java.io.Serializable;
+import java.math.MathContext;
+
+/**
+ * This class represents a converter of numeric values.
+ *
+ * It is not required for sub-classes to be immutable
+ * (e.g. currency converter).
+ *
+ * Sub-classes must ensure unicity of the {@linkplain #IDENTITY identity}
+ * converter. In other words, if the result of an operation is equivalent
+ * to the identity converter, then the unique {@link #IDENTITY} instance
+ * should be returned.
+ *
+ * @author Jean-Marie Dautelle
+ * @author Werner Keil
+ * @version 1.2 ($Revision: 188 $), $Date: 2010-02-24 13:07:13 +0100 (Mi, 24 Feb 2010) $
+ */
+public abstract class UnitConverter implements Serializable {
+
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 2557410026012911803L;
+
+ /**
+ * Holds the identity converter (unique). This converter does nothing
+ * (ONE.convert(x) == x
). This instance is unique.
+ */
+ public static final UnitConverter IDENTITY = new Identity();
+
+ /**
+ * Default constructor.
+ */
+ protected UnitConverter() {
+ }
+
+ /**
+ * Returns the inverse of this converter. If x
is a valid
+ * value, then x == inverse().convert(convert(x))
to within
+ * the accuracy of computer arithmetic.
+ *
+ * @return the inverse of this converter.
+ */
+ public abstract UnitConverter inverse();
+
+ /**
+ * Converts a double
value.
+ *
+ * @param value the numeric value to convert.
+ * @return the double
value after conversion.
+ */
+ public abstract double convert(double value);
+
+ /**
+ * Converts a {@link Number} value.
+ *
+ * @param value the numeric value to convert.
+ * @param ctx the math context being used for conversion.
+ * @return the decimal value after conversion.
+ * @throws ArithmeticException if the result is inexact but the
+ * rounding mode is MathContext.UNLIMITED
or
+ * mathContext.precision == 0
and the quotient has a
+ * non-terminating decimal expansion.
+ */
+ public abstract Number convert(Number value, MathContext ctx) throws ArithmeticException;
+
+ /**
+ * Converts a {@link Number} value.
+ *
+ * @param value the numeric value to convert.
+ * @return the decimal value after conversion.
+ * @throws ArithmeticException if the result is inexact but the
+ * rounding mode is MathContext.UNLIMITED
or
+ * mathContext.precision == 0
and the quotient has a
+ * non-terminating decimal expansion.
+ */
+ public Number convert(Number value) throws ArithmeticException {
+ return convert(value, MathContext.UNLIMITED);
+ }
+
+ /**
+ * Indicates whether this converter is considered to be the the same as the
+ * one specified.
+ *
+ * @param cvtr the converter with which to compare.
+ * @return true
if the specified object is a converter
+ * considered equals to this converter;false
otherwise.
+ */
+ @Override
+ public abstract boolean equals(Object cvtr);
+
+ /**
+ * Returns a hash code value for this converter. Equals object have equal
+ * hash codes.
+ *
+ * @return this converter hash code value.
+ * @see #equals
+ */
+ @Override
+ public abstract int hashCode();
+
+ /**
+ * Concatenates this converter with another converter. The resulting
+ * converter is equivalent to first converting by the specified converter
+ * (right converter), and then converting by this converter (left converter).
+ *
+ * Note: Implementations must ensure that the {@link #IDENTITY} instance
+ * is returned if the resulting converter is an identity
+ * converter.
+ *
+ * @param converter the other converter.
+ * @return the concatenation of this converter with the other converter.
+ */
+ public UnitConverter concatenate(UnitConverter converter) {
+ return (converter == IDENTITY) ? this : new CompoundImpl(this, converter);
+ }
+
+ /**
+ * Indicates if this converter is linear. A converter is linear if
+ * convert(u + v) == convert(u) + convert(v)
and
+ * convert(r * u) == r * convert(u)
. For linear converters the
+ * following property always hold:[code]
+ * y1 = c1.convert(x1);
+ * y2 = c2.convert(x2);
+ * // then y1*y2 == c1.concatenate(c2).convert(x1*x2)
+ * [/code]
+ *
+ * @return true
if this converter is made of distinct converter;
+ * false
otherwise.
+ */
+ public abstract boolean isLinear();
+
+ /**
+ * This interface is implemented by converters made up of two
+ * separate converters (in matrix notation
+ * [compound] = [left] x [right]
).
+ */
+ interface Compound {
+
+ /**
+ * Returns the left converter of this compound converter
+ * (the last one performing the conversion).
+ *
+ * @return the left converter.
+ */
+ UnitConverter getLeft();
+
+ /**
+ * Returns the right converter of this compound converter
+ * (the first one performing the conversion).
+ *
+ * @return the right converter.
+ */
+ UnitConverter getRight();
+ }
+
+ /**
+ * This inner class represents the identity converter (singleton).
+ */
+ private static final class Identity extends UnitConverter {
+
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 7675901502919547460L;
+
+ @Override
+ public Identity inverse() {
+ return this;
+ }
+
+ @Override
+ public double convert(double value) {
+ return value;
+ }
+
+ @Override
+ public Number convert(Number value, MathContext ctx) {
+ return value;
+ }
+
+ @Override
+ public UnitConverter concatenate(UnitConverter converter) {
+ return converter;
+ }
+
+ @Override
+ public boolean equals(Object cvtr) {
+ return this == cvtr; // Unique instance.
+ }
+
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+
+ @Override
+ public boolean isLinear() {
+ return true;
+ }
+ }
+
+ /**
+ * This inner class represents a compound converter (non-linear).
+ */
+ private static final class CompoundImpl extends UnitConverter implements Compound {
+
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 2242882007946934958L;
+
+ /**
+ * Holds the first converter.
+ */
+ private final UnitConverter left;
+
+ /**
+ * Holds the second converter.
+ */
+ private final UnitConverter right;
+
+ /**
+ * Creates a compound converter resulting from the combined
+ * transformation of the specified converters.
+ *
+ * @param left the left converter.
+ * @param right the right converter.
+ */
+ private CompoundImpl(UnitConverter left, UnitConverter right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ @Override
+ public UnitConverter inverse() {
+ return new CompoundImpl(right.inverse(), left.inverse());
+ }
+
+ @Override
+ public double convert(double value) {
+ return left.convert(right.convert(value));
+ }
+
+ @Override
+ public Number convert(Number value, MathContext ctx) {
+ return left.convert(right.convert(value, ctx), ctx);
+ }
+
+ @Override
+ public boolean equals(Object cvtr) {
+ if (this == cvtr)
+ return true;
+ if (!(cvtr instanceof Compound))
+ return false;
+ Compound that = (Compound) cvtr;
+ return (this.left.equals(that.getLeft()))
+ && (this.right.equals(that.getRight()));
+ }
+
+ @Override
+ public int hashCode() {
+ return left.hashCode() + right.hashCode();
+ }
+
+ @Override
+ public boolean isLinear() {
+ return left.isLinear() && right.isLinear();
+ }
+
+ public UnitConverter getLeft() {
+ return left;
+ }
+
+ public UnitConverter getRight() {
+ return right;
+ }
+ }
+}
diff --git a/src/main/java/javax/measure/unit/converter/AddConverter.java b/src/main/java/javax/measure/unit/converter/AddConverter.java
new file mode 100644
index 0000000..ac9e53c
--- /dev/null
+++ b/src/main/java/javax/measure/unit/converter/AddConverter.java
@@ -0,0 +1,115 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+package javax.measure.unit.converter;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.MathContext;
+import javax.measure.unit.UnitConverter;
+
+
+/**
+ * This class represents a converter adding a constant offset
+ * to numeric values (double
based).
+ *
+ * Instances of this class are immutable.
+ *
+ * @author Jean-Marie Dautelle
+ * @author Werner Keil
+ *
+ * @version 1.0.1 ($Revision: 169 $), $Date: 2010-02-21 18:48:40 +0100 (So, 21 Feb 2010) $
+ */
+public final class AddConverter extends UnitConverter {
+
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 8088797685241019815L;
+
+ /**
+ * Holds the offset.
+ */
+ private final double offset;
+
+ /**
+ * Creates an add converter with the specified offset.
+ *
+ * @param offset the offset value.
+ * @throws IllegalArgumentException if offset is 0.0
+ * (would result in identity converter).
+ */
+ public AddConverter(double offset) {
+ if (offset == 0.0) {
+ throw new IllegalArgumentException("Would result in identity converter");
+ }
+ this.offset = offset;
+ }
+
+ /**
+ * Returns the offset value for this add converter.
+ *
+ * @return the offset value.
+ */
+ public double getOffset() {
+ return offset;
+ }
+
+ @Override
+ public UnitConverter concatenate(UnitConverter converter) {
+ if (converter instanceof AddConverter) {
+ double newOffset = offset + ((AddConverter) converter).offset;
+ return newOffset == 0.0 ? IDENTITY : new AddConverter(newOffset);
+ } else {
+ return super.concatenate(converter);
+ }
+ }
+
+ @Override
+ public AddConverter inverse() {
+ return new AddConverter(-offset);
+ }
+
+ @Override
+ public double convert(double value) {
+ return value + offset;
+ }
+
+ @Override
+ public Number convert(Number value, MathContext ctx) throws ArithmeticException {
+ if (value instanceof BigDecimal) {
+ return ((BigDecimal)value).add(BigDecimal.valueOf(offset), ctx);
+ } else if (value instanceof BigInteger) {
+ return ((BigInteger)value).add(BigInteger.valueOf((long) offset));
+ }
+ return convert(value.doubleValue());
+ }
+
+ @Override
+ public final String toString() {
+ return "AddConverter(" + offset + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof AddConverter)) {
+ return false;
+ }
+ AddConverter that = (AddConverter) obj;
+ return this.offset == that.offset;
+ }
+
+ @Override
+ public int hashCode() {
+ long bits = Double.doubleToLongBits(offset);
+ return (int) (bits ^ (bits >>> 32));
+ }
+
+ @Override
+ public boolean isLinear() {
+ return false;
+ }
+}
diff --git a/src/main/java/javax/measure/unit/converter/ExpConverter.java b/src/main/java/javax/measure/unit/converter/ExpConverter.java
new file mode 100644
index 0000000..f7afbd5
--- /dev/null
+++ b/src/main/java/javax/measure/unit/converter/ExpConverter.java
@@ -0,0 +1,99 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+package javax.measure.unit.converter;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import javax.measure.unit.UnitConverter;
+
+
+/**
+ * This class represents a exponential converter of limited precision.
+ * Such converter is typically used to create inverse of logarithmic unit.
+ *
+ *
Instances of this class are immutable.
+ *
+ * @author Jean-Marie Dautelle
+ * @author Werner Keil
+ * @version 1.0.1 ($Revision: 169 $), $Date: 2010-02-21 18:48:40 +0100 (So, 21 Feb 2010) $
+ */
+public final class ExpConverter extends UnitConverter {
+
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -1862583888012861945L;
+
+ /**
+ * Holds the logarithmic base.
+ */
+ private final double base;
+
+ /**
+ * Holds the natural logarithm of the base.
+ */
+ private final double logOfBase;
+
+ /**
+ * Creates a logarithmic converter having the specified base.
+ *
+ * @param base the logarithmic base (e.g. Math.E
for
+ * the Natural Logarithm).
+ */
+ public ExpConverter(double base) {
+ this.base = base;
+ this.logOfBase = Math.log(base);
+ }
+
+ /**
+ * Returns the exponential base of this converter.
+ *
+ * @return the exponential base (e.g. Math.E
for
+ * the Natural Exponential).
+ */
+ public double getBase() {
+ return base;
+ }
+
+ @Override
+ public UnitConverter inverse() {
+ return new LogConverter(base);
+ }
+
+ @Override
+ public final String toString() {
+ return "ExpConverter("+ base + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ExpConverter))
+ return false;
+ ExpConverter that = (ExpConverter) obj;
+ return this.base == that.base;
+ }
+
+ @Override
+ public int hashCode() {
+ long bits = Double.doubleToLongBits(base);
+ return (int) (bits ^ (bits >>> 32));
+ }
+
+ @Override
+ public double convert(double amount) {
+ return Math.exp(logOfBase * amount);
+ }
+
+ @Override
+ public Number convert(Number value, MathContext ctx) throws ArithmeticException {
+ return BigDecimal.valueOf(convert(value.doubleValue())); // Reverts to double conversion.
+ }
+
+ @Override
+ public boolean isLinear() {
+ return false;
+ }
+
+}
diff --git a/src/main/java/javax/measure/unit/converter/LogConverter.java b/src/main/java/javax/measure/unit/converter/LogConverter.java
new file mode 100644
index 0000000..2403c82
--- /dev/null
+++ b/src/main/java/javax/measure/unit/converter/LogConverter.java
@@ -0,0 +1,100 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+package javax.measure.unit.converter;
+
+import java.math.BigDecimal;
+import java.math.MathContext;
+import javax.measure.unit.UnitConverter;
+
+
+/**
+ * This class represents a logarithmic converter of limited precision.
+ * Such converter is typically used to create logarithmic unit.
+ * For example:[code]
+ * Unit BEL = Unit.ONE.transform(new LogConverter(10).inverse());
+ * [/code]
+ *
+ * Instances of this class are immutable.
+ *
+ * @author Jean-Marie Dautelle
+ * @author Werner Keil
+ * @version 1.3 ($Revision: 169 $), $Date: 2010-02-21 18:48:40 +0100 (So, 21 Feb 2010) $
+ */
+public final class LogConverter extends UnitConverter {
+
+ /** The serialVersionUID */
+ private static final long serialVersionUID = -5581266460675123322L;
+
+ /**
+ * Holds the logarithmic base.
+ */
+ private final double base;
+ /**
+ * Holds the natural logarithm of the base.
+ */
+ private final double logOfBase;
+
+ /**
+ * Creates a logarithmic converter having the specified base.
+ *
+ * @param base the logarithmic base (e.g. Math.E
for
+ * the Natural Logarithm).
+ */
+ public LogConverter(double base) {
+ this.base = base;
+ logOfBase = Math.log(base);
+ }
+
+ /**
+ * Returns the logarithmic base of this converter.
+ *
+ * @return the logarithmic base (e.g. Math.E
for
+ * the Natural Logarithm).
+ */
+ public double getBase() {
+ return base;
+ }
+
+ @Override
+ public UnitConverter inverse() {
+ return new ExpConverter(base);
+ }
+
+ @Override
+ public final String toString() {
+ return "LogConverter("+ base + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof LogConverter))
+ return false;
+ LogConverter that = (LogConverter) obj;
+ return this.base == that.base;
+ }
+
+ @Override
+ public int hashCode() {
+ long bits = Double.doubleToLongBits(base);
+ return (int) (bits ^ (bits >>> 32));
+ }
+
+ @Override
+ public double convert(double amount) {
+ return Math.log(amount) / logOfBase;
+ }
+
+ @Override
+ public Number convert(Number value, MathContext ctx) throws ArithmeticException {
+ return BigDecimal.valueOf(convert(value.doubleValue())); // Reverts to double conversion.
+ }
+
+ @Override
+ public boolean isLinear() {
+ return false;
+ }
+}
diff --git a/src/main/java/javax/measure/unit/converter/MultiplyConverter.java b/src/main/java/javax/measure/unit/converter/MultiplyConverter.java
new file mode 100644
index 0000000..d03b165
--- /dev/null
+++ b/src/main/java/javax/measure/unit/converter/MultiplyConverter.java
@@ -0,0 +1,108 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+package javax.measure.unit.converter;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.MathContext;
+import javax.measure.unit.UnitConverter;
+
+
+/**
+ * This class represents a converter multiplying numeric values by a
+ * constant scaling factor (double
based).
+ *
+ * Instances of this class are immutable.
+ *
+ * @author Jean-Marie Dautelle
+ * @author Werner Keil
+ * @version 1.4 ($Revision: 169 $), $Date: 2010-02-21 18:48:40 +0100 (So, 21 Feb 2010) $
+ */
+public final class MultiplyConverter extends UnitConverter {
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 6497743504427978825L;
+
+ /**
+ * Holds the scale factor.
+ */
+ private final double factor;
+
+ /**
+ * Creates a multiply converter with the specified scale factor.
+ *
+ * @param factor the scaling factor.
+ * @throws IllegalArgumentException if coefficient is 1.0
+ * (would result in identity converter)
+ */
+ public MultiplyConverter(double factor) {
+ if (factor == 1.0)
+ throw new IllegalArgumentException("Would result in identity converter");
+ this.factor = factor;
+ }
+
+ /**
+ * Returns the scale factor of this converter.
+ *
+ * @return the scale factor.
+ */
+ public double getFactor() {
+ return factor;
+ }
+
+ @Override
+ public UnitConverter concatenate(UnitConverter converter) {
+ if (converter instanceof MultiplyConverter) {
+ double newfactor = factor * ((MultiplyConverter) converter).factor;
+ return newfactor == 1.0 ? IDENTITY : new MultiplyConverter(newfactor);
+ } else
+ return super.concatenate(converter);
+ }
+
+ @Override
+ public MultiplyConverter inverse() {
+ return new MultiplyConverter(1.0 / factor);
+ }
+
+ @Override
+ public double convert(double value) {
+ return value * factor;
+ }
+
+ @Override
+ public Number convert(Number value, MathContext ctx) throws ArithmeticException {
+ if (value instanceof BigDecimal) {
+ return ((BigDecimal)value).multiply(BigDecimal.valueOf(factor), ctx);
+ } else if (value instanceof BigInteger) {
+ return ((BigInteger)value).multiply(BigInteger.valueOf((long) factor));
+ }
+ return convert(value.doubleValue());
+ }
+
+ @Override
+ public final String toString() {
+ return "MultiplyConverter("+ factor + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof MultiplyConverter))
+ return false;
+ MultiplyConverter that = (MultiplyConverter) obj;
+ return this.factor == that.factor;
+ }
+
+ @Override
+ public int hashCode() {
+ long bits = Double.doubleToLongBits(factor);
+ return (int)(bits ^ (bits >>> 32));
+ }
+
+ @Override
+ public boolean isLinear() {
+ return true;
+ }
+}
diff --git a/src/main/java/javax/measure/unit/converter/RationalConverter.java b/src/main/java/javax/measure/unit/converter/RationalConverter.java
new file mode 100644
index 0000000..eadfd13
--- /dev/null
+++ b/src/main/java/javax/measure/unit/converter/RationalConverter.java
@@ -0,0 +1,142 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+package javax.measure.unit.converter;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.MathContext;
+import javax.measure.unit.UnitConverter;
+
+
+/**
+ * This class represents a converter multiplying numeric values by an
+ * exact scaling factor (represented as the quotient of two
+ * BigInteger
numbers).
+ *
+ * Instances of this class are immutable.
+ *
+ * @author Jean-Marie Dautelle
+ * @author Werner Keil
+ * @version 1.1 ($Revision: 178 $), $Date: 2010-02-22 20:18:18 +0100 (Mo, 22 Feb 2010) $
+ */
+public final class RationalConverter extends UnitConverter {
+
+ /** The serialVersionUID */
+ private static final long serialVersionUID = 5313011404391445406L;
+
+ /**
+ * Holds the converter dividend.
+ */
+ private final BigInteger dividend;
+ /**
+ * Holds the converter divisor (always positive).
+ */
+ private final BigInteger divisor;
+
+ /**
+ * Creates a rational converter with the specified dividend and
+ * divisor.
+ *
+ * @param dividend the dividend.
+ * @param divisor the positive divisor.
+ * @throws IllegalArgumentException if divisor <= 0
+ * @throws IllegalArgumentException if dividend == divisor
+ */
+ public RationalConverter(BigInteger dividend, BigInteger divisor) {
+ if (divisor.compareTo(BigInteger.ZERO) <= 0)
+ throw new IllegalArgumentException("Negative or zero divisor");
+ if (dividend.equals(divisor))
+ throw new IllegalArgumentException("Would result in identity converter");
+ this.dividend = dividend; // Exact conversion.
+ this.divisor = divisor; // Exact conversion.
+ }
+
+ /**
+ * Returns the integer dividend for this rational converter.
+ *
+ * @return this converter dividend.
+ */
+ public BigInteger getDividend() {
+ return dividend;
+ }
+
+ /**
+ * Returns the integer (positive) divisor for this rational converter.
+ *
+ * @return this converter divisor.
+ */
+ public BigInteger getDivisor() {
+ return divisor;
+ }
+
+ @Override
+ public double convert(double value) {
+ return value * toDouble(dividend) / toDouble(divisor);
+ }
+
+ // Optimization of BigInteger.doubleValue() (implementation too inneficient).
+ private static double toDouble(BigInteger integer) {
+ return (integer.bitLength() < 64) ? integer.longValue() : integer.doubleValue();
+ }
+
+ @Override
+ public Number convert(Number value, MathContext ctx) throws ArithmeticException {
+ if (value instanceof BigInteger) {
+ return ((BigInteger)value).multiply(dividend).divide(divisor);
+ } else if (value instanceof BigDecimal){
+ return ((BigDecimal)value).toBigInteger().multiply(dividend).divide(divisor);
+ } else {
+ return BigInteger.valueOf(value.longValue()).multiply(dividend).divide(divisor);
+ }
+ }
+
+ @Override
+ public UnitConverter concatenate(UnitConverter converter) {
+ if (converter instanceof RationalConverter) {
+ RationalConverter that = (RationalConverter) converter;
+ BigInteger dividend = this.getDividend().multiply(that.getDividend());
+ BigInteger divisor = this.getDivisor().multiply(that.getDivisor());
+ BigInteger gcd = dividend.gcd(divisor);
+ dividend = dividend.divide(gcd);
+ divisor = divisor.divide(gcd);
+ return (dividend.equals(BigInteger.ONE) && divisor.equals(BigInteger.ONE))
+ ? IDENTITY : new RationalConverter(dividend, divisor);
+ } else
+ return super.concatenate(converter);
+ }
+
+ @Override
+ public RationalConverter inverse() {
+ return dividend.signum() == -1 ? new RationalConverter(getDivisor().negate(), getDividend().negate())
+ : new RationalConverter(getDivisor(), getDividend());
+ }
+
+ @Override
+ public final String toString() {
+ return "RationalConverter("+ dividend + "," + divisor + ")";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof RationalConverter))
+ return false;
+ RationalConverter that = (RationalConverter) obj;
+ return this.dividend.equals(that.dividend) &&
+ this.divisor.equals(that.divisor);
+ }
+
+ @Override
+ public int hashCode() {
+ return dividend.hashCode() + divisor.hashCode();
+ }
+
+ @Override
+ public boolean isLinear() {
+ return true;
+ }
+
+}
diff --git a/src/main/java/javax/measure/unit/format/AnnotatedUnit.java b/src/main/java/javax/measure/unit/format/AnnotatedUnit.java
new file mode 100644
index 0000000..8cbb892
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/AnnotatedUnit.java
@@ -0,0 +1,139 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+package javax.measure.unit.format;
+
+import java.util.Map;
+import javax.measure.quantity.Quantity;
+import javax.measure.unit.Unit;
+import javax.measure.unit.UnitConverter;
+
+/**
+ * This class represents an annotated unit. It allows for unit specialization
+ * and annotation without changing the unit semantic. For example:[code]
+ * public class Size implements Length {
+ * private double meters;
+ * ...
+ * public static class Unit extends AnnotatedUnit {
+ * private Unit(javax.measure.Unit realUnit, String annotation) {
+ * super(actualUnit, annotation);
+ * }
+ * public static Size.Unit METER = new Size.Unit(SI.METRE, "size"); // Equivalent to SI.METRE
+ * public static Size.Unit INCH = new Size.Unit(NonSI.INCH, "size"); // Equivalent to NonSI.INCH
+ * }
+ * }[/code]
+ * Annotation are often written between curly braces behind units
+ * but they do not change, for example "%{vol}", "kg{total}", or
+ * "{RBC}" (for "red blood cells") are equivalent to "%", "kg", and "1"
+ * respectively.
+ *
+ * Note: This class supports the {@code UnitFormat} implementation {@code LocalFormat},
+ * hence it is currently part of the internal format API. If required and made public, it's possible
+ * to locate in the "unit" package.
+ *
+ * @param The type of the quantity measured by this unit.
+ *
+ * @author Jean-Marie Dautelle
+ * @author Werner Keil
+ * @version 1.0.1 ($Revision: 180 $), $Date: 2010-02-23 16:11:17 +0100 (Di, 23 Feb 2010) $
+ */
+class AnnotatedUnit> extends Unit {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Holds the annotation.
+ */
+ private final String annotation;
+
+ /**
+ * Holds the actual unit, never an annotated unit.
+ */
+ private final Unit actualUnit;
+
+ Unit getActualUnit() {
+ return actualUnit;
+ }
+
+ /**
+ * Creates an annotated unit for the specified unit.
+ *
+ * @param actualUnit the real unit.
+ * @param annotation the annotation.
+ */
+ public AnnotatedUnit(Unit actualUnit, String annotation) {
+ this.actualUnit = (actualUnit instanceof AnnotatedUnit>)
+ ? ((AnnotatedUnit) actualUnit).actualUnit : actualUnit;
+ this.annotation = annotation;
+ }
+
+ /**
+ * Returns an annotated unit equivalent to this unit. The annotation
+ * does not change the unit semantic.
+ * Annotation are often written between curly braces behind units.
+ * For example, annotated units "%{vol}", "kg{total}", or "{RBC}"
+ * (for "red blood cells") are equivalent to "%", "kg", and "1" respectively.
+ *
+ * @param annotation the new symbol for the alternate unit.
+ * @return the alternate unit.
+ * @throws UnsupportedOperationException if this unit is not a metric unit.
+ * @throws IllegalArgumentException if the specified symbol is already
+ * associated to a different unit.
+ */
+ public final Unit annotate(String annotation) {
+ return new AnnotatedUnit(this, annotation);
+ }
+
+ /**
+ * Returns the annotation (if any) of this unit.
+ * The default implementation returns null
(no annotation).
+ *
+ * @return this unit annotation or null
if this unit has not
+ * specific symbol associated with (e.g. product of units).
+ */
+ public String getAnnotation() {
+ return annotation;
+ }
+
+ @Override
+ public String getSymbol() {
+ return actualUnit.getSymbol();
+ }
+
+ @Override
+ public Map, Integer> getProductUnits() {
+ return actualUnit.getProductUnits();
+ }
+
+ @Override
+ public Unit toMetric() {
+ return actualUnit.toMetric();
+ }
+
+ @Override
+ public UnitConverter getConverterToMetric() {
+ return actualUnit.getConverterToMetric();
+ }
+
+ @Override
+ public boolean equals(Object that) {
+ if (this == that)
+ return true;
+ if (!(that instanceof AnnotatedUnit>))
+ return false;
+ AnnotatedUnit> thatUnit = (AnnotatedUnit>) that;
+ return this.actualUnit.equals(thatUnit.actualUnit) &&
+ this.annotation.equals(thatUnit.annotation);
+ }
+
+ @Override
+ public int hashCode() {
+ return actualUnit.hashCode() + annotation.hashCode();
+ }
+}
diff --git a/src/main/java/javax/measure/unit/format/LocalFormat.java b/src/main/java/javax/measure/unit/format/LocalFormat.java
new file mode 100644
index 0000000..eed5299
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/LocalFormat.java
@@ -0,0 +1,589 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+package javax.measure.unit.format;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.math.BigInteger;
+import java.text.ParsePosition;
+import java.util.Locale;
+import java.util.Map;
+import java.util.ResourceBundle;
+
+import javax.measure.unit.BaseUnit;
+import javax.measure.unit.MetricSystem;
+import javax.measure.unit.Unit;
+import javax.measure.unit.UnitConverter;
+import javax.measure.unit.UnitFormat;
+
+/**
+ * This class represents the local sensitive format.
+ *
+ * Here is the grammar for Units in Extended Backus-Naur Form (EBNF)
+ *
+ * Note that the grammar has been left-factored to be suitable for use by a top-down
+ * parser generator such as JavaCC
+ *
+ *
+ *
+ * Lexical Entities:
+ *
+ *
+ * <sign>
+ * :=
+ * "+" | "-"
+ *
+ *
+ * <digit>
+ * :=
+ * "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
+ *
+ *
+ * <superscript_digit>
+ * :=
+ * "⁰" | "¹" | "²" | "³" | "⁴" | "⁵" | "⁶" | "⁷" | "⁸" | "⁹"
+ *
+ *
+ * <integer>
+ * :=
+ * (<digit>)+
+ *
+ *
+ * <number>
+ * :=
+ * (<sign>)? (<digit>)* (".")? (<digit>)+ (("e" | "E") (<sign>)? (<digit>)+)?
+ *
+ *
+ * <exponent>
+ * :=
+ * ( "^" ( <sign> )? <integer> ) | ( "^(" (<sign>)? <integer> ( "/" (<sign>)? <integer> )? ")" ) | ( <superscript_digit> )+
+ *
+ *
+ * <initial_char>
+ * :=
+ * ? Any Unicode character excluding the following: ASCII control & whitespace (\u0000 - \u0020), decimal digits '0'-'9', '(' (\u0028), ')' (\u0029), '*' (\u002A), '+' (\u002B), '-' (\u002D), '.' (\u002E), '/' (\u005C), ':' (\u003A), '^' (\u005E), '²' (\u00B2), '³' (\u00B3), '·' (\u00B7), '¹' (\u00B9), '⁰' (\u2070), '⁴' (\u2074), '⁵' (\u2075), '⁶' (\u2076), '⁷' (\u2077), '⁸' (\u2078), '⁹' (\u2079) ?
+ *
+ *
+ * <unit_identifier>
+ * :=
+ * <initial_char> ( <initial_char> | <digit> )*
+ *
+ *
+ * Non-Terminals:
+ *
+ *
+ * <unit_expr>
+ * :=
+ * <compound_expr>
+ *
+ *
+ * <compound_expr>
+ * :=
+ * <add_expr> ( ":" <add_expr> )*
+ *
+ *
+ * <add_expr>
+ * :=
+ * ( <number> <sign> )? <mul_expr> ( <sign> <number> )?
+ *
+ *
+ * <mul_expr>
+ * :=
+ * <exponent_expr> ( ( ( "*" | "·" ) <exponent_expr> ) | ( "/" <exponent_expr> ) )*
+ *
+ *
+ * <exponent_expr>
+ * :=
+ * ( <atomic_expr> ( <exponent> )? ) | (<integer> "^" <atomic_expr>) | ( ( "log" ( <integer> )? ) | "ln" ) "(" <add_expr> ")" )
+ *
+ *
+ * <atomic_expr>
+ * :=
+ * <number> | <unit_identifier> | ( "(" <add_expr> ")" )
+ *
+ *
+ *
+ * @author Eric Russell
+ * @author Werner Keil
+ * @version 1.4 ($Revision: 187 $), $Date: 2010-02-24 12:34:43 +0100 (Mi, 24 Feb 2010) $
+ */
+public class LocalFormat extends UnitFormat {
+
+ //////////////////////////////////////////////////////
+ // Class variables //
+ //////////////////////////////////////////////////////
+ /**
+ * serialVersionUID
+ */
+ private static final long serialVersionUID = -2046025264383639924L;
+
+ /**
+ * Name of the resource bundle
+ */
+ private static final String BUNDLE_NAME = LocalFormat.class.getPackage().getName() + ".messages"; //$NON-NLS-1$
+
+ /** Default locale instance. If the default locale is changed after the class is
+ initialized, this instance will no longer be used. */
+ private static final LocalFormat DEFAULT_INSTANCE = new LocalFormat(new SymbolMapImpl(ResourceBundle.getBundle(BUNDLE_NAME)));
+
+ /** Multiplicand character */
+ private static final char MIDDLE_DOT = '\u00b7'; //$NON-NLS-1$
+
+ /** Operator precedence for the addition and subtraction operations */
+ private static final int ADDITION_PRECEDENCE = 0;
+
+ /** Operator precedence for the multiplication and division operations */
+ private static final int PRODUCT_PRECEDENCE = ADDITION_PRECEDENCE + 2;
+
+ /** Operator precedence for the exponentiation and logarithm operations */
+ private static final int EXPONENT_PRECEDENCE = PRODUCT_PRECEDENCE + 2;
+
+ /**
+ * Operator precedence for a unit identifier containing no mathematical
+ * operations (i.e., consisting exclusively of an identifier and possibly
+ * a prefix). Defined to be Integer.MAX_VALUE
so that no
+ * operator can have a higher precedence.
+ */
+ private static final int NOOP_PRECEDENCE = Integer.MAX_VALUE;
+
+ ///////////////////
+ // Class methods //
+ ///////////////////
+ /** Returns the instance for the current default locale (non-ascii characters are allowed) */
+ public static LocalFormat getInstance() {
+ return DEFAULT_INSTANCE;
+ }
+
+ /**
+ * Returns an instance for the given locale.
+ * @param locale
+ */
+ public static LocalFormat getInstance(Locale locale) {
+ return new LocalFormat(new SymbolMapImpl(ResourceBundle.getBundle(BUNDLE_NAME, locale)));
+ }
+
+ /** Returns an instance for the given symbol map. */
+ protected static LocalFormat getInstance(SymbolMapImpl symbols) {
+ return new LocalFormat(symbols);
+ }
+ ////////////////////////
+ // Instance variables //
+ ////////////////////////
+ /**
+ * The symbol map used by this instance to map between
+ * {@link javax.measure.Unit Unit}s and String
s, etc...
+ */
+ private transient SymbolMapImpl symbolMap;
+
+ //////////////////
+ // Constructors //
+ //////////////////
+ /**
+ * Base constructor.
+ *
+ * @param symbols the symbol mapping.
+ */
+ private LocalFormat(SymbolMapImpl symbols) {
+ symbolMap = symbols;
+ }
+
+ ////////////////////////
+ // Instance methods //
+ ////////////////////////
+ /**
+ * Get the symbol map used by this instance to map between
+ * {@link javax.measure.Unit Unit}s and String
s, etc...
+ * @return SymbolMap the current symbol map
+ */
+ public SymbolMapImpl getSymbolMap() {
+ return symbolMap;
+ }
+
+ ////////////////
+ // Formatting //
+ ////////////////
+ @Override
+ public Appendable format(Unit> unit, Appendable appendable) throws IOException {
+ formatInternal(unit, appendable);
+ if (unit instanceof AnnotatedUnit>) {
+ AnnotatedUnit> annotatedUnit = (AnnotatedUnit>)unit;
+ if (annotatedUnit.getAnnotation() != null) {
+ appendable.append('{');
+ appendable.append(annotatedUnit.getAnnotation());
+ appendable.append('}');
+ }
+ }
+ return appendable;
+ }
+
+ @Override
+ public Unit> parse(CharSequence csq, ParsePosition cursor) throws IllegalArgumentException {
+ // Parsing reads the whole character sequence from the parse position.
+ int start = cursor.getIndex();
+ int end = csq.length();
+ if (end <= start) {
+ return Unit.ONE;
+ }
+ String source = csq.subSequence(start, end).toString().trim();
+ if (source.length() == 0) {
+ return Unit.ONE;
+ }
+ try {
+ UnitParser parser = new UnitParser(symbolMap, new StringReader(source));
+ Unit> result = parser.parseUnit();
+ cursor.setIndex(end);
+ return result;
+ } catch (ParseException e) {
+ if (e.currentToken != null) {
+ cursor.setErrorIndex(start + e.currentToken.endColumn);
+ } else {
+ cursor.setErrorIndex(start);
+ }
+ throw new IllegalArgumentException(e.getMessage());
+ } catch (TokenMgrError e) {
+ cursor.setErrorIndex(start);
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+
+ /**
+ * Format the given unit to the given StringBuffer, then return the operator
+ * precedence of the outermost operator in the unit expression that was
+ * formatted. See {@link ConverterFormat} for the constants that define the
+ * various precedence values.
+ * @param unit the unit to be formatted
+ * @param buffer the StringBuffer
to be written to
+ * @return the operator precedence of the outermost operator in the unit
+ * expression that was output
+ */
+ private int formatInternal0(Unit> unit, Appendable buffer) throws IOException {
+ String symbol = symbolMap.getSymbol(unit);
+ if (symbol != null) {
+ buffer.append(symbol);
+ return NOOP_PRECEDENCE;
+ } else {
+ symbol = unit.getSymbol();
+ if (symbol != null) {
+ buffer.append(symbol);
+ return NOOP_PRECEDENCE;
+ }
+ }
+ Map, Integer> units = unit.getProductUnits();
+ if (units == null) { // Simple unit.
+ buffer.append(unit.getConverterToMetric().toString());
+ buffer.append(' ');
+ buffer.append(unit.toMetric().toString());
+ return NOOP_PRECEDENCE;
+ }
+ int negativeExponentCount = 0;
+ boolean start = true;
+ // Write positive exponents first...
+ for (Map.Entry, Integer> entry : units.entrySet()) {
+ int pow = entry.getValue();
+ if (pow >= 0) {
+ formatExponent(entry.getKey(), pow, 1, !start, buffer);
+ start = false;
+ } else {
+ negativeExponentCount += 1;
+ }
+ }
+ // ..then write negative exponents.
+ if (negativeExponentCount > 0) {
+ if (start) {
+ buffer.append('1');
+ }
+ buffer.append('/');
+ if (negativeExponentCount > 1) {
+ buffer.append('(');
+ }
+ start = true;
+ for (Map.Entry, Integer> entry : units.entrySet()) {
+ int pow = entry.getValue();
+ if (pow < 0) {
+ formatExponent(entry.getKey(), -pow, 1, !start, buffer);
+ start = false;
+ }
+ }
+ if (negativeExponentCount > 1) {
+ buffer.append(')');
+ }
+ }
+ return PRODUCT_PRECEDENCE;
+ }
+
+ /**
+ * Format the given unit to the given StringBuffer, then return the operator
+ * precedence of the outermost operator in the unit expression that was
+ * formatted. See {@link ConverterFormat} for the constants that define the
+ * various precedence values.
+ * @param unit the unit to be formatted
+ * @param buffer the StringBuffer
to be written to
+ * @return the operator precedence of the outermost operator in the unit
+ * expression that was output
+ */
+ private int formatInternal(Unit> unit, Appendable buffer) throws IOException {
+ if (unit instanceof AnnotatedUnit>) {
+ unit = ((AnnotatedUnit>) unit).getActualUnit();
+ }
+ String symbol = symbolMap.getSymbol(unit);
+ if (symbol != null) {
+ buffer.append(symbol);
+ return NOOP_PRECEDENCE;
+ } else if (unit.getProductUnits() != null) {
+ Map, Integer> productUnit = unit.getProductUnits();
+ int negativeExponentCount = 0;
+ // Write positive exponents first...
+ boolean start = true;
+ for (Map.Entry, Integer> e : productUnit.entrySet()) {
+ int pow = e.getValue();
+ if (pow >= 0) {
+ formatExponent(e.getKey(), pow, 1, !start, buffer);
+ start = false;
+ } else {
+ negativeExponentCount += 1;
+ }
+ }
+ // ..then write negative exponents.
+ if (negativeExponentCount > 0) {
+ if (start) {
+ buffer.append('1');
+ }
+ buffer.append('/');
+ if (negativeExponentCount > 1) {
+ buffer.append('(');
+ }
+ start = true;
+ for (Map.Entry, Integer> e : productUnit.entrySet()) {
+ int pow = e.getValue();
+ if (pow < 0) {
+ formatExponent(e.getKey(), -pow, 1, !start, buffer);
+ start = false;
+ }
+ }
+ if (negativeExponentCount > 1) {
+ buffer.append(')');
+ }
+ }
+ return PRODUCT_PRECEDENCE;
+ } else if (unit instanceof BaseUnit>) {
+ buffer.append(((BaseUnit>) unit).getSymbol());
+ return NOOP_PRECEDENCE;
+ } else if (unit.getSymbol() != null) { // Alternate unit.
+ buffer.append(unit.getSymbol());
+ return NOOP_PRECEDENCE;
+ } else { // A transformed unit or new unit type!
+ UnitConverter converter = null;
+ boolean printSeparator = false;
+ StringBuilder temp = new StringBuilder();
+ int unitPrecedence = NOOP_PRECEDENCE;
+ Unit> parentUnits = unit.toMetric();
+ converter = unit.getConverterToMetric();
+ if (parentUnits.equals(MetricSystem.KILOGRAM)) {
+ // More special-case hackery to work around gram/kilogram
+ // incosistency
+ if (unit.equals(MetricSystem.GRAM)) {
+ buffer.append("g");
+ return NOOP_PRECEDENCE;
+ }
+ parentUnits = MetricSystem.GRAM;
+ converter = unit.getConverterTo((Unit)MetricSystem.GRAM);
+ }
+ unitPrecedence = formatInternal(parentUnits, temp);
+ printSeparator = !parentUnits.equals(Unit.ONE);
+ int result = formatConverter(converter, printSeparator, unitPrecedence, temp);
+ buffer.append(temp);
+ return result;
+ }
+ }
+
+
+ /**
+ * Format the given unit raised to the given fractional power to the
+ * given StringBuffer
.
+ * @param unit Unit the unit to be formatted
+ * @param pow int the numerator of the fractional power
+ * @param root int the denominator of the fractional power
+ * @param continued boolean true
if the converter expression
+ * should begin with an operator, otherwise false
. This will
+ * always be true unless the unit being modified is equal to Unit.ONE.
+ * @param buffer StringBuffer the buffer to append to. No assumptions should
+ * be made about its content.
+ */
+ private void formatExponent(Unit> unit, int pow, int root, boolean continued, Appendable buffer) throws IOException {
+ if (continued) {
+ buffer.append(MIDDLE_DOT);
+ }
+ StringBuffer temp = new StringBuffer();
+ int unitPrecedence = formatInternal(unit, temp);
+
+ if (unitPrecedence < PRODUCT_PRECEDENCE) {
+ temp.insert(0, '('); //$NON-NLS-1$
+ temp.append(')'); //$NON-NLS-1$
+ }
+ buffer.append(temp);
+ if ((root == 1) && (pow == 1)) {
+ // do nothing
+ } else if ((root == 1) && (pow > 1)) {
+ String powStr = Integer.toString(pow);
+ for (int i = 0; i
+ < powStr.length(); i += 1) {
+ char c = powStr.charAt(i);
+ switch (c) {
+ case '0':
+ buffer.append('\u2070'); //$NON-NLS-1$
+ break;
+ case '1':
+ buffer.append('\u00b9'); //$NON-NLS-1$
+ break;
+ case '2':
+ buffer.append('\u00b2'); //$NON-NLS-1$
+ break;
+ case '3':
+ buffer.append('\u00b3'); //$NON-NLS-1$
+ break;
+ case '4':
+ buffer.append('\u2074'); //$NON-NLS-1$
+ break;
+ case '5':
+ buffer.append('\u2075'); //$NON-NLS-1$
+ break;
+ case '6':
+ buffer.append('\u2076'); //$NON-NLS-1$
+ break;
+ case '7':
+ buffer.append('\u2077'); //$NON-NLS-1$
+ break;
+ case '8':
+ buffer.append('\u2078'); //$NON-NLS-1$
+ break;
+ case '9':
+ buffer.append('\u2079'); //$NON-NLS-1$
+ break;
+ }
+ }
+ } else if (root == 1) {
+ buffer.append('^'); //$NON-NLS-1$
+ buffer.append(String.valueOf(pow));
+ } else {
+ buffer.append("^("); //$NON-NLS-1$
+ buffer.append(String.valueOf(pow));
+ buffer.append('/'); //$NON-NLS-1$
+ buffer.append(String.valueOf(root));
+ buffer.append(')'); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Formats the given converter to the given StringBuffer and returns the
+ * operator precedence of the converter's mathematical operation. This is
+ * the default implementation, which supports all built-in UnitConverter
+ * implementations. Note that it recursively calls itself in the case of
+ * a {@link javax.measure.converter.UnitConverter.Compound Compound}
+ * converter.
+ * @param converter the converter to be formatted
+ * @param continued true
if the converter expression should
+ * begin with an operator, otherwise false
.
+ * @param unitPrecedence the operator precedence of the operation expressed
+ * by the unit being modified by the given converter.
+ * @param buffer the StringBuffer
to append to.
+ * @return the operator precedence of the given UnitConverter
+ */
+ private int formatConverter(UnitConverter converter,
+ boolean continued,
+ int unitPrecedence,
+ StringBuilder buffer) {
+ ParsePrefix prefix = symbolMap.getPrefixObject(converter);
+ if ((prefix != null) && (unitPrecedence == NOOP_PRECEDENCE)) {
+ buffer.insert(0, symbolMap.getSymbol(prefix));
+ return NOOP_PRECEDENCE;
+ } else if (converter instanceof javax.measure.unit.converter.AddConverter) {
+ if (unitPrecedence < ADDITION_PRECEDENCE) {
+ buffer.insert(0, '(');
+ buffer.append(')');
+ }
+ double offset = ((javax.measure.unit.converter.AddConverter) converter).getOffset();
+ if (offset < 0) {
+ buffer.append("-");
+ offset = -offset;
+ } else if (continued) {
+ buffer.append("+");
+ }
+ long lOffset = (long) offset;
+ if (lOffset == offset) {
+ buffer.append(lOffset);
+ } else {
+ buffer.append(offset);
+ }
+ return ADDITION_PRECEDENCE;
+ } else if (converter instanceof javax.measure.unit.converter.LogConverter) {
+ double base = ((javax.measure.unit.converter.LogConverter) converter).getBase();
+ StringBuffer expr = new StringBuffer();
+ if (base == StrictMath.E) {
+ expr.append("ln");
+ } else {
+ expr.append("log");
+ if (base != 10) {
+ expr.append((int) base);
+ }
+ }
+ expr.append("(");
+ buffer.insert(0, expr);
+ buffer.append(")");
+ return EXPONENT_PRECEDENCE;
+ } else if (converter instanceof javax.measure.unit.converter.ExpConverter) {
+ if (unitPrecedence < EXPONENT_PRECEDENCE) {
+ buffer.insert(0, '(');
+ buffer.append(')');
+ }
+ StringBuffer expr = new StringBuffer();
+ double base = ((javax.measure.unit.converter.ExpConverter) converter).getBase();
+ if (base == StrictMath.E) {
+ expr.append('e');
+ } else {
+ expr.append((int) base);
+ }
+ expr.append('^');
+ buffer.insert(0, expr);
+ return EXPONENT_PRECEDENCE;
+ } else if (converter instanceof javax.measure.unit.converter.MultiplyConverter) {
+ if (unitPrecedence < PRODUCT_PRECEDENCE) {
+ buffer.insert(0, '(');
+ buffer.append(')');
+ }
+ if (continued) {
+ buffer.append(MIDDLE_DOT);
+ }
+ double factor = ((javax.measure.unit.converter.MultiplyConverter) converter).getFactor();
+ long lFactor = (long) factor;
+ if (lFactor == factor) {
+ buffer.append(lFactor);
+ } else {
+ buffer.append(factor);
+ }
+ return PRODUCT_PRECEDENCE;
+ } else if (converter instanceof javax.measure.unit.converter.RationalConverter) {
+ if (unitPrecedence < PRODUCT_PRECEDENCE) {
+ buffer.insert(0, '(');
+ buffer.append(')');
+ }
+ javax.measure.unit.converter.RationalConverter rationalConverter = (javax.measure.unit.converter.RationalConverter) converter;
+ if (!rationalConverter.getDividend().equals(BigInteger.ONE)) {
+ if (continued) {
+ buffer.append(MIDDLE_DOT);
+ }
+ buffer.append(rationalConverter.getDividend());
+ }
+ if (!rationalConverter.getDivisor().equals(BigInteger.ONE)) {
+ buffer.append('/');
+ buffer.append(rationalConverter.getDivisor());
+ }
+ return PRODUCT_PRECEDENCE;
+ } else {
+ throw new IllegalArgumentException("Unable to format the given UnitConverter: " + converter.getClass());
+ }
+ }
+}
diff --git a/src/main/java/javax/measure/unit/format/ParseException.java b/src/main/java/javax/measure/unit/format/ParseException.java
new file mode 100644
index 0000000..c1c62e9
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/ParseException.java
@@ -0,0 +1,197 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 5.0 */
+/* JavaCCOptions:KEEP_LINE_COL=null */
+package javax.measure.unit.format;
+
+/**
+ * This exception is thrown when parse errors are encountered.
+ * You can explicitly create objects of this exception type by
+ * calling the method generateParseException in the generated
+ * parser.
+ *
+ * You can modify this class to customize your error reporting
+ * mechanisms so long as you retain the public fields.
+ *
+ * @author Jean-Marie Dautelle
+ * @author Werner Keil
+ * @version 1.0 ($Revision: 199 $), $Date: 2010-02-24 19:57:58 +0100 (Mi, 24 Feb 2010) $
+ */
+class ParseException extends RuntimeException {
+
+ /**
+ * The version identifier for this Serializable class.
+ * Increment only if the serialized form of the
+ * class changes.
+ */
+ private static final long serialVersionUID = 2932151235799168061L;
+
+ /**
+ * This constructor is used by the method "generateParseException"
+ * in the generated parser. Calling this constructor generates
+ * a new object of this type with the fields "currentToken",
+ * "expectedTokenSequences", and "tokenImage" set.
+ */
+ public ParseException(Token currentTokenVal,
+ int[][] expectedTokenSequencesVal,
+ String[] tokenImageVal
+ )
+ {
+ super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal));
+ currentToken = currentTokenVal;
+ expectedTokenSequences = expectedTokenSequencesVal;
+ tokenImage = tokenImageVal;
+ }
+
+ /**
+ * The following constructors are for use by you for whatever
+ * purpose you can think of. Constructing the exception in this
+ * manner makes the exception behave in the normal way - i.e., as
+ * documented in the class "Throwable". The fields "errorToken",
+ * "expectedTokenSequences", and "tokenImage" do not contain
+ * relevant information. The JavaCC generated code does not use
+ * these constructors.
+ */
+
+ public ParseException() {
+ super();
+ }
+
+ /** Constructor with message. */
+ public ParseException(String message) {
+ super(message);
+ }
+
+
+ /**
+ * This is the last token that has been consumed successfully. If
+ * this object has been created due to a parse error, the token
+ * followng this token will (therefore) be the first error token.
+ */
+ public Token currentToken;
+
+ /**
+ * Each entry in this array is an array of integers. Each array
+ * of integers represents a sequence of tokens (by their ordinal
+ * values) that is expected at this point of the parse.
+ */
+ public int[][] expectedTokenSequences;
+
+ /**
+ * This is a reference to the "tokenImage" array of the generated
+ * parser within which the parse error occurred. This array is
+ * defined in the generated ...Constants interface.
+ */
+ public String[] tokenImage;
+
+ /**
+ * It uses "currentToken" and "expectedTokenSequences" to generate a parse
+ * error message and returns it. If this object has been created
+ * due to a parse error, and you do not catch it (it gets thrown
+ * from the parser) the correct error message
+ * gets displayed.
+ */
+ private static String initialise(Token currentToken,
+ int[][] expectedTokenSequences,
+ String[] tokenImage) {
+ String eol = System.getProperty("line.separator", "\n");
+ StringBuffer expected = new StringBuffer();
+ int maxSize = 0;
+ for (int i = 0; i < expectedTokenSequences.length; i++) {
+ if (maxSize < expectedTokenSequences[i].length) {
+ maxSize = expectedTokenSequences[i].length;
+ }
+ for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+ expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' ');
+ }
+ if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) {
+ expected.append("...");
+ }
+ expected.append(eol).append(" ");
+ }
+ String retval = "Encountered \"";
+ Token tok = currentToken.next;
+ for (int i = 0; i < maxSize; i++) {
+ if (i != 0) retval += " ";
+ if (tok.kind == 0) {
+ retval += tokenImage[0];
+ break;
+ }
+ retval += " " + tokenImage[tok.kind];
+ retval += " \"";
+ retval += add_escapes(tok.image);
+ retval += " \"";
+ tok = tok.next;
+ }
+ retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn;
+ retval += "." + eol;
+ if (expectedTokenSequences.length == 1) {
+ retval += "Was expecting:" + eol + " ";
+ } else {
+ retval += "Was expecting one of:" + eol + " ";
+ }
+ retval += expected.toString();
+ return retval;
+ }
+
+ /**
+ * The end of line string for this machine.
+ */
+ protected String eol = System.getProperty("line.separator", "\n");
+
+ /**
+ * Used to convert raw characters to their escaped version
+ * when these raw version cannot be used as part of an ASCII
+ * string literal.
+ */
+ static String add_escapes(String str) {
+ StringBuffer retval = new StringBuffer();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i))
+ {
+ case 0 :
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+}
+/* JavaCC - OriginalChecksum=c67b0f8ee6c642900399352b33f90efd (do not edit this line) */
diff --git a/src/main/java/javax/measure/unit/format/ParsePrefix.java b/src/main/java/javax/measure/unit/format/ParsePrefix.java
new file mode 100644
index 0000000..58106d6
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/ParsePrefix.java
@@ -0,0 +1,61 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+package javax.measure.unit.format;
+
+import javax.measure.unit.converter.RationalConverter;
+import java.math.BigInteger;
+import javax.measure.unit.UnitConverter;
+
+
+/**
+ * This class represents the prefixes recognized when parsing/formatting.
+ *
+ * @version 1.0
+ */
+enum ParsePrefix {
+
+ YOTTA(new RationalConverter(BigInteger.TEN.pow(24), BigInteger.ONE)),
+ ZETTA(new RationalConverter(BigInteger.TEN.pow(21), BigInteger.ONE)),
+ EXA(new RationalConverter(BigInteger.TEN.pow(18), BigInteger.ONE)),
+ PETA(new RationalConverter(BigInteger.TEN.pow(15), BigInteger.ONE)),
+ TERA(new RationalConverter(BigInteger.TEN.pow(12), BigInteger.ONE)),
+ GIGA(new RationalConverter(BigInteger.TEN.pow(9), BigInteger.ONE)),
+ MEGA(new RationalConverter(BigInteger.TEN.pow(6), BigInteger.ONE)),
+ KILO(new RationalConverter(BigInteger.TEN.pow(3), BigInteger.ONE)),
+ HECTO(new RationalConverter(BigInteger.TEN.pow(2), BigInteger.ONE)),
+ DEKA(new RationalConverter(BigInteger.TEN.pow(1), BigInteger.ONE)),
+ DECI(new RationalConverter( BigInteger.ONE, BigInteger.TEN.pow(1))),
+ CENTI(new RationalConverter( BigInteger.ONE, BigInteger.TEN.pow(2))),
+ MILLI(new RationalConverter( BigInteger.ONE, BigInteger.TEN.pow(3))),
+ MICRO(new RationalConverter( BigInteger.ONE, BigInteger.TEN.pow(6))),
+ NANO(new RationalConverter( BigInteger.ONE, BigInteger.TEN.pow(9))),
+ PICO(new RationalConverter( BigInteger.ONE, BigInteger.TEN.pow(12))),
+ FEMTO(new RationalConverter( BigInteger.ONE, BigInteger.TEN.pow(15))),
+ ATTO(new RationalConverter( BigInteger.ONE, BigInteger.TEN.pow(18))),
+ ZEPTO(new RationalConverter( BigInteger.ONE, BigInteger.TEN.pow(21))),
+ YOCTO(new RationalConverter( BigInteger.ONE, BigInteger.TEN.pow(24)));
+
+ private final UnitConverter converter;
+
+ /**
+ * Creates a new prefix.
+ *
+ * @param converter the associated unit converter.
+ */
+ ParsePrefix (UnitConverter converter) {
+ this.converter = converter;
+ }
+
+ /**
+ * Returns the corresponding unit converter.
+ *
+ * @return the unit converter.
+ */
+ public UnitConverter getConverter() {
+ return converter;
+ }
+}
diff --git a/src/main/java/javax/measure/unit/format/SimpleCharStream.java b/src/main/java/javax/measure/unit/format/SimpleCharStream.java
new file mode 100644
index 0000000..3b02066
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/SimpleCharStream.java
@@ -0,0 +1,477 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+/* Generated By:JavaCC: Do not edit this line. SimpleCharStream.java Version 5.0 */
+/* JavaCCOptions:STATIC=false,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+package javax.measure.unit.format;
+
+/**
+ * An implementation of interface CharStream, where the stream is assumed to
+ * contain only ASCII characters (without unicode processing).
+ */
+
+class SimpleCharStream
+{
+/** Whether parser is static. */
+ static final boolean staticFlag = false;
+ int bufsize;
+ int available;
+ int tokenBegin;
+/** Position in buffer. */
+ private int bufpos = -1;
+ protected int bufline[];
+ protected int bufcolumn[];
+
+ protected int column = 0;
+ protected int line = 1;
+
+ protected boolean prevCharIsCR = false;
+ protected boolean prevCharIsLF = false;
+
+ protected java.io.Reader inputStream;
+
+ protected char[] buffer;
+ protected int maxNextCharInd = 0;
+ protected int inBuf = 0;
+ protected int tabSize = 8;
+
+ protected void setTabSize(int i) { tabSize = i; }
+ protected int getTabSize(int i) { return tabSize; }
+
+
+ protected void ExpandBuff(boolean wrapAround)
+ {
+ char[] newbuffer = new char[bufsize + 2048];
+ int newbufline[] = new int[bufsize + 2048];
+ int newbufcolumn[] = new int[bufsize + 2048];
+
+ try
+ {
+ if (wrapAround)
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ System.arraycopy(buffer, 0, newbuffer, bufsize - tokenBegin, bufpos);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ System.arraycopy(bufline, 0, newbufline, bufsize - tokenBegin, bufpos);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ System.arraycopy(bufcolumn, 0, newbufcolumn, bufsize - tokenBegin, bufpos);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = (bufpos += (bufsize - tokenBegin));
+ }
+ else
+ {
+ System.arraycopy(buffer, tokenBegin, newbuffer, 0, bufsize - tokenBegin);
+ buffer = newbuffer;
+
+ System.arraycopy(bufline, tokenBegin, newbufline, 0, bufsize - tokenBegin);
+ bufline = newbufline;
+
+ System.arraycopy(bufcolumn, tokenBegin, newbufcolumn, 0, bufsize - tokenBegin);
+ bufcolumn = newbufcolumn;
+
+ maxNextCharInd = (bufpos -= tokenBegin);
+ }
+ }
+ catch (Throwable t)
+ {
+ throw new Error(t.getMessage());
+ }
+
+
+ bufsize += 2048;
+ available = bufsize;
+ tokenBegin = 0;
+ }
+
+ protected void FillBuff() throws java.io.IOException
+ {
+ if (maxNextCharInd == available)
+ {
+ if (available == bufsize)
+ {
+ if (tokenBegin > 2048)
+ {
+ bufpos = maxNextCharInd = 0;
+ available = tokenBegin;
+ }
+ else if (tokenBegin < 0)
+ bufpos = maxNextCharInd = 0;
+ else
+ ExpandBuff(false);
+ }
+ else if (available > tokenBegin)
+ available = bufsize;
+ else if ((tokenBegin - available) < 2048)
+ ExpandBuff(true);
+ else
+ available = tokenBegin;
+ }
+
+ int i;
+ try {
+ if ((i = inputStream.read(buffer, maxNextCharInd, available - maxNextCharInd)) == -1)
+ {
+ inputStream.close();
+ throw new java.io.IOException();
+ }
+ else
+ maxNextCharInd += i;
+ return;
+ }
+ catch(java.io.IOException e) {
+ --bufpos;
+ backup(0);
+ if (tokenBegin == -1)
+ tokenBegin = bufpos;
+ throw e;
+ }
+ }
+
+/** Start. */
+ protected char beginToken() throws java.io.IOException
+ {
+ tokenBegin = -1;
+ char c = readChar();
+ tokenBegin = bufpos;
+
+ return c;
+ }
+
+ protected void UpdateLineColumn(char c)
+ {
+ column++;
+
+ if (prevCharIsLF)
+ {
+ prevCharIsLF = false;
+ line += (column = 1);
+ }
+ else if (prevCharIsCR)
+ {
+ prevCharIsCR = false;
+ if (c == '\n')
+ {
+ prevCharIsLF = true;
+ }
+ else
+ line += (column = 1);
+ }
+
+ switch (c)
+ {
+ case '\r' :
+ prevCharIsCR = true;
+ break;
+ case '\n' :
+ prevCharIsLF = true;
+ break;
+ case '\t' :
+ column--;
+ column += (tabSize - (column % tabSize));
+ break;
+ default :
+ break;
+ }
+
+ bufline[bufpos] = line;
+ bufcolumn[bufpos] = column;
+ }
+
+/** Read a character. */
+ protected char readChar() throws java.io.IOException
+ {
+ if (inBuf > 0)
+ {
+ --inBuf;
+
+ if (++bufpos == bufsize)
+ bufpos = 0;
+
+ return buffer[bufpos];
+ }
+
+ if (++bufpos >= maxNextCharInd)
+ FillBuff();
+
+ char c = buffer[bufpos];
+
+ UpdateLineColumn(c);
+ return c;
+ }
+
+ @Deprecated
+ /**
+ * @deprecated
+ * @see #getEndColumn
+ */
+
+ protected int getColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ @Deprecated
+ /**
+ * @deprecated
+ * @see #getEndLine
+ */
+
+ protected int getLine() {
+ return bufline[bufpos];
+ }
+
+ /** Get token end column number. */
+ protected int getEndColumn() {
+ return bufcolumn[bufpos];
+ }
+
+ /** Get token end line number. */
+ protected int getEndLine() {
+ return bufline[bufpos];
+ }
+
+ /** Get token beginning column number. */
+ protected int getBeginColumn() {
+ return bufcolumn[tokenBegin];
+ }
+
+ /** Get token beginning line number. */
+ protected int getBeginLine() {
+ return bufline[tokenBegin];
+ }
+
+/** Backup a number of characters. */
+ protected void backup(int amount) {
+
+ inBuf += amount;
+ if ((bufpos -= amount) < 0)
+ bufpos += bufsize;
+ }
+
+ /** Constructor. */
+ protected SimpleCharStream(java.io.Reader dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+
+ /** Constructor. */
+ protected SimpleCharStream(java.io.Reader dstream, int startline,
+ int startcolumn)
+ {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ /** Constructor. */
+ protected SimpleCharStream(java.io.Reader dstream)
+ {
+ this(dstream, 1, 1, 4096);
+ }
+
+ /** Reinitialise. */
+ void ReInit(java.io.Reader dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ inputStream = dstream;
+ line = startline;
+ column = startcolumn - 1;
+
+ if (buffer == null || buffersize != buffer.length)
+ {
+ available = bufsize = buffersize;
+ buffer = new char[buffersize];
+ bufline = new int[buffersize];
+ bufcolumn = new int[buffersize];
+ }
+ prevCharIsLF = prevCharIsCR = false;
+ tokenBegin = inBuf = maxNextCharInd = 0;
+ bufpos = -1;
+ }
+
+ /** Reinitialise. */
+ protected void ReInit(java.io.Reader dstream, int startline,
+ int startcolumn)
+ {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+
+ /** Reinitialise. */
+ protected void ReInit(java.io.Reader dstream)
+ {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ /** Constructor. */
+ protected SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+ {
+ this(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+ }
+
+ /** Constructor. */
+ protected SimpleCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ /** Constructor. */
+ protected SimpleCharStream(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn) throws java.io.UnsupportedEncodingException
+ {
+ this(dstream, encoding, startline, startcolumn, 4096);
+ }
+
+ /** Constructor. */
+ protected SimpleCharStream(java.io.InputStream dstream, int startline,
+ int startcolumn)
+ {
+ this(dstream, startline, startcolumn, 4096);
+ }
+
+ /** Constructor. */
+ protected SimpleCharStream(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+ {
+ this(dstream, encoding, 1, 1, 4096);
+ }
+
+ /** Constructor. */
+ protected SimpleCharStream(java.io.InputStream dstream)
+ {
+ this(dstream, 1, 1, 4096);
+ }
+
+ /** Reinitialise. */
+ protected void ReInit(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn, int buffersize) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(encoding == null ? new java.io.InputStreamReader(dstream) : new java.io.InputStreamReader(dstream, encoding), startline, startcolumn, buffersize);
+ }
+
+ /** Reinitialise. */
+ protected void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn, int buffersize)
+ {
+ ReInit(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize);
+ }
+
+ /** Reinitialise. */
+ protected void ReInit(java.io.InputStream dstream, String encoding) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(dstream, encoding, 1, 1, 4096);
+ }
+
+ /** Reinitialise. */
+ protected void ReInit(java.io.InputStream dstream)
+ {
+ ReInit(dstream, 1, 1, 4096);
+ }
+ /** Reinitialise. */
+ protected void ReInit(java.io.InputStream dstream, String encoding, int startline,
+ int startcolumn) throws java.io.UnsupportedEncodingException
+ {
+ ReInit(dstream, encoding, startline, startcolumn, 4096);
+ }
+ /** Reinitialise. */
+ protected void ReInit(java.io.InputStream dstream, int startline,
+ int startcolumn)
+ {
+ ReInit(dstream, startline, startcolumn, 4096);
+ }
+ /** Get token literal value. */
+ protected String getImage()
+ {
+ if (bufpos >= tokenBegin)
+ return new String(buffer, tokenBegin, bufpos - tokenBegin + 1);
+ else
+ return new String(buffer, tokenBegin, bufsize - tokenBegin) +
+ new String(buffer, 0, bufpos + 1);
+ }
+
+ /** Get the suffix. */
+ protected char[] getSuffix(int len)
+ {
+ char[] ret = new char[len];
+
+ if ((bufpos + 1) >= len)
+ System.arraycopy(buffer, bufpos - len + 1, ret, 0, len);
+ else
+ {
+ System.arraycopy(buffer, bufsize - (len - bufpos - 1), ret, 0,
+ len - bufpos - 1);
+ System.arraycopy(buffer, 0, ret, len - bufpos - 1, bufpos + 1);
+ }
+
+ return ret;
+ }
+
+ /** Reset buffer when finished. */
+ protected void Done()
+ {
+ buffer = null;
+ bufline = null;
+ bufcolumn = null;
+ }
+
+ /**
+ * Method to adjust line and column numbers for the start of a token.
+ */
+ protected void adjustBeginLineColumn(int newLine, int newCol)
+ {
+ int start = tokenBegin;
+ int len;
+
+ if (bufpos >= tokenBegin)
+ {
+ len = bufpos - tokenBegin + inBuf + 1;
+ }
+ else
+ {
+ len = bufsize - tokenBegin + bufpos + 1 + inBuf;
+ }
+
+ int i = 0, j = 0, k = 0;
+ int nextColDiff = 0, columnDiff = 0;
+
+ while (i < len && bufline[j = start % bufsize] == bufline[k = ++start % bufsize])
+ {
+ bufline[j] = newLine;
+ nextColDiff = columnDiff + bufcolumn[k] - bufcolumn[j];
+ bufcolumn[j] = newCol + columnDiff;
+ columnDiff = nextColDiff;
+ i++;
+ }
+
+ if (i < len)
+ {
+ bufline[j] = newLine++;
+ bufcolumn[j] = newCol + columnDiff;
+
+ while (i++ < len)
+ {
+ if (bufline[j = start % bufsize] != bufline[++start % bufsize])
+ bufline[j] = newLine++;
+ else
+ bufline[j] = newLine;
+ }
+ }
+
+ line = bufline[j];
+ column = bufcolumn[j];
+ }
+
+}
+/* JavaCC - OriginalChecksum=ec4e178f3ccf05ea2ca32d15e09312ca (do not edit this line) */
diff --git a/src/main/java/javax/measure/unit/format/SymbolMapImpl.java b/src/main/java/javax/measure/unit/format/SymbolMapImpl.java
new file mode 100644
index 0000000..6cdcacb
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/SymbolMapImpl.java
@@ -0,0 +1,184 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+package javax.measure.unit.format;
+
+import java.lang.reflect.Field;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.ResourceBundle;
+import javax.measure.unit.Unit;
+import javax.measure.unit.UnitConverter;
+import javax.measure.unit.UnitFormat;
+
+
+/**
+ * This class holds the default implementation of the SymbolMap
+ * interface.
+ *
+ * No attempt is made to verify the uniqueness of the mappings.
+ *
+ * Mappings are read from a ResourceBundle
, the keys
+ * of which should consist of a fully-qualified class name, followed
+ * by a dot ('.'), and then the name of a static field belonging
+ * to that class, followed optionally by another dot and a number.
+ * If the trailing dot and number are not present, the value
+ * associated with the key is treated as a
+ * {@linkplain SymbolMap#label(javax.measure.Unit, String) label},
+ * otherwise if the trailing dot and number are present, the value
+ * is treated as an {@linkplain SymbolMap#alias(javax.measure.Unit,String) alias}.
+ * Aliases map from String to Unit only, whereas labels map in both
+ * directions. A given unit may have any number of aliases, but may
+ * have only one label.
+ *
+ * @author Eric Russell
+ * @author Werner Keil
+ * @version 1.4 ($Revision: 180 $), $Date: 2010-02-23 16:11:17 +0100 (Di, 23 Feb 2010) $
+ */
+class SymbolMapImpl implements UnitFormat.SymbolMap {
+
+ private final Map> symbolToUnit;
+ private final Map, String> unitToSymbol;
+ private final Map symbolToPrefix;
+ private final Map prefixToSymbol;
+ private final Map converterToPrefix;
+
+ /**
+ * Creates an empty mapping.
+ */
+ public SymbolMapImpl () {
+ symbolToUnit = new HashMap>();
+ unitToSymbol = new HashMap, String>();
+ symbolToPrefix = new HashMap();
+ prefixToSymbol = new HashMap();
+ converterToPrefix = new HashMap();
+ }
+
+ /**
+ * Creates a symbol map from the specified resource bundle,
+ *
+ * @param rb the resource bundle.
+ */
+ public SymbolMapImpl (ResourceBundle rb) {
+ this();
+ for (Enumeration i = rb.getKeys(); i.hasMoreElements();) {
+ String fqn = i.nextElement();
+ String symbol = rb.getString(fqn);
+ boolean isAlias = false;
+ int lastDot = fqn.lastIndexOf('.');
+ String className = fqn.substring(0, lastDot);
+ String fieldName = fqn.substring(lastDot+1, fqn.length());
+ if (Character.isDigit(fieldName.charAt(0))) {
+ isAlias = true;
+ fqn = className;
+ lastDot = fqn.lastIndexOf('.');
+ className = fqn.substring(0, lastDot);
+ fieldName = fqn.substring(lastDot+1, fqn.length());
+ }
+ try {
+ Class> c = Class.forName(className);
+ Field field = c.getField(fieldName);
+ Object value = field.get(null);
+ if (value instanceof Unit>) {
+ if (isAlias) {
+ alias((Unit>)value, symbol);
+ } else {
+ label((Unit>)value, symbol);
+ }
+ } else if (value instanceof ParsePrefix) {
+ label((ParsePrefix)value, symbol);
+ } else {
+ throw new ClassCastException("unable to cast "+value+" to Unit or Prefix");
+ }
+ } catch (Exception e) {
+ System.err.println("ERROR reading Unit names: " + e.toString());
+ }
+ }
+ }
+
+ public void label (Unit> unit, String symbol) {
+ symbolToUnit.put(symbol, unit);
+ unitToSymbol.put(unit, symbol);
+ }
+
+ public void alias (Unit> unit, String symbol) {
+ symbolToUnit.put(symbol, unit);
+ }
+
+ public void prefix (UnitConverter cvtr, String symbol) {
+ throw new UnsupportedOperationException("Prefixes are not modifiable");
+ }
+
+ public Unit> getUnit (String symbol) {
+ return symbolToUnit.get(symbol);
+ }
+
+ public String getSymbol (Unit> unit) {
+ return unitToSymbol.get(unit);
+ }
+
+ public String getPrefix (UnitConverter cvtr) {
+ ParsePrefix prefix = getPrefixObject(cvtr);
+ if (prefix == null) return null;
+ return prefixToSymbol.get(prefix);
+ }
+
+ public UnitConverter getConverter(String prefix) {
+ ParsePrefix prefixObject = (ParsePrefix) symbolToPrefix.get(prefix);
+ if (prefixObject == null) return null;
+ return prefixObject.getConverter();
+ }
+
+ /**
+ * Attaches a label to the specified prefix. For example:[code]
+ * symbolMap.label(Prefix.GIGA, "G");
+ * symbolMap.label(Prefix.MICRO, "µ");
+ * [/code]
+ */
+ void label(ParsePrefix prefix, String symbol) {
+ symbolToPrefix.put(symbol, prefix);
+ prefixToSymbol.put(prefix, symbol);
+ converterToPrefix.put(prefix.getConverter(), prefix);
+ }
+
+ /**
+ * Returns the prefix (if any) for the specified symbol.
+ *
+ * @param symbol the unit symbol.
+ * @return the corresponding prefix or null
if none.
+ */
+ ParsePrefix getPrefix (String symbol) {
+ for (Iterator i = symbolToPrefix.keySet().iterator(); i.hasNext(); ) {
+ String pfSymbol = i.next();
+ if (symbol.startsWith(pfSymbol)) {
+ return (ParsePrefix)symbolToPrefix.get(pfSymbol);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the prefix for the specified converter.
+ *
+ * @param converter the unit converter.
+ * @return the corresponding prefix or null
if none.
+ */
+ ParsePrefix getPrefixObject (UnitConverter converter) {
+ return converterToPrefix.get(converter);
+ }
+
+ /**
+ * Returns the symbol for the specified prefix.
+ *
+ * @param prefix the prefix.
+ * @return the corresponding symbol or null
if none.
+ */
+ String getSymbol (ParsePrefix prefix) {
+ return prefixToSymbol.get(prefix);
+ }
+}
diff --git a/src/main/java/javax/measure/unit/format/Token.java b/src/main/java/javax/measure/unit/format/Token.java
new file mode 100644
index 0000000..8de0622
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/Token.java
@@ -0,0 +1,137 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+/* Generated By:JavaCC: Do not edit this line. Token.java Version 5.0 */
+/* JavaCCOptions:TOKEN_EXTENDS=,KEEP_LINE_COL=null,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
+package javax.measure.unit.format;
+
+/**
+ * Describes the input token stream.
+ */
+
+class Token implements java.io.Serializable {
+
+ /**
+ * The version identifier for this Serializable class.
+ * Increment only if the serialized form of the
+ * class changes.
+ */
+ private static final long serialVersionUID = 2188279658897600591L;
+
+ /**
+ * An integer that describes the kind of this token. This numbering
+ * system is determined by JavaCCParser, and a table of these numbers is
+ * stored in the file ...Constants.java.
+ */
+ int kind;
+
+ /** The line number of the first character of this Token. */
+ int beginLine;
+ /** The column number of the first character of this Token. */
+ int beginColumn;
+ /** The line number of the last character of this Token. */
+ int endLine;
+ /** The column number of the last character of this Token. */
+ int endColumn;
+
+ /**
+ * The string image of the token.
+ */
+ String image;
+
+ /**
+ * A reference to the next regular (non-special) token from the input
+ * stream. If this is the last token from the input stream, or if the
+ * token manager has not read tokens beyond this one, this field is
+ * set to null. This is true only if this token is also a regular
+ * token. Otherwise, see below for a description of the contents of
+ * this field.
+ */
+ Token next;
+
+ /**
+ * This field is used to access special tokens that occur prior to this
+ * token, but after the immediately preceding regular (non-special) token.
+ * If there are no such special tokens, this field is set to null.
+ * When there are more than one such special token, this field refers
+ * to the last of these special tokens, which in turn refers to the next
+ * previous special token through its specialToken field, and so on
+ * until the first special token (whose specialToken field is null).
+ * The next fields of special tokens refer to other special tokens that
+ * immediately follow it (without an intervening regular token). If there
+ * is no such token, this field is null.
+ */
+ Token specialToken;
+
+ /**
+ * An optional attribute value of the Token.
+ * Tokens which are not used as syntactic sugar will often contain
+ * meaningful values that will be used later on by the compiler or
+ * interpreter. This attribute value is often different from the image.
+ * Any subclass of Token that actually wants to return a non-null value can
+ * override this method as appropriate.
+ */
+ public Object getValue() {
+ return null;
+ }
+
+ /**
+ * No-argument constructor
+ */
+ public Token() {}
+
+ /**
+ * Constructs a new token for the specified Image.
+ */
+ public Token(int kind)
+ {
+ this(kind, null);
+ }
+
+ /**
+ * Constructs a new token for the specified Image and Kind.
+ */
+ public Token(int kind, String image)
+ {
+ this.kind = kind;
+ this.image = image;
+ }
+
+ /**
+ * Returns the image.
+ */
+ public String toString()
+ {
+ return image;
+ }
+
+ /**
+ * Returns a new Token object, by default. However, if you want, you
+ * can create and return subclass objects based on the value of ofKind.
+ * Simply add the cases to the switch for all those special cases.
+ * For example, if you have a subclass of Token called IDToken that
+ * you want to create if ofKind is ID, simply add something like :
+ *
+ * case MyParserConstants.ID : return new IDToken(ofKind, image);
+ *
+ * to the following switch statement. Then you can cast matchedToken
+ * variable to the appropriate type and use sit in your lexical actions.
+ */
+ public static Token newToken(int ofKind, String image)
+ {
+ switch(ofKind)
+ {
+ default : return new Token(ofKind, image);
+ }
+ }
+
+ public static Token newToken(int ofKind)
+ {
+ return newToken(ofKind, null);
+ }
+
+}
+/* JavaCC - OriginalChecksum=08d08345e10cca30522247698d4478e6 (do not edit this line) */
diff --git a/src/main/java/javax/measure/unit/format/TokenMgrError.java b/src/main/java/javax/measure/unit/format/TokenMgrError.java
new file mode 100644
index 0000000..f8a998d
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/TokenMgrError.java
@@ -0,0 +1,154 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+/* Generated By:JavaCC: Do not edit this line. TokenMgrError.java Version 5.0 */
+/* JavaCCOptions: */
+package javax.measure.unit.format;
+
+/** Token Manager Error. */
+class TokenMgrError extends Error
+{
+
+ /**
+ * The version identifier for this Serializable class.
+ * Increment only if the serialized form of the
+ * class changes.
+ */
+ private static final long serialVersionUID = -3348968864772188432L;
+
+ /*
+ * Ordinals for various reasons why an Error of this type can be thrown.
+ */
+
+
+ /**
+ * Lexical error occurred.
+ */
+ static final int LEXICAL_ERROR = 0;
+
+ /**
+ * An attempt was made to create a second instance of a static token manager.
+ */
+ static final int STATIC_LEXER_ERROR = 1;
+
+ /**
+ * Tried to change to an invalid lexical state.
+ */
+ static final int INVALID_LEXICAL_STATE = 2;
+
+ /**
+ * Detected (and bailed out of) an infinite loop in the token manager.
+ */
+ static final int LOOP_DETECTED = 3;
+
+ /**
+ * Indicates the reason why the exception is thrown. It will have
+ * one of the above 4 values.
+ */
+ int errorCode;
+
+ /**
+ * Replaces unprintable characters by their escaped (or unicode escaped)
+ * equivalents in the given string
+ */
+ protected static final String addEscapes(String str) {
+ StringBuffer retval = new StringBuffer();
+ char ch;
+ for (int i = 0; i < str.length(); i++) {
+ switch (str.charAt(i))
+ {
+ case 0 :
+ continue;
+ case '\b':
+ retval.append("\\b");
+ continue;
+ case '\t':
+ retval.append("\\t");
+ continue;
+ case '\n':
+ retval.append("\\n");
+ continue;
+ case '\f':
+ retval.append("\\f");
+ continue;
+ case '\r':
+ retval.append("\\r");
+ continue;
+ case '\"':
+ retval.append("\\\"");
+ continue;
+ case '\'':
+ retval.append("\\\'");
+ continue;
+ case '\\':
+ retval.append("\\\\");
+ continue;
+ default:
+ if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) {
+ String s = "0000" + Integer.toString(ch, 16);
+ retval.append("\\u" + s.substring(s.length() - 4, s.length()));
+ } else {
+ retval.append(ch);
+ }
+ continue;
+ }
+ }
+ return retval.toString();
+ }
+
+ /**
+ * Returns a detailed message for the Error when it is thrown by the
+ * token manager to indicate a lexical error.
+ * Parameters :
+ * EOFSeen : indicates if EOF caused the lexical error
+ * curLexState : lexical state in which this error occurred
+ * errorLine : line number when the error occurred
+ * errorColumn : column number when the error occurred
+ * errorAfter : prefix that was seen before this error occurred
+ * curchar : the offending character
+ * Note: You can customize the lexical error message by modifying this method.
+ */
+ protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) {
+ return("Lexical error at line " +
+ errorLine + ", column " +
+ errorColumn + ". Encountered: " +
+ (EOFSeen ? " " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int)curChar + "), ") +
+ "after : \"" + addEscapes(errorAfter) + "\"");
+ }
+
+ /**
+ * You can also modify the body of this method to customize your error messages.
+ * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not
+ * of end-users concern, so you can return something like :
+ *
+ * "Internal Error : Please file a bug report .... "
+ *
+ * from this method for such cases in the release version of your parser.
+ */
+ public String getMessage() {
+ return super.getMessage();
+ }
+
+ /*
+ * Constructors of various flavors follow.
+ */
+
+ /** No arg constructor. */
+ public TokenMgrError() {
+ }
+
+ /** Constructor with message and reason. */
+ public TokenMgrError(String message, int reason) {
+ super(message);
+ errorCode = reason;
+ }
+
+ /** Full Constructor. */
+ public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) {
+ this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason);
+ }
+}
+/* JavaCC - OriginalChecksum=8a6e5be586cca28053ad55584e013006 (do not edit this line) */
diff --git a/src/main/java/javax/measure/unit/format/UnitParser.java b/src/main/java/javax/measure/unit/format/UnitParser.java
new file mode 100644
index 0000000..c6a5d1b
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/UnitParser.java
@@ -0,0 +1,781 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+/* Generated By:JavaCC: Do not edit this line. UnitParser.java */
+package javax.measure.unit.format;
+
+import javax.measure.unit.Unit;
+import javax.measure.unit.converter.LogConverter;
+
+/** */
+class UnitParser implements UnitParserConstants {
+
+ private static class Exponent {
+ final int pow;
+ final int root;
+ public Exponent (int pow, int root) {
+ this.pow = pow;
+ this.root = root;
+ }
+ }
+
+ private SymbolMapImpl _symbols;
+
+ UnitParser (SymbolMapImpl symbols, java.io.Reader in) {
+ this(in);
+ _symbols = symbols;
+ }
+
+//
+// Parser productions
+//
+ javax.measure.unit.Unit> parseUnit() throws ParseException {
+ javax.measure.unit.Unit> result;
+ result = compoundExpr();
+ jj_consume_token(0);
+ {if (true) return result;}
+ throw new Error("Missing return statement in function");
+ }
+
+ Unit> compoundExpr() throws ParseException {
+ Unit> result = javax.measure.unit.Unit.ONE;
+ Unit temp = javax.measure.unit.Unit.ONE;
+ result = addExpr();
+ label_1:
+ while (true) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case COLON:
+ ;
+ break;
+ default:
+ jj_la1[0] = jj_gen;
+ break label_1;
+ }
+ jj_consume_token(COLON);
+ temp = addExpr();
+ // result=result.compound(temp);
+ }
+ {if (true) return result;}
+ throw new Error("Missing return statement in function");
+ }
+
+ Unit> addExpr() throws ParseException {
+ Unit> result = javax.measure.unit.Unit.ONE;
+ Number n1 = null;
+ Token sign1 = null;
+ Number n2 = null;
+ Token sign2 = null;
+ if (jj_2_1(2147483647)) {
+ n1 = numberExpr();
+ sign1 = sign();
+ } else {
+ ;
+ }
+ result = mulExpr();
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case PLUS:
+ case MINUS:
+ sign2 = sign();
+ n2 = numberExpr();
+ break;
+ default:
+ jj_la1[1] = jj_gen;
+ ;
+ }
+ if (n1 != null) {
+ if (sign1.image.equals("-")) {
+ result = result.multiply(-1);
+ }
+ result = result.add(n1.doubleValue());
+ }
+ if (n2 != null) {
+ double offset = n2.doubleValue();
+ if (sign2.image.equals("-")) {
+ offset = -offset;
+ }
+ result = result.add(offset);
+ }
+ {if (true) return result;}
+ throw new Error("Missing return statement in function");
+ }
+
+ javax.measure.unit.Unit mulExpr() throws ParseException {
+ javax.measure.unit.Unit result = javax.measure.unit.Unit.ONE;
+ javax.measure.unit.Unit temp = javax.measure.unit.Unit.ONE;
+ result = exponentExpr();
+ label_2:
+ while (true) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case ASTERISK:
+ case MIDDLE_DOT:
+ case SOLIDUS:
+ ;
+ break;
+ default:
+ jj_la1[2] = jj_gen;
+ break label_2;
+ }
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case ASTERISK:
+ case MIDDLE_DOT:
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case ASTERISK:
+ jj_consume_token(ASTERISK);
+ break;
+ case MIDDLE_DOT:
+ jj_consume_token(MIDDLE_DOT);
+ break;
+ default:
+ jj_la1[3] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ temp = exponentExpr();
+ result=result.multiply(temp);
+ break;
+ case SOLIDUS:
+ jj_consume_token(SOLIDUS);
+ temp = exponentExpr();
+ result=result.divide(temp);
+ break;
+ default:
+ jj_la1[4] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ {if (true) return result;}
+ throw new Error("Missing return statement in function");
+ }
+
+ javax.measure.unit.Unit exponentExpr() throws ParseException {
+ javax.measure.unit.Unit result = javax.measure.unit.Unit.ONE;
+ javax.measure.unit.Unit temp = javax.measure.unit.Unit.ONE;
+ Exponent exponent = null;
+ Token token = null;
+ if (jj_2_2(2147483647)) {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case INTEGER:
+ token = jj_consume_token(INTEGER);
+ break;
+ case E:
+ token = jj_consume_token(E);
+ break;
+ default:
+ jj_la1[5] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ jj_consume_token(CARET);
+ result = atomicExpr();
+ double base;
+ if (token.kind == INTEGER) {
+ base = Integer.parseInt(token.image);
+ } else {
+ base = StrictMath.E;
+ }
+ {if (true) return result.transform(new javax.measure.unit.converter.LogConverter(base).inverse());}
+ } else {
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case OPEN_PAREN:
+ case INTEGER:
+ case FLOATING_POINT:
+ case UNIT_IDENTIFIER:
+ result = atomicExpr();
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case CARET:
+ case SUPERSCRIPT_INTEGER:
+ exponent = exp();
+ break;
+ default:
+ jj_la1[6] = jj_gen;
+ ;
+ }
+ if (exponent != null) {
+ if (exponent.pow != 1) {
+ result = result.pow(exponent.pow);
+ }
+ if (exponent.root != 1) {
+ result = result.root(exponent.root);
+ }
+ }
+ {if (true) return result;}
+ break;
+ case LOG:
+ case NAT_LOG:
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case LOG:
+ jj_consume_token(LOG);
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case INTEGER:
+ token = jj_consume_token(INTEGER);
+ break;
+ default:
+ jj_la1[7] = jj_gen;
+ ;
+ }
+ break;
+ case NAT_LOG:
+ token = jj_consume_token(NAT_LOG);
+ break;
+ default:
+ jj_la1[8] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ jj_consume_token(OPEN_PAREN);
+ result = addExpr();
+ jj_consume_token(CLOSE_PAREN);
+ double base = 10;
+ if (token != null) {
+ if (token.kind == INTEGER) {
+ base = Integer.parseInt(token.image);
+ } else if (token.kind == NAT_LOG) {
+ base = StrictMath.E;
+ }
+ }
+ {if (true) return result.transform(new LogConverter(base));}
+ break;
+ default:
+ jj_la1[9] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ Unit> atomicExpr() throws ParseException {
+ Unit> result = Unit.ONE;
+ Unit> temp = Unit.ONE;
+ Number n = null;
+ Token token = null;
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case INTEGER:
+ case FLOATING_POINT:
+ n = numberExpr();
+ if (n instanceof Integer) {
+ {if (true) return result.multiply(n.intValue());}
+ } else {
+ {if (true) return result.multiply(n.doubleValue());}
+ }
+ break;
+ case UNIT_IDENTIFIER:
+ token = jj_consume_token(UNIT_IDENTIFIER);
+ javax.measure.unit.Unit unit = _symbols.getUnit(token.image);
+ if (unit == null) {
+ ParsePrefix prefix = _symbols.getPrefix(token.image);
+ if (prefix != null) {
+ String prefixSymbol = _symbols.getSymbol(prefix);
+ unit = _symbols.getUnit(token.image.substring(prefixSymbol.length()));
+ if (unit != null) {
+ {if (true) return unit.transform(prefix.getConverter());}
+ }
+ }
+ {if (true) throw new ParseException();}
+ } else {
+ {if (true) return unit;}
+ }
+ break;
+ case OPEN_PAREN:
+ jj_consume_token(OPEN_PAREN);
+ result = addExpr();
+ jj_consume_token(CLOSE_PAREN);
+ {if (true) return result;}
+ break;
+ default:
+ jj_la1[10] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ Token sign() throws ParseException {
+ Token result = null;
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case PLUS:
+ result = jj_consume_token(PLUS);
+ break;
+ case MINUS:
+ result = jj_consume_token(MINUS);
+ break;
+ default:
+ jj_la1[11] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ {if (true) return result;}
+ throw new Error("Missing return statement in function");
+ }
+
+ Number numberExpr() throws ParseException {
+ Token token = null;
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case INTEGER:
+ token = jj_consume_token(INTEGER);
+ {if (true) return Long.valueOf(token.image);}
+ break;
+ case FLOATING_POINT:
+ token = jj_consume_token(FLOATING_POINT);
+ {if (true) return Double.valueOf(token.image);}
+ break;
+ default:
+ jj_la1[12] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ Exponent exp() throws ParseException {
+ Token powSign = null;
+ Token powToken = null;
+ Token rootSign = null;
+ Token rootToken = null;
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case CARET:
+ jj_consume_token(CARET);
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case PLUS:
+ case MINUS:
+ case INTEGER:
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case PLUS:
+ case MINUS:
+ powSign = sign();
+ break;
+ default:
+ jj_la1[13] = jj_gen;
+ ;
+ }
+ powToken = jj_consume_token(INTEGER);
+ int pow = Integer.parseInt(powToken.image);
+ if ((powSign != null) && powSign.image.equals("-")) {
+ pow = -pow;
+ }
+ {if (true) return new Exponent(pow, 1);}
+ break;
+ case OPEN_PAREN:
+ jj_consume_token(OPEN_PAREN);
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case PLUS:
+ case MINUS:
+ powSign = sign();
+ break;
+ default:
+ jj_la1[14] = jj_gen;
+ ;
+ }
+ powToken = jj_consume_token(INTEGER);
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case SOLIDUS:
+ jj_consume_token(SOLIDUS);
+ switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
+ case PLUS:
+ case MINUS:
+ rootSign = sign();
+ break;
+ default:
+ jj_la1[15] = jj_gen;
+ ;
+ }
+ rootToken = jj_consume_token(INTEGER);
+ break;
+ default:
+ jj_la1[16] = jj_gen;
+ ;
+ }
+ jj_consume_token(CLOSE_PAREN);
+ pow = Integer.parseInt(powToken.image);
+ if ((powSign != null) && powSign.image.equals("-")) {
+ pow = -pow;
+ }
+ int root = 1;
+ if (rootToken != null) {
+ root = Integer.parseInt(rootToken.image);
+ if ((rootSign != null) && rootSign.image.equals("-")) {
+ root = -root;
+ }
+ }
+ {if (true) return new Exponent(pow, root);}
+ default:
+ jj_la1[17] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ case SUPERSCRIPT_INTEGER:
+ powToken = jj_consume_token(SUPERSCRIPT_INTEGER);
+ int pow = 0;
+ for (int i = 0; i < powToken.image.length(); i += 1) {
+ pow *= 10;
+ switch (powToken.image.charAt(i)) {
+ case '\u00b9': pow += 1; break;
+ case '\u00b2': pow += 2; break;
+ case '\u00b3': pow += 3; break;
+ case '\u2074': pow += 4; break;
+ case '\u2075': pow += 5; break;
+ case '\u2076': pow += 6; break;
+ case '\u2077': pow += 7; break;
+ case '\u2078': pow += 8; break;
+ case '\u2079': pow += 9; break;
+ }
+ }
+ {if (true) return new Exponent(pow, 1);}
+ break;
+ default:
+ jj_la1[18] = jj_gen;
+ jj_consume_token(-1);
+ throw new ParseException();
+ }
+ throw new Error("Missing return statement in function");
+ }
+
+ private boolean jj_2_1(int xla) {
+ jj_la = xla; jj_lastpos = jj_scanpos = token;
+ try { return !jj_3_1(); }
+ catch(LookaheadSuccess ls) { return true; }
+ finally { jj_save(0, xla); }
+ }
+
+ private boolean jj_2_2(int xla) {
+ jj_la = xla; jj_lastpos = jj_scanpos = token;
+ try { return !jj_3_2(); }
+ catch(LookaheadSuccess ls) { return true; }
+ finally { jj_save(1, xla); }
+ }
+
+ private boolean jj_3R_3() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_3R_5()) {
+ jj_scanpos = xsp;
+ if (jj_3R_6()) return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_6() {
+ if (jj_scan_token(FLOATING_POINT)) return true;
+ return false;
+ }
+
+ private boolean jj_3_2() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(14)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(19)) return true;
+ }
+ if (jj_scan_token(CARET)) return true;
+ return false;
+ }
+
+ private boolean jj_3_1() {
+ if (jj_3R_3()) return true;
+ if (jj_3R_4()) return true;
+ return false;
+ }
+
+ private boolean jj_3R_4() {
+ Token xsp;
+ xsp = jj_scanpos;
+ if (jj_scan_token(5)) {
+ jj_scanpos = xsp;
+ if (jj_scan_token(6)) return true;
+ }
+ return false;
+ }
+
+ private boolean jj_3R_5() {
+ if (jj_scan_token(INTEGER)) return true;
+ return false;
+ }
+
+ /** Generated Token Manager. */
+ private UnitParserTokenManager token_source;
+ private SimpleCharStream jj_input_stream;
+ /** Current token. */
+ private Token token;
+ /** Next token. */
+ private Token jj_nt;
+ private int jj_ntk;
+ private Token jj_scanpos, jj_lastpos;
+ private int jj_la;
+ private int jj_gen;
+ final private int[] jj_la1 = new int[19];
+ static private int[] jj_la1_0;
+ static {
+ jj_la1_init_0();
+ }
+ private static void jj_la1_init_0() {
+ jj_la1_0 = new int[] {0x800,0x60,0x380,0x180,0x380,0x84000,0x8400,0x4000,0x60000,0x175000,0x115000,0x60,0x14000,0x60,0x60,0x60,0x200,0x5060,0x8400,};
+ }
+ final private JJCalls[] jj_2_rtns = new JJCalls[2];
+ private boolean jj_rescan = false;
+ private int jj_gc = 0;
+
+ /** Constructor with InputStream. */
+ UnitParser(java.io.InputStream stream) {
+ this(stream, null);
+ }
+ /** Constructor with InputStream and supplied encoding */
+ UnitParser(java.io.InputStream stream, String encoding) {
+ try { jj_input_stream = new SimpleCharStream(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+ token_source = new UnitParserTokenManager(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 19; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ /** Reinitialise. */
+ private void reInit(java.io.InputStream stream) {
+ reInit(stream, null);
+ }
+ /** Reinitialise. */
+ private void reInit(java.io.InputStream stream, String encoding) {
+ try { jj_input_stream.ReInit(stream, encoding, 1, 1); } catch(java.io.UnsupportedEncodingException e) { throw new RuntimeException(e); }
+ token_source.reInit(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 19; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ /** Constructor. */
+ UnitParser(java.io.Reader stream) {
+ jj_input_stream = new SimpleCharStream(stream, 1, 1);
+ token_source = new UnitParserTokenManager(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 19; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ /** Reinitialise. */
+ private void reInit(java.io.Reader stream) {
+ jj_input_stream.ReInit(stream, 1, 1);
+ token_source.reInit(jj_input_stream);
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 19; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ /** Constructor with generated Token Manager. */
+ UnitParser(UnitParserTokenManager tm) {
+ token_source = tm;
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 19; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ /** Reinitialise. */
+ private void reInit(UnitParserTokenManager tm) {
+ token_source = tm;
+ token = new Token();
+ jj_ntk = -1;
+ jj_gen = 0;
+ for (int i = 0; i < 19; i++) jj_la1[i] = -1;
+ for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
+ }
+
+ private Token jj_consume_token(int kind) throws ParseException {
+ Token oldToken;
+ if ((oldToken = token).next != null) token = token.next;
+ else token = token.next = token_source.getNextToken();
+ jj_ntk = -1;
+ if (token.kind == kind) {
+ jj_gen++;
+ if (++jj_gc > 100) {
+ jj_gc = 0;
+ for (int i = 0; i < jj_2_rtns.length; i++) {
+ JJCalls c = jj_2_rtns[i];
+ while (c != null) {
+ if (c.gen < jj_gen) c.first = null;
+ c = c.next;
+ }
+ }
+ }
+ return token;
+ }
+ token = oldToken;
+ jj_kind = kind;
+ throw generateParseException();
+ }
+
+ static private final class LookaheadSuccess extends java.lang.Error {
+
+ /**
+ *
+ */
+ private static final long serialVersionUID = -8192240240676284081L; }
+ final private LookaheadSuccess jj_ls = new LookaheadSuccess();
+ private boolean jj_scan_token(int kind) {
+ if (jj_scanpos == jj_lastpos) {
+ jj_la--;
+ if (jj_scanpos.next == null) {
+ jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
+ } else {
+ jj_lastpos = jj_scanpos = jj_scanpos.next;
+ }
+ } else {
+ jj_scanpos = jj_scanpos.next;
+ }
+ if (jj_rescan) {
+ int i = 0; Token tok = token;
+ while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; }
+ if (tok != null) jj_add_error_token(kind, i);
+ }
+ if (jj_scanpos.kind != kind) return true;
+ if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls;
+ return false;
+ }
+
+
+/** Get the next Token. */
+ final Token getNextToken() {
+ if (token.next != null) token = token.next;
+ else token = token.next = token_source.getNextToken();
+ jj_ntk = -1;
+ jj_gen++;
+ return token;
+ }
+
+/** Get the specific Token. */
+ final Token getToken(int index) {
+ Token t = token;
+ for (int i = 0; i < index; i++) {
+ if (t.next != null) t = t.next;
+ else t = t.next = token_source.getNextToken();
+ }
+ return t;
+ }
+
+ private int jj_ntk() {
+ if ((jj_nt=token.next) == null)
+ return (jj_ntk = (token.next=token_source.getNextToken()).kind);
+ else
+ return (jj_ntk = jj_nt.kind);
+ }
+
+ private java.util.List jj_expentries = new java.util.ArrayList();
+ private int[] jj_expentry;
+ private int jj_kind = -1;
+ private int[] jj_lasttokens = new int[100];
+ private int jj_endpos;
+
+ private void jj_add_error_token(int kind, int pos) {
+ if (pos >= 100) return;
+ if (pos == jj_endpos + 1) {
+ jj_lasttokens[jj_endpos++] = kind;
+ } else if (jj_endpos != 0) {
+ jj_expentry = new int[jj_endpos];
+ for (int i = 0; i < jj_endpos; i++) {
+ jj_expentry[i] = jj_lasttokens[i];
+ }
+ jj_entries_loop: for (java.util.Iterator> it = jj_expentries.iterator(); it.hasNext();) {
+ int[] oldentry = (int[])(it.next());
+ if (oldentry.length == jj_expentry.length) {
+ for (int i = 0; i < jj_expentry.length; i++) {
+ if (oldentry[i] != jj_expentry[i]) {
+ continue jj_entries_loop;
+ }
+ }
+ jj_expentries.add(jj_expentry);
+ break jj_entries_loop;
+ }
+ }
+ if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind;
+ }
+ }
+
+ /** Generate ParseException. */
+ ParseException generateParseException() {
+ jj_expentries.clear();
+ boolean[] la1tokens = new boolean[21];
+ if (jj_kind >= 0) {
+ la1tokens[jj_kind] = true;
+ jj_kind = -1;
+ }
+ for (int i = 0; i < 19; i++) {
+ if (jj_la1[i] == jj_gen) {
+ for (int j = 0; j < 32; j++) {
+ if ((jj_la1_0[i] & (1< jj_gen) {
+ jj_la = p.arg; jj_lastpos = jj_scanpos = p.first;
+ switch (i) {
+ case 0: jj_3_1(); break;
+ case 1: jj_3_2(); break;
+ }
+ }
+ p = p.next;
+ } while (p != null);
+ } catch(LookaheadSuccess ls) { }
+ }
+ jj_rescan = false;
+ }
+
+ private void jj_save(int index, int xla) {
+ JJCalls p = jj_2_rtns[index];
+ while (p.gen > jj_gen) {
+ if (p.next == null) { p = p.next = new JJCalls(); break; }
+ p = p.next;
+ }
+ p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla;
+ }
+
+ static final class JJCalls {
+ int gen;
+ Token first;
+ int arg;
+ JJCalls next;
+ }
+
+}
diff --git a/src/main/java/javax/measure/unit/format/UnitParserConstants.java b/src/main/java/javax/measure/unit/format/UnitParserConstants.java
new file mode 100644
index 0000000..58db7f9
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/UnitParserConstants.java
@@ -0,0 +1,88 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+/* Generated By:JavaCC: Do not edit this line. UnitParserConstants.java */
+package javax.measure.unit.format;
+
+
+/**
+ * Token literal values and constants.
+ * Generated by org.javacc.parser.OtherFilesGen#start()
+ */
+interface UnitParserConstants {
+
+ /** End of File. */
+ int EOF = 0;
+ /** RegularExpression Id. */
+ int DIGIT = 1;
+ /** RegularExpression Id. */
+ int SUPERSCRIPT_DIGIT = 2;
+ /** RegularExpression Id. */
+ int INITIAL_CHAR = 3;
+ /** RegularExpression Id. */
+ int EXTENDED_CHAR = 4;
+ /** RegularExpression Id. */
+ int PLUS = 5;
+ /** RegularExpression Id. */
+ int MINUS = 6;
+ /** RegularExpression Id. */
+ int ASTERISK = 7;
+ /** RegularExpression Id. */
+ int MIDDLE_DOT = 8;
+ /** RegularExpression Id. */
+ int SOLIDUS = 9;
+ /** RegularExpression Id. */
+ int CARET = 10;
+ /** RegularExpression Id. */
+ int COLON = 11;
+ /** RegularExpression Id. */
+ int OPEN_PAREN = 12;
+ /** RegularExpression Id. */
+ int CLOSE_PAREN = 13;
+ /** RegularExpression Id. */
+ int INTEGER = 14;
+ /** RegularExpression Id. */
+ int SUPERSCRIPT_INTEGER = 15;
+ /** RegularExpression Id. */
+ int FLOATING_POINT = 16;
+ /** RegularExpression Id. */
+ int LOG = 17;
+ /** RegularExpression Id. */
+ int NAT_LOG = 18;
+ /** RegularExpression Id. */
+ int E = 19;
+ /** RegularExpression Id. */
+ int UNIT_IDENTIFIER = 20;
+
+ /** Lexical state. */
+ int DEFAULT = 0;
+
+ /** Literal token values. */
+ String[] tokenImage = {
+ "",
+ "",
+ "",
+ "",
+ "",
+ "\"+\"",
+ "\"-\"",
+ "\"*\"",
+ "\"\\u00b7\"",
+ "\"/\"",
+ "\"^\"",
+ "\":\"",
+ "\"(\"",
+ "\")\"",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "\"e\"",
+ "",
+ };
+
+}
diff --git a/src/main/java/javax/measure/unit/format/UnitParserTokenManager.java b/src/main/java/javax/measure/unit/format/UnitParserTokenManager.java
new file mode 100644
index 0000000..4601cbd
--- /dev/null
+++ b/src/main/java/javax/measure/unit/format/UnitParserTokenManager.java
@@ -0,0 +1,478 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+/* Generated By:JavaCC: Do not edit this line. UnitParserTokenManager.java */
+package javax.measure.unit.format;
+
+/** Token Manager. */
+class UnitParserTokenManager implements UnitParserConstants
+{
+
+ /** Debug output. */
+ java.io.PrintStream debugStream = System.out;
+ /** Set debug output. */
+ void setDebugStream(java.io.PrintStream ds) { debugStream = ds; }
+private final int jjStopStringLiteralDfa_0(int pos, long active0)
+{
+ switch (pos)
+ {
+ default :
+ return -1;
+ }
+}
+private final int jjStartNfa_0(int pos, long active0)
+{
+ return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0), pos + 1);
+}
+private int jjStopAtPos(int pos, int kind)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ return pos + 1;
+}
+private int jjMoveStringLiteralDfa0_0()
+{
+ switch(curChar)
+ {
+ case 40:
+ return jjStopAtPos(0, 12);
+ case 41:
+ return jjStopAtPos(0, 13);
+ case 42:
+ return jjStopAtPos(0, 7);
+ case 43:
+ return jjStopAtPos(0, 5);
+ case 45:
+ return jjStopAtPos(0, 6);
+ case 47:
+ return jjStopAtPos(0, 9);
+ case 58:
+ return jjStopAtPos(0, 11);
+ case 94:
+ return jjStopAtPos(0, 10);
+ case 101:
+ return jjStartNfaWithStates_0(0, 19, 7);
+ case 183:
+ return jjStopAtPos(0, 8);
+ default :
+ return jjMoveNfa_0(6, 0);
+ }
+}
+private int jjStartNfaWithStates_0(int pos, int kind, int state)
+{
+ jjmatchedKind = kind;
+ jjmatchedPos = pos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return pos + 1; }
+ return jjMoveNfa_0(state, pos + 1);
+}
+static final long[] jjbitVec0 = {
+ 0x0L, 0x0L, 0x20c000000000000L, 0x0L
+};
+static final long[] jjbitVec1 = {
+ 0x0L, 0x3f1000000000000L, 0x0L, 0x0L
+};
+static final long[] jjbitVec2 = {
+ 0xfffffffefffffffeL, 0xffffffffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+static final long[] jjbitVec4 = {
+ 0x0L, 0x0L, 0xfd73ffffffffffffL, 0xffffffffffffffffL
+};
+static final long[] jjbitVec5 = {
+ 0xffffffffffffffffL, 0xfc0effffffffffffL, 0xffffffffffffffffL, 0xffffffffffffffffL
+};
+private int jjMoveNfa_0(int startState, int curPos)
+{
+ int startsAt = 0;
+ jjnewStateCnt = 15;
+ int i = 1;
+ jjstateSet[0] = startState;
+ int kind = 0x7fffffff;
+ for (;;)
+ {
+ if (++jjround == 0x7fffffff)
+ ReInitRounds();
+ if (curChar < 64)
+ {
+ long l = 1L << curChar;
+ do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 6:
+ if ((0xf80010fe00000000L & l) != 0L)
+ {
+ if (kind > 20)
+ kind = 20;
+ jjCheckNAdd(7);
+ }
+ else if ((0x3ff000000000000L & l) != 0L)
+ {
+ if (kind > 14)
+ kind = 14;
+ jjCheckNAddStates(0, 4);
+ }
+ else if (curChar == 46)
+ jjCheckNAdd(2);
+ break;
+ case 1:
+ if (curChar == 46)
+ jjCheckNAdd(2);
+ break;
+ case 2:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 16)
+ kind = 16;
+ jjCheckNAddTwoStates(2, 3);
+ break;
+ case 4:
+ if ((0x280000000000L & l) != 0L)
+ jjCheckNAdd(5);
+ break;
+ case 5:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 16)
+ kind = 16;
+ jjCheckNAdd(5);
+ break;
+ case 7:
+ if ((0xfbff10fe00000000L & l) == 0L)
+ break;
+ if (kind > 20)
+ kind = 20;
+ jjCheckNAdd(7);
+ break;
+ case 8:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 14)
+ kind = 14;
+ jjCheckNAddStates(0, 4);
+ break;
+ case 9:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 14)
+ kind = 14;
+ jjCheckNAdd(9);
+ break;
+ case 10:
+ if ((0x3ff000000000000L & l) == 0L)
+ break;
+ if (kind > 16)
+ kind = 16;
+ jjCheckNAddStates(5, 8);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else if (curChar < 128)
+ {
+ long l = 1L << (curChar & 077);
+ do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 6:
+ if ((0xffffffffbfffffffL & l) != 0L)
+ {
+ if (kind > 20)
+ kind = 20;
+ jjCheckNAdd(7);
+ }
+ if (curChar == 108)
+ jjAddStates(9, 10);
+ break;
+ case 3:
+ if ((0x2000000020L & l) != 0L)
+ jjAddStates(11, 12);
+ break;
+ case 7:
+ if ((0xffffffffbfffffffL & l) == 0L)
+ break;
+ if (kind > 20)
+ kind = 20;
+ jjCheckNAdd(7);
+ break;
+ case 11:
+ if (curChar == 108)
+ jjAddStates(9, 10);
+ break;
+ case 12:
+ if (curChar == 111)
+ jjstateSet[jjnewStateCnt++] = 13;
+ break;
+ case 13:
+ if (curChar == 103 && kind > 17)
+ kind = 17;
+ break;
+ case 14:
+ if (curChar == 110 && kind > 18)
+ kind = 18;
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ else
+ {
+ int hiByte = (int)(curChar >> 8);
+ int i1 = hiByte >> 6;
+ long l1 = 1L << (hiByte & 077);
+ int i2 = (curChar & 0xff) >> 6;
+ long l2 = 1L << (curChar & 077);
+ do
+ {
+ switch(jjstateSet[--i])
+ {
+ case 6:
+ if (jjCanMove_0(hiByte, i1, i2, l1, l2))
+ {
+ if (kind > 15)
+ kind = 15;
+ jjCheckNAdd(0);
+ }
+ if (jjCanMove_1(hiByte, i1, i2, l1, l2))
+ {
+ if (kind > 20)
+ kind = 20;
+ jjCheckNAdd(7);
+ }
+ break;
+ case 0:
+ if (!jjCanMove_0(hiByte, i1, i2, l1, l2))
+ break;
+ if (kind > 15)
+ kind = 15;
+ jjCheckNAdd(0);
+ break;
+ case 7:
+ if (!jjCanMove_1(hiByte, i1, i2, l1, l2))
+ break;
+ if (kind > 20)
+ kind = 20;
+ jjCheckNAdd(7);
+ break;
+ default : break;
+ }
+ } while(i != startsAt);
+ }
+ if (kind != 0x7fffffff)
+ {
+ jjmatchedKind = kind;
+ jjmatchedPos = curPos;
+ kind = 0x7fffffff;
+ }
+ ++curPos;
+ if ((i = jjnewStateCnt) == (startsAt = 15 - (jjnewStateCnt = startsAt)))
+ return curPos;
+ try { curChar = input_stream.readChar(); }
+ catch(java.io.IOException e) { return curPos; }
+ }
+}
+static final int[] jjnextStates = {
+ 9, 1, 2, 3, 10, 1, 2, 3, 10, 12, 14, 4, 5,
+};
+private static final boolean jjCanMove_0(int hiByte, int i1, int i2, long l1, long l2)
+{
+ switch(hiByte)
+ {
+ case 0:
+ return ((jjbitVec0[i2] & l2) != 0L);
+ case 32:
+ return ((jjbitVec1[i2] & l2) != 0L);
+ default :
+ return false;
+ }
+}
+private static final boolean jjCanMove_1(int hiByte, int i1, int i2, long l1, long l2)
+{
+ switch(hiByte)
+ {
+ case 0:
+ return ((jjbitVec4[i2] & l2) != 0L);
+ case 32:
+ return ((jjbitVec5[i2] & l2) != 0L);
+ default :
+ if ((jjbitVec2[i1] & l1) != 0L)
+ return true;
+ return false;
+ }
+}
+
+/** Token literal values. */
+private static final String[] jjstrLiteralImages = {
+"", null, null, null, null, "\53", "\55", "\52", "\267", "\57", "\136", "\72",
+"\50", "\51", null, null, null, null, null, "\145", null, };
+
+/** Lexer state names. */
+private static final String[] lexStateNames = {
+ "DEFAULT",
+};
+protected SimpleCharStream input_stream;
+private final int[] jjrounds = new int[15];
+private final int[] jjstateSet = new int[30];
+protected char curChar;
+/** Constructor. */
+UnitParserTokenManager(SimpleCharStream stream){
+ if (SimpleCharStream.staticFlag)
+ throw new Error("ERROR: Cannot use a static CharStream class with a non-static lexical analyzer.");
+ input_stream = stream;
+}
+
+/** Constructor. */
+UnitParserTokenManager(SimpleCharStream stream, int lexState){
+ this(stream);
+ switchTo(lexState);
+}
+
+/** Reinitialise parser. */
+void reInit(SimpleCharStream stream)
+{
+ jjmatchedPos = jjnewStateCnt = 0;
+ curLexState = defaultLexState;
+ input_stream = stream;
+ ReInitRounds();
+}
+private void ReInitRounds()
+{
+ int i;
+ jjround = 0x80000001;
+ for (i = 15; i-- > 0;)
+ jjrounds[i] = 0x80000000;
+}
+
+/** Reinitialise parser. */
+void reInit(SimpleCharStream stream, int lexState)
+{
+ reInit(stream);
+ switchTo(lexState);
+}
+
+/** Switch to specified lex state. */
+void switchTo(int lexState)
+{
+ if (lexState >= 1 || lexState < 0)
+ throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE);
+ else
+ curLexState = lexState;
+}
+
+protected Token jjFillToken()
+{
+ final Token t;
+ final String curTokenImage;
+ final int beginLine;
+ final int endLine;
+ final int beginColumn;
+ final int endColumn;
+ String im = jjstrLiteralImages[jjmatchedKind];
+ curTokenImage = (im == null) ? input_stream.getImage() : im;
+ beginLine = input_stream.getBeginLine();
+ beginColumn = input_stream.getBeginColumn();
+ endLine = input_stream.getEndLine();
+ endColumn = input_stream.getEndColumn();
+ t = Token.newToken(jjmatchedKind, curTokenImage);
+
+ t.beginLine = beginLine;
+ t.endLine = endLine;
+ t.beginColumn = beginColumn;
+ t.endColumn = endColumn;
+
+ return t;
+}
+
+int curLexState = 0;
+int defaultLexState = 0;
+int jjnewStateCnt;
+int jjround;
+int jjmatchedPos;
+int jjmatchedKind;
+
+/** Get the next Token. */
+Token getNextToken()
+{
+ Token matchedToken;
+ int curPos = 0;
+
+ EOFLoop :
+ for (;;)
+ {
+ try
+ {
+ curChar = input_stream.beginToken();
+ }
+ catch(java.io.IOException e)
+ {
+ jjmatchedKind = 0;
+ matchedToken = jjFillToken();
+ return matchedToken;
+ }
+
+ jjmatchedKind = 0x7fffffff;
+ jjmatchedPos = 0;
+ curPos = jjMoveStringLiteralDfa0_0();
+ if (jjmatchedKind != 0x7fffffff)
+ {
+ if (jjmatchedPos + 1 < curPos)
+ input_stream.backup(curPos - jjmatchedPos - 1);
+ matchedToken = jjFillToken();
+ return matchedToken;
+ }
+ int error_line = input_stream.getEndLine();
+ int error_column = input_stream.getEndColumn();
+ String error_after = null;
+ boolean EOFSeen = false;
+ try { input_stream.readChar(); input_stream.backup(1); }
+ catch (java.io.IOException e1) {
+ EOFSeen = true;
+ error_after = curPos <= 1 ? "" : input_stream.getImage();
+ if (curChar == '\n' || curChar == '\r') {
+ error_line++;
+ error_column = 0;
+ }
+ else
+ error_column++;
+ }
+ if (!EOFSeen) {
+ input_stream.backup(1);
+ error_after = curPos <= 1 ? "" : input_stream.getImage();
+ }
+ throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR);
+ }
+}
+
+private void jjCheckNAdd(int state)
+{
+ if (jjrounds[state] != jjround)
+ {
+ jjstateSet[jjnewStateCnt++] = state;
+ jjrounds[state] = jjround;
+ }
+}
+private void jjAddStates(int start, int end)
+{
+ do {
+ jjstateSet[jjnewStateCnt++] = jjnextStates[start];
+ } while (start++ != end);
+}
+private void jjCheckNAddTwoStates(int state1, int state2)
+{
+ jjCheckNAdd(state1);
+ jjCheckNAdd(state2);
+}
+
+private void jjCheckNAddStates(int start, int end)
+{
+ do {
+ jjCheckNAdd(jjnextStates[start]);
+ } while (start++ != end);
+}
+
+}
diff --git a/src/main/java/src.zip b/src/main/java/src.zip
new file mode 100644
index 0000000..a5ff713
Binary files /dev/null and b/src/main/java/src.zip differ
diff --git a/src/main/javacc/javax/measure/unit/format/UnitParser.jj b/src/main/javacc/javax/measure/unit/format/UnitParser.jj
new file mode 100644
index 0000000..42666ca
--- /dev/null
+++ b/src/main/javacc/javax/measure/unit/format/UnitParser.jj
@@ -0,0 +1,337 @@
+/*
+ * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
+ * Copyright (C) 2008-2010 - JScience
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software is
+ * freely granted, provided that this notice is preserved.
+ */
+
+options {
+ STATIC = false;
+ UNICODE_INPUT = true;
+ DEBUG_PARSER = false;
+ DEBUG_LOOKAHEAD = false;
+ DEBUG_TOKEN_MANAGER = false;
+ FORCE_LA_CHECK = true;
+}
+
+PARSER_BEGIN(UnitParser)
+
+package javax.measure.unit.format;
+
+/**
+ * @author Eric Russell
+ * @author Werner Keil
+ * @version 1.0.2 ($Revision: 148 $), $Date: 2010-02-18 07:19:01 +0100 (Do, 18 Feb 2010) $
+ */
+class UnitParser {
+
+ private static class Exponent {
+ public final int pow;
+ public final int root;
+ public Exponent (int pow, int root) {
+ this.pow = pow;
+ this.root = root;
+ }
+ }
+
+ private SymbolMap symbols;
+
+ public UnitParser (SymbolMap symbols, java.io.Reader in) {
+ this(in);
+ this.symbols = symbols;
+ }
+}
+
+PARSER_END(UnitParser)
+
+//
+// Lexical entities
+//
+
+TOKEN: { <#DIGIT: [ "0","1","2","3","4","5","6","7","8","9" ] >
+ | <#SUPERSCRIPT_DIGIT: [ "\u2070", "\u00B9", "\u00B2", "\u00B3", "\u2074", "\u2075", "\u2076", "\u2077", "\u2078", "\u2079" ] >
+ | <#INITIAL_CHAR: ~["\u0000"-"\u0020", "(", ")", "*", "+", "-", ".", "/", "0"-"9", ":", "^", "\u00B2", "\u00B3", "\u00B7", "\u00B9", "\u2070", "\u2074", "\u2075", "\u2076", "\u2077", "\u2078", "\u2079" ] >
+ | <#EXTENDED_CHAR: ~["\u0000"-"\u0020", "(", ")", "*", "+", "-", ".", "/", ":", "^", "\u00B2", "\u00B3", "\u00B7", "\u00B9", "\u2070", "\u2074", "\u2075", "\u2076", "\u2077", "\u2078", "\u2079" ] > }
+TOKEN: { }
+TOKEN: { }
+TOKEN: { }
+TOKEN: { }
+TOKEN: { }
+TOKEN: { }
+TOKEN: { }
+TOKEN: { }
+TOKEN: { }
+TOKEN: { )+ > }
+TOKEN: { )+ > }
+TOKEN: { )* (".")? ()+ (("e" | "E") (()|())? ()+)? > }
+TOKEN: {
+ |
+ | }
+TOKEN: { ()* > }
+
+//
+// Parser productions
+//
+
+javax.measure.unit.Unit parseUnit () :
+ {
+ javax.measure.unit.Unit result;
+ }
+{
+ result=compoundExpr()
+ {
+ return result;
+ }
+}
+
+javax.measure.unit.Unit compoundExpr () :
+ {
+ javax.measure.unit.Unit result = javax.measure.unit.Unit.ONE;
+ javax.measure.unit.Unit temp = javax.measure.unit.Unit.ONE;
+ }
+{
+(
+ result=addExpr()
+ (
+ temp=addExpr() { result=result.compound(temp); }
+ )*
+ { return result; }
+)
+}
+
+javax.measure.unit.Unit addExpr () :
+ {
+ javax.measure.unit.Unit result = javax.measure.unit.Unit.ONE;
+ Number n1 = null;
+ Token sign1 = null;
+ Number n2 = null;
+ Token sign2 = null;
+ }
+{
+(
+ ( LOOKAHEAD(numberExpr() sign()) n1=numberExpr() sign1=sign() )?
+ result=mulExpr()
+ ( sign2=sign() n2=numberExpr() )?
+ {
+ if (n1 != null) {
+ if (sign1.image.equals("-")) {
+ result = result.multiply(-1);
+ }
+ result = result.plus(n1.doubleValue());
+ }
+ if (n2 != null) {
+ double offset = n2.doubleValue();
+ if (sign2.image.equals("-")) {
+ offset = -offset;
+ }
+ result = result.plus(offset);
+ }
+ return result;
+ }
+)
+}
+
+javax.measure.unit.Unit mulExpr () :
+ {
+ javax.measure.unit.Unit result = javax.measure.unit.Unit.ONE;
+ javax.measure.unit.Unit temp = javax.measure.unit.Unit.ONE;
+ }
+{
+(
+ result=exponentExpr()
+ (
+ ( ( | ) temp=exponentExpr() { result=result.multiply(temp); } )
+ |
+ ( temp=exponentExpr() { result=result.divide(temp); } )
+ )*
+ { return result; }
+)
+}
+
+javax.measure.unit.Unit exponentExpr () :
+ {
+ javax.measure.unit.Unit result = javax.measure.unit.Unit.ONE;
+ javax.measure.unit.Unit temp = javax.measure.unit.Unit.ONE;
+ Exponent exponent = null;
+ Token token = null;
+ }
+{
+(
+ LOOKAHEAD(( | ) )
+ ( ((token=) | token=) result=atomicExpr() )
+ {
+ double base;
+ if (token.kind == INTEGER) {
+ base = Integer.parseInt(token.image);
+ } else {
+ base = StrictMath.E;
+ }
+ return result.transform(new javax.measure.converter.LogConverter(base).inverse());
+ }
+|
+ ( result=atomicExpr() ( exponent=exp() )? )
+ {
+ if (exponent != null) {
+ if (exponent.pow != 1) {
+ result = result.pow(exponent.pow);
+ }
+ if (exponent.root != 1) {
+ result = result.root(exponent.root);
+ }
+ }
+ return result;
+ }
+|
+ ( ( ( (token=)? ) | token= ) result=addExpr() )
+ {
+ double base = 10;
+ if (token != null) {
+ if (token.kind == INTEGER) {
+ base = Integer.parseInt(token.image);
+ } else if (token.kind == NAT_LOG) {
+ base = StrictMath.E;
+ }
+ }
+ return result.transform(new javax.measure.converter.LogConverter(base));
+ }
+)
+}
+
+javax.measure.unit.Unit atomicExpr () :
+ {
+ javax.measure.unit.Unit result = javax.measure.unit.Unit.ONE;
+ javax.measure.unit.Unit temp = javax.measure.unit.Unit.ONE;
+ Number n = null;
+ Token token = null;
+ }
+{
+(
+ ( n=numberExpr() )
+ {
+ if (n instanceof Integer) {
+ return result.multiply(n.intValue());
+ } else {
+ return result.multiply(n.doubleValue());
+ }
+ }
+|
+ ( token= )
+ {
+ javax.measure.unit.Unit unit = _symbols.getUnit(token.image);
+ if (unit == null) {
+ ParsePrefix prefix = _symbols.getPrefix(token.image);
+ if (prefix != null) {
+ String prefixSymbol = _symbols.getSymbol(prefix);
+ unit = _symbols.getUnit(token.image.substring(prefixSymbol.length()));
+ if (unit != null) {
+ return unit.transform(prefix.getConverter());
+ }
+ }
+ throw new ParseException();
+ } else {
+ return unit;
+ }
+ }
+|
+ ( result=addExpr() )
+ {
+ return result;
+ }
+)
+}
+
+Token sign () :
+ {
+ Token result = null;
+ }
+{
+(
+ (result=)
+|
+ (result=)
+)
+ {
+ return result;
+ }
+}
+
+Number numberExpr () :
+ {
+ Token token = null;
+ }
+{
+(
+ ( token= )
+ {
+ return Long.valueOf(token.image);
+ }
+|
+ ( token= )
+ {
+ return Double.valueOf(token.image);
+ }
+)
+}
+
+Exponent exp () :
+ {
+ Token powSign = null;
+ Token powToken = null;
+ Token rootSign = null;
+ Token rootToken = null;
+ }
+{
+(
+ (
+
+ (
+ ( (powSign=sign())? powToken= )
+ {
+ int pow = Integer.parseInt(powToken.image);
+ if ((powSign != null) && powSign.image.equals("-")) {
+ pow = -pow;
+ }
+ return new Exponent(pow, 1);
+ }
+ |
+ ( (powSign=sign())? powToken= ( (rootSign=sign())? rootToken= )? )
+ {
+ pow = Integer.parseInt(powToken.image);
+ if ((powSign != null) && powSign.image.equals("-")) {
+ pow = -pow;
+ }
+ int root = 1;
+ if (rootToken != null) {
+ root = Integer.parseInt(rootToken.image);
+ if ((rootSign != null) && rootSign.image.equals("-")) {
+ root = -root;
+ }
+ }
+ return new Exponent(pow, root);
+ }
+ )
+ )
+|
+ ( powToken= )
+ {
+ int pow = 0;
+ for (int i = 0; i < powToken.image.length(); i += 1) {
+ pow *= 10;
+ switch (powToken.image.charAt(i)) {
+ case '\u00B9': pow += 1; break;
+ case '\u00B2': pow += 2; break;
+ case '\u00B3': pow += 3; break;
+ case '\u2074': pow += 4; break;
+ case '\u2075': pow += 5; break;
+ case '\u2076': pow += 6; break;
+ case '\u2077': pow += 7; break;
+ case '\u2078': pow += 8; break;
+ case '\u2079': pow += 9; break;
+ }
+ }
+ return new Exponent(pow, 1);
+ }
+)
+}
diff --git a/src/main/resources/javax/measure/unit/format/messages.properties b/src/main/resources/javax/measure/unit/format/messages.properties
new file mode 100644
index 0000000..5a1353c
--- /dev/null
+++ b/src/main/resources/javax/measure/unit/format/messages.properties
@@ -0,0 +1,85 @@
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+#
+# Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+# See LICENSE.txt for the Specification License
+#
+# NOTE: as a Java properties file, this file must use the
+# ISO 8859-1 encoding, so all non-ASCII Unicode characters
+# must be escaped using the \uXXXX syntax.
+# See http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html#encoding
+
+##javax.measure.unit.ImperialSystem.OUNCE_LIQUID = fl oz UK
+##javax.measure.unit.ImperialSystem.TON_UK = ton_uk
+# SI Units
+javax.measure.unit.MetricSystem.AMPERE = A
+javax.measure.unit.MetricSystem.AMPERE_TURN = At
+javax.measure.unit.MetricSystem.BECQUEREL = Bq
+javax.measure.unit.MetricSystem.CANDELA = cd
+javax.measure.unit.MetricSystem.CELSIUS = \u00B0C
+javax.measure.unit.MetricSystem.CELSIUS.1 = \u2103
+javax.measure.unit.MetricSystem.CELSIUS.2 = Celsius
+javax.measure.unit.MetricSystem.COULOMB = C
+javax.measure.unit.MetricSystem.FARAD = F
+javax.measure.unit.MetricSystem.GRAM = g
+javax.measure.unit.MetricSystem.GRAY = Gy
+javax.measure.unit.MetricSystem.HENRY = H
+javax.measure.unit.MetricSystem.HERTZ = Hz
+javax.measure.unit.MetricSystem.HERTZ.2 = hertz
+javax.measure.unit.MetricSystem.JOULE = J
+javax.measure.unit.MetricSystem.KATAL = kat
+javax.measure.unit.MetricSystem.KELVIN = K
+javax.measure.unit.MetricSystem.LUMEN = lm
+javax.measure.unit.MetricSystem.LUX = lx
+javax.measure.unit.MetricSystem.METRE = m
+javax.measure.unit.MetricSystem.MOLE = mol
+javax.measure.unit.MetricSystem.NEWTON = N
+javax.measure.unit.MetricSystem.OHM = \u03A9
+javax.measure.unit.MetricSystem.PASCAL = Pa
+javax.measure.unit.MetricSystem.RADIAN = rad
+javax.measure.unit.MetricSystem.SECOND = s
+javax.measure.unit.MetricSystem.SIEMENS = S
+javax.measure.unit.MetricSystem.SIEVERT = Sv
+javax.measure.unit.MetricSystem.STERADIAN = sr
+javax.measure.unit.MetricSystem.TESLA = T
+javax.measure.unit.MetricSystem.VOLT = V
+javax.measure.unit.MetricSystem.WATT = W
+javax.measure.unit.MetricSystem.WEBER = Wb
+# Non-SI Units
+##javax.measure.unit.NonSI.PERCENT = %
+##javax.measure.unit.NonSI.POINT = pt
+javax.measure.unit.USCustomarySystem.ELECTRON_VOLT = eV
+javax.measure.unit.USCustomarySystem.FLUID_DRAM = fl dr
+javax.measure.unit.USCustomarySystem.FOOT = ft
+javax.measure.unit.USCustomarySystem.LITER = l
+javax.measure.unit.USCustomarySystem.METER = m
+javax.measure.unit.USCustomarySystem.MILES_PER_HOUR = mph
+javax.measure.unit.USCustomarySystem.MINIM = min
+javax.measure.unit.USCustomarySystem.OIL_BARREL = bbl
+javax.measure.unit.USCustomarySystem.OUNCE = oz
+javax.measure.unit.USCustomarySystem.OUNCE_LIQUID = fl oz
+javax.measure.unit.USCustomarySystem.TABLESPOON = Tbsp
+javax.measure.unit.USCustomarySystem.TEASPOON = tsp
+javax.measure.unit.USCustomarySystem.TON = ton
+# SI Prefixes
+javax.measure.unit.format.ParsePrefix.ATTO = a
+javax.measure.unit.format.ParsePrefix.CENTI = c
+javax.measure.unit.format.ParsePrefix.DECI = d
+javax.measure.unit.format.ParsePrefix.DEKA = da
+javax.measure.unit.format.ParsePrefix.EXA = E
+javax.measure.unit.format.ParsePrefix.FEMTO = f
+javax.measure.unit.format.ParsePrefix.GIGA = G
+javax.measure.unit.format.ParsePrefix.HECTO = h
+javax.measure.unit.format.ParsePrefix.KILO = k
+javax.measure.unit.format.ParsePrefix.MEGA = M
+javax.measure.unit.format.ParsePrefix.MICRO = \u00B5
+javax.measure.unit.format.ParsePrefix.MILLI = m
+javax.measure.unit.format.ParsePrefix.NANO = n
+javax.measure.unit.format.ParsePrefix.PETA = P
+javax.measure.unit.format.ParsePrefix.PICO = p
+javax.measure.unit.format.ParsePrefix.TERA = T
+javax.measure.unit.format.ParsePrefix.YOCTO = y
+javax.measure.unit.format.ParsePrefix.YOTTA = Y
+javax.measure.unit.format.ParsePrefix.ZEPTO = z
+javax.measure.unit.format.ParsePrefix.ZETTA = Z
diff --git a/src/main/resources/javax/measure/unit/format/messages_de.properties b/src/main/resources/javax/measure/unit/format/messages_de.properties
new file mode 100644
index 0000000..6feed14
--- /dev/null
+++ b/src/main/resources/javax/measure/unit/format/messages_de.properties
@@ -0,0 +1,118 @@
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+#
+# Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+# See LICENSE.txt for the Specification License
+#
+# See LICENSE.txt for the full Specification License
+#
+#
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+#
+# JEAN-MARIE DAUTELLE, WERNER KEIL ARE WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS LICENSE AGREEMENT ("AGREEMENT"). PLEASE READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY. BY DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY THEM, SELECT THE "DECLINE" BUTTON AT THE BOTTOM OF THIS PAGE AND THE DOWNLOADING PROCESS WILL NOT CONTINUE.
+#
+# Specification: JSR 275 - Units Specification ("Specification")
+#
+# Version: 0.9.4
+# Status: Pre-FCS Public Release
+# Release: December 4, 2009
+#
+# Copyright 2005-2009 Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+#
+# NOTICE
+#
+# The Specification is protected by copyright and the information described therein may be protected by one or more U.S. patents, foreign patents, or pending applications. Except as provided under the following license, no part of the Specification may be reproduced in any form by any means without the prior written authorization of Jean-Marie Dautelle, Werner Keil and its
+# licensors, if any. Any use of the Specification and the information described therein will be governed by the terms and conditions of this Agreement.
+#
+#
+# Subject to the terms and conditions of this license, including your compliance with Paragraphs 1, 2 and 3 below, Jean-Marie Dautelle and Werner Keil hereby grant you a fully-paid, non-exclusive, non-transferable, limited license (without the right to sublicense) under Jean-Marie Dautelle and Werner Keil's intellectual property rights to:
+#
+# 1. Review the Specification for the purposes of evaluation. This includes: (i) developing implementations of the Specification for your internal, non-commercial use; (ii) discussing the Specification with any third party; and (iii) excerpting brief portions of the Specification in oral or written communications which discuss the Specification provided that such excerpts do not in the aggregate constitute a significant portion of the Specification.
+#
+# 2. Distribute implementations of the Specification to third parties for their testing and evaluation use, provided that any such implementation:
+#
+# (i) does not modify, subset, superset or otherwise extend the Licensor Name Space, or include any public or protected packages, classes, Java interfaces, fields or methods within the Licensor Name Space other than those required/authorized by the Specification or Specifications being implemented;
+#
+# (ii) is clearly and prominently marked with the word "UNTESTED" or "EARLY ACCESS" or "INCOMPATIBLE" or "UNSTABLE" or "BETA" in any list of available builds and in proximity to every link initiating its download, where the list or link is under Licensee's control; and
+#
+# (iii) includes the following notice:
+# "This is an implementation of an early-draft specification developed under the Java Community Process (JCP) and is made available for testing and evaluation purposes only. The code is not compatible with any specification of the JCP."
+#
+# 3. Distribute applications written to the Specification to third parties for their testing and evaluation use, provided that any such application includes the following notice:
+# "This is an application written to interoperate with an early-draft specification developed under the Java Community Process (JCP) and is made available for testing and evaluation purposes only. The code is not compatible with any specification of the JCP."
+#
+# The grant set forth above concerning your distribution of implementations of the Specification is contingent upon your agreement to terminate development and distribution of your implementation of early draft upon final completion of the Specification. If you fail to do so, the foregoing grant shall be considered null and void.
+#
+# Other than this limited license, you acquire no right, title or interest in or to the Specification or any other Jean-Marie Dautelle and Werner Keil intellectual property, and the Specification may only be used in accordance with the license terms set forth herein. This license will expire on the earlier of: (a) two (2) years from the date of Release listed above; (b) the date on which the final version of the Specification is publicly released; or (c) the date on which the Java Specification Request (JSR) to which the Specification corresponds is withdrawn. In addition, this license will terminate immediately without notice from Jean-Marie Dautelle, Werner Keil if you fail to comply with any provision of this license. Upon termination, you must cease use of or destroy the Specification.
+#
+# "Licensor Name Space" means the public class or interface declarations whose names begin with "java", "javax", "org.jscience" or their equivalents in any subsequent naming convention adopted through the Java Community Process, or any recognized successors or replacements thereof
+#
+#
+# TRADEMARKS
+#
+# No right, title, or interest in or to any trademarks, service marks, or trade names of Jean-Marie Dautelle, Werner Keil or Jean-Marie Dautelle and Werner Keil's licensors is granted hereunder. Java and Java-related logos, marks and names are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
+#
+#
+# DISCLAIMER OF WARRANTIES
+#
+# THE SPECIFICATION IS PROVIDED "AS IS" AND IS EXPERIMENTAL AND MAY CONTAIN DEFECTS OR DEFICIENCIES WHICH CANNOT OR WILL NOT BE CORRECTED BY JEAN-MARIE DAUTELLE, WERNER KEIL. JEAN-MARIE DAUTELLE AND WERNER KEIL MAKE NO REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT THAT THE CONTENTS OF THE SPECIFICATION ARE SUITABLE FOR ANY PURPOSE OR THAT ANY PRACTICE OR IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADE SECRETS OR OTHER RIGHTS. This document does not represent any commitment to release or implement any portion of the Specification in any product.
+#
+# THE SPECIFICATION COULD INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS. CHANGES ARE PERIODICALLY ADDED TO THE INFORMATION THEREIN; THESE CHANGES WILL BE INCORPORATED INTO NEW VERSIONS OF THE SPECIFICATION, IF ANY. JEAN-MARIE DAUTELL AND WERNER KEIL MAY MAKE IMPROVEMENTS AND/OR CHANGES TO THE PRODUCT(S) AND/OR THE PROGRAM(S) DESCRIBED IN THE SPECIFICATION AT ANY TIME. Any use of such changes in the Specification will be governed by the then-current license for the applicable version of the Specification.
+#
+#
+# LIMITATION OF LIABILITY
+#
+# TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL JEAN-MARIE DAUTELLE, WERNER KEIL OR THEIR LICENSORS BE LIABLE FOR ANY DAMAGES, INCLUDING WITHOUT LIMITATION, LOST REVENUE, PROFITS OR DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF OR RELATED TO ANY FURNISHING, PRACTICING, MODIFYING OR ANY USE OF THE SPECIFICATION, EVEN IF JEAN-MARIE DAUTELLE, WERNER KEIL AND/OR ITS LICENSORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+#
+# You will hold Jean-Marie Dautelle, Werner Keil (and its licensors) harmless from any claims based on your use of the Specification for any purposes other than the limited right of evaluation as described above, and from any claims that later versions or releases of any Specification furnished to you are incompatible with the Specification provided to you under this license.
+#
+#
+# RESTRICTED RIGHTS LEGEND
+#
+# If this Software is being acquired by or on behalf of the U.S. Government or by a U.S. Government prime contractor or subcontractor (at any tier), then the Government's rights in the Software and accompanying documentation shall be only as set forth in this license; this is in
+# accordance with 48 C.F.R. 227.7201 through 227.7202-4 (for Department of Defense (DoD) acquisitions) and with 48 C.F.R. 2.101 and 12.212 (for non-DoD acquisitions).
+#
+#
+# REPORT
+#
+# You may wish to report any ambiguities, inconsistencies or inaccuracies you may find in connection with your evaluation of the Specification ("Feedback"). To the extent that you provide Jean-Marie Dautelle, Werner Keil with any Feedback, you hereby: (i) agree that such Feedback is provided on a non-proprietary and non-confidential basis, and (ii) grant Jean-Marie Dautelle, Werner Keil a perpetual, non-exclusive, worldwide, fully paid-up, irrevocable license, with the right to sublicense through multiple levels of sublicensees, to incorporate, disclose, and use without limitation the Feedback for any purpose related to the Specification and future versions, implementations, and test suites thereof.
+#
+#
+# GENERAL TERMS
+#
+# Any action related to this Agreement will be governed by California law and controlling U.S. federal law. The U.N. Convention for the International Sale of Goods and the choice of law rules of any jurisdiction will not apply.
+#
+# The Specification is subject to U.S. export control laws and may be subject to export or import regulations in other countries. Licensee agrees to comply strictly with all such laws and regulations and acknowledges that it has the responsibility to obtain such licenses to export, re-export or import as may be required after delivery to Licensee.
+#
+# This Agreement is the parties' entire agreement relating to its subject matter. It supersedes all prior or contemporaneous oral or written communications, proposals, conditions, representations and warranties and prevails over any conflicting or additional terms of any quote, order, acknowledgment, or other communication between the parties relating to its subject matter during the term of this Agreement. No modification to this Agreement will be binding, unless in writing and signed by an authorized representative of each party.
+#
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+#$Id: messages_de.properties 180 2010-02-23 15:11:17Z keilw $
+
+##javax.measure.unit.ImperialSystem.GALLON_UK = gallone_uk
+##javax.measure.unit.ImperialSystem.OUNCE_LIQUID = fl uz UK
+##javax.measure.unit.ImperialSystem.TON_UK = tonne_uk
+##javax.measure.unit.NonSI.DAY = tag
+##javax.measure.unit.NonSI.DAY_SIDEREAL = tag_sidereal
+##javax.measure.unit.NonSI.POINT = pkt
+##javax.measure.unit.NonSI.ROENTGEN = R\u00F6ntgen
+javax.measure.unit.USCustomarySystem.FOOT = fu\u00DF
+javax.measure.unit.USCustomarySystem.FOOT_SURVEY = fu\u00DF_survey_us
+javax.measure.unit.USCustomarySystem.GALLON_DRY = gallone_trocken_us
+##javax.measure.unit.USCustomarySystem.GRADE = grad
+##javax.measure.unit.USCustomarySystem.HORSEPOWER = ps
+##javax.measure.unit.USCustomarySystem.LIGHT_YEAR = lj
+javax.measure.unit.USCustomarySystem.MILES_PER_HOUR = mps
+javax.measure.unit.USCustomarySystem.OIL_BARREL = \u00F6lfa\u00DF
+javax.measure.unit.USCustomarySystem.OUNCE = uz
+javax.measure.unit.USCustomarySystem.OUNCE_LIQUID = fl uz US
+##javax.measure.unit.USCustomarySystem.SPHERE = sph\u00E4re
+javax.measure.unit.USCustomarySystem.TABLESPOON = Essl
+javax.measure.unit.USCustomarySystem.TEASPOON = teel
+javax.measure.unit.USCustomarySystem.TON = tonne_us
+##javax.measure.unit.USCustomarySystem.WEEK = woche
+##javax.measure.unit.USCustomarySystem.YEAR_CALENDAR = jahr
+##javax.measure.unit.USCustomarySystem.YEAR_SIDEREAL = jahr_sidereal
diff --git a/src/main/resources/javax/measure/unit/format/messages_en_GB.properties b/src/main/resources/javax/measure/unit/format/messages_en_GB.properties
new file mode 100644
index 0000000..e6ad917
--- /dev/null
+++ b/src/main/resources/javax/measure/unit/format/messages_en_GB.properties
@@ -0,0 +1,97 @@
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+#
+# Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+# See LICENSE.txt for the Specification License
+#
+# See LICENSE.txt for the full Specification License
+#
+#
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+#
+# JEAN-MARIE DAUTELLE, WERNER KEIL ARE WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS LICENSE AGREEMENT ("AGREEMENT"). PLEASE READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY. BY DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF THIS AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY THEM, SELECT THE "DECLINE" BUTTON AT THE BOTTOM OF THIS PAGE AND THE DOWNLOADING PROCESS WILL NOT CONTINUE.
+#
+# Specification: JSR 275 - Units Specification ("Specification")
+#
+# Version: 0.9.4
+# Status: Pre-FCS Public Release
+# Release: December 4, 2009
+#
+# Copyright 2005-2009 Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+#
+# NOTICE
+#
+# The Specification is protected by copyright and the information described therein may be protected by one or more U.S. patents, foreign patents, or pending applications. Except as provided under the following license, no part of the Specification may be reproduced in any form by any means without the prior written authorization of Jean-Marie Dautelle, Werner Keil and its
+# licensors, if any. Any use of the Specification and the information described therein will be governed by the terms and conditions of this Agreement.
+#
+#
+# Subject to the terms and conditions of this license, including your compliance with Paragraphs 1, 2 and 3 below, Jean-Marie Dautelle and Werner Keil hereby grant you a fully-paid, non-exclusive, non-transferable, limited license (without the right to sublicense) under Jean-Marie Dautelle and Werner Keil's intellectual property rights to:
+#
+# 1. Review the Specification for the purposes of evaluation. This includes: (i) developing implementations of the Specification for your internal, non-commercial use; (ii) discussing the Specification with any third party; and (iii) excerpting brief portions of the Specification in oral or written communications which discuss the Specification provided that such excerpts do not in the aggregate constitute a significant portion of the Specification.
+#
+# 2. Distribute implementations of the Specification to third parties for their testing and evaluation use, provided that any such implementation:
+#
+# (i) does not modify, subset, superset or otherwise extend the Licensor Name Space, or include any public or protected packages, classes, Java interfaces, fields or methods within the Licensor Name Space other than those required/authorized by the Specification or Specifications being implemented;
+#
+# (ii) is clearly and prominently marked with the word "UNTESTED" or "EARLY ACCESS" or "INCOMPATIBLE" or "UNSTABLE" or "BETA" in any list of available builds and in proximity to every link initiating its download, where the list or link is under Licensee's control; and
+#
+# (iii) includes the following notice:
+# "This is an implementation of an early-draft specification developed under the Java Community Process (JCP) and is made available for testing and evaluation purposes only. The code is not compatible with any specification of the JCP."
+#
+# 3. Distribute applications written to the Specification to third parties for their testing and evaluation use, provided that any such application includes the following notice:
+# "This is an application written to interoperate with an early-draft specification developed under the Java Community Process (JCP) and is made available for testing and evaluation purposes only. The code is not compatible with any specification of the JCP."
+#
+# The grant set forth above concerning your distribution of implementations of the Specification is contingent upon your agreement to terminate development and distribution of your implementation of early draft upon final completion of the Specification. If you fail to do so, the foregoing grant shall be considered null and void.
+#
+# Other than this limited license, you acquire no right, title or interest in or to the Specification or any other Jean-Marie Dautelle and Werner Keil intellectual property, and the Specification may only be used in accordance with the license terms set forth herein. This license will expire on the earlier of: (a) two (2) years from the date of Release listed above; (b) the date on which the final version of the Specification is publicly released; or (c) the date on which the Java Specification Request (JSR) to which the Specification corresponds is withdrawn. In addition, this license will terminate immediately without notice from Jean-Marie Dautelle, Werner Keil if you fail to comply with any provision of this license. Upon termination, you must cease use of or destroy the Specification.
+#
+# "Licensor Name Space" means the public class or interface declarations whose names begin with "java", "javax", "org.jscience" or their equivalents in any subsequent naming convention adopted through the Java Community Process, or any recognized successors or replacements thereof
+#
+#
+# TRADEMARKS
+#
+# No right, title, or interest in or to any trademarks, service marks, or trade names of Jean-Marie Dautelle, Werner Keil or Jean-Marie Dautelle and Werner Keil's licensors is granted hereunder. Java and Java-related logos, marks and names are trademarks or registered trademarks of Sun Microsystems, Inc. in the U.S. and other countries.
+#
+#
+# DISCLAIMER OF WARRANTIES
+#
+# THE SPECIFICATION IS PROVIDED "AS IS" AND IS EXPERIMENTAL AND MAY CONTAIN DEFECTS OR DEFICIENCIES WHICH CANNOT OR WILL NOT BE CORRECTED BY JEAN-MARIE DAUTELLE, WERNER KEIL. JEAN-MARIE DAUTELLE AND WERNER KEIL MAKE NO REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT THAT THE CONTENTS OF THE SPECIFICATION ARE SUITABLE FOR ANY PURPOSE OR THAT ANY PRACTICE OR IMPLEMENTATION OF SUCH CONTENTS WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADE SECRETS OR OTHER RIGHTS. This document does not represent any commitment to release or implement any portion of the Specification in any product.
+#
+# THE SPECIFICATION COULD INCLUDE TECHNICAL INACCURACIES OR TYPOGRAPHICAL ERRORS. CHANGES ARE PERIODICALLY ADDED TO THE INFORMATION THEREIN; THESE CHANGES WILL BE INCORPORATED INTO NEW VERSIONS OF THE SPECIFICATION, IF ANY. JEAN-MARIE DAUTELL AND WERNER KEIL MAY MAKE IMPROVEMENTS AND/OR CHANGES TO THE PRODUCT(S) AND/OR THE PROGRAM(S) DESCRIBED IN THE SPECIFICATION AT ANY TIME. Any use of such changes in the Specification will be governed by the then-current license for the applicable version of the Specification.
+#
+#
+# LIMITATION OF LIABILITY
+#
+# TO THE EXTENT NOT PROHIBITED BY LAW, IN NO EVENT WILL JEAN-MARIE DAUTELLE, WERNER KEIL OR THEIR LICENSORS BE LIABLE FOR ANY DAMAGES, INCLUDING WITHOUT LIMITATION, LOST REVENUE, PROFITS OR DATA, OR FOR SPECIAL, INDIRECT, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF OR RELATED TO ANY FURNISHING, PRACTICING, MODIFYING OR ANY USE OF THE SPECIFICATION, EVEN IF JEAN-MARIE DAUTELLE, WERNER KEIL AND/OR ITS LICENSORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+#
+# You will hold Jean-Marie Dautelle, Werner Keil (and its licensors) harmless from any claims based on your use of the Specification for any purposes other than the limited right of evaluation as described above, and from any claims that later versions or releases of any Specification furnished to you are incompatible with the Specification provided to you under this license.
+#
+#
+# RESTRICTED RIGHTS LEGEND
+#
+# If this Software is being acquired by or on behalf of the U.S. Government or by a U.S. Government prime contractor or subcontractor (at any tier), then the Government's rights in the Software and accompanying documentation shall be only as set forth in this license; this is in
+# accordance with 48 C.F.R. 227.7201 through 227.7202-4 (for Department of Defense (DoD) acquisitions) and with 48 C.F.R. 2.101 and 12.212 (for non-DoD acquisitions).
+#
+#
+# REPORT
+#
+# You may wish to report any ambiguities, inconsistencies or inaccuracies you may find in connection with your evaluation of the Specification ("Feedback"). To the extent that you provide Jean-Marie Dautelle, Werner Keil with any Feedback, you hereby: (i) agree that such Feedback is provided on a non-proprietary and non-confidential basis, and (ii) grant Jean-Marie Dautelle, Werner Keil a perpetual, non-exclusive, worldwide, fully paid-up, irrevocable license, with the right to sublicense through multiple levels of sublicensees, to incorporate, disclose, and use without limitation the Feedback for any purpose related to the Specification and future versions, implementations, and test suites thereof.
+#
+#
+# GENERAL TERMS
+#
+# Any action related to this Agreement will be governed by California law and controlling U.S. federal law. The U.N. Convention for the International Sale of Goods and the choice of law rules of any jurisdiction will not apply.
+#
+# The Specification is subject to U.S. export control laws and may be subject to export or import regulations in other countries. Licensee agrees to comply strictly with all such laws and regulations and acknowledges that it has the responsibility to obtain such licenses to export, re-export or import as may be required after delivery to Licensee.
+#
+# This Agreement is the parties' entire agreement relating to its subject matter. It supersedes all prior or contemporaneous oral or written communications, proposals, conditions, representations and warranties and prevails over any conflicting or additional terms of any quote, order, acknowledgment, or other communication between the parties relating to its subject matter during the term of this Agreement. No modification to this Agreement will be binding, unless in writing and signed by an authorized representative of each party.
+
+##javax.measure.unit.ImperialSystem.GALLON_UK = gal
+##javax.measure.unit.ImperialSystem.OUNCE_LIQUID = fl oz
+##javax.measure.unit.ImperialSystem.TON_UK = ton
+javax.measure.unit.USCustomarySystem.GALLON_LIQUID = gal_us
+javax.measure.unit.USCustomarySystem.OUNCE_LIQUID = fl oz US
+javax.measure.unit.USCustomarySystem.TON = ton_us
diff --git a/src/main/resources/javax/measure/unit/format/messages_en_US.properties b/src/main/resources/javax/measure/unit/format/messages_en_US.properties
new file mode 100644
index 0000000..545cdcb
--- /dev/null
+++ b/src/main/resources/javax/measure/unit/format/messages_en_US.properties
@@ -0,0 +1,12 @@
+#
+# Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+# See LICENSE.txt for the Specification License
+#
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+
+javax.measure.unit.USCustomarySystem.ELECTRON_VOLT = eV
+javax.measure.unit.USCustomarySystem.GALLON_LIQUID = gal
+javax.measure.unit.USCustomarySystem.OUNCE_LIQUID = fl oz
+javax.measure.unit.USCustomarySystem.TON = ton
diff --git a/src/main/resources/javax/measure/unit/format/messages_fr.properties b/src/main/resources/javax/measure/unit/format/messages_fr.properties
new file mode 100644
index 0000000..c1b842f
--- /dev/null
+++ b/src/main/resources/javax/measure/unit/format/messages_fr.properties
@@ -0,0 +1,11 @@
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+#
+# Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+# See LICENSE.txt for the Specification License
+#
+# NOTE: as a Java properties file, this file must use the
+# ISO 8859-1 encoding, so all non-ASCII Unicode characters
+# must be escaped using the \uXXXX syntax.
+# See http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html#encoding
diff --git a/src/main/resources/javax/measure/unit/format/messages_fr_CA.properties b/src/main/resources/javax/measure/unit/format/messages_fr_CA.properties
new file mode 100644
index 0000000..5b4f52b
--- /dev/null
+++ b/src/main/resources/javax/measure/unit/format/messages_fr_CA.properties
@@ -0,0 +1,14 @@
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+#
+# Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+# See LICENSE.txt for the Specification License
+#
+# NOTE: as a Java properties file, this file must use the
+# ISO 8859-1 encoding, so all non-ASCII Unicode characters
+# must be escaped using the \uXXXX syntax.
+# See http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html#encoding
+
+javax.measure.unit.USCustomarySystem.FOOT = pi
+javax.measure.unit.USCustomarySystem.OUNCE_LIQUID = oz liq
diff --git a/src/main/resources/javax/measure/unit/format/messages_ja.properties b/src/main/resources/javax/measure/unit/format/messages_ja.properties
new file mode 100644
index 0000000..d61eaeb
--- /dev/null
+++ b/src/main/resources/javax/measure/unit/format/messages_ja.properties
@@ -0,0 +1,83 @@
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+#
+# Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+# See LICENSE.txt for the Specification License
+#
+# NOTE: as a Java properties file, this file must use the
+# ISO 8859-1 encoding, so all non-ASCII Unicode characters
+# must be escaped using the \uXXXX syntax.
+# See http://java.sun.com/j2se/1.5.0/docs/api/java/util/Properties.html#encoding
+
+##javax.measure.unit.ImperialSystem.OUNCE_LIQUID = fl oz UK
+##javax.measure.unit.ImperialSystem.TON_UK = ton_uk
+# SI Units
+javax.measure.unit.MetricSystem.AMPERE = A
+javax.measure.unit.MetricSystem.AMPERE_TURN = At
+javax.measure.unit.MetricSystem.BECQUEREL = Bq
+javax.measure.unit.MetricSystem.CANDELA = cd
+javax.measure.unit.MetricSystem.CELSIUS = \u00B0C
+javax.measure.unit.MetricSystem.CELSIUS.1 = \u2103
+javax.measure.unit.MetricSystem.CELSIUS.2 = \u6442\u6C0F\u6E29\u5EA6
+javax.measure.unit.MetricSystem.COULOMB = C
+javax.measure.unit.MetricSystem.FARAD = F
+javax.measure.unit.MetricSystem.GRAM = g
+javax.measure.unit.MetricSystem.GRAY = Gy
+javax.measure.unit.MetricSystem.HENRY = H
+javax.measure.unit.MetricSystem.HERTZ = Hz
+javax.measure.unit.MetricSystem.HERTZ.2 = \u30D8\u30EB\u30C4
+javax.measure.unit.MetricSystem.JOULE = J
+javax.measure.unit.MetricSystem.KATAL = kat
+javax.measure.unit.MetricSystem.KELVIN = K
+javax.measure.unit.MetricSystem.LUMEN = lm
+javax.measure.unit.MetricSystem.LUX = lx
+javax.measure.unit.MetricSystem.METRE = m
+javax.measure.unit.MetricSystem.MOLE = mol
+javax.measure.unit.MetricSystem.NEWTON = N
+javax.measure.unit.MetricSystem.OHM = \u03A9
+javax.measure.unit.MetricSystem.PASCAL = Pa
+javax.measure.unit.MetricSystem.RADIAN = rad
+javax.measure.unit.MetricSystem.SECOND = s
+javax.measure.unit.MetricSystem.SIEMENS = S
+javax.measure.unit.MetricSystem.SIEVERT = Sv
+javax.measure.unit.MetricSystem.STERADIAN = sr
+javax.measure.unit.MetricSystem.TESLA = T
+javax.measure.unit.MetricSystem.VOLT = V
+javax.measure.unit.MetricSystem.WATT = W
+javax.measure.unit.MetricSystem.WEBER = Wb
+# Non-SI Units
+##javax.measure.unit.NonSI.PERCENT = %
+##javax.measure.unit.NonSI.POINT = pt
+javax.measure.unit.USCustomarySystem.FLUID_DRAM = fl dr
+javax.measure.unit.USCustomarySystem.LITER = l
+javax.measure.unit.USCustomarySystem.METER = m
+javax.measure.unit.USCustomarySystem.MILES_PER_HOUR = mph
+javax.measure.unit.USCustomarySystem.MINIM = min
+javax.measure.unit.USCustomarySystem.OIL_BARREL = bbl
+javax.measure.unit.USCustomarySystem.OUNCE = oz
+javax.measure.unit.USCustomarySystem.OUNCE_LIQUID = fl oz
+javax.measure.unit.USCustomarySystem.TABLESPOON = Tbsp
+javax.measure.unit.USCustomarySystem.TEASPOON = tsp
+javax.measure.unit.USCustomarySystem.TON = \u30C8\u30F3
+# SI Prefixes
+javax.measure.unit.format.ParsePrefix.ATTO = a
+javax.measure.unit.format.ParsePrefix.CENTI = c
+javax.measure.unit.format.ParsePrefix.DECI = d
+javax.measure.unit.format.ParsePrefix.DEKA = da
+javax.measure.unit.format.ParsePrefix.EXA = E
+javax.measure.unit.format.ParsePrefix.FEMTO = f
+javax.measure.unit.format.ParsePrefix.GIGA = G
+javax.measure.unit.format.ParsePrefix.HECTO = h
+javax.measure.unit.format.ParsePrefix.KILO = k
+javax.measure.unit.format.ParsePrefix.MEGA = M
+javax.measure.unit.format.ParsePrefix.MICRO = \u00B5
+javax.measure.unit.format.ParsePrefix.MILLI = m
+javax.measure.unit.format.ParsePrefix.NANO = n
+javax.measure.unit.format.ParsePrefix.PETA = P
+javax.measure.unit.format.ParsePrefix.PICO = p
+javax.measure.unit.format.ParsePrefix.TERA = T
+javax.measure.unit.format.ParsePrefix.YOCTO = y
+javax.measure.unit.format.ParsePrefix.YOTTA = Y
+javax.measure.unit.format.ParsePrefix.ZEPTO = z
+javax.measure.unit.format.ParsePrefix.ZETTA = Z
diff --git a/src/main/resources/javax/measure/unit/format/messages_pt.properties b/src/main/resources/javax/measure/unit/format/messages_pt.properties
new file mode 100644
index 0000000..764a26a
--- /dev/null
+++ b/src/main/resources/javax/measure/unit/format/messages_pt.properties
@@ -0,0 +1,13 @@
+#
+# Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+# See LICENSE.txt for the Specification License
+#
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+
+javax.measure.unit.USCustomarySystem.MINIM = minim
+javax.measure.unit.USCustomarySystem.TABLESPOON.2 = Colher de sopa
+javax.measure.unit.USCustomarySystem.TEASPOON.2 = Colher de ch\u00E1
+javax.measure.unit.USCustomarySystem.TON = t
+javax.measure.unit.format.ParsePrefix.MICRO = mu
diff --git a/src/main/resources/javax/measure/unit/format/messages_ru.properties b/src/main/resources/javax/measure/unit/format/messages_ru.properties
new file mode 100644
index 0000000..1261846
--- /dev/null
+++ b/src/main/resources/javax/measure/unit/format/messages_ru.properties
@@ -0,0 +1,68 @@
+#
+# Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+# All rights reserved.
+#
+# See LICENSE.txt for the Specification License
+#
+#Generated by ResourceBundle Editor (http://eclipse-rbe.sourceforge.net)
+
+javax.measure.unit.MetricSystem.BECQUEREL = \u0411\u043A
+javax.measure.unit.MetricSystem.CANDELA = \u043A\u0434
+javax.measure.unit.MetricSystem.COULOMB = \u041A\u043B
+javax.measure.unit.MetricSystem.FARAD = \u0424
+javax.measure.unit.MetricSystem.GRAM = \u0433
+javax.measure.unit.MetricSystem.GRAY = \u0413\u0440
+javax.measure.unit.MetricSystem.HENRY = \u0413\u043D
+javax.measure.unit.MetricSystem.HERTZ = \u0413\u0446
+javax.measure.unit.MetricSystem.HERTZ.2 = \u0433\u0435\u0440\u0446
+javax.measure.unit.MetricSystem.JOULE = \u0414\u0436
+javax.measure.unit.MetricSystem.KATAL = \u043A\u0430\u0442
+javax.measure.unit.MetricSystem.KELVIN = \u041A
+javax.measure.unit.MetricSystem.LUMEN = \u043B\u043C
+javax.measure.unit.MetricSystem.LUX = \u043B\u043A
+javax.measure.unit.MetricSystem.METRE = \u043C
+javax.measure.unit.MetricSystem.MOLE = \u043C\u043E\u043B\u044C
+javax.measure.unit.MetricSystem.NEWTON = \u041D
+javax.measure.unit.MetricSystem.OHM = \u041E\u043C
+javax.measure.unit.MetricSystem.PASCAL = \u041F\u0430
+javax.measure.unit.MetricSystem.RADIAN = \u0440\u0430\u0434
+javax.measure.unit.MetricSystem.SECOND = \u0441
+javax.measure.unit.MetricSystem.SIEMENS = \u0421\u043C
+javax.measure.unit.MetricSystem.SIEVERT = \u0417\u0432
+javax.measure.unit.MetricSystem.STERADIAN = \u0441\u0440
+javax.measure.unit.MetricSystem.TESLA = \u0422\u043B
+javax.measure.unit.MetricSystem.VOLT = \u0412
+javax.measure.unit.MetricSystem.WATT = \u0412\u0442
+javax.measure.unit.MetricSystem.WEBER = \u0412\u0431
+javax.measure.unit.USCustomarySystem.ELECTRON_VOLT = e\u0412
+javax.measure.unit.USCustomarySystem.FLUID_DRAM = \u0434\u0440\u0430\u0445\u043C\u0430
+javax.measure.unit.USCustomarySystem.LITER = \u043B
+javax.measure.unit.USCustomarySystem.METER = \u043C
+javax.measure.unit.USCustomarySystem.MILES_PER_HOUR = \u043C\u0438\u043B\u044C \u0432 \u0447\u0430\u0441
+javax.measure.unit.USCustomarySystem.MINIM = \u043C\u0438\u043D\u0438\u043C
+javax.measure.unit.USCustomarySystem.OIL_BARREL = \u0431\u0430\u0440\u0440\u0435\u043B\u044C
+javax.measure.unit.USCustomarySystem.OUNCE = \u0443\u043D\u0446\u0438\u044F
+javax.measure.unit.USCustomarySystem.OUNCE_LIQUID = \u0436\u0438\u0434\u043A\u0430\u044F \u0443\u043D\u0446\u0438\u044F
+javax.measure.unit.USCustomarySystem.TABLESPOON = \u0441\u0442 \u043B\u043E\u0436\u043A\u0430
+javax.measure.unit.USCustomarySystem.TEASPOON = \u0447\u0430\u0439\u043D\u0430\u044F \u043B\u043E\u0436\u043A\u0430
+javax.measure.unit.USCustomarySystem.TON = \u0422
+javax.measure.unit.format.ParsePrefix.ATTO = a
+javax.measure.unit.format.ParsePrefix.CENTI = c
+javax.measure.unit.format.ParsePrefix.DECI = \u0434
+javax.measure.unit.format.ParsePrefix.DEKA = \u0434\u0430
+javax.measure.unit.format.ParsePrefix.EXA = \u042D
+javax.measure.unit.format.ParsePrefix.FEMTO = \u0444
+javax.measure.unit.format.ParsePrefix.GIGA = \u0413
+javax.measure.unit.format.ParsePrefix.HECTO = \u0433
+javax.measure.unit.format.ParsePrefix.KILO = \u043A
+javax.measure.unit.format.ParsePrefix.MEGA = M
+javax.measure.unit.format.ParsePrefix.MICRO = \u043C\u043A
+javax.measure.unit.format.ParsePrefix.MILLI = \u043C
+javax.measure.unit.format.ParsePrefix.NANO = \u043D
+javax.measure.unit.format.ParsePrefix.PETA = \u041F
+javax.measure.unit.format.ParsePrefix.PICO = \u043F
+javax.measure.unit.format.ParsePrefix.TERA = T
+javax.measure.unit.format.ParsePrefix.YOCTO = \u0438
+javax.measure.unit.format.ParsePrefix.YOTTA = \u0418
+javax.measure.unit.format.ParsePrefix.ZEPTO = \u0437
+javax.measure.unit.format.ParsePrefix.ZETTA = \u0417
diff --git a/src/test/java/javax/measure/Benchmark.java b/src/test/java/javax/measure/Benchmark.java
new file mode 100644
index 0000000..51f2a24
--- /dev/null
+++ b/src/test/java/javax/measure/Benchmark.java
@@ -0,0 +1,73 @@
+package javax.measure;
+
+import java.util.Random;
+import javax.measure.quantity.Mass;
+import javax.measure.quantity.QuantityFactory;
+import javax.measure.unit.Unit;
+import javax.measure.unit.UnitConverter;
+import static javax.measure.unit.MetricSystem.*;
+
+public class Benchmark {
+ private static final int N = 100000;
+
+ @SuppressWarnings({"unchecked","rawtypes"}) // Because of generic array creation.
+ private static final Unit[] UNITS = new Unit[] {
+ MEGA(GRAM),
+ KILOGRAM,
+ GRAM,
+ CENTI(GRAM),
+ MILLI(GRAM),
+ MICRO(GRAM)
+ };
+
+ private static long usingQuantities(final long seed) {
+ long time = System.currentTimeMillis();
+ final Random r = new Random(seed);
+ final QuantityFactory factory = QuantityFactory.getInstance(Mass.class);
+ final Mass[] m = new Mass[N];
+ for (int i=0; i targetUnit = UNITS[r.nextInt(UNITS.length)];
+ double sum = 0;
+ for (int i=0; i factory = QuantityFactory.getInstance(Length.class);
+ final Length length = factory.create(4.0, METRE);
+
+ assertSame ("Wrong tuple element.", METRE, length.getUnit());
+ assertEquals("Wrong tuple element.", Double.valueOf(4.0), length.getValue());
+ assertEquals("Wrong conversion.", 4.0, length.doubleValue(METRE), 0.0);
+ assertEquals("Wrong conversion.", 400.0, length.doubleValue(CENTI(METRE)), 0.0);
+ assertEquals("Wrong conversion.", 400, length.getValue(CENTI(METRE)).longValue());
+
+ final Length other = factory.create(8.0, METRE);
+ assertSame ("Expected same implementation class.", length.getClass(), other.getClass());
+ assertFalse("Quantities shall not be equal.", length.equals(other));
+ assertFalse("Quantities shall not be equal.", length.hashCode() == other.hashCode());
+
+ final Length equivalent = factory.create(4.0, METRE);
+ assertSame ("Expected same implementation class.", length.getClass(), equivalent.getClass());
+ assertFalse ("Quantities shall not be equal.", equivalent.equals(other));
+ assertTrue ("Quantities shall be equal.", equivalent.equals(length));
+ assertTrue ("'equals' shall be symmetric.", length.equals(equivalent));
+ assertEquals("Expected same hash code.", length.hashCode(), equivalent.hashCode());
+ }
+
+ /**
+ * Tests the creation quantities backed by the {@link Number} class.
+ */
+ @Test
+ public void testNumber() {
+ final QuantityFactory factory = QuantityFactory.getInstance(Length.class);
+ final BigInteger value = BigInteger.valueOf(4);
+ final Length length = factory.create(value, METRE);
+
+ assertSame ("Wrong tuple element.", METRE, length.getUnit());
+ assertEquals ("Wrong tuple element.", BigInteger.valueOf(4), length.getValue());
+ assertEquals("Wrong conversion.", 4.0, length.doubleValue(METRE), 0.0);
+ assertEquals("Wrong conversion.", 400.0, length.doubleValue(CENTI(METRE)), 0.0);
+ assertEquals("Wrong conversion.", 400, length.getValue(CENTI(METRE)).longValue());
+
+ // Quantity equivalent to 'length', but backed by a double.
+ // This is not the same class, but should nevertheless by considered equivalent.
+ final Length equivalent = factory.create(4, METRE);
+ assertTrue ("Quantities shall be equal.", equivalent.equals(length));
+ assertTrue ("'equals' shall be symmetric.", length.equals(equivalent));
+ assertEquals ("Expected same hash code.", length.hashCode(), equivalent.hashCode());
+ }
+}
diff --git a/src/test/java/javax/measure/unit/Planet.java b/src/test/java/javax/measure/unit/Planet.java
new file mode 100644
index 0000000..b4f6a3c
--- /dev/null
+++ b/src/test/java/javax/measure/unit/Planet.java
@@ -0,0 +1,58 @@
+/**
+ * Copyright (c) 2005-2010, Jean-Marie Dautelle, Werner Keil
+ * All rights reserved.
+ *
+ * See LICENSE.txt for the Specification License
+ */
+
+package javax.measure.unit;
+
+import javax.measure.quantity.Length;
+import javax.measure.quantity.Mass;
+import static javax.measure.unit.USCustomarySystem.METER;
+import static javax.measure.unit.MetricSystem.KILOGRAM;
+
+/**
+ * @author Werner Keil
+ * @author Josh Bloch
+ *
+ * This enum is based on Josh Bloch's example in "Effective Java Second Edition"
+ *
+ *
+ * Suppose you want to add data and behavior to an enum.
+ * For example consider the planets of the solar system.
+ * Each planet knows its mass and radius, and can calculate its surface gravity and the weight of an object on the planet.
+ * Here is how it looks:
+ *
+ */
+public enum Planet {
+ MERCURY (KILOGRAM.multiply(3.303e+23), METER.multiply(2.4397e6)),
+ VENUS (KILOGRAM.multiply(4.869e+24), METER.multiply(6.0518e6)),
+ EARTH (KILOGRAM.multiply(5.976e+24), METER.multiply(6.37814e6)),
+ MARS (KILOGRAM.multiply(6.421e+23), METER.multiply(3.3972e6)),
+ JUPITER (KILOGRAM.multiply(1.9e+27), METER.multiply(7.1492e7)),
+ SATURN (KILOGRAM.multiply(5.688e+26), METER.multiply(6.0268e7)),
+ URANUS (KILOGRAM.multiply(8.686e+25), METER.multiply(2.5559e7)),
+ NEPTUNE (KILOGRAM.multiply(1.024e+26), METER.multiply(2.4746e7)),
+ PLUTO (KILOGRAM.multiply(1.27e+22), METER.multiply(1.137e6));
+
+ private final Unit mass; // in kilograms
+ private final Unit radius; // in meters
+ Planet(Unit mass, Unit radius) {
+ this.mass = mass;
+ this.radius = radius;
+ }
+ public Unit mass() { return mass; }
+ public Unit radius() { return radius; }
+
+ // universal gravitational constant (m3 kg-1 s-2)
+// public static Unit G = Unit.valueOf(6.67300E-11);
+ private static double G = 6.67300E-11;
+
+ public Unit> surfaceGravity() {
+ return mass.divide(radius.multiply(radius)).multiply(G);
+ }
+ public Unit> surfaceWeight(Unit otherMass) {
+ return otherMass.multiply(surfaceGravity());
+ }
+}