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