001/*
002 * Units of Measurement Systems for Java
003 * Copyright (c) 2005-2017, Jean-Marie Dautelle, Werner Keil, V2COM.
004 *
005 * All rights reserved.
006 *
007 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
008 *
009 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
010 *
011 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
012 *
013 * 3. Neither the name of JSR-363, Units of Measurement nor the names of their contributors may be used to endorse or promote products derived from this software without specific prior written permission.
014 *
015 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
016 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
017 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
018 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
019 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
020 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
021 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
022 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
023 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
024 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
025 */
026package systems.uom.ucum.internal.format;
027
028import static tec.uom.se.AbstractUnit.ONE;
029
030import javax.measure.Unit;
031
032import tec.uom.se.AbstractUnit;
033import tec.uom.se.format.SymbolMap;
034import tec.uom.se.unit.MetricPrefix;
035import static systems.uom.ucum.internal.format.UCUMTokenConstants.*;
036
037/**
038 * <p> 
039 * Parser definition for parsing {@link AbstractUnit Unit}s
040 * according to the <a href="http://unitsofmeasure.org">
041 * Uniform Code for CommonUnits of Measure</a>.
042 * 
043 * @author <a href="mailto:eric-r@northwestern.edu">Eric Russell</a>
044 * @author <a href="mailto:units@catmedia.us">Werner Keil</a>
045 * @see <a href="http://unitsofmeasure.org">UCUM</a>
046 * @version 0.6, March 14, 2017
047 */
048public final class UCUMFormatParser {
049
050    private SymbolMap symbols;
051
052    public UCUMFormatParser(SymbolMap symbols, java.io.InputStream in) {
053        this(in);
054        this.symbols = symbols;
055    }
056
057//
058// Parser productions
059//
060    final public Unit parseUnit() throws TokenException {
061        Unit u;
062        u = Term();
063        jj_consume_token(0);
064        {
065            return u;
066        }
067    }
068    
069    final public Unit Term() throws TokenException {
070        Unit result = ONE;
071        Unit temp = ONE;
072        result = Component();
073        label_1:
074        while (true) {
075            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
076                case DOT:
077                case SOLIDUS:
078                    break;
079                default:
080                    jj_la1[0] = jj_gen;
081                    break label_1;
082            }
083            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
084                case DOT:
085                    jj_consume_token(DOT);
086                    temp = Component();
087                    result = result.multiply(temp);
088                    break;
089                case SOLIDUS:
090                    jj_consume_token(SOLIDUS);
091                    temp = Component();
092                    result = result.divide(temp);
093                    break;
094                default:
095                    jj_la1[1] = jj_gen;
096                    jj_consume_token(-1);
097                    throw new TokenException();
098            }
099        }
100        {
101            return result;
102        }
103    }
104
105    final public Unit Component() throws TokenException {
106        Unit result = (AbstractUnit) ONE;
107        Token token = null;
108        if (jj_2_1(2147483647)) {
109            result = Annotatable();
110            token = jj_consume_token(ANNOTATION);
111            {
112                return ((AbstractUnit)result).annotate(token.image.substring(1, token.image.length() - 1));
113            }
114        } else {
115            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
116                case ATOM:
117                    result = Annotatable(); {
118                    return result;
119                }
120                case ANNOTATION:
121                    token = jj_consume_token(ANNOTATION); {
122                    return ((AbstractUnit)result).annotate(token.image.substring(1, token.image.length() - 1));
123                }
124                case FACTOR:
125                    token = jj_consume_token(FACTOR);
126                    long factor = Long.parseLong(token.image); {
127                    return result.multiply(factor);
128                }
129                case SOLIDUS:
130                    jj_consume_token(SOLIDUS);
131                    result = Component(); {
132                    return ONE.divide(result);
133                }
134                case 14:
135                    jj_consume_token(14);
136                    result = Term();
137                    jj_consume_token(15); {
138                    return result;
139                }
140                default:
141                    jj_la1[2] = jj_gen;
142                    jj_consume_token(-1);
143                    throw new TokenException();
144            }
145        }
146    }
147
148    final public Unit Annotatable() throws TokenException {
149        Unit result = ONE;
150        Token token1 = null;
151        Token token2 = null;
152        if (jj_2_2(2147483647)) {
153            result = SimpleUnit();
154            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
155                case SIGN:
156                    token1 = jj_consume_token(SIGN);
157                    break;
158                default:
159                    jj_la1[3] = jj_gen;
160            }
161            token2 = jj_consume_token(FACTOR);
162            int exponent = Integer.parseInt(token2.image);
163            if ((token1 != null) && token1.image.equals("-")) {
164                {
165                    return result.pow(-exponent);
166                }
167            } else {
168                {
169                    return result.pow(exponent);
170                }
171            }
172        } else {
173            switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) {
174                case ATOM:
175                    result = SimpleUnit(); {
176                    return result;
177                }
178                default:
179                    jj_la1[4] = jj_gen;
180                    jj_consume_token(-1);
181                    throw new TokenException();
182            }
183        }
184    }
185
186    final public Unit SimpleUnit() throws TokenException {
187        Token token = null;
188        token = jj_consume_token(ATOM);
189        Unit unit = symbols.getUnit(token.image);
190        if (unit == null) {
191            MetricPrefix prefix = symbols.getPrefix(token.image);
192            if (prefix != null) {
193                String prefixSymbol = symbols.getSymbol(prefix);
194                unit = symbols.getUnit(token.image.substring(prefixSymbol.length()));
195                if (unit != null) {
196                    {
197                        return unit.transform(prefix.getConverter());
198                    }
199                }
200            }
201            {
202                throw new TokenException();
203            }
204        } else {
205            {
206                return unit;
207            }
208        }
209    }
210
211    private boolean jj_2_1(int xla) {
212        jj_la = xla;
213        jj_lastpos = jj_scanpos = token;
214        try {
215            return !jj_3_1();
216        } catch (LookaheadSuccess ls) {
217            return true;
218        } finally {
219            jj_save(0, xla);
220        }
221    }
222
223    private boolean jj_2_2(int xla) {
224        jj_la = xla;
225        jj_lastpos = jj_scanpos = token;
226        try {
227            return !jj_3_2();
228        } catch (LookaheadSuccess ls) {
229            return true;
230        } finally {
231            jj_save(1, xla);
232        }
233    }
234
235    private boolean jj_3_1() {
236        return jj_3R_2() || jj_scan_token(ANNOTATION);
237    }
238
239    private boolean jj_3R_5() {
240        return jj_3R_3();
241    }
242
243    private boolean jj_3R_4() {
244        if (jj_3R_3())
245            return true;
246        Token xsp;
247        xsp = jj_scanpos;
248        if (jj_scan_token(10))
249            jj_scanpos = xsp;
250        return jj_scan_token(FACTOR);
251    }
252
253    private boolean jj_3_2() {
254        if (jj_3R_3())
255            return true;
256        Token xsp;
257        xsp = jj_scanpos;
258        if (jj_scan_token(10))
259            jj_scanpos = xsp;
260        return jj_scan_token(FACTOR);
261    }
262
263    private boolean jj_3R_3() {
264        return jj_scan_token(ATOM);
265    }
266
267    private boolean jj_3R_2() {
268        Token xsp;
269        xsp = jj_scanpos;
270        if (jj_3R_4()) {
271            jj_scanpos = xsp;
272            if (jj_3R_5())
273                return true;
274        }
275        return false;
276    }
277    /** Generated Token Manager. */
278    public UCUMTokenManager token_source;
279
280    UCUMCharStream jj_input_stream;
281
282    /** Current token. */
283    public Token token;
284
285    /** Next token. */
286    public Token jj_nt;
287
288    private int jj_ntk;
289
290    private Token jj_scanpos, jj_lastpos;
291
292    private int jj_la;
293
294    private int jj_gen;
295
296    final private int[] jj_la1 = new int[5];
297
298    static private int[] jj_la1_0;
299
300    static {
301        jj_la1_init_0();
302    }
303
304    private static void jj_la1_init_0() {
305        jj_la1_0 = new int[]{0x1800, 0x1800, 0x7300, 0x400, 0x2000,};
306    }
307    final private JJCalls[] jj_2_rtns = new JJCalls[2];
308
309    private boolean jj_rescan = false;
310
311    private int jj_gc = 0;
312
313    /** Constructor with InputStream. */
314    public UCUMFormatParser(java.io.InputStream stream) {
315        this(stream, null);
316    }
317
318    /** Constructor with InputStream and supplied encoding */
319    public UCUMFormatParser(java.io.InputStream stream, String encoding) {
320        try {
321            jj_input_stream = new UCUMCharStream(stream, encoding, 1, 1);
322        } catch (java.io.UnsupportedEncodingException e) {
323            throw new RuntimeException(e);
324        }
325        token_source = new UCUMTokenManager(jj_input_stream);
326        token = new Token();
327        jj_ntk = -1;
328        jj_gen = 0;
329        for (int i = 0; i < 5; i++) {
330            jj_la1[i] = -1;
331        }
332        for (int i = 0; i < jj_2_rtns.length; i++) {
333            jj_2_rtns[i] = new JJCalls();
334        }
335    }
336
337    /** Reinitialise. */
338    public void ReInit(java.io.InputStream stream) {
339        ReInit(stream, null);
340    }
341
342    /** Reinitialise. */
343    public void ReInit(java.io.InputStream stream, String encoding) {
344        try {
345            jj_input_stream.ReInit(stream, encoding, 1, 1);
346        } catch (java.io.UnsupportedEncodingException e) {
347            throw new RuntimeException(e);
348        }
349        token_source.ReInit(jj_input_stream);
350        token = new Token();
351        jj_ntk = -1;
352        jj_gen = 0;
353        for (int i = 0; i < 5; i++) {
354            jj_la1[i] = -1;
355        }
356        for (int i = 0; i < jj_2_rtns.length; i++) {
357            jj_2_rtns[i] = new JJCalls();
358        }
359    }
360
361    /** Constructor. */
362    public UCUMFormatParser(java.io.Reader stream) {
363        jj_input_stream = new UCUMCharStream(stream, 1, 1);
364        token_source = new UCUMTokenManager(jj_input_stream);
365        token = new Token();
366        jj_ntk = -1;
367        jj_gen = 0;
368        for (int i = 0; i < 5; i++) {
369            jj_la1[i] = -1;
370        }
371        for (int i = 0; i < jj_2_rtns.length; i++) {
372            jj_2_rtns[i] = new JJCalls();
373        }
374    }
375
376    /** Reinitialise. */
377    public void ReInit(java.io.Reader stream) {
378        jj_input_stream.ReInit(stream, 1, 1);
379        token_source.ReInit(jj_input_stream);
380        token = new Token();
381        jj_ntk = -1;
382        jj_gen = 0;
383        for (int i = 0; i < 5; i++) {
384            jj_la1[i] = -1;
385        }
386        for (int i = 0; i < jj_2_rtns.length; i++) {
387            jj_2_rtns[i] = new JJCalls();
388        }
389    }
390
391    /** Constructor with generated Token Manager. */
392    public UCUMFormatParser(UCUMTokenManager tm) {
393        token_source = tm;
394        token = new Token();
395        jj_ntk = -1;
396        jj_gen = 0;
397        for (int i = 0; i < 5; i++) {
398            jj_la1[i] = -1;
399        }
400        for (int i = 0; i < jj_2_rtns.length; i++) {
401            jj_2_rtns[i] = new JJCalls();
402        }
403    }
404
405    /** Reinitialise. */
406    public void ReInit(UCUMTokenManager tm) {
407        token_source = tm;
408        token = new Token();
409        jj_ntk = -1;
410        jj_gen = 0;
411        for (int i = 0; i < 5; i++) {
412            jj_la1[i] = -1;
413        }
414        for (int i = 0; i < jj_2_rtns.length; i++) {
415            jj_2_rtns[i] = new JJCalls();
416        }
417    }
418
419    private Token jj_consume_token(int kind) throws TokenException {
420        Token oldToken;
421        if ((oldToken = token).next != null)
422            token = token.next;
423        else
424            token = token.next = token_source.getNextToken();
425        jj_ntk = -1;
426        if (token.kind == kind) {
427            jj_gen++;
428            if (++jj_gc > 100) {
429                jj_gc = 0;
430                for (JJCalls jj_2_rtn : jj_2_rtns) {
431                    JJCalls c = jj_2_rtn;
432                    while (c != null) {
433                        if (c.gen < jj_gen)
434                            c.first = null;
435                        c = c.next;
436                    }
437                }
438            }
439            return token;
440        }
441        token = oldToken;
442        jj_kind = kind;
443        throw raiseTokenException();
444    }
445
446    static private final class LookaheadSuccess extends java.lang.Error {
447
448        /**
449         *
450         */
451        private static final long serialVersionUID = -1747326813448392305L;
452
453    }
454    final private LookaheadSuccess jj_ls = new LookaheadSuccess();
455
456    private boolean jj_scan_token(int kind) {
457        if (jj_scanpos == jj_lastpos) {
458            jj_la--;
459            if (jj_scanpos.next == null) {
460                jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken();
461            } else {
462                jj_lastpos = jj_scanpos = jj_scanpos.next;
463            }
464        } else {
465            jj_scanpos = jj_scanpos.next;
466        }
467        if (jj_rescan) {
468            int i = 0;
469            Token tok = token;
470            while (tok != null && tok != jj_scanpos) {
471                i++;
472                tok = tok.next;
473            }
474            if (tok != null)
475                jj_add_error_token(kind, i);
476        }
477        if (jj_scanpos.kind != kind)
478            return true;
479        if (jj_la == 0 && jj_scanpos == jj_lastpos)
480            throw jj_ls;
481        return false;
482    }
483
484    /** Get the next Token. */
485    final public Token getNextToken() {
486        if (token.next != null)
487            token = token.next;
488        else
489            token = token.next = token_source.getNextToken();
490        jj_ntk = -1;
491        jj_gen++;
492        return token;
493    }
494
495    /** Get the specific Token. */
496    final public Token getToken(int index) {
497        Token t = token;
498        for (int i = 0; i < index; i++) {
499            if (t.next != null)
500                t = t.next;
501            else
502                t = t.next = token_source.getNextToken();
503        }
504        return t;
505    }
506
507    private int jj_ntk() {
508        if ((jj_nt = token.next) == null)
509            return (jj_ntk = (token.next = token_source.getNextToken()).kind);
510        else
511            return (jj_ntk = jj_nt.kind);
512    }
513    private java.util.List<int[]> jj_expentries = new java.util.ArrayList<>();
514
515    private int[] jj_expentry;
516
517    private int jj_kind = -1;
518
519    private int[] jj_lasttokens = new int[100];
520
521    private int jj_endpos;
522
523    private void jj_add_error_token(int kind, int pos) {
524        if (pos >= 100)
525            return;
526        if (pos == jj_endpos + 1) {
527            jj_lasttokens[jj_endpos++] = kind;
528        } else if (jj_endpos != 0) {
529            jj_expentry = new int[jj_endpos];
530            System.arraycopy(jj_lasttokens, 0, jj_expentry, 0, jj_endpos);
531            jj_entries_loop:
532            for (int[] jj_expentry1 : jj_expentries) {
533                if (jj_expentry1.length == jj_expentry.length) {
534                    for (int i = 0; i < jj_expentry.length; i++) {
535                        if (jj_expentry1[i] != jj_expentry[i]) {
536                            continue jj_entries_loop;
537                        }
538                    }
539                    jj_expentries.add(jj_expentry);
540                    break;
541                }
542            }
543            if (pos != 0)
544                jj_lasttokens[(jj_endpos = pos) - 1] = kind;
545        }
546    }
547
548    /** Generate TokenException. */
549    TokenException raiseTokenException() {
550        jj_expentries.clear();
551        boolean[] la1tokens = new boolean[16];
552        if (jj_kind >= 0) {
553            la1tokens[jj_kind] = true;
554            jj_kind = -1;
555        }
556        for (int i = 0; i < 5; i++) {
557            if (jj_la1[i] == jj_gen) {
558                for (int j = 0; j < 32; j++) {
559                    if ((jj_la1_0[i] & (1 << j)) != 0) {
560                        la1tokens[j] = true;
561                    }
562                }
563            }
564        }
565        for (int i = 0; i < 16; i++) {
566            if (la1tokens[i]) {
567                jj_expentry = new int[1];
568                jj_expentry[0] = i;
569                jj_expentries.add(jj_expentry);
570            }
571        }
572        jj_endpos = 0;
573        jj_rescan_token();
574        jj_add_error_token(0, 0);
575        int[][] exptokseq = new int[jj_expentries.size()][];
576        for (int i = 0; i < jj_expentries.size(); i++) {
577            exptokseq[i] = jj_expentries.get(i);
578        }
579        return new TokenException(token, exptokseq, tokenImage);
580    }
581
582    /** Enable tracing. */
583    final public void enable_tracing() {
584    }
585
586    /** Disable tracing. */
587    final public void disable_tracing() {
588    }
589
590    private void jj_rescan_token() {
591        jj_rescan = true;
592        for (int i = 0; i < 2; i++) {
593            try {
594                JJCalls p = jj_2_rtns[i];
595                do {
596                    if (p.gen > jj_gen) {
597                        jj_la = p.arg;
598                        jj_lastpos = jj_scanpos = p.first;
599                        switch (i) {
600                            case 0:
601                                jj_3_1();
602                                break;
603                            case 1:
604                                jj_3_2();
605                                break;
606                        }
607                    }
608                    p = p.next;
609                } while (p != null);
610            } catch (LookaheadSuccess ls) {
611            }
612        }
613        jj_rescan = false;
614    }
615
616    private void jj_save(int index, int xla) {
617        JJCalls p = jj_2_rtns[index];
618        while (p.gen > jj_gen) {
619            if (p.next == null) {
620                p = p.next = new JJCalls();
621                break;
622            }
623            p = p.next;
624        }
625        p.gen = jj_gen + xla - jj_la;
626        p.first = token;
627        p.arg = xla;
628    }
629
630    static final class JJCalls {
631
632        int gen;
633
634        Token first;
635
636        int arg;
637
638        JJCalls next;
639
640    }
641}