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}