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