001/*
002 * Units of Measurement Reference Implementation
003 * Copyright (c) 2005-2018, Jean-Marie Dautelle, Werner Keil, Otavio Santana.
004 *
005 * All rights reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without modification,
008 * are permitted provided that the following conditions are met:
009 *
010 * 1. Redistributions of source code must retain the above copyright notice,
011 *    this list of conditions and the following disclaimer.
012 *
013 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
014 *    and the following disclaimer in the documentation and/or other materials provided with the distribution.
015 *
016 * 3. Neither the name of JSR-385, Indriya nor the names of their contributors may be used to endorse or promote products
017 *    derived from this software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
021 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
023 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
026 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package tech.units.indriya.format;
031
032import static tech.units.indriya.unit.MetricPrefix.CENTI;
033import static tech.units.indriya.unit.MetricPrefix.DECI;
034import static tech.units.indriya.unit.MetricPrefix.KILO;
035import static tech.units.indriya.unit.MetricPrefix.MICRO;
036import static tech.units.indriya.unit.MetricPrefix.MILLI;
037
038import java.io.IOException;
039import java.text.FieldPosition;
040import java.text.ParsePosition;
041import java.util.HashMap;
042import java.util.Map;
043import java.util.stream.Collectors;
044import java.util.stream.Stream;
045
046import tech.units.indriya.unit.MetricPrefix;
047import javax.measure.Quantity;
048import javax.measure.Unit;
049import javax.measure.UnitConverter;
050import javax.measure.format.ParserException;
051import javax.measure.format.UnitFormat;
052
053import tech.units.indriya.AbstractUnit;
054import tech.units.indriya.function.AddConverter;
055import tech.units.indriya.function.MultiplyConverter;
056import tech.units.indriya.function.PowersOfIntConverter;
057import tech.units.indriya.function.RationalConverter;
058import tech.units.indriya.unit.AlternateUnit;
059import tech.units.indriya.unit.AnnotatedUnit;
060import tech.units.indriya.unit.BaseUnit;
061import tech.units.indriya.unit.Prefix;
062import tech.units.indriya.unit.ProductUnit;
063import tech.units.indriya.unit.TransformedUnit;
064import tech.units.indriya.unit.Units;
065
066/**
067 * <p>
068 * This class implements the {@link UnitFormat} interface for formatting and parsing {@link Unit units}.
069 * </p>
070 * 
071 * <p>
072 * For all SI units, the 20 SI prefixes used to form decimal multiples and sub-multiples of SI units are recognized. {@link Units} are directly
073 * recognized. For example:<br>
074 * <code>
075 *        AbstractUnit.parse("m°C").equals(MetricPrefix.MILLI(Units.CELSIUS))
076 *        AbstractUnit.parse("kW").equals(MetricPrefix.KILO(Units.WATT))
077 *        AbstractUnit.parse("ft").equals(Units.METRE.multiply(0.3048))</code>
078 * </p>
079 *
080 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a>
081 * @author <a href="mailto:units@catmedia.us">Werner Keil</a>
082 * @author Eric Russell
083 * @version 1.5, April 15, 2019
084 * @since 1.0
085 */
086public abstract class SimpleUnitFormat extends AbstractUnitFormat {
087  /**
088   * 
089   */
090  // private static final long serialVersionUID = 4149424034841739785L;
091        
092  /**
093   * Flavor of this format
094   *
095   * @author Werner
096   *
097   */
098  public static enum Flavor {
099    Default, ASCII
100  }
101  
102  // Initializes the standard unit database for SI units.
103
104  private static final Unit<?>[] SI_UNITS = { Units.AMPERE, Units.BECQUEREL, Units.CANDELA, Units.COULOMB, Units.FARAD, Units.GRAY, Units.HENRY,
105      Units.HERTZ, Units.JOULE, Units.KATAL, Units.KELVIN, Units.LUMEN, Units.LUX, Units.METRE, Units.MOLE, Units.NEWTON, Units.OHM, Units.PASCAL,
106      Units.RADIAN, Units.SECOND, Units.SIEMENS, Units.SIEVERT, Units.STERADIAN, Units.TESLA, Units.VOLT, Units.WATT, Units.WEBER };
107
108  private static final Prefix[] PREFIXES = MetricPrefix.values();
109  
110  private static final String[] PREFIX_SYMBOLS =  
111                  Stream.of(PREFIXES)
112                  .map(Prefix::getSymbol)
113                  .collect(Collectors.toList())
114                  .toArray(new String[] {});
115
116  private static final UnitConverter[] PREFIX_CONVERTERS =  
117                  Stream.of(PREFIXES)
118                  .map(PowersOfIntConverter::of)
119                  .collect(Collectors.toList())
120                  .toArray(new UnitConverter[] {});
121
122
123  /**
124   * Holds the standard unit format.
125   */
126  private static final DefaultFormat DEFAULT = new DefaultFormat();
127
128  /**
129   * Holds the ASCIIFormat unit format.
130   */
131  private static final ASCIIFormat ASCII = new ASCIIFormat();
132
133  /**
134   * Returns the unit format for the default locale (format used by {@link AbstractUnit#parse(CharSequence) AbstractUnit.parse(CharSequence)} and
135   * {@link Unit#toString() Unit.toString()}).
136   *
137   * @return the default unit format (locale sensitive).
138   */
139  public static SimpleUnitFormat getInstance() {
140    return getInstance(Flavor.Default);
141  }
142
143  /**
144   * Returns the {@link SimpleUnitFormat} in the desired {@link Flavor}
145   *
146   * @return the instance for the given {@link Flavor}.
147   */
148  public static SimpleUnitFormat getInstance(Flavor flavor) {
149    switch (flavor) {
150      case ASCII:
151        return SimpleUnitFormat.ASCII;
152      default:
153        return DEFAULT;
154    }
155  }
156
157  /**
158   * Base constructor.
159   */
160  protected SimpleUnitFormat() {
161  }
162
163  /**
164   * Formats the specified unit.
165   *
166   * @param unit
167   *          the unit to format.
168   * @param appendable
169   *          the appendable destination.
170   * @throws IOException
171   *           if an error occurs.
172   */
173  public abstract Appendable format(Unit<?> unit, Appendable appendable) throws IOException;
174
175  /**
176   * Parses a sequence of character to produce a unit or a rational product of unit.
177   *
178   * @param csq
179   *          the <code>CharSequence</code> to parse.
180   * @param pos
181   *          an object holding the parsing index and error position.
182   * @return an {@link Unit} parsed from the character sequence.
183   * @throws IllegalArgumentException
184   *           if the character sequence contains an illegal syntax.
185   */
186  @SuppressWarnings("rawtypes")
187  public abstract Unit<? extends Quantity> parseProductUnit(CharSequence csq, ParsePosition pos) throws ParserException;
188
189  /**
190   * Parses a sequence of character to produce a single unit.
191   *
192   * @param csq
193   *          the <code>CharSequence</code> to parse.
194   * @param pos
195   *          an object holding the parsing index and error position.
196   * @return an {@link Unit} parsed from the character sequence.
197   * @throws IllegalArgumentException
198   *           if the character sequence does not contain a valid unit identifier.
199   */
200  @SuppressWarnings("rawtypes")
201  public abstract Unit<? extends Quantity> parseSingleUnit(CharSequence csq, ParsePosition pos) throws ParserException;
202
203  /**
204   * Attaches a system-wide label to the specified unit. For example: <code> SimpleUnitFormat.getInstance().label(DAY.multiply(365), "year");
205   * SimpleUnitFormat.getInstance().label(METER.multiply(0.3048), "ft"); </code> If the specified label is already associated to an unit the previous
206   * association is discarded or ignored.
207   *
208   * @param unit
209   *          the unit being labeled.
210   * @param label
211   *          the new label for this unit.
212   * @throws IllegalArgumentException
213   *           if the label is not a {@link SimpleUnitFormat#isValidIdentifier(String)} valid identifier.
214   */
215  public abstract void label(Unit<?> unit, String label);
216
217  /**
218   * Attaches a system-wide alias to this unit. Multiple aliases may be attached to the same unit. Aliases are used during parsing to recognize
219   * different variants of the same unit. For example: <code> SimpleUnitFormat.getInstance().alias(METER.multiply(0.3048), "foot");
220   * SimpleUnitFormat.getInstance().alias(METER.multiply(0.3048), "feet"); SimpleUnitFormat.getInstance().alias(METER, "meter");
221   * SimpleUnitFormat.getInstance().alias(METER, "metre"); </code> If the specified label is already associated to an unit the previous association is
222   * discarded or ignored.
223   *
224   * @param unit
225   *          the unit being aliased.
226   * @param alias
227   *          the alias attached to this unit.
228   * @throws IllegalArgumentException
229   *           if the label is not a {@link SimpleUnitFormat#isValidIdentifier(String)} valid identifier.
230   */
231  public abstract void alias(Unit<?> unit, String alias);
232
233  /**
234   * Indicates if the specified name can be used as unit identifier.
235   *
236   * @param name
237   *          the identifier to be tested.
238   * @return <code>true</code> if the name specified can be used as label or alias for this format;<code>false</code> otherwise.
239   */
240  public abstract boolean isValidIdentifier(String name);
241
242  /**
243   * Formats an unit and appends the resulting text to a given string buffer (implements <code>java.text.Format</code>).
244   *
245   * @param unit
246   *          the unit to format.
247   * @param toAppendTo
248   *          where the text is to be appended
249   * @param pos
250   *          the field position (not used).
251   * @return <code>toAppendTo</code>
252   */
253  public final StringBuffer format(Object unit, final StringBuffer toAppendTo, FieldPosition pos) {
254    try {
255      final Object dest = toAppendTo;
256      if (dest instanceof Appendable) {
257        format((Unit<?>) unit, (Appendable) dest);
258      } else { // When retroweaver is used to produce 1.4 binaries. TODO is this still relevant?
259        format((Unit<?>) unit, new Appendable() {
260
261          public Appendable append(char arg0) throws IOException {
262            toAppendTo.append(arg0);
263            return null;
264          }
265
266          public Appendable append(CharSequence arg0) throws IOException {
267            toAppendTo.append(arg0);
268            return null;
269          }
270
271          public Appendable append(CharSequence arg0, int arg1, int arg2) throws IOException {
272            toAppendTo.append(arg0.subSequence(arg1, arg2));
273            return null;
274          }
275        });
276      }
277      return toAppendTo;
278    } catch (IOException e) {
279      throw new Error(e); // Should never happen.
280    }
281  }
282
283  /**
284   * Parses the text from a string to produce an object (implements <code>java.text.Format</code>).
285   *
286   * @param source
287   *          the string source, part of which should be parsed.
288   * @param pos
289   *          the cursor position.
290   * @return the corresponding unit or <code>null</code> if the string cannot be parsed.
291   */
292  public final Unit<?> parseObject(String source, ParsePosition pos) throws ParserException {
293    return parseProductUnit(source, pos);
294  }
295
296  /**
297   * This class represents an exponent with both a power (numerator) and a root (denominator).
298   */
299  private static class Exponent {
300    public final int pow;
301    public final int root;
302
303    public Exponent(int pow, int root) {
304      this.pow = pow;
305      this.root = root;
306    }
307  }
308
309  /**
310   * This class represents the standard format.
311   */
312  protected static class DefaultFormat extends SimpleUnitFormat {
313    private static final int EOF = 0;
314    private static final int IDENTIFIER = 1;
315    private static final int OPEN_PAREN = 2;
316    private static final int CLOSE_PAREN = 3;
317    private static final int EXPONENT = 4;
318    private static final int MULTIPLY = 5;
319    private static final int DIVIDE = 6;
320    private static final int PLUS = 7;
321    private static final int INTEGER = 8;
322    private static final int FLOAT = 9;
323
324    /**
325     * Holds the name to unit mapping.
326     */
327    protected final HashMap<String, Unit<?>> nameToUnit = new HashMap<>();
328
329    /**
330     * Holds the unit to name mapping.
331     */
332    protected final HashMap<Unit<?>, String> unitToName = new HashMap<>();
333
334    @Override
335    public void label(Unit<?> unit, String label) {
336      if (!isValidIdentifier(label))
337        throw new IllegalArgumentException("Label: " + label + " is not a valid identifier.");
338      synchronized (this) {
339        nameToUnit.put(label, unit);
340        unitToName.put(unit, label);
341      }
342    }
343
344    @Override
345    public void alias(Unit<?> unit, String alias) {
346      if (!isValidIdentifier(alias))
347        throw new IllegalArgumentException("Alias: " + alias + " is not a valid identifier.");
348      synchronized (this) {
349        nameToUnit.put(alias, unit);
350      }
351    }
352
353    @Override
354    public boolean isValidIdentifier(String name) {
355      if ((name == null) || (name.length() == 0))
356        return false;
357      /*
358       * for (int i = 0; i < name.length(); i++) { if
359       * (!isUnitIdentifierPart(name.charAt(i))) return false; }
360       */
361      return isUnitIdentifierPart(name.charAt(0));
362    }
363
364    protected static boolean isUnitIdentifierPart(char ch) {
365      return Character.isLetter(ch)
366          || (!Character.isWhitespace(ch) && !Character.isDigit(ch) && (ch != '\u00b7') && (ch != '*') && (ch != '/') && (ch != '(') && (ch != ')')
367              && (ch != '[') && (ch != ']') && (ch != '\u00b9') && (ch != '\u00b2') && (ch != '\u00b3') && (ch != '^') && (ch != '+') && (ch != '-'));
368    }
369
370    // Returns the name for the specified unit or null if product unit.
371    protected String nameFor(Unit<?> unit) {
372      // Searches label database.
373      String label = unitToName.get(unit);
374      if (label != null)
375        return label;
376      if (unit instanceof BaseUnit)
377        return ((BaseUnit<?>) unit).getSymbol();
378      if (unit instanceof AlternateUnit)
379        return ((AlternateUnit<?>) unit).getSymbol();
380      if (unit instanceof TransformedUnit) {
381        TransformedUnit<?> tfmUnit = (TransformedUnit<?>) unit;
382        if (tfmUnit.getSymbol() != null) {
383                return tfmUnit.getSymbol();
384        }
385        Unit<?> baseUnit = tfmUnit.getParentUnit();
386        UnitConverter cvtr = tfmUnit.getConverter(); // tfmUnit.getSystemConverter();
387        StringBuilder result = new StringBuilder();
388        String baseUnitName = baseUnit.toString();
389        String prefix = prefixFor(cvtr);
390        if ((baseUnitName.indexOf('\u00b7') >= 0) || (baseUnitName.indexOf('*') >= 0) || (baseUnitName.indexOf('/') >= 0)) {
391          // We could use parentheses whenever baseUnits is an
392          // instanceof ProductUnit, but most ProductUnits have
393          // aliases,
394          // so we'd end up with a lot of unnecessary parentheses.
395          result.append('(');
396          result.append(baseUnitName);
397          result.append(')');
398        } else {
399          result.append(baseUnitName);
400        }
401        if (prefix != null) {
402          result.insert(0, prefix);
403        } else {
404          if (cvtr instanceof AddConverter) {
405            result.append('+');
406            result.append(((AddConverter) cvtr).getOffset());
407          } else if (cvtr instanceof RationalConverter) {
408            double dividend = ((RationalConverter) cvtr).getDividend().doubleValue();
409            if (dividend != 1) {
410              result.append('*');
411              result.append(dividend);
412            }
413            double divisor = ((RationalConverter) cvtr).getDivisor().doubleValue();
414            if (divisor != 1) {
415              result.append('/');
416              result.append(divisor);
417            }
418          } else if (cvtr instanceof MultiplyConverter) {
419            result.append('*');
420            result.append(((MultiplyConverter) cvtr).getFactor());
421          } else { // Other converters.
422            return "[" + baseUnit + "?]";
423          }
424        }
425        return result.toString();
426      }
427      if (unit instanceof AnnotatedUnit<?>) {
428        AnnotatedUnit<?> annotatedUnit = (AnnotatedUnit<?>) unit;
429        final StringBuilder annotable = new StringBuilder(nameFor(annotatedUnit.getActualUnit()));
430        if (annotatedUnit.getAnnotation() != null) {
431          annotable.append('{'); // TODO maybe also configure this one similar to Compound separator
432          annotable.append(annotatedUnit.getAnnotation());
433          annotable.append('}');
434        }
435        return annotable.toString();
436      }
437      return null; // Product unit.
438    }
439
440    // Returns the prefix for the specified unit converter.
441    protected String prefixFor(UnitConverter converter) {
442      for (int i = 0; i < PREFIX_CONVERTERS.length; i++) {
443        if (PREFIX_CONVERTERS[i].equals(converter)) {
444          return PREFIX_SYMBOLS[i];
445        }
446      }
447      return null; // TODO or return blank?
448    }
449
450    // Returns the unit for the specified name.
451    protected Unit<?> unitFor(String name) {
452      Unit<?> unit = nameToUnit.get(name);
453      if (unit != null)
454        return unit;
455      unit = SYMBOL_TO_UNIT.get(name);
456      return unit;
457    }
458
459    // //////////////////////////
460    // Parsing.
461    @SuppressWarnings({ "rawtypes", "unchecked" })
462    public Unit<? extends Quantity> parseSingleUnit(CharSequence csq, ParsePosition pos) throws ParserException {
463      int startIndex = pos.getIndex();
464      String name = readIdentifier(csq, pos);
465      Unit unit = unitFor(name);
466      check(unit != null, name + " not recognized", csq, startIndex);
467      return unit;
468    }
469
470    @SuppressWarnings({ "rawtypes", "unchecked" })
471    @Override
472    public Unit<? extends Quantity> parseProductUnit(CharSequence csq, ParsePosition pos) throws ParserException {
473      Unit result = AbstractUnit.ONE;
474      int token = nextToken(csq, pos);
475      switch (token) {
476        case IDENTIFIER:
477          result = parseSingleUnit(csq, pos);
478          break;
479        case OPEN_PAREN:
480          pos.setIndex(pos.getIndex() + 1);
481          result = parseProductUnit(csq, pos);
482          token = nextToken(csq, pos);
483          check(token == CLOSE_PAREN, "')' expected", csq, pos.getIndex());
484          pos.setIndex(pos.getIndex() + 1);
485          break;
486      }
487      token = nextToken(csq, pos);
488      while (true) {
489        switch (token) {
490          case EXPONENT:
491            Exponent e = readExponent(csq, pos);
492            if (e.pow != 1) {
493              result = result.pow(e.pow);
494            }
495            if (e.root != 1) {
496              result = result.root(e.root);
497            }
498            break;
499          case MULTIPLY:
500            pos.setIndex(pos.getIndex() + 1);
501            token = nextToken(csq, pos);
502            if (token == INTEGER) {
503              long n = readLong(csq, pos);
504              if (n != 1) {
505                result = result.multiply(n);
506              }
507            } else if (token == FLOAT) {
508              double d = readDouble(csq, pos);
509              if (d != 1.0) {
510                result = result.multiply(d);
511              }
512            } else {
513              result = result.multiply(parseProductUnit(csq, pos));
514            }
515            break;
516          case DIVIDE:
517            pos.setIndex(pos.getIndex() + 1);
518            token = nextToken(csq, pos);
519            if (token == INTEGER) {
520              long n = readLong(csq, pos);
521              if (n != 1) {
522                result = result.divide(n);
523              }
524            } else if (token == FLOAT) {
525              double d = readDouble(csq, pos);
526              if (d != 1.0) {
527                result = result.divide(d);
528              }
529            } else {
530              result = result.divide(parseProductUnit(csq, pos));
531            }
532            break;
533          case PLUS:
534            pos.setIndex(pos.getIndex() + 1);
535            token = nextToken(csq, pos);
536            if (token == INTEGER) {
537              long n = readLong(csq, pos);
538              if (n != 1) {
539                result = result.shift(n);
540              }
541            } else if (token == FLOAT) {
542              double d = readDouble(csq, pos);
543              if (d != 1.0) {
544                result = result.shift(d);
545              }
546            } else {
547              throw new ParserException("not a number", pos.getIndex());
548            }
549            break;
550          case EOF:
551          case CLOSE_PAREN:
552            return result;
553          default:
554            throw new ParserException("unexpected token " + token, pos.getIndex());
555        }
556        token = nextToken(csq, pos);
557      }
558    }
559
560    private int nextToken(CharSequence csq, ParsePosition pos) {
561      final int length = csq.length();
562      while (pos.getIndex() < length) {
563        char c = csq.charAt(pos.getIndex());
564        if (isUnitIdentifierPart(c)) {
565          return IDENTIFIER;
566        } else if (c == '(') {
567          return OPEN_PAREN;
568        } else if (c == ')') {
569          return CLOSE_PAREN;
570        } else if ((c == '^') || (c == '\u00b9') || (c == '\u00b2') || (c == '\u00b3')) {
571          return EXPONENT;
572        } else if (c == '*') {
573          char c2 = csq.charAt(pos.getIndex() + 1);
574          if (c2 == '*') {
575            return EXPONENT;
576          } else {
577            return MULTIPLY;
578          }
579        } else if (c == '\u00b7') {
580          return MULTIPLY;
581        } else if (c == '/') {
582          return DIVIDE;
583        } else if (c == '+') {
584          return PLUS;
585        } else if ((c == '-') || Character.isDigit(c)) {
586          int index = pos.getIndex() + 1;
587          while ((index < length) && (Character.isDigit(c) || (c == '-') || (c == '.') || (c == 'E'))) {
588            c = csq.charAt(index++);
589            if (c == '.') {
590              return FLOAT;
591            }
592          }
593          return INTEGER;
594        }
595        pos.setIndex(pos.getIndex() + 1);
596      }
597      return EOF;
598    }
599
600    private void check(boolean expr, String message, CharSequence csq, int index) throws ParserException {
601      if (!expr) {
602        throw new ParserException(message + " (in " + csq + " at index " + index + ")", index);
603      }
604    }
605
606    private Exponent readExponent(CharSequence csq, ParsePosition pos) {
607      char c = csq.charAt(pos.getIndex());
608      if (c == '^') {
609        pos.setIndex(pos.getIndex() + 1);
610      } else if (c == '*') {
611        pos.setIndex(pos.getIndex() + 2);
612      }
613      final int length = csq.length();
614      int pow = 0;
615      boolean isPowNegative = false;
616      int root = 0;
617      boolean isRootNegative = false;
618      boolean isRoot = false;
619      while (pos.getIndex() < length) {
620        c = csq.charAt(pos.getIndex());
621        if (c == '\u00b9') {
622          if (isRoot) {
623            root = root * 10 + 1;
624          } else {
625            pow = pow * 10 + 1;
626          }
627        } else if (c == '\u00b2') {
628          if (isRoot) {
629            root = root * 10 + 2;
630          } else {
631            pow = pow * 10 + 2;
632          }
633        } else if (c == '\u00b3') {
634          if (isRoot) {
635            root = root * 10 + 3;
636          } else {
637            pow = pow * 10 + 3;
638          }
639        } else if (c == '-') {
640          if (isRoot) {
641            isRootNegative = true;
642          } else {
643            isPowNegative = true;
644          }
645        } else if ((c >= '0') && (c <= '9')) {
646          if (isRoot) {
647            root = root * 10 + (c - '0');
648          } else {
649            pow = pow * 10 + (c - '0');
650          }
651        } else if (c == ':') {
652          isRoot = true;
653        } else {
654          break;
655        }
656        pos.setIndex(pos.getIndex() + 1);
657      }
658      if (pow == 0)
659        pow = 1;
660      if (root == 0)
661        root = 1;
662      return new Exponent(isPowNegative ? -pow : pow, isRootNegative ? -root : root);
663    }
664
665    private long readLong(CharSequence csq, ParsePosition pos) {
666      final int length = csq.length();
667      int result = 0;
668      boolean isNegative = false;
669      while (pos.getIndex() < length) {
670        char c = csq.charAt(pos.getIndex());
671        if (c == '-') {
672          isNegative = true;
673        } else if ((c >= '0') && (c <= '9')) {
674          result = result * 10 + (c - '0');
675        } else {
676          break;
677        }
678        pos.setIndex(pos.getIndex() + 1);
679      }
680      return isNegative ? -result : result;
681    }
682
683    private double readDouble(CharSequence csq, ParsePosition pos) {
684      final int length = csq.length();
685      int start = pos.getIndex();
686      int end = start + 1;
687      while (end < length) {
688        if ("0123456789+-.E".indexOf(csq.charAt(end)) < 0) {
689          break;
690        }
691        end += 1;
692      }
693      pos.setIndex(end + 1);
694      return Double.parseDouble(csq.subSequence(start, end).toString());
695    }
696
697    private String readIdentifier(CharSequence csq, ParsePosition pos) {
698      final int length = csq.length();
699      int start = pos.getIndex();
700      int i = start;
701      while ((++i < length) && isUnitIdentifierPart(csq.charAt(i))) {
702      }
703      pos.setIndex(i);
704      return csq.subSequence(start, i).toString();
705    }
706
707    // //////////////////////////
708    // Formatting.
709
710    @Override
711    public Appendable format(Unit<?> unit, Appendable appendable) throws IOException {
712      String name = nameFor(unit);
713      if (name != null) {
714        return appendable.append(name);
715      }
716      if (!(unit instanceof ProductUnit)) {
717        throw new IllegalArgumentException("Cannot format given Object as a Unit");
718      }
719
720      // Product unit.
721      ProductUnit<?> productUnit = (ProductUnit<?>) unit;
722      int invNbr = 0;
723
724      // Write positive exponents first.
725      boolean start = true;
726      for (int i = 0; i < productUnit.getUnitCount(); i++) {
727        int pow = productUnit.getUnitPow(i);
728        if (pow >= 0) {
729          if (!start) {
730            appendable.append('\u00b7'); // Separator.
731          }
732          name = nameFor(productUnit.getUnit(i));
733          int root = productUnit.getUnitRoot(i);
734          append(appendable, name, pow, root);
735          start = false;
736        } else {
737          invNbr++;
738        }
739      }
740
741      // Write negative exponents.
742      if (invNbr != 0) {
743        if (start) {
744          appendable.append('1'); // e.g. 1/s
745        }
746        appendable.append('/');
747        if (invNbr > 1) {
748          appendable.append('(');
749        }
750        start = true;
751        for (int i = 0; i < productUnit.getUnitCount(); i++) {
752          int pow = productUnit.getUnitPow(i);
753          if (pow < 0) {
754            name = nameFor(productUnit.getUnit(i));
755            int root = productUnit.getUnitRoot(i);
756            if (!start) {
757              appendable.append('\u00b7'); // Separator.
758            }
759            append(appendable, name, -pow, root);
760            start = false;
761          }
762        }
763        if (invNbr > 1) {
764          appendable.append(')');
765        }
766      }
767      return appendable;
768    }
769
770    private void append(Appendable appendable, CharSequence symbol, int pow, int root) throws IOException {
771      appendable.append(symbol);
772      if ((pow != 1) || (root != 1)) {
773        // Write exponent.
774        if ((pow == 2) && (root == 1)) {
775          appendable.append('\u00b2'); // Square
776        } else if ((pow == 3) && (root == 1)) {
777          appendable.append('\u00b3'); // Cubic
778        } else {
779          // Use general exponent form.
780          appendable.append('^');
781          appendable.append(String.valueOf(pow));
782          if (root != 1) {
783            appendable.append(':');
784            appendable.append(String.valueOf(root));
785          }
786        }
787      }
788    }
789
790    // private static final long serialVersionUID = 1L;
791
792    @Override
793    public Unit<?> parse(CharSequence csq) throws ParserException {
794      return parse(csq, 0);
795    }
796
797    @Override
798    protected SymbolMap getSymbols() {
799      return null;
800    }
801
802    protected Unit<?> parse(CharSequence csq, int index) throws IllegalArgumentException {
803      return parse(csq, new ParsePosition(index));
804    }
805
806    @Override
807    protected Unit<?> parse(CharSequence csq, ParsePosition cursor) throws IllegalArgumentException {
808      return parseObject(csq.toString(), cursor);
809    }
810
811        @Override
812        public boolean isLocaleSensitive() {
813                // TODO Auto-generated method stub
814                return false;
815        }
816  }
817
818  /**
819   * This class represents the ASCII format.
820   */
821  protected final static class ASCIIFormat extends DefaultFormat {
822
823    @Override
824    protected String nameFor(Unit<?> unit) {
825      // First search if specific ASCII name should be used.
826      String name = unitToName.get(unit);
827      if (name != null)
828        return name;
829      // Else returns default name.
830      return DEFAULT.nameFor(unit);
831    }
832
833    @Override
834    protected Unit<?> unitFor(String name) {
835      // First search if specific ASCII name.
836      Unit<?> unit = nameToUnit.get(name);
837      if (unit != null)
838        return unit;
839      // Else returns default mapping.
840      return DEFAULT.unitFor(name);
841    }
842
843    @Override
844    public Appendable format(Unit<?> unit, Appendable appendable) throws IOException {
845      String name = nameFor(unit);
846      if (name != null)
847        return appendable.append(name);
848      if (!(unit instanceof ProductUnit))
849        throw new IllegalArgumentException("Cannot format given Object as a Unit");
850
851      ProductUnit<?> productUnit = (ProductUnit<?>) unit;
852      for (int i = 0; i < productUnit.getUnitCount(); i++) {
853        if (i != 0) {
854          appendable.append('*'); // Separator.
855        }
856        name = nameFor(productUnit.getUnit(i));
857        int pow = productUnit.getUnitPow(i);
858        int root = productUnit.getUnitRoot(i);
859        appendable.append(name);
860        if ((pow != 1) || (root != 1)) {
861          // Use general exponent form.
862          appendable.append('^');
863          appendable.append(String.valueOf(pow));
864          if (root != 1) {
865            appendable.append(':');
866            appendable.append(String.valueOf(root));
867          }
868        }
869      }
870      return appendable;
871    }
872
873    @Override
874    public boolean isValidIdentifier(String name) {
875      if ((name == null) || (name.length() == 0))
876        return false;
877      // label must not begin with a digit or mathematical operator
878      return isUnitIdentifierPart(name.charAt(0)) && isAllASCII(name);
879      /*
880       * for (int i = 0; i < name.length(); i++) { if
881       * (!isAsciiCharacter(name.charAt(i))) return false; } return true;
882       */
883    }
884  }
885
886  /**
887   * Holds the unique symbols collection (base units or alternate units).
888   */
889  private static final Map<String, Unit<?>> SYMBOL_TO_UNIT = new HashMap<>();
890
891  private static final String MU = "\u03bc";
892  
893  private static String asciiPrefix(String prefix) {
894    return "µ".equals(prefix) ? "micro" : prefix;
895  }
896  
897  private static String asciiSymbol(String s) {
898     return "Ω".equals(s) ? "Ohm" : s;
899  }
900
901  /** to check if a string only contains US-ASCII characters */
902  protected static boolean isAllASCII(String input) {
903    boolean isASCII = true;
904    for (int i = 0; i < input.length(); i++) {
905      int c = input.charAt(i);
906      if (c > 0x7F) {
907        isASCII = false;
908        break;
909      }
910    }
911    return isASCII;
912  }
913  
914  // Initializations
915  static {
916    for (int i = 0; i < SI_UNITS.length; i++) {
917        AbstractUnit<?> si = (AbstractUnit<?>) SI_UNITS[i];
918      String symbol = (si instanceof BaseUnit) ? ((BaseUnit<?>) si).getSymbol() : ((AlternateUnit<?>) si).getSymbol();
919      DEFAULT.label(si, symbol);
920      if (isAllASCII(symbol))
921        ASCII.label(si, symbol);
922      for (int j = 0; j < PREFIX_SYMBOLS.length; j++) {
923        AbstractUnit<?> u = si.prefix(PREFIXES[j]);
924        DEFAULT.label(u, PREFIX_SYMBOLS[j] + symbol);
925        if ( "µ".equals(PREFIX_SYMBOLS[j]) ) {
926          DEFAULT.alias(u, MU + symbol);
927          ASCII.label(u, "micro" + asciiSymbol(symbol));
928        }
929      }
930    }
931    
932    // Special case for KILOGRAM.
933    DEFAULT.label(Units.GRAM, "g");
934    for (int i = 0; i < PREFIX_SYMBOLS.length; i++) {
935      if (MultiplyConverter.of(KILO).equals(PREFIX_CONVERTERS[i]))
936         continue; // kg is already defined.
937      DEFAULT.label(Units.KILOGRAM.prefix(PREFIXES[i]).prefix(MILLI), PREFIX_SYMBOLS[i] + "g");
938      if ( "µ".equals(PREFIX_SYMBOLS[i]) ) {
939        ASCII.label(Units.KILOGRAM.prefix(PREFIXES[i]).prefix(MILLI), "microg");
940      }
941    }
942    
943    // Hack, somehow µg is not found.
944    SYMBOL_TO_UNIT.put(MICRO.getSymbol() + "g", MICRO(Units.GRAM));
945    SYMBOL_TO_UNIT.put("μg", MICRO(Units.GRAM));
946    SYMBOL_TO_UNIT.put(MU + "g", MICRO(Units.GRAM));
947
948
949    // Alias and ASCIIFormat for Ohm
950    DEFAULT.alias(Units.OHM, "Ohm");
951    ASCII.label(Units.OHM, "Ohm");
952    for (int i = 0; i < PREFIX_SYMBOLS.length; i++) {
953      DEFAULT.alias(Units.OHM.prefix(PREFIXES[i]), PREFIX_SYMBOLS[i] + "Ohm");
954      ASCII.label(Units.OHM.prefix(PREFIXES[i]), asciiPrefix(PREFIX_SYMBOLS[i]) + "Ohm");
955    }
956
957    // Special case for DEGREE_CELSIUS.
958    DEFAULT.label(Units.CELSIUS, "℃");
959    DEFAULT.alias(Units.CELSIUS, "°C");
960    ASCII.label(Units.CELSIUS, "Celsius");
961    for (int i = 0; i < PREFIX_SYMBOLS.length; i++) {
962      DEFAULT.label(Units.CELSIUS.prefix(PREFIXES[i]), PREFIX_SYMBOLS[i] + "℃");
963      DEFAULT.alias(Units.CELSIUS.prefix(PREFIXES[i]), PREFIX_SYMBOLS[i] + "°C");
964      ASCII.label(Units.CELSIUS.prefix(PREFIXES[i]), asciiPrefix(PREFIX_SYMBOLS[i]) + "Celsius");
965    }
966
967    DEFAULT.label(Units.PERCENT, "%");
968    DEFAULT.label(Units.KILOGRAM, "kg");
969    ASCII.label(Units.KILOGRAM, "kg");
970    DEFAULT.label(Units.METRE, "m");
971    ASCII.label(Units.METRE, "m");
972    DEFAULT.label(Units.SECOND, "s");
973    ASCII.label(Units.SECOND, "s");
974    DEFAULT.label(Units.MINUTE, "min");
975    DEFAULT.label(Units.HOUR, "h");
976    DEFAULT.label(Units.DAY, "day");
977    DEFAULT.alias(Units.DAY, "d");
978    DEFAULT.label(Units.WEEK, "week");
979    DEFAULT.label(Units.YEAR, "year");
980    DEFAULT.alias(Units.YEAR, "days365");
981    ASCII.label(Units.KILOMETRE_PER_HOUR, "km/h");
982    DEFAULT.label(Units.KILOMETRE_PER_HOUR, "km/h");
983    DEFAULT.label(Units.CUBIC_METRE, "\u33A5");
984    ASCII.label(Units.CUBIC_METRE, "m3");
985    ASCII.label(Units.LITRE, "l");
986    DEFAULT.label(Units.LITRE, "l");
987    DEFAULT.label(MICRO(Units.LITRE), "µl");
988    ASCII.label(MICRO(Units.LITRE), "microL");
989    ASCII.label(MILLI(Units.LITRE), "mL");
990    DEFAULT.label(MILLI(Units.LITRE), "ml");
991    ASCII.label(CENTI(Units.LITRE), "cL");
992    DEFAULT.label(CENTI(Units.LITRE), "cl");
993    ASCII.label(DECI(Units.LITRE), "dL");
994    DEFAULT.label(DECI(Units.LITRE), "dl");
995    DEFAULT.label(Units.NEWTON, "N");
996    ASCII.label(Units.NEWTON, "N");
997    DEFAULT.label(Units.RADIAN, "rad");
998    ASCII.label(Units.RADIAN, "rad");
999
1000    DEFAULT.label(AbstractUnit.ONE, "one");
1001    ASCII.label(AbstractUnit.ONE, "one");
1002  }
1003}