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