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