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