1 /* 2 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 import java.io.BufferedReader; 26 import java.io.File; 27 import java.io.FileInputStream; 28 import java.io.InputStreamReader; 29 import java.util.*; 30 31 public class CalendarTestEngine { 32 private static File file; 33 private static BufferedReader in; 34 private static int testCount; 35 private static int lineno; 36 private static Locale locale; 37 private static TimeZone timezone; 38 private static boolean leniency = true; 39 main(String[] args)40 public static void main(String[] args) throws Exception { 41 Locale loc = Locale.getDefault(); 42 TimeZone tz = TimeZone.getDefault(); 43 locale = loc; 44 timezone = tz; 45 try { 46 for (String arg : args) { 47 file = new File(arg); 48 FileInputStream fis = new FileInputStream(file); 49 System.out.println("Starting " + file.getName() + "..."); 50 in = new BufferedReader(new InputStreamReader(fis)); 51 testCount = lineno = 0; 52 run(); 53 System.out.println("Completed " + file.getName()); 54 } 55 } finally { 56 Locale.setDefault(loc); 57 TimeZone.setDefault(tz); 58 } 59 } 60 run()61 private static void run() throws Exception { 62 String line; 63 int section = 0; 64 Map<String, CalendarAdapter> cals = new HashMap<String, CalendarAdapter>(); 65 CalendarAdapter calendar = null; 66 Result result = new Result(); 67 68 while ((line = in.readLine()) != null) { 69 lineno++; 70 line = line.trim(); 71 // Skip blank and comment lines 72 if (line.length() == 0 || line.charAt(0) == '#') { 73 continue; 74 } 75 int comment = line.indexOf('#'); 76 if (comment != -1) { 77 line = line.substring(0, comment).trim(); 78 } 79 Scanner sc = new Scanner(line); 80 String token = sc.next(); 81 Symbol operation = symbol(token); 82 if (operation == null) { 83 throw new RuntimeException(lineno() + "wrong op? " + token); 84 } 85 86 87 if (operation.type == Symbol.Type.EXCEPTION) { 88 String className = sc.next(); 89 Class clazz = Exceptions.get(className); 90 Exception e = result.getException(); 91 if (!clazz.isInstance(e)) { 92 throw new RuntimeException(lineno() + "unexpected exception: got: " + e 93 + ", expected=" + clazz); 94 } 95 } 96 97 Exception x = result.getException(); 98 if (x != null) { 99 throw new RuntimeException(lineno(result.getLineno()) + "Unexpected exception", x); 100 } 101 102 try { 103 switch (operation.type) { 104 case LOCALE: 105 { 106 String lang = sc.next(); 107 String country = "", var = ""; 108 if (sc.hasNext()) { 109 country = sc.next(); 110 if (sc.hasNext()) { 111 var = sc.next(); 112 } 113 } 114 locale = new Locale(lang, country, var); 115 } 116 break; 117 118 case TIMEZONE: 119 { 120 if (sc.hasNext()) { 121 String id = sc.next(); 122 timezone = TimeZone.getTimeZone(id); 123 if (!timezone.getID().equals(id)) { 124 System.err.printf("Warning: line %d: may get wrong time zone? " 125 +"(specified: %s vs. actual: %s)%n", 126 lineno, id, timezone.getID()); 127 } 128 } 129 } 130 break; 131 132 case NEW: 133 { 134 Symbol op = symbol(sc.next()); 135 Calendar cal = null; 136 switch (op.type) { 137 case INSTANCE: 138 cal = Calendar.getInstance(timezone, locale); 139 break; 140 case GREGORIAN: 141 cal = new GregorianAdapter(timezone, locale); 142 break; 143 default: 144 symbolError(op); 145 break; 146 } 147 cal.setLenient(leniency); 148 calendar = new CalendarAdapter(cal); 149 if (sc.hasNext()) { 150 String name = sc.next(); 151 cals.put(name.toLowerCase(Locale.ROOT), calendar); 152 if (!leniency) { 153 System.out.printf("%s%s is non-lenient%n", lineno(), name); 154 } 155 } else { 156 throw new RuntimeException(lineno() + "Missing associated name"); 157 } 158 } 159 break; 160 161 case TEST: 162 testCount++; 163 if (sc.hasNext()) { 164 System.out.printf("Test#%d:%s%n", testCount, sc.findInLine(".+")); 165 } else { 166 System.out.printf("Test#%d:%n", testCount); 167 } 168 break; 169 170 case USE: 171 { 172 String name = sc.next().toLowerCase(Locale.ROOT); 173 CalendarAdapter c = cals.get(name); 174 if (c == null) { 175 throw new CalendarTestException(lineno() + "calendar " + name 176 + " not found."); 177 } 178 calendar = c; 179 } 180 break; 181 182 case ASSIGN: 183 { 184 long v = getLong(sc); 185 String to = sc.next().toLowerCase(Locale.ROOT); 186 boolean assign = true; 187 if (sc.hasNext()) { 188 Symbol condition = symbol(sc.next()); 189 if (condition.type == Symbol.Type.IF) { 190 long v1 = getLong(sc); 191 Symbol op = symbol(sc.next()); 192 long v2 = getLong(sc); 193 assign = relation(v1, op, v2); 194 } else { 195 symbolError(condition); 196 } 197 } 198 if (assign) 199 Variable.newVar(to, v); 200 } 201 break; 202 203 case EVAL: 204 { 205 long v1 = getLong(sc); 206 String op = sc.next(); 207 Symbol operator = symbol(op); 208 if (operator == null) { 209 throw new RuntimeException("op " + op + " invalid"); 210 } 211 long v2 = getLong(sc); 212 if (operator.isArithmetic()) { 213 long value = 0; 214 switch (operator.type) { 215 case PLUS: 216 value = v1 + v2; 217 break; 218 219 case MINUS: 220 value = v1 - v2; 221 break; 222 223 case MULTIPLY: 224 value = v1 * v2; 225 break; 226 227 case DIVIDE: 228 value = v1 / v2; 229 break; 230 231 case MOD: 232 value = v1 % v2; 233 break; 234 235 default: 236 symbolError(operator); 237 break; 238 } 239 result.setValue(value); 240 } else { 241 if (!relation(v1, operator, v2)) { 242 throw new RuntimeException("not " + v1 + " " + op + " " + v2); 243 } 244 } 245 } 246 break; 247 248 case CLEAR: 249 { 250 Symbol sym = symbol(sc.next()); 251 if (sym.type == Symbol.Type.ALL) { 252 calendar.clearAll(); 253 } else if (sym.type == Symbol.Type.FIELD) { 254 int f = sym.value(); 255 calendar.clearField(f); 256 } else { 257 symbolError(sym); 258 } 259 } 260 break; 261 262 case GET: 263 { 264 Symbol sym = symbol(sc.next()); 265 switch (sym.type) { 266 case FIELD: 267 { 268 int f = sym.value(); 269 int v = calendar.get(f); 270 result.setValue(v); 271 } 272 break; 273 274 case MILLIS: 275 { 276 long v = calendar.getTimeInMillis(); 277 result.setValue(v); 278 } 279 break; 280 281 case MINIMUM: 282 { 283 int f = getInt(sc); 284 int v = calendar.getMinimum(f); 285 result.setValue(v); 286 } 287 break; 288 289 case GREATESTMINIMUM: 290 { 291 int f = getInt(sc); 292 int v = calendar.getGreatestMinimum(f); 293 result.setValue(v); 294 } 295 break; 296 297 case ACTUALMINIMUM: 298 { 299 int f = getInt(sc); 300 int v = calendar.getActualMinimum(f); 301 result.setValue(v); 302 } 303 break; 304 305 case MAXIMUM: 306 { 307 int f = getInt(sc); 308 int v = calendar.getMaximum(f); 309 result.setValue(v); 310 } 311 break; 312 313 case LEASTMAXIMUM: 314 { 315 int f = getInt(sc); 316 int v = calendar.getLeastMaximum(f); 317 result.setValue(v); 318 } 319 break; 320 321 case ACTUALMAXIMUM: 322 { 323 int f = getInt(sc); 324 int v = calendar.getActualMaximum(f); 325 result.setValue(v); 326 } 327 break; 328 329 case FIRSTDAYOFWEEK: 330 { 331 result.setValue(calendar.getFirstDayOfWeek()); 332 } 333 break; 334 335 case MINIMALDAYSINFIRSTWEEK: 336 { 337 int v = calendar.getMinimalDaysInFirstWeek(); 338 result.setValue(v); 339 } 340 break; 341 342 default: 343 symbolError(sym); 344 break; 345 } 346 } 347 break; 348 349 case ADD: 350 case ROLL: 351 { 352 Symbol sym = symbol(sc.next()); 353 if (sym.type == Symbol.Type.FIELD) { 354 int f = sym.value(); 355 int v = sc.nextInt(); 356 switch (operation.type) { 357 case ADD: 358 calendar.add(f, v); 359 break; 360 361 case ROLL: 362 calendar.roll(f, v); 363 break; 364 } 365 } else { 366 symbolError(sym); 367 } 368 } 369 break; 370 371 case SET: 372 { 373 Symbol sym = symbol(sc.next()); 374 switch (sym.type) { 375 case FIELD: 376 { 377 int f = sym.value(); 378 int v = getInt(sc); 379 calendar.set(f, v); 380 } 381 break; 382 383 case MILLIS: 384 { 385 long v = getLong(sc); 386 calendar.setTimeInMillis(v); 387 } 388 break; 389 390 case DATE: 391 { 392 int a = getInt(sc); 393 int b = getInt(sc); 394 int c = getInt(sc); 395 if (sc.hasNext()) { 396 int d = getInt(sc); 397 // era, year, month, dayOfMonth 398 calendar.setDate(a, b, c, d); 399 } else { 400 // year, month, dayOfMonth 401 calendar.setDate(a, b, c); 402 } 403 } 404 break; 405 406 case DATETIME: 407 { 408 int y = getInt(sc); 409 int m = getInt(sc); 410 int d = getInt(sc); 411 int hh = getInt(sc); 412 int mm = getInt(sc); 413 int ss = getInt(sc); 414 calendar.setDateTime(y, m, d, hh, mm, ss); 415 } 416 break; 417 418 case TIMEOFDAY: 419 { 420 int hh = getInt(sc); 421 int mm = getInt(sc); 422 int ss = getInt(sc); 423 int ms = getInt(sc); 424 calendar.setTimeOfDay(hh, mm, ss, ms); 425 } 426 break; 427 428 case FIRSTDAYOFWEEK: 429 { 430 int v = getInt(sc); 431 calendar.setFirstDayOfWeek(v); 432 } 433 break; 434 435 case MINIMALDAYSINFIRSTWEEK: 436 { 437 int v = getInt(sc); 438 calendar.setMinimalDaysInFirstWeek(v); 439 } 440 break; 441 442 case LENIENT: 443 if (calendar != null) { 444 calendar.setLenient(true); 445 } 446 leniency = true; 447 break; 448 449 case NONLENIENT: 450 if (calendar != null) { 451 calendar.setLenient(false); 452 } 453 leniency = false; 454 break; 455 456 default: 457 symbolError(sym); 458 } 459 if (sc.hasNext()) { 460 throw new RuntimeException(lineno() + "extra param(s) " 461 + sc.findInLine(".+")); 462 } 463 } 464 break; 465 466 case CHECK: 467 { 468 Symbol sym = symbol(sc.next()); 469 boolean stat = false; 470 switch (sym.type) { 471 case MILLIS: 472 { 473 long millis = getLong(sc); 474 stat = calendar.checkMillis(millis); 475 } 476 break; 477 478 case FIELD: 479 { 480 int f = sym.value(); 481 int v = getInt(sc); 482 stat = calendar.checkField(f, v); 483 } 484 break; 485 486 case DATE: 487 { 488 int a = getInt(sc); 489 int b = getInt(sc); 490 int c = getInt(sc); 491 if (sc.hasNext()) { 492 int d = getInt(sc); 493 // era year month dayOfMonth 494 stat = calendar.checkDate(a, b, c, d); 495 } else { 496 // year month dayOfMonth 497 stat = calendar.checkDate(a, b, c); 498 } 499 } 500 break; 501 502 case DATETIME: 503 { 504 int y = getInt(sc); 505 int m = getInt(sc); 506 int d = getInt(sc); 507 int hh = getInt(sc); 508 int mm = getInt(sc); 509 int ss = getInt(sc); 510 if (sc.hasNext()) { 511 int ms = getInt(sc); 512 stat = calendar.checkDateTime(y, m, d, hh, mm, ss, ms); 513 } else { 514 stat = calendar.checkDateTime(y, m, d, hh, mm, ss); 515 } 516 } 517 break; 518 519 case TIMEOFDAY: 520 { 521 int hh = sc.nextInt(); 522 int mm = sc.nextInt(); 523 int ss = sc.nextInt(); 524 int millis = sc.nextInt(); 525 stat = calendar.checkTimeOfDay(hh, mm, ss, millis); 526 } 527 break; 528 529 case MINIMUM: 530 { 531 int f = getInt(sc); 532 int v = getInt(sc); 533 stat = calendar.checkMinimum(f, v); 534 } 535 break; 536 537 case GREATESTMINIMUM: 538 { 539 int f = getInt(sc); 540 int v = getInt(sc); 541 stat = calendar.checkGreatestMinimum(f, v); 542 } 543 break; 544 545 case ACTUALMINIMUM: 546 { 547 int f = getInt(sc); 548 int v = getInt(sc); 549 stat = calendar.checkActualMinimum(f, v); 550 } 551 break; 552 553 case MAXIMUM: 554 { 555 int f = getInt(sc); 556 int v = getInt(sc); 557 stat = calendar.checkMaximum(f, v); 558 } 559 break; 560 561 case LEASTMAXIMUM: 562 { 563 int f = getInt(sc); 564 int v = getInt(sc); 565 stat = calendar.checkLeastMaximum(f, v); 566 } 567 break; 568 569 case ACTUALMAXIMUM: 570 { 571 int f = getInt(sc); 572 int v = getInt(sc); 573 stat = calendar.checkActualMaximum(f, v); 574 } 575 break; 576 577 default: 578 throw new RuntimeException(lineno() + "Unknown operand"); 579 } 580 if (!stat) { 581 throw new RuntimeException(lineno() + calendar.getMessage()); 582 } 583 } 584 break; 585 586 case PRINT: 587 { 588 String s = sc.next(); 589 if (s.charAt(0) == '$') { 590 Variable var = variable(s); 591 if (var == null) 592 throw new RuntimeException(lineno() + "Unknown token: " + s); 593 System.out.printf("%s%s=%d%n", lineno(), s, var.longValue()); 594 break; 595 } 596 597 Symbol sym = symbol(s); 598 switch (sym.type) { 599 case INSTANCE: 600 { 601 Calendar cal = calendar; 602 String name = "current"; 603 if (sc.hasNext()) { 604 name = sc.next(); 605 cal = cals.get(name.toLowerCase(Locale.ROOT)); 606 } 607 System.out.printf("%s%s=%s%n", lineno(), name, cal); 608 } 609 break; 610 611 case FIELD: 612 { 613 int f = sym.value(); 614 String remark = ""; 615 if (sc.hasNext()) { 616 remark = sc.findInLine(".+"); 617 } 618 System.out.printf("%s%s=%d %s%n", lineno(), calendar.fieldName(f), 619 calendar.get(f), remark); 620 } 621 break; 622 623 case MILLIS: 624 { 625 String remark = ""; 626 if (sc.hasNext()) { 627 remark = sc.findInLine(".+"); 628 } 629 System.out.printf("%sMillis=%d %s%n", lineno(), 630 calendar.getTimeInMillis(), remark); 631 } 632 break; 633 634 case MINIMUM: 635 System.out.printf("%s%s=%d%n", lineno(), 636 s, calendar.getMinimum(getInt(sc))); 637 break; 638 639 case GREATESTMINIMUM: 640 System.out.printf("%s%s=%d%n", lineno(), 641 s, calendar.getGreatestMinimum(getInt(sc))); 642 break; 643 644 case ACTUALMINIMUM: 645 System.out.printf("%s%s=%d%n", lineno(), 646 s, calendar.getActualMinimum(getInt(sc))); 647 break; 648 649 case MAXIMUM: 650 System.out.printf("%s%s=%d%n", lineno(), 651 s, calendar.getMaximum(getInt(sc))); 652 break; 653 654 case LEASTMAXIMUM: 655 System.out.printf("%s%s=%d%n", lineno(), 656 s, calendar.getLeastMaximum(getInt(sc))); 657 break; 658 659 case ACTUALMAXIMUM: 660 System.out.printf("%s%s=%d%n", lineno(), 661 s, calendar.getActualMaximum(getInt(sc))); 662 break; 663 664 case DATE: 665 System.out.println(lineno() + calendar.toDateString()); 666 break; 667 668 case DATETIME: 669 System.out.println(lineno() + calendar.toDateTimeString()); 670 break; 671 672 case TIMEZONE: 673 System.out.println(lineno() + "timezone=" + timezone); 674 break; 675 676 case LOCALE: 677 System.out.println(lineno() + "locale=" + locale); 678 break; 679 } 680 } 681 } 682 } catch (CalendarTestException cte) { 683 throw cte; 684 } catch (NoSuchElementException nsee) { 685 throw new NoSuchElementException(lineno() + "syntax error"); 686 } catch (Exception e) { 687 result.setException(e); 688 result.setLineno(lineno); 689 } 690 } 691 Exception x = result.getException(); 692 if (x != null) { 693 throw new RuntimeException(lineno(result.getLineno()) + "Unexpected exception", x); 694 } 695 } 696 symbol(String s)697 private static Symbol symbol(String s) { 698 return Symbol.get(s.toLowerCase(Locale.ROOT)); 699 } 700 variable(String s)701 private static Variable variable(String s) { 702 return Variable.get(s.toLowerCase(Locale.ROOT)); 703 } 704 getInt(Scanner sc)705 private static int getInt(Scanner sc) { 706 if (sc.hasNextInt()) { 707 return sc.nextInt(); 708 } 709 710 String s = sc.next(); 711 if (s.charAt(0) == '$') { 712 Variable var = variable(s); 713 if (var == null) 714 throw new RuntimeException(lineno() + "Unknown token: " + s); 715 return var.intValue(); 716 } 717 Symbol sym = symbol(s); 718 if (sym == null) 719 throw new RuntimeException(lineno() + "Unknown token: " + s); 720 return sym.value(); 721 } 722 getLong(Scanner sc)723 private static long getLong(Scanner sc) { 724 if (sc.hasNextLong()) { 725 return sc.nextLong(); 726 } 727 728 String s = sc.next(); 729 if (s.charAt(0) == '$') { 730 Variable var = variable(s); 731 if (var == null) 732 throw new RuntimeException(lineno() + "Unknown token: " + s); 733 return var.longValue(); 734 } 735 Symbol sym = symbol(s); 736 if (sym == null) 737 throw new RuntimeException(lineno() + "Unknown token: " + s); 738 return sym.value(); 739 } 740 relation(long v1, Symbol relop, long v2)741 private static boolean relation(long v1, Symbol relop, long v2) { 742 boolean result = false; 743 switch (relop.type) { 744 case GT: 745 result = v1 > v2; 746 break; 747 748 case GE: 749 result = v1 >= v2; 750 break; 751 752 case EQ: 753 result = v1 == v2; 754 break; 755 756 case NEQ: 757 result = v1 != v2; 758 break; 759 760 case LE: 761 result = v1 <= v2; 762 break; 763 764 case LT: 765 result = v1 < v2; 766 break; 767 } 768 return result; 769 } 770 771 private static String lineno() { 772 return lineno(lineno); 773 } 774 775 private static String lineno(int ln) { 776 return file.getName() + ":" + ln + ": "; 777 } 778 779 private static void symbolError(Symbol sym) { 780 throw new RuntimeException(lineno + ": unexpected symbol: " + sym); 781 } 782 } 783