| /* |
| * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences. |
| * Copyright (C) 2006 - JScience (http://jscience.org/) |
| * All rights reserved. |
| * |
| * Permission to use, copy, modify, and distribute this software is |
| * freely granted, provided that this notice is preserved. |
| */ |
| package javax.measure.unit; |
| |
| import static javax.measure.unit.SI.E1; |
| import static javax.measure.unit.SI.E12; |
| import static javax.measure.unit.SI.E15; |
| import static javax.measure.unit.SI.E18; |
| import static javax.measure.unit.SI.E2; |
| import static javax.measure.unit.SI.E21; |
| import static javax.measure.unit.SI.E24; |
| import static javax.measure.unit.SI.E3; |
| import static javax.measure.unit.SI.E6; |
| import static javax.measure.unit.SI.E9; |
| import static javax.measure.unit.SI.Em1; |
| import static javax.measure.unit.SI.Em12; |
| import static javax.measure.unit.SI.Em15; |
| import static javax.measure.unit.SI.Em18; |
| import static javax.measure.unit.SI.Em2; |
| import static javax.measure.unit.SI.Em21; |
| import static javax.measure.unit.SI.Em24; |
| import static javax.measure.unit.SI.Em3; |
| import static javax.measure.unit.SI.Em6; |
| import static javax.measure.unit.SI.Em9; |
| |
| import java.io.IOException; |
| import java.text.FieldPosition; |
| import java.text.Format; |
| import java.text.ParseException; |
| import java.text.ParsePosition; |
| import java.util.HashMap; |
| import java.util.Locale; |
| |
| //@RETROWEAVER import javolution.text.Appendable; |
| import javax.measure.converter.AddConverter; |
| import javax.measure.converter.MultiplyConverter; |
| import javax.measure.converter.RationalConverter; |
| import javax.measure.converter.UnitConverter; |
| import javax.measure.quantity.Quantity; |
| |
| /** |
| * <p> This class provides the interface for formatting and parsing {@link |
| * Unit units}.</p> |
| * |
| * <p> For all {@link SI} units, the 20 SI prefixes used to form decimal |
| * multiples and sub-multiples of SI units are recognized. |
| * {@link NonSI} units are directly recognized. For example:[code] |
| * Unit.valueOf("m°C").equals(SI.MILLI(SI.CELSIUS)) |
| * Unit.valueOf("kW").equals(SI.KILO(SI.WATT)) |
| * Unit.valueOf("ft").equals(SI.METER.multiply(0.3048))[/code]</p> |
| * |
| * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> |
| * @author Eric Russell |
| * @version 1.3, August 29, 2006 |
| */ |
| public abstract class UnitFormat extends Format { |
| |
| /** |
| * |
| */ |
| private static final long serialVersionUID = 1098093680937760914L; |
| |
| /** |
| * Holds the standard unit format. |
| */ |
| private static final DefaultFormat DEFAULT = new DefaultFormat(); |
| |
| /** |
| * Holds the ASCIIFormat unit format. |
| */ |
| private static final ASCIIFormat ASCII = new ASCIIFormat(); |
| |
| /** |
| * Returns the unit format for the default locale (format used by |
| * {@link Unit#valueOf(CharSequence) Unit.valueOf(CharSequence)} and |
| * {@link Unit#toString() Unit.toString()}). |
| * |
| * @return the default unit format (locale sensitive). |
| */ |
| public static UnitFormat getInstance() { |
| return UnitFormat.getInstance(Locale.getDefault()); |
| } |
| |
| /** |
| * Returns the unit format for the specified locale. |
| * |
| * @return the unit format for the specified locale. |
| */ |
| public static UnitFormat getInstance(Locale inLocale) { |
| return DEFAULT; // TBD: Implement Locale Format. |
| } |
| |
| /** |
| * Returns the <a href="http://aurora.regenstrief.org/UCUM/ucum.html">UCUM |
| * </a> international unit format; this format uses characters range |
| * <code>0000-007F</code> exclusively and <b>is not</b> locale-sensitive. |
| * For example: <code>kg.m/s2</code> |
| * |
| * @return the UCUM international format. |
| */ |
| public static UnitFormat getUCUMInstance() { |
| return UnitFormat.ASCII; // TBD - Provide UCUM implementation. |
| } |
| |
| /** |
| * Base constructor. |
| */ |
| protected UnitFormat() { |
| } |
| |
| /** |
| * Formats the specified unit. |
| * |
| * @param unit the unit to format. |
| * @param appendable the appendable destination. |
| * @throws IOException if an error occurs. |
| */ |
| public abstract Appendable format(Unit<?> unit, Appendable appendable) |
| throws IOException; |
| |
| /** |
| * Parses a sequence of character to produce a unit or a rational product |
| * of unit. |
| * |
| * @param csq the <code>CharSequence</code> to parse. |
| * @param pos an object holding the parsing index and error position. |
| * @return an {@link Unit} parsed from the character sequence. |
| * @throws IllegalArgumentException if the character sequence contains |
| * an illegal syntax. |
| */ |
| public abstract Unit<? extends Quantity> parseProductUnit(CharSequence csq, ParsePosition pos) |
| throws ParseException; |
| |
| /** |
| * Parses a sequence of character to produce a single unit. |
| * |
| * @param csq the <code>CharSequence</code> to parse. |
| * @param pos an object holding the parsing index and error position. |
| * @return an {@link Unit} parsed from the character sequence. |
| * @throws IllegalArgumentException if the character sequence does not contain |
| * a valid unit identifier. |
| */ |
| public abstract Unit<? extends Quantity> parseSingleUnit(CharSequence csq, ParsePosition pos) |
| throws ParseException; |
| |
| /** |
| * Attaches a system-wide label to the specified unit. For example: |
| * [code] |
| * UnitFormat.getInstance().label(DAY.multiply(365), "year"); |
| * UnitFormat.getInstance().label(METER.multiply(0.3048), "ft"); |
| * [/code] |
| * If the specified label is already associated to an unit the previous |
| * association is discarded or ignored. |
| * |
| * @param unit the unit being labelled. |
| * @param label the new label for this unit. |
| * @throws IllegalArgumentException if the label is not a |
| * {@link UnitFormat#isValidIdentifier(String)} valid identifier. |
| */ |
| public abstract void label(Unit<?> unit, String label); |
| |
| /** |
| * Attaches a system-wide alias to this unit. Multiple aliases may |
| * be attached to the same unit. Aliases are used during parsing to |
| * recognize different variants of the same unit. For example: |
| * [code] |
| * UnitFormat.getLocaleInstance().alias(METER.multiply(0.3048), "foot"); |
| * UnitFormat.getLocaleInstance().alias(METER.multiply(0.3048), "feet"); |
| * UnitFormat.getLocaleInstance().alias(METER, "meter"); |
| * UnitFormat.getLocaleInstance().alias(METER, "metre"); |
| * [/code] |
| * If the specified label is already associated to an unit the previous |
| * association is discarded or ignored. |
| * |
| * @param unit the unit being aliased. |
| * @param alias the alias attached to this unit. |
| * @throws IllegalArgumentException if the label is not a |
| * {@link UnitFormat#isValidIdentifier(String)} valid identifier. |
| */ |
| public abstract void alias(Unit<?> unit, String alias); |
| |
| /** |
| * Indicates if the specified name can be used as unit identifier. |
| * |
| * @param name the identifier to be tested. |
| * @return <code>true</code> if the name specified can be used as |
| * label or alias for this format;<code>false</code> otherwise. |
| */ |
| public abstract boolean isValidIdentifier(String name); |
| |
| /** |
| * Formats an unit and appends the resulting text to a given string |
| * buffer (implements <code>java.text.Format</code>). |
| * |
| * @param unit the unit to format. |
| * @param toAppendTo where the text is to be appended |
| * @param pos the field position (not used). |
| * @return <code>toAppendTo</code> |
| */ |
| public final StringBuffer format(Object unit, final StringBuffer toAppendTo, |
| FieldPosition pos) { |
| try { |
| Object dest = toAppendTo; |
| if (dest instanceof Appendable) { |
| format((Unit<?>) unit, (Appendable)dest); |
| } else { // When retroweaver is used to produce 1.4 binaries. |
| format((Unit<?>) unit, new Appendable() { |
| |
| public Appendable append(char arg0) throws IOException { |
| toAppendTo.append(arg0); |
| return null; |
| } |
| |
| public Appendable append(CharSequence arg0) throws IOException { |
| toAppendTo.append(arg0); |
| return null; |
| } |
| |
| public Appendable append(CharSequence arg0, int arg1, int arg2) throws IOException { |
| toAppendTo.append(arg0.subSequence(arg1, arg2)); |
| return null; |
| }}); |
| } |
| return toAppendTo; |
| } catch (IOException e) { |
| throw new Error(e); // Should never happen. |
| } |
| } |
| |
| /** |
| * Parses the text from a string to produce an object |
| * (implements <code>java.text.Format</code>). |
| * |
| * @param source the string source, part of which should be parsed. |
| * @param pos the cursor position. |
| * @return the corresponding unit or <code>null</code> if the string |
| * cannot be parsed. |
| */ |
| public final Unit<?> parseObject(String source, ParsePosition pos) { |
| int start = pos.getIndex(); |
| try { |
| return parseProductUnit(source, pos); |
| } catch (ParseException e) { |
| pos.setIndex(start); |
| pos.setErrorIndex(e.getErrorOffset()); |
| return null; |
| } |
| } |
| |
| |
| /** |
| * This class represents an exponent with both a power (numerator) |
| * and a root (denominator). |
| */ |
| private static class Exponent { |
| public final int pow; |
| public final int root; |
| public Exponent (int pow, int root) { |
| this.pow = pow; |
| this.root = root; |
| } |
| } |
| |
| /** |
| * This class represents the standard format. |
| */ |
| protected static class DefaultFormat extends UnitFormat { |
| |
| /** |
| * Holds the name to unit mapping. |
| */ |
| final HashMap<String, Unit<?>> _nameToUnit = new HashMap<String, Unit<?>>(); |
| |
| /** |
| * Holds the unit to name mapping. |
| */ |
| final HashMap<Unit<?>, String> _unitToName = new HashMap<Unit<?>, String>(); |
| |
| @Override |
| public void label(Unit<?> unit, String label) { |
| if (!isValidIdentifier(label)) |
| throw new IllegalArgumentException("Label: " + label |
| + " is not a valid identifier."); |
| synchronized (this) { |
| _nameToUnit.put(label, unit); |
| _unitToName.put(unit, label); |
| } |
| } |
| |
| @Override |
| public void alias(Unit<?> unit, String alias) { |
| if (!isValidIdentifier(alias)) |
| throw new IllegalArgumentException("Alias: " + alias |
| + " is not a valid identifier."); |
| synchronized (this) { |
| _nameToUnit.put(alias, unit); |
| } |
| } |
| |
| @Override |
| public boolean isValidIdentifier(String name) { |
| if ((name == null) || (name.length() == 0)) |
| return false; |
| for (int i = 0; i < name.length(); i++) { |
| if (!isUnitIdentifierPart(name.charAt(i))) |
| return false; |
| } |
| return true; |
| } |
| |
| static boolean isUnitIdentifierPart(char ch) { |
| return Character.isLetter(ch) || |
| (!Character.isWhitespace(ch) && !Character.isDigit(ch) |
| && (ch != '·') && (ch != '*') && (ch != '/') |
| && (ch != '(') && (ch != ')') && (ch != '[') && (ch != ']') |
| && (ch != '¹') && (ch != '²') && (ch != '³') |
| && (ch != '^') && (ch != '+') && (ch != '-')); |
| } |
| |
| // Returns the name for the specified unit or null if product unit. |
| public String nameFor(Unit<?> unit) { |
| // Searches label database. |
| String label = _unitToName.get(unit); |
| if (label != null) |
| return label; |
| if (unit instanceof BaseUnit) |
| return ((BaseUnit<?>) unit).getSymbol(); |
| if (unit instanceof AlternateUnit) |
| return ((AlternateUnit<?>) unit).getSymbol(); |
| if (unit instanceof TransformedUnit) { |
| TransformedUnit<?> tfmUnit = (TransformedUnit<?>) unit; |
| Unit<?> baseUnits = tfmUnit.getStandardUnit(); |
| UnitConverter cvtr = tfmUnit.toStandardUnit(); |
| StringBuffer result = new StringBuffer(); |
| String baseUnitName = baseUnits.toString(); |
| if ((baseUnitName.indexOf('·') >= 0) || |
| (baseUnitName.indexOf('*') >= 0) || |
| (baseUnitName.indexOf('/') >= 0)) { |
| // We could use parentheses whenever baseUnits is an |
| // instanceof ProductUnit, but most ProductUnits have aliases, |
| // so we'd end up with a lot of unnecessary parentheses. |
| result.append('('); |
| result.append(baseUnitName); |
| result.append(')'); |
| } else { |
| result.append(baseUnitName); |
| } |
| if (cvtr instanceof AddConverter) { |
| result.append('+'); |
| result.append(((AddConverter) cvtr).getOffset()); |
| } else if (cvtr instanceof RationalConverter) { |
| long dividend = ((RationalConverter) cvtr).getDividend(); |
| if (dividend != 1) { |
| result.append('*'); |
| result.append(dividend); |
| } |
| long divisor = ((RationalConverter) cvtr).getDivisor(); |
| if (divisor != 1) { |
| result.append('/'); |
| result.append(divisor); |
| } ; |
| } else if (cvtr instanceof MultiplyConverter) { |
| result.append('*'); |
| result.append(((MultiplyConverter) cvtr).getFactor()); |
| } else { // Other converters. |
| return "[" + baseUnits + "?]"; |
| } |
| return result.toString(); |
| } |
| // Compound unit. |
| if (unit instanceof CompoundUnit) { |
| CompoundUnit<?> cpdUnit = (CompoundUnit<?>) unit; |
| return nameFor(cpdUnit.getHigher()).toString() + ":" |
| + nameFor(cpdUnit.getLower()); |
| } |
| return null; // Product unit. |
| } |
| |
| // Returns the unit for the specified name. |
| public Unit<?> unitFor(String name) { |
| Unit<?> unit = _nameToUnit.get(name); |
| if (unit != null) |
| return unit; |
| unit = Unit.SYMBOL_TO_UNIT.get(name); |
| return unit; |
| } |
| |
| //////////////////////////// |
| // Parsing. |
| |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| public Unit<? extends Quantity> parseSingleUnit(CharSequence csq, ParsePosition pos) |
| throws ParseException { |
| int startIndex = pos.getIndex(); |
| String name = readIdentifier(csq, pos); |
| Unit unit = unitFor(name); |
| check(unit != null, name + " not recognized", csq, startIndex); |
| return unit; |
| } |
| |
| @SuppressWarnings({ "unchecked", "rawtypes" }) |
| @Override |
| public Unit<? extends Quantity> parseProductUnit(CharSequence csq, ParsePosition pos) |
| throws ParseException { |
| Unit result = Unit.ONE; |
| int token = nextToken(csq, pos); |
| switch (token) { |
| case IDENTIFIER: |
| result = parseSingleUnit(csq, pos); |
| break; |
| case OPEN_PAREN: |
| pos.setIndex(pos.getIndex() + 1); |
| result = parseProductUnit(csq, pos); |
| token = nextToken(csq, pos); |
| check(token == CLOSE_PAREN, "')' expected", csq, pos.getIndex()); |
| pos.setIndex(pos.getIndex() + 1); |
| break; |
| } |
| token = nextToken(csq, pos); |
| while (true) { |
| switch (token) { |
| case EXPONENT: |
| Exponent e = readExponent(csq, pos); |
| if (e.pow != 1) { |
| result = result.pow(e.pow); |
| } |
| if (e.root != 1) { |
| result = result.root(e.root); |
| } |
| break; |
| case MULTIPLY: |
| pos.setIndex(pos.getIndex() + 1); |
| token = nextToken(csq, pos); |
| if (token == INTEGER) { |
| long n = readLong(csq, pos); |
| if (n != 1) { |
| result = result.times(n); |
| } |
| } else if (token == FLOAT) { |
| double d = readDouble(csq, pos); |
| if (d != 1.0) { |
| result = result.times(d); |
| } |
| } else { |
| result = result.times(parseProductUnit(csq, pos)); |
| } |
| break; |
| case DIVIDE: |
| pos.setIndex(pos.getIndex() + 1); |
| token = nextToken(csq, pos); |
| if (token == INTEGER) { |
| long n = readLong(csq, pos); |
| if (n != 1) { |
| result = result.divide(n); |
| } |
| } else if (token == FLOAT) { |
| double d = readDouble(csq, pos); |
| if (d != 1.0) { |
| result = result.divide(d); |
| } |
| } else { |
| result = result.divide(parseProductUnit(csq, pos)); |
| } |
| break; |
| case PLUS: |
| pos.setIndex(pos.getIndex() + 1); |
| token = nextToken(csq, pos); |
| if (token == INTEGER) { |
| long n = readLong(csq, pos); |
| if (n != 1) { |
| result = result.plus(n); |
| } |
| } else if (token == FLOAT) { |
| double d = readDouble(csq, pos); |
| if (d != 1.0) { |
| result = result.plus(d); |
| } |
| } else { |
| throw new ParseException("not a number", pos.getIndex()); |
| } |
| break; |
| case EOF: |
| case CLOSE_PAREN: |
| return result; |
| default: |
| throw new ParseException("unexpected token " + token, pos.getIndex()); |
| } |
| token = nextToken(csq, pos); |
| } |
| } |
| |
| private static final int EOF = 0; |
| private static final int IDENTIFIER = 1; |
| private static final int OPEN_PAREN= 2; |
| private static final int CLOSE_PAREN= 3; |
| private static final int EXPONENT = 4; |
| private static final int MULTIPLY = 5; |
| private static final int DIVIDE = 6; |
| private static final int PLUS = 7; |
| private static final int INTEGER = 8; |
| private static final int FLOAT = 9; |
| |
| private int nextToken(CharSequence csq, ParsePosition pos) { |
| final int length = csq.length(); |
| while (pos.getIndex() < length) { |
| char c = csq.charAt(pos.getIndex()); |
| if (isUnitIdentifierPart(c)) { |
| return IDENTIFIER; |
| } else if (c == '(') { |
| return OPEN_PAREN; |
| } else if (c == ')') { |
| return CLOSE_PAREN; |
| } else if ((c == '^') || (c == '¹') || (c == '²') || (c == '³')) { |
| return EXPONENT; |
| } else if (c == '*') { |
| char c2 = csq.charAt(pos.getIndex() + 1); |
| if (c2 == '*') { |
| return EXPONENT; |
| } else { |
| return MULTIPLY; |
| } |
| } else if (c == '·') { |
| return MULTIPLY; |
| } else if (c == '/') { |
| return DIVIDE; |
| } else if (c == '+') { |
| return PLUS; |
| } else if ((c == '-') || Character.isDigit(c)) { |
| int index = pos.getIndex()+1; |
| while ((index < length) && |
| (Character.isDigit(c) || (c == '-') || (c == '.') || (c == 'E'))) { |
| c = csq.charAt(index++); |
| if (c == '.') { |
| return FLOAT; |
| } |
| } |
| return INTEGER; |
| } |
| pos.setIndex(pos.getIndex() + 1); |
| } |
| return EOF; |
| } |
| |
| private void check(boolean expr, String message, CharSequence csq, |
| int index) throws ParseException { |
| if (!expr) { |
| throw new ParseException(message + " (in " + csq |
| + " at index " + index + ")", index); |
| } |
| } |
| |
| private Exponent readExponent (CharSequence csq, ParsePosition pos) { |
| char c = csq.charAt(pos.getIndex()); |
| if (c == '^') { |
| pos.setIndex(pos.getIndex()+1); |
| } else if (c == '*') { |
| pos.setIndex(pos.getIndex()+2); |
| } |
| final int length = csq.length(); |
| int pow = 0; |
| boolean isPowNegative = false; |
| int root = 0; |
| boolean isRootNegative = false; |
| boolean isRoot = false; |
| while (pos.getIndex() < length) { |
| c = csq.charAt(pos.getIndex()); |
| if (c == '¹') { |
| if (isRoot) { |
| root = root * 10 + 1; |
| } else { |
| pow = pow * 10 + 1; |
| } |
| } else if (c == '²') { |
| if (isRoot) { |
| root = root * 10 + 2; |
| } else { |
| pow = pow * 10 + 2; |
| } |
| } else if (c == '³') { |
| if (isRoot) { |
| root = root * 10 + 3; |
| } else { |
| pow = pow * 10 + 3; |
| } |
| } else if (c == '-') { |
| if (isRoot) { |
| isRootNegative = true; |
| } else { |
| isPowNegative = true; |
| } |
| } else if ((c >= '0') && (c <= '9')) { |
| if (isRoot) { |
| root = root * 10 + (c - '0'); |
| } else { |
| pow = pow * 10 + (c - '0'); |
| } |
| } else if (c == ':') { |
| isRoot = true; |
| } else { |
| break; |
| } |
| pos.setIndex(pos.getIndex()+1); |
| } |
| if (pow == 0) pow = 1; |
| if (root == 0) root = 1; |
| return new Exponent(isPowNegative ? -pow : pow, |
| isRootNegative ? -root : root); |
| } |
| |
| private long readLong (CharSequence csq, ParsePosition pos) { |
| final int length = csq.length(); |
| int result = 0; |
| boolean isNegative = false; |
| while (pos.getIndex() < length) { |
| char c = csq.charAt(pos.getIndex()); |
| if (c == '-') { |
| isNegative = true; |
| } else if ((c >= '0') && (c <= '9')) { |
| result = result * 10 + (c - '0'); |
| } else { |
| break; |
| } |
| pos.setIndex(pos.getIndex()+1); |
| } |
| return isNegative ? -result : result; |
| } |
| |
| private double readDouble (CharSequence csq, ParsePosition pos) { |
| final int length = csq.length(); |
| int start = pos.getIndex(); |
| int end = start+1; |
| while (end < length) { |
| if ("012356789+-.E".indexOf(csq.charAt(end)) < 0) { |
| break; |
| } |
| end += 1; |
| } |
| pos.setIndex(end+1); |
| return Double.parseDouble(csq.subSequence(start,end).toString()); |
| } |
| |
| private String readIdentifier(CharSequence csq, ParsePosition pos) { |
| final int length = csq.length(); |
| int start = pos.getIndex(); |
| int i = start; |
| while ((++i < length) && isUnitIdentifierPart(csq.charAt(i))) { } |
| pos.setIndex(i); |
| return csq.subSequence(start, i).toString(); |
| } |
| |
| //////////////////////////// |
| // Formatting. |
| |
| @Override |
| public Appendable format(Unit<?> unit, Appendable appendable) |
| throws IOException { |
| String name = nameFor(unit); |
| if (name != null) |
| return appendable.append(name); |
| if (!(unit instanceof ProductUnit)) |
| throw new IllegalArgumentException("Cannot format given Object as a Unit"); |
| |
| // Product unit. |
| ProductUnit<?> productUnit = (ProductUnit<?>) unit; |
| int invNbr = 0; |
| |
| // Write positive exponents first. |
| boolean start = true; |
| for (int i = 0; i < productUnit.getUnitCount(); i++) { |
| int pow = productUnit.getUnitPow(i); |
| if (pow >= 0) { |
| if (!start) { |
| appendable.append('·'); // Separator. |
| } |
| name = nameFor(productUnit.getUnit(i)); |
| int root = productUnit.getUnitRoot(i); |
| append(appendable, name, pow, root); |
| start = false; |
| } else { |
| invNbr++; |
| } |
| } |
| |
| // Write negative exponents. |
| if (invNbr != 0) { |
| if (start) { |
| appendable.append('1'); // e.g. 1/s |
| } |
| appendable.append('/'); |
| if (invNbr > 1) { |
| appendable.append('('); |
| } |
| start = true; |
| for (int i = 0; i < productUnit.getUnitCount(); i++) { |
| int pow = productUnit.getUnitPow(i); |
| if (pow < 0) { |
| name = nameFor(productUnit.getUnit(i)); |
| int root = productUnit.getUnitRoot(i); |
| if (!start) { |
| appendable.append('·'); // Separator. |
| } |
| append(appendable, name, -pow, root); |
| start = false; |
| } |
| } |
| if (invNbr > 1) { |
| appendable.append(')'); |
| } |
| } |
| return appendable; |
| } |
| |
| private void append(Appendable appendable, CharSequence symbol, |
| int pow, int root) throws IOException { |
| appendable.append(symbol); |
| if ((pow != 1) || (root != 1)) { |
| // Write exponent. |
| if ((pow == 2) && (root == 1)) { |
| appendable.append('²'); // Square |
| } else if ((pow == 3) && (root == 1)) { |
| appendable.append('³'); // Cubic |
| } else { |
| // Use general exponent form. |
| appendable.append('^'); |
| appendable.append(String.valueOf(pow)); |
| if (root != 1) { |
| appendable.append(':'); |
| appendable.append(String.valueOf(root)); |
| } |
| } |
| } |
| } |
| |
| private static final long serialVersionUID = 1L; |
| } |
| |
| /** |
| * This class represents the ASCIIFormat format. |
| */ |
| protected static class ASCIIFormat extends DefaultFormat { |
| |
| @Override |
| public String nameFor(Unit<?> unit) { |
| // First search if specific ASCII name should be used. |
| String name = _unitToName.get(unit); |
| if (name != null) |
| return name; |
| // Else returns default name. |
| return DEFAULT.nameFor(unit); |
| } |
| |
| @Override |
| public Unit<?> unitFor(String name) { |
| // First search if specific ASCII name. |
| Unit<?> unit = _nameToUnit.get(name); |
| if (unit != null) |
| return unit; |
| // Else returns default mapping. |
| return DEFAULT.unitFor(name); |
| } |
| |
| @Override |
| public Appendable format(Unit<?> unit, Appendable appendable) |
| throws IOException { |
| String name = nameFor(unit); |
| if (name != null) |
| return appendable.append(name); |
| if (!(unit instanceof ProductUnit)) |
| throw new IllegalArgumentException( |
| "Cannot format given Object as a Unit"); |
| |
| ProductUnit<?> productUnit = (ProductUnit<?>) unit; |
| for (int i = 0; i < productUnit.getUnitCount(); i++) { |
| if (i != 0) { |
| appendable.append('*'); // Separator. |
| } |
| name = nameFor(productUnit.getUnit(i)); |
| int pow = productUnit.getUnitPow(i); |
| int root = productUnit.getUnitRoot(i); |
| appendable.append(name); |
| if ((pow != 1) || (root != 1)) { |
| // Use general exponent form. |
| appendable.append('^'); |
| appendable.append(String.valueOf(pow)); |
| if (root != 1) { |
| appendable.append(':'); |
| appendable.append(String.valueOf(root)); |
| } |
| } |
| } |
| return appendable; |
| } |
| |
| private static final long serialVersionUID = 1L; |
| } |
| |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // Initializes the standard unit database for SI units. |
| |
| private static final Unit<?>[] SI_UNITS = { SI.AMPERE, SI.BECQUEREL, |
| SI.CANDELA, SI.COULOMB, SI.FARAD, SI.GRAY, SI.HENRY, SI.HERTZ, |
| SI.JOULE, SI.KATAL, SI.KELVIN, SI.LUMEN, SI.LUX, SI.METRE, SI.MOLE, |
| SI.NEWTON, SI.OHM, SI.PASCAL, SI.RADIAN, SI.SECOND, SI.SIEMENS, |
| SI.SIEVERT, SI.STERADIAN, SI.TESLA, SI.VOLT, SI.WATT, SI.WEBER }; |
| |
| private static final String[] PREFIXES = { "Y", "Z", "E", "P", "T", "G", |
| "M", "k", "h", "da", "d", "c", "m", "µ", "n", "p", "f", "a", "z", |
| "y" }; |
| |
| private static final UnitConverter[] CONVERTERS = { E24, E21, E18, E15, E12, |
| E9, E6, E3, E2, E1, Em1, Em2, Em3, Em6, Em9, Em12, |
| Em15, Em18, Em21, Em24 }; |
| |
| private static String asciiPrefix(String prefix) { |
| return prefix == "µ" ? "micro" : prefix; |
| } |
| |
| static { |
| for (int i = 0; i < SI_UNITS.length; i++) { |
| for (int j = 0; j < PREFIXES.length; j++) { |
| Unit<?> si = SI_UNITS[i]; |
| Unit<?> u = si.transform(CONVERTERS[j]); |
| String symbol = (si instanceof BaseUnit) ? ((BaseUnit<?>) si) |
| .getSymbol() : ((AlternateUnit<?>) si).getSymbol(); |
| DEFAULT.label(u, PREFIXES[j] + symbol); |
| if (PREFIXES[j] == "µ") { |
| ASCII.label(u, "micro" + symbol); |
| } |
| } |
| } |
| // Special case for KILOGRAM. |
| DEFAULT.label(SI.GRAM, "g"); |
| for (int i = 0; i < PREFIXES.length; i++) { |
| if (CONVERTERS[i] == E3) continue; // kg is already defined. |
| DEFAULT.label(SI.KILOGRAM.transform(CONVERTERS[i].concatenate(Em3)), |
| PREFIXES[i] + "g"); |
| if (PREFIXES[i] == "µ") { |
| ASCII.label(SI.KILOGRAM.transform(CONVERTERS[i].concatenate(Em3)), "microg"); |
| } |
| } |
| |
| // Alias and ASCIIFormat for Ohm |
| DEFAULT.alias(SI.OHM, "Ohm"); |
| ASCII.label(SI.OHM, "Ohm"); |
| for (int i = 0; i < PREFIXES.length; i++) { |
| DEFAULT.alias(SI.OHM.transform(CONVERTERS[i]), PREFIXES[i] + "Ohm"); |
| ASCII.label(SI.OHM.transform(CONVERTERS[i]), asciiPrefix(PREFIXES[i]) + "Ohm"); |
| } |
| |
| // Special case for DEGREE_CElSIUS. |
| DEFAULT.label(SI.CELSIUS, "℃"); |
| DEFAULT.alias(SI.CELSIUS, "°C"); |
| ASCII.label(SI.CELSIUS, "Celsius"); |
| for (int i = 0; i < PREFIXES.length; i++) { |
| DEFAULT.label(SI.CELSIUS.transform(CONVERTERS[i]), PREFIXES[i] + "℃"); |
| DEFAULT.alias(SI.CELSIUS.transform(CONVERTERS[i]), PREFIXES[i] + "°C"); |
| ASCII.label(SI.CELSIUS.transform(CONVERTERS[i]), asciiPrefix(PREFIXES[i]) + "Celsius"); |
| } |
| } |
| |
| //////////////////////////////////////////////////////////////////////////// |
| // To be moved in resource bundle in future release (locale dependent). |
| static { |
| DEFAULT.label(NonSI.PERCENT, "%"); |
| DEFAULT.label(NonSI.DECIBEL, "dB"); |
| DEFAULT.label(NonSI.G, "grav"); |
| DEFAULT.label(NonSI.ATOM, "atom"); |
| DEFAULT.label(NonSI.REVOLUTION, "rev"); |
| DEFAULT.label(NonSI.DEGREE_ANGLE, "°"); |
| ASCII.label(NonSI.DEGREE_ANGLE, "degree_angle"); |
| DEFAULT.label(NonSI.MINUTE_ANGLE, "'"); |
| DEFAULT.label(NonSI.SECOND_ANGLE, "\""); |
| DEFAULT.label(NonSI.CENTIRADIAN, "centiradian"); |
| DEFAULT.label(NonSI.GRADE, "grade"); |
| DEFAULT.label(NonSI.ARE, "a"); |
| DEFAULT.label(NonSI.HECTARE, "ha"); |
| DEFAULT.label(NonSI.BYTE, "byte"); |
| DEFAULT.label(NonSI.MINUTE, "min"); |
| DEFAULT.label(NonSI.HOUR, "h"); |
| DEFAULT.label(NonSI.DAY, "day"); |
| DEFAULT.label(NonSI.WEEK, "week"); |
| DEFAULT.label(NonSI.YEAR, "year"); |
| DEFAULT.label(NonSI.MONTH, "month"); |
| DEFAULT.label(NonSI.DAY_SIDEREAL, "day_sidereal"); |
| DEFAULT.label(NonSI.YEAR_SIDEREAL, "year_sidereal"); |
| DEFAULT.label(NonSI.YEAR_CALENDAR, "year_calendar"); |
| DEFAULT.label(NonSI.E, "e"); |
| DEFAULT.label(NonSI.FARADAY, "Fd"); |
| DEFAULT.label(NonSI.FRANKLIN, "Fr"); |
| DEFAULT.label(NonSI.GILBERT, "Gi"); |
| DEFAULT.label(NonSI.ERG, "erg"); |
| DEFAULT.label(NonSI.ELECTRON_VOLT, "eV"); |
| DEFAULT.label(SI.KILO(NonSI.ELECTRON_VOLT), "keV"); |
| DEFAULT.label(SI.MEGA(NonSI.ELECTRON_VOLT), "MeV"); |
| DEFAULT.label(SI.GIGA(NonSI.ELECTRON_VOLT), "GeV"); |
| DEFAULT.label(NonSI.LAMBERT, "La"); |
| DEFAULT.label(NonSI.FOOT, "ft"); |
| DEFAULT.label(NonSI.FOOT_SURVEY_US, "foot_survey_us"); |
| DEFAULT.label(NonSI.YARD, "yd"); |
| DEFAULT.label(NonSI.INCH, "in"); |
| DEFAULT.label(NonSI.MILE, "mi"); |
| DEFAULT.label(NonSI.NAUTICAL_MILE, "nmi"); |
| DEFAULT.label(NonSI.MILES_PER_HOUR, "mph"); |
| DEFAULT.label(NonSI.ANGSTROM, "Å"); |
| ASCII.label(NonSI.ANGSTROM, "Angstrom"); |
| DEFAULT.label(NonSI.ASTRONOMICAL_UNIT, "ua"); |
| DEFAULT.label(NonSI.LIGHT_YEAR, "ly"); |
| DEFAULT.label(NonSI.PARSEC, "pc"); |
| DEFAULT.label(NonSI.POINT, "pt"); |
| DEFAULT.label(NonSI.PIXEL, "pixel"); |
| DEFAULT.label(NonSI.MAXWELL, "Mx"); |
| DEFAULT.label(NonSI.GAUSS, "G"); |
| DEFAULT.label(NonSI.ATOMIC_MASS, "u"); |
| DEFAULT.label(NonSI.ELECTRON_MASS, "me"); |
| DEFAULT.label(NonSI.POUND, "lb"); |
| DEFAULT.label(NonSI.OUNCE, "oz"); |
| DEFAULT.label(NonSI.TON_US, "ton_us"); |
| DEFAULT.label(NonSI.TON_UK, "ton_uk"); |
| DEFAULT.label(NonSI.METRIC_TON, "t"); |
| DEFAULT.label(NonSI.DYNE, "dyn"); |
| DEFAULT.label(NonSI.KILOGRAM_FORCE, "kgf"); |
| DEFAULT.label(NonSI.POUND_FORCE, "lbf"); |
| DEFAULT.label(NonSI.HORSEPOWER, "hp"); |
| DEFAULT.label(NonSI.ATMOSPHERE, "atm"); |
| DEFAULT.label(NonSI.BAR, "bar"); |
| DEFAULT.label(NonSI.MILLIMETER_OF_MERCURY, "mmHg"); |
| DEFAULT.label(NonSI.INCH_OF_MERCURY, "inHg"); |
| DEFAULT.label(NonSI.RAD, "rd"); |
| DEFAULT.label(NonSI.REM, "rem"); |
| DEFAULT.label(NonSI.CURIE, "Ci"); |
| DEFAULT.label(NonSI.RUTHERFORD, "Rd"); |
| DEFAULT.label(NonSI.SPHERE, "sphere"); |
| DEFAULT.label(NonSI.RANKINE, "°R"); |
| ASCII.label(NonSI.RANKINE, "degree_rankine"); |
| DEFAULT.label(NonSI.FAHRENHEIT, "°F"); |
| ASCII.label(NonSI.FAHRENHEIT, "degree_fahrenheit"); |
| DEFAULT.label(NonSI.KNOT, "kn"); |
| DEFAULT.label(NonSI.MACH, "Mach"); |
| DEFAULT.label(NonSI.C, "c"); |
| DEFAULT.label(NonSI.LITRE, "L"); |
| DEFAULT.label(SI.MICRO(NonSI.LITRE), "µL"); |
| ASCII.label(SI.MICRO(NonSI.LITRE), "microL"); |
| DEFAULT.label(SI.MILLI(NonSI.LITRE), "mL"); |
| DEFAULT.label(SI.CENTI(NonSI.LITRE), "cL"); |
| DEFAULT.label(SI.DECI(NonSI.LITRE), "dL"); |
| DEFAULT.label(NonSI.GALLON_LIQUID_US, "gal_us"); |
| DEFAULT.label(NonSI.OUNCE_LIQUID_US, "oz_liquid_us"); |
| DEFAULT.label(NonSI.GALLON_DRY_US, "gallon_dry_us"); |
| DEFAULT.label(NonSI.GALLON_UK, "gallon_uk"); |
| DEFAULT.label(NonSI.OUNCE_LIQUID_UK, "oz_liquid_uk"); |
| DEFAULT.label(NonSI.ROENTGEN, "Roentgen"); |
| if (Locale.getDefault().getCountry().equals("GB")) { |
| DEFAULT.label(NonSI.GALLON_UK, "gal_uk"); |
| DEFAULT.label(NonSI.OUNCE_LIQUID_UK, "oz_liquid_uk"); |
| } |
| } |
| } |