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