]> git.aero2k.de Git - tmp/jakarta-migration.git/blob
560fa020b232440da3514e5edcef100b1469b0fd
[tmp/jakarta-migration.git] /
1 package org.collectionspace.services.structureddate.antlr;
2
3 import java.util.Stack;
4
5 import org.antlr.v4.runtime.ANTLRInputStream;
6 import org.antlr.v4.runtime.BailErrorStrategy;
7 import org.antlr.v4.runtime.CommonTokenStream;
8 import org.antlr.v4.runtime.FailedPredicateException;
9 import org.antlr.v4.runtime.InputMismatchException;
10 import org.antlr.v4.runtime.NoViableAltException;
11 import org.antlr.v4.runtime.Parser;
12 import org.antlr.v4.runtime.RecognitionException;
13 import org.antlr.v4.runtime.Token;
14 import org.antlr.v4.runtime.TokenStream;
15 import org.antlr.v4.runtime.misc.ParseCancellationException;
16 import org.antlr.v4.runtime.tree.TerminalNode;
17 import org.collectionspace.services.structureddate.Date;
18 import org.collectionspace.services.structureddate.DateUtils;
19 import org.collectionspace.services.structureddate.DeferredCenturyEndDate;
20 import org.collectionspace.services.structureddate.DeferredCenturyStartDate;
21 import org.collectionspace.services.structureddate.DeferredDate;
22 import org.collectionspace.services.structureddate.DeferredDecadeEndDate;
23 import org.collectionspace.services.structureddate.DeferredDecadeStartDate;
24 import org.collectionspace.services.structureddate.DeferredHalfCenturyEndDate;
25 import org.collectionspace.services.structureddate.DeferredHalfCenturyStartDate;
26 import org.collectionspace.services.structureddate.DeferredMillenniumEndDate;
27 import org.collectionspace.services.structureddate.DeferredMillenniumStartDate;
28 import org.collectionspace.services.structureddate.DeferredPartialCenturyEndDate;
29 import org.collectionspace.services.structureddate.DeferredPartialCenturyStartDate;
30 import org.collectionspace.services.structureddate.DeferredPartialDecadeEndDate;
31 import org.collectionspace.services.structureddate.DeferredPartialDecadeStartDate;
32 import org.collectionspace.services.structureddate.DeferredQuarterCenturyEndDate;
33 import org.collectionspace.services.structureddate.DeferredQuarterCenturyStartDate;
34 import org.collectionspace.services.structureddate.Era;
35 import org.collectionspace.services.structureddate.Part;
36 import org.collectionspace.services.structureddate.StructuredDateInternal;
37 import org.collectionspace.services.structureddate.StructuredDateEvaluator;
38 import org.collectionspace.services.structureddate.StructuredDateFormatException;
39 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.AllOrPartOfContext;
40 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.BeforeOrAfterDateContext;
41 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.CenturyContext;
42 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.CertainDateContext;
43 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.DateContext;
44 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.DayFirstDateContext;
45 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.DayOrYearFirstDateContext;
46 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.DecadeContext;
47 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.DisplayDateContext;
48 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.EraContext;
49 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.HalfCenturyContext;
50 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.HalfYearContext;
51 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.HyphenatedRangeContext;
52 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.InvMonthYearContext;
53 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.InvSeasonYearContext;
54 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.InvStrDateContext;
55 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.InvStrDateEraLastDateContext;
56 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.MillenniumContext;
57 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.MonthContext;
58 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.MonthInYearRangeContext;
59 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.MonthYearContext;
60 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NthCenturyRangeContext;
61 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NthContext;
62 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NthHalfContext;
63 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NthQuarterContext;
64 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NthQuarterInYearRangeContext;
65 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NthQuarterYearContext;
66 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NumCenturyContext;
67 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NumContext;
68 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NumDateContext;
69 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NumDayInMonthRangeContext;
70 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NumDayOfMonthContext;
71 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NumDecadeContext;
72 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NumMonthContext;
73 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.NumYearContext;
74 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.PartOfContext;
75 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.PartialCenturyContext;
76 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.PartialDecadeContext;
77 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.PartialYearContext;
78 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.QuarterCenturyContext;
79 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.QuarterInYearRangeContext;
80 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.QuarterYearContext;
81 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.SeasonYearContext;
82 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.StrCenturyContext;
83 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.StrDateContext;
84 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.StrDayInMonthRangeContext;
85 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.StrMonthContext;
86 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.StrSeasonContext;
87 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.StrSeasonInYearRangeContext;
88 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.UncertainDateContext;
89 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.UnknownDateContext;
90 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.YearContext;
91 import org.collectionspace.services.structureddate.antlr.StructuredDateParser.YearSpanningWinterContext;
92
93 /**
94  * A StructuredDateEvaluator that uses an ANTLR parser to parse the display date,
95  * and an ANTLR listener to generate a structured date from the resulting parse
96  * tree.
97  */
98 public class ANTLRStructuredDateEvaluator extends StructuredDateBaseListener implements StructuredDateEvaluator {
99         /**
100          * The result of the evaluation.
101          */
102         protected StructuredDateInternal result;
103
104         /**
105          * The operation stack. The parse listener methods that are implemented here
106          * pop input parameters off the stack, and push results back on to the stack.
107          */
108         protected Stack<Object> stack;
109
110         public ANTLRStructuredDateEvaluator() {
111
112         }
113
114         @Override
115         public StructuredDateInternal evaluate(String displayDate) throws StructuredDateFormatException {
116                 stack = new Stack<Object>();
117
118                 result = new StructuredDateInternal();
119                 result.setDisplayDate(displayDate);
120
121                 // Instantiate a parser from the lowercased display date, so that parsing will be
122                 // case insensitive.
123                 ANTLRInputStream inputStream = new ANTLRInputStream(displayDate.toLowerCase());
124                 StructuredDateLexer lexer = new StructuredDateLexer(inputStream);
125                 CommonTokenStream tokenStream = new CommonTokenStream(lexer);
126                 StructuredDateParser parser = new StructuredDateParser(tokenStream);
127
128                 // Don't try to recover from parse errors, just bail.
129                 parser.setErrorHandler(new BailErrorStrategy());
130
131                 // Don't print error messages to the console.
132                 parser.removeErrorListeners();
133
134                 // Generate our own custom error messages.
135                 parser.addParseListener(this);
136
137                 try {
138                         // Attempt to fulfill the oneDisplayDate rule of the grammar.
139                         parser.oneDisplayDate();
140                 }
141                 catch(ParseCancellationException e) {
142                         // ParseCancellationException is thrown by the BailErrorStrategy when there is a
143                         // parse error, with the underlying RecognitionException as the cause.
144                         RecognitionException re = (RecognitionException) e.getCause();
145
146                         throw new StructuredDateFormatException(getErrorMessage(re), re);
147                 }
148
149                 // The parsing was successful. Return the result.
150                 return result;
151         }
152
153         @Override
154         public void exitDisplayDate(DisplayDateContext ctx) {
155                 if (ctx.exception != null) return;
156
157                 Date latestDate = (Date) stack.pop();
158                 Date earliestDate = (Date) stack.pop();
159
160                 // If the earliest date and the latest date are the same, it's just a "single" date.
161                 // There's no need to have the latest, so set it to null.
162
163                 if (earliestDate.equals(latestDate)) {
164                         latestDate = null;
165                 }
166
167                 result.setEarliestSingleDate(earliestDate);
168                 result.setLatestDate(latestDate);
169         }
170         @Override
171         public void exitUnknownDate(UnknownDateContext ctx) {
172                 if (ctx.exception != null) return;
173
174                 // Dummy dates
175                 stack.push(new Date());
176                 stack.push(new Date());
177         }
178
179         @Override
180         public void exitBeforeOrAfterDate(BeforeOrAfterDateContext ctx) {
181                 if (ctx.exception != null) return;
182
183                 Date latestDate = (Date) stack.pop();
184                 Date earliestDate = (Date) stack.pop();
185
186                 // Set null eras to the default.
187
188                 if (earliestDate.getEra() == null) {
189                         earliestDate.setEra(Date.DEFAULT_ERA);
190                 }
191
192                 if (latestDate.getEra() == null) {
193                         latestDate.setEra(Date.DEFAULT_ERA);
194                 }
195
196                 // Finalize any deferred calculations.
197
198                 if (latestDate instanceof DeferredDate) {
199                         ((DeferredDate) latestDate).resolveDate();
200                 }
201
202                 if (earliestDate instanceof DeferredDate) {
203                         ((DeferredDate) earliestDate).resolveDate();
204                 }
205
206                 // Calculate the earliest date or end date.
207
208                 if (ctx.BEFORE() != null) {
209                         latestDate = earliestDate;
210                         earliestDate = DateUtils.getEarliestBeforeDate(earliestDate, latestDate);
211                 }
212                 else if (ctx.AFTER() != null) {
213                         earliestDate = latestDate;
214                         latestDate = DateUtils.getLatestAfterDate(earliestDate, latestDate);
215                 }
216
217                 stack.push(earliestDate);
218                 stack.push(latestDate);
219         }
220
221         @Override
222         public void exitUncertainDate(UncertainDateContext ctx) {
223                 if (ctx.exception != null) return;
224
225                 Date latestDate = (Date) stack.pop();
226                 Date earliestDate = (Date) stack.pop();
227
228                 int earliestInterval = DateUtils.getCircaIntervalYears(earliestDate.getYear(), earliestDate.getEra());
229                 int latestInterval = DateUtils.getCircaIntervalYears(latestDate.getYear(), latestDate.getEra());
230
231                 // Express the circa interval as a qualifier.
232
233                 // stack.push(earliestDate.withQualifier(QualifierType.MINUS, earliestInterval, QualifierUnit.YEARS));
234                 // stack.push(latestDate.withQualifier(QualifierType.PLUS, latestInterval, QualifierUnit.YEARS));
235
236                 // OR:
237
238                 // Express the circa interval as an offset calculated into the year.
239
240                 DateUtils.subtractYears(earliestDate, earliestInterval);
241                 DateUtils.addYears(latestDate, latestInterval);
242
243                 stack.push(earliestDate);
244                 stack.push(latestDate);
245         }
246
247         @Override
248         public void exitCertainDate(CertainDateContext ctx) {
249                 if (ctx.exception != null) return;
250
251                 Date latestDate = (Date) stack.pop();
252                 Date earliestDate = (Date) stack.pop();
253
254                 // Set null eras to the default.
255
256                 if (earliestDate.getEra() == null) {
257                         earliestDate.setEra(Date.DEFAULT_ERA);
258                 }
259
260                 if (latestDate.getEra() == null) {
261                         latestDate.setEra(Date.DEFAULT_ERA);
262                 }
263
264                 // Finalize any deferred calculations.
265
266                 if (latestDate instanceof DeferredDate) {
267                         ((DeferredDate) latestDate).resolveDate();
268                 }
269
270                 if (earliestDate instanceof DeferredDate) {
271                         ((DeferredDate) earliestDate).resolveDate();
272                 }
273
274                 stack.push(earliestDate);
275                 stack.push(latestDate);
276         }
277
278         @Override
279         public void exitHyphenatedRange(HyphenatedRangeContext ctx) {
280                 if (ctx.exception != null) return;
281
282                 Date latestEndDate = (Date) stack.pop();
283                 stack.pop(); // latestStartDate
284                 stack.pop(); // earliestEndDate
285                 Date earliestStartDate = (Date) stack.pop();
286
287                 // If no era was explicitly specified for the first date,
288                 // make it inherit the era of the second date.
289
290                 if (earliestStartDate.getEra() == null && latestEndDate.getEra() != null) {
291                         earliestStartDate.setEra(latestEndDate.getEra());
292                 }
293
294                 // Finalize any deferred calculations.
295
296                 if (earliestStartDate instanceof DeferredDate) {
297                         ((DeferredDate) earliestStartDate).resolveDate();
298                 }
299
300                 if (latestEndDate instanceof DeferredDate) {
301                         ((DeferredDate) latestEndDate).resolveDate();
302                 }
303
304                 stack.push(earliestStartDate);
305                 stack.push(latestEndDate);
306         }
307
308         @Override
309         public void exitNthCenturyRange(NthCenturyRangeContext ctx) {
310                 if (ctx.exception != null) return;
311
312                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
313                 Integer endN = (Integer) stack.pop();
314                 Part endPart = (Part) stack.pop();
315                 Integer startN = (Integer) stack.pop();
316                 Part startPart = (Part) stack.pop();
317
318                 if (era == null) {
319                         era = Date.DEFAULT_ERA;
320                 }
321
322                 int startYear = DateUtils.nthCenturyToYear(startN);
323                 int endYear = DateUtils.nthCenturyToYear(endN);
324
325                 stack.push(startPart == null ? DateUtils.getCenturyStartDate(startYear, era) : DateUtils.getPartialCenturyStartDate(startYear, startPart, era));
326                 stack.push(startPart == null ? DateUtils.getCenturyEndDate(startYear, era) : DateUtils.getPartialCenturyEndDate(startYear, startPart, era));
327                 stack.push(endPart == null ? DateUtils.getCenturyStartDate(endYear, era) : DateUtils.getPartialCenturyStartDate(endYear, endPart, era));
328                 stack.push(endPart == null ? DateUtils.getCenturyEndDate(endYear, era) : DateUtils.getPartialCenturyEndDate(endYear, endPart, era));
329         }
330
331         @Override
332         public void exitMonthInYearRange(MonthInYearRangeContext ctx) {
333                 if (ctx.exception != null) return;
334
335                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
336                 Integer year = (Integer) stack.pop();
337                 Integer numMonthEnd = (Integer) stack.pop();
338                 Integer numMonthStart = (Integer) stack.pop();
339
340                 stack.push(new Date(year, numMonthStart, 1, era));
341                 stack.push(new Date(year, numMonthStart, DateUtils.getDaysInMonth(numMonthStart, year, era), era));
342                 stack.push(new Date(year, numMonthEnd, 1, era));
343                 stack.push(new Date(year, numMonthEnd, DateUtils.getDaysInMonth(numMonthEnd, year, era), era));
344         }
345
346         @Override
347         public void exitQuarterInYearRange(QuarterInYearRangeContext ctx) {
348                 if (ctx.exception != null) return;
349
350                 Era era = (Era) stack.pop();
351                 Integer year = (Integer) stack.pop();
352                 Integer lastQuarter = (Integer) stack.pop();
353                 Integer firstQuarter = (Integer) stack.pop();
354
355                 stack.push(DateUtils.getQuarterYearStartDate(firstQuarter, year).withEra(era));
356                 stack.push(DateUtils.getQuarterYearEndDate(firstQuarter, year, era).withEra(era));
357                 stack.push(DateUtils.getQuarterYearStartDate(lastQuarter, year).withEra(era));
358                 stack.push(DateUtils.getQuarterYearEndDate(lastQuarter, year, era).withEra(era));
359         }
360
361         @Override
362         public void exitStrDayInMonthRange(StrDayInMonthRangeContext ctx) {
363                 if (ctx.exception != null) return;
364
365                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
366                 Integer year = (Integer) stack.pop();
367                 Integer dayOfMonthEnd = (Integer) stack.pop();
368                 Integer dayOfMonthStart = (Integer) stack.pop();
369                 Integer numMonth = (Integer) stack.pop();
370
371                 stack.push(new Date(year, numMonth, dayOfMonthStart, era));
372                 stack.push(new Date(year, numMonth, dayOfMonthStart, era));
373                 stack.push(new Date(year, numMonth, dayOfMonthEnd, era));
374                 stack.push(new Date(year, numMonth, dayOfMonthEnd, era));
375         }
376
377         @Override
378         public void exitNumDayInMonthRange(NumDayInMonthRangeContext ctx) {
379                 if (ctx.exception != null) return;
380
381                 Era era = (Era) stack.pop();
382                 Integer num1 = (Integer) stack.pop();
383                 Integer num2 = (Integer) stack.pop();
384                 Integer num3 = (Integer) stack.pop();
385                 Integer num4 = (Integer) stack.pop();
386
387                 /* We can distinguish whether it is M/D-D/Y (Case 1) or M/Y-M/Y (Case 2) by checking if
388                         The num1, num4, num2 and num1, num4, num3 are valid dates, since this would equate to
389                         a set of two ranges. For examples: 04/13-19/1995 would be 04/13/1995-04/19/1995. If both these
390                         dates are valid, we know that it shouldn't be interpreted as 04/01/13 - 19/31/1995 since these arent valid dates!
391                 */
392
393
394                 Integer lateYear = num1;
395                 Integer earlyMonth = num4;
396                 Integer dayOfMonthEnd = num2;
397                 Integer dayOfMonthStart = num3;
398
399                 if (DateUtils.isValidDate(num1, num4, num2, era) && DateUtils.isValidDate(num1, num4, num3, era)) {
400                         // No need to alter the arguments, so just push to the stack
401                         stack.push(new Date(lateYear, earlyMonth, dayOfMonthStart, era));
402                         stack.push(new Date(lateYear, earlyMonth, dayOfMonthStart, era));
403                         stack.push(new Date(lateYear, earlyMonth, dayOfMonthEnd, era));
404                         stack.push(new Date(lateYear, earlyMonth, dayOfMonthEnd, era));
405
406                 } else {
407                         // Separated these by case, since it makes the code more legible
408                         Integer latestMonth = num2;
409                         Integer earliestYear = num3;
410
411                         stack.push(new Date(earliestYear, earlyMonth, 1, era)); // Earliest Early Date
412                         stack.push(new Date(earliestYear, earlyMonth, DateUtils.getDaysInMonth(earlyMonth, earliestYear, era), era)); // Latest Early Date
413                         stack.push(new Date(lateYear, latestMonth, 1, era)); // Earliest Latest Date
414                         stack.push(new Date(lateYear, latestMonth, DateUtils.getDaysInMonth(latestMonth, lateYear, era), era)); // Latest Late Date
415                         
416                 }
417         }
418
419         @Override
420         public void exitDate(DateContext ctx) {
421                 if (ctx.exception != null) return;
422
423                 // Expect the canonical year-month-day-era ordering
424                 // to be on the stack.
425
426                 Era era = (stack.size() == 3) ? null : (Era) stack.pop();
427                 Integer dayOfMonth = (Integer) stack.pop();
428                 Integer numMonth = (Integer) stack.pop();
429                 Integer year = (Integer) stack.pop();
430
431                 // For the latest date we could either return null, or a copy of the earliest date,
432                 // since the UI doesn't care. Use a copy of the earliest date, since it makes
433                 // things easier here if we don't have to test for null up the tree.
434
435                 stack.push(new Date(year, numMonth, dayOfMonth, era));
436                 stack.push(new Date(year, numMonth, dayOfMonth, era));
437         }
438
439         @Override
440         public void exitNumDate(NumDateContext ctx) {
441                 if (ctx.exception != null) return;
442
443                 // This could either be year-month-day, or
444                 // month-day-year. Try to determine which,
445                 // and reorder the stack into the canonical
446                 // year-month-day-era ordering.
447
448                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
449                 Integer num3 = (Integer) stack.pop();
450                 Integer num2 = (Integer) stack.pop();
451                 Integer num1 = (Integer) stack.pop();
452
453                 // Default to a month-day-year interpretation.
454
455                 int numMonth = num1;
456                 int dayOfMonth = num2;
457                 int year = num3;
458
459                 if (DateUtils.isValidDate(num3, num1, num2, era)) {
460                         // Interpreting as month-day-year produces a valid date. Go with it.
461                 }
462                 else if (DateUtils.isValidDate(num1, num2, num3, era)) {
463                         // Interpreting as month-day-year doesn't produce a valid date, but
464                         // year-month-day does. Go with year-month-day.
465
466                         year = num1;
467                         numMonth = num2;
468                         dayOfMonth = num3;
469                 }
470
471                 stack.push(year);
472                 stack.push(numMonth);
473                 stack.push(dayOfMonth);
474                 stack.push(era);
475         }
476
477         @Override
478         public void exitStrDate(StrDateContext ctx) {
479                 if (ctx.exception != null) return;
480
481                 // Reorder the stack into a canonical ordering,
482                 // year-month-day-era.
483
484                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
485                 Integer year = (Integer) stack.pop();
486                 Integer dayOfMonth = (Integer) stack.pop();
487                 Integer numMonth = (Integer) stack.pop();
488
489                 stack.push(year);
490                 stack.push(numMonth);
491                 stack.push(dayOfMonth);
492                 stack.push(era);
493         }
494
495         @Override
496         public void exitInvStrDate(InvStrDateContext ctx) {
497                 if (ctx.exception != null) return;
498         
499                 // Reorder the stack into a canonical ordering,
500                 // year-month-day-era.
501
502                 Integer dayOfMonth = (Integer) stack.pop();
503                 Integer numMonth = (Integer) stack.pop();
504                 Integer year = (Integer) stack.pop();
505                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
506
507                 stack.push(year);
508                 stack.push(numMonth);
509                 stack.push(dayOfMonth);
510                 stack.push(era);
511         }
512
513         @Override
514         public void exitDayFirstDate(DayFirstDateContext ctx) {
515                 if (ctx.exception != null) return ;
516                 
517                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
518                 Integer year = (Integer) stack.pop();
519                 Integer month = (Integer) stack.pop();
520                 Integer dayOfMonth = (Integer) stack.pop();
521
522                 stack.push(year);
523                 stack.push(month);
524                 stack.push(dayOfMonth);
525                 stack.push(era);
526         }
527
528         @Override
529         public void exitDayOrYearFirstDate(DayOrYearFirstDateContext ctx) {
530                 if (ctx.exception != null) return;
531
532                 Era era = null;
533                 Integer num2 = (Integer) stack.pop();
534                 Integer numMonth = (Integer) stack.pop();
535                 Integer num1 = (Integer) stack.pop();
536
537                 Integer year = num1;
538                 Integer dayOfMonth = num2;
539
540                 if (DateUtils.isValidDate(num1, numMonth, num2, era)) {
541                         // The first number is a year. Already correct
542                 } else if (DateUtils.isValidDate(num2, numMonth, num1, era)) {
543                         // The second number is a year.
544                         year = num2;
545                         dayOfMonth = num1;
546                 }
547
548                 stack.push(year);
549                 stack.push(numMonth);
550                 stack.push(dayOfMonth);
551
552                 if (dayOfMonth > 31 || dayOfMonth <= 0) {
553                         throw new StructuredDateFormatException("unexpected day of month '" + Integer.toString(dayOfMonth) + "'");
554                 }
555                 if (year == 0) {
556                         throw new StructuredDateFormatException("unexpected year '" + Integer.toString(year) + "'");
557                 }
558         }
559
560         @Override
561         public void exitInvStrDateEraLastDate(InvStrDateEraLastDateContext ctx) {
562                 if (ctx.exception != null) return;
563
564                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
565                 Integer dayOfMonth = (Integer) stack.pop();
566                 Integer month = (Integer) stack.pop();
567                 Integer year = (Integer) stack.pop();
568
569                 stack.push(year);
570                 stack.push(month);
571                 stack.push(dayOfMonth);
572                 stack.push(era);
573         }
574
575         @Override
576         public void exitMonth(MonthContext ctx) {
577                 if (ctx.exception != null) return;
578
579                 Era era = (Era) stack.pop();
580                 Integer year = (Integer) stack.pop();
581                 Integer numMonth = (Integer) stack.pop();
582
583                 stack.push(new Date(year, numMonth, 1, era));
584                 stack.push(new Date(year, numMonth, DateUtils.getDaysInMonth(numMonth, year, era), era));
585         }
586
587         @Override
588         public void exitMonthYear(MonthYearContext ctx) {
589                 if (ctx.exception != null) return;
590
591                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
592
593                 stack.push(era);
594         }
595
596         @Override
597         public void exitInvMonthYear(InvMonthYearContext ctx) {
598                 if (ctx.exception != null) return;
599
600                 // Invert the arguments.
601
602                 Integer numMonth = (Integer) stack.pop();
603                 Integer year = (Integer) stack.pop();
604                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
605
606                 stack.push(numMonth);
607                 stack.push(year);
608                 stack.push(era);
609         }
610
611         @Override
612         public void exitYearSpanningWinter(YearSpanningWinterContext ctx) {
613                 if (ctx.exception != null) return;
614
615                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
616                 Integer endYear = (Integer) stack.pop();
617                 Integer startYear = (Integer) stack.pop();
618
619                 stack.push(new Date(startYear, 12, 1).withEra(era));
620                 stack.push(DateUtils.getQuarterYearEndDate(1, endYear, era).withEra(era));
621         }
622
623         @Override
624         public void exitPartialYear(PartialYearContext ctx) {
625                 if (ctx.exception != null) return;
626
627                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
628                 Integer year = (Integer) stack.pop();
629                 Part part = (Part) stack.pop();
630
631                 stack.push(DateUtils.getPartialYearStartDate(part, year).withEra(era));
632                 stack.push(DateUtils.getPartialYearEndDate(part, year, era).withEra(era));
633         }
634
635         @Override
636         public void exitQuarterYear(QuarterYearContext ctx) {
637                 if (ctx.exception != null) return;
638
639                 Era era = (Era) stack.pop();
640                 Integer year = (Integer) stack.pop();
641                 Integer quarter = (Integer) stack.pop();
642
643                 stack.push(DateUtils.getQuarterYearStartDate(quarter, year).withEra(era));
644                 stack.push(DateUtils.getQuarterYearEndDate(quarter, year, era).withEra(era));
645         }
646
647         @Override
648         public void exitHalfYear(HalfYearContext ctx) {
649                 if (ctx.exception != null) return;
650
651                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
652                 Integer year = (Integer) stack.pop();
653                 Integer half = (Integer) stack.pop();
654
655                 stack.push(DateUtils.getHalfYearStartDate(half, year).withEra(era));
656                 stack.push(DateUtils.getHalfYearEndDate(half, year, era).withEra(era));
657         }
658
659         @Override
660         public void exitInvSeasonYear(InvSeasonYearContext ctx) {
661                 if (ctx.exception != null) return;
662
663                 // Invert the arguments.
664
665                 Integer quarter = (Integer) stack.pop();
666                 Integer year = (Integer) stack.pop();
667                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
668
669                 stack.push(quarter);
670                 stack.push(year);
671                 stack.push(era);
672         }
673
674         @Override 
675         public void exitSeasonYear(SeasonYearContext ctx) {
676                 if (ctx.exception != null) return;
677
678                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
679                 stack.push(era);
680
681         }
682
683         @Override
684         public void exitYear(YearContext ctx) {
685                 if (ctx.exception != null) return;
686
687                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
688                 Integer year = (Integer) stack.pop();
689
690                 stack.push(new Date(year, 1, 1, era));
691                 stack.push(new Date(year, 12, 31, era));
692         }
693
694         @Override
695         public void exitPartialDecade(PartialDecadeContext ctx) {
696                 if (ctx.exception != null) return;
697
698                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
699                 Integer year = (Integer) stack.pop();
700                 Part part = (Part) stack.pop();
701
702                 if (era != null) {
703                         // If the era was explicitly specified, the start and end years
704                         // may be calculated now.
705
706                         stack.push(DateUtils.getPartialDecadeStartDate(year, part, era));
707                         stack.push(DateUtils.getPartialDecadeEndDate(year, part, era));
708                 }
709                 else {
710                         // If the era was not explicitly specified, the start and end years
711                         // can't be calculated yet. The calculation must be deferred until
712                         // later. For example, this partial decade may be the start of a hyphenated
713                         // range, where the era will be inherited from the era of the end of
714                         // the range; this era won't be known until farther up the parse tree,
715                         // when both sides of the range will have been parsed.
716
717                         stack.push(new DeferredPartialDecadeStartDate(year, part));
718                         stack.push(new DeferredPartialDecadeEndDate(year, part));
719                 }
720         }
721
722         @Override
723         public void exitDecade(DecadeContext ctx) {
724                 if (ctx.exception != null) return;
725
726                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
727                 Integer year = (Integer) stack.pop();
728
729                 // Calculate the start and end year of the decade, which depends on the era.
730
731                 if (era != null) {
732                         // If the era was explicitly specified, the start and end years
733                         // may be calculated now.
734
735                         stack.push(DateUtils.getDecadeStartDate(year, era));
736                         stack.push(DateUtils.getDecadeEndDate(year, era));
737                 }
738                 else {
739                         // If the era was not explicitly specified, the start and end years
740                         // can't be calculated yet. The calculation must be deferred until
741                         // later. For example, this decade may be the start of a hyphenated
742                         // range, where the era will be inherited from the era of the end of
743                         // the range; this era won't be known until farther up the parse tree,
744                         // when both sides of the range will have been parsed.
745
746                         stack.push(new DeferredDecadeStartDate(year));
747                         stack.push(new DeferredDecadeEndDate(year));
748                 }
749         }
750
751         @Override
752         public void exitPartialCentury(PartialCenturyContext ctx) {
753                 if (ctx.exception != null) return;
754
755                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
756                 Integer year = (Integer) stack.pop();
757                 Part part = (Part) stack.pop();
758
759                 if (era != null) {
760                         // If the era was explicitly specified, the start and end years
761                         // may be calculated now.
762
763                         stack.push(DateUtils.getPartialCenturyStartDate(year, part, era));
764                         stack.push(DateUtils.getPartialCenturyEndDate(year, part, era));
765                 }
766                 else {
767                         // If the era was not explicitly specified, the start and end years
768                         // can't be calculated yet. The calculation must be deferred until
769                         // later. For example, this partial century may be the start of a hyphenated
770                         // range, where the era will be inherited from the era of the end of
771                         // the range; this era won't be known until farther up the parse tree,
772                         // when both sides of the range will have been parsed.
773
774                         stack.push(new DeferredPartialCenturyStartDate(year, part));
775                         stack.push(new DeferredPartialCenturyEndDate(year, part));
776                 }
777         }
778
779         @Override
780         public void exitQuarterCentury(QuarterCenturyContext ctx) {
781                 if (ctx.exception != null) return;
782
783                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
784                 Integer year = (Integer) stack.pop();
785                 Integer quarter = (Integer) stack.pop();
786
787                 if (era != null) {
788                         // If the era was explicitly specified, the start and end years
789                         // may be calculated now.
790
791                         stack.push(DateUtils.getQuarterCenturyStartDate(year, quarter, era));
792                         stack.push(DateUtils.getQuarterCenturyEndDate(year, quarter, era));
793                 }
794                 else {
795                         // If the era was not explicitly specified, the start and end years
796                         // can't be calculated yet. The calculation must be deferred until
797                         // later. For example, this century may be the start of a hyphenated
798                         // range, where the era will be inherited from the era of the end of
799                         // the range; this era won't be known until farther up the parse tree,
800                         // when both sides of the range will have been parsed.
801
802                         stack.push(new DeferredQuarterCenturyStartDate(year, quarter));
803                         stack.push(new DeferredQuarterCenturyEndDate(year, quarter));
804                 }
805         }
806
807         @Override
808         public void exitHalfCentury(HalfCenturyContext ctx) {
809                 if (ctx.exception != null) return;
810
811                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
812                 Integer year = (Integer) stack.pop();
813                 Integer half = (Integer) stack.pop();
814
815                 if (era != null) {
816                         // If the era was explicitly specified, the start and end years
817                         // may be calculated now.
818
819                         stack.push(DateUtils.getHalfCenturyStartDate(year, half, era));
820                         stack.push(DateUtils.getHalfCenturyEndDate(year, half, era));
821                 }
822                 else {
823                         // If the era was not explicitly specified, the start and end years
824                         // can't be calculated yet. The calculation must be deferred until
825                         // later. For example, this half century may be the start of a hyphenated
826                         // range, where the era will be inherited from the era of the end of
827                         // the range; this era won't be known until farther up the parse tree,
828                         // when both sides of the range will have been parsed.
829
830                         stack.push(new DeferredHalfCenturyStartDate(year, half));
831                         stack.push(new DeferredHalfCenturyEndDate(year, half));
832                 }
833         }
834
835         @Override
836         public void exitCentury(CenturyContext ctx) {
837                 if (ctx.exception != null) return;
838
839                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
840                 Integer year = (Integer) stack.pop();
841
842                 if (era != null) {
843                         // If the era was explicitly specified, the start and end years
844                         // may be calculated now.
845
846                         stack.push(DateUtils.getCenturyStartDate(year, era));
847                         stack.push(DateUtils.getCenturyEndDate(year, era));
848                 }
849                 else {
850                         // If the era was not explicitly specified, the start and end years
851                         // can't be calculated yet. The calculation must be deferred until
852                         // later. For example, this quarter century may be the start of a hyphenated
853                         // range, where the era will be inherited from the era of the end of
854                         // the range; this era won't be known until farther up the parse tree,
855                         // when both sides of the range will have been parsed.
856
857                         stack.push(new DeferredCenturyStartDate(year));
858                         stack.push(new DeferredCenturyEndDate(year));
859                 }
860         }
861
862         @Override
863         public void exitMillennium(MillenniumContext ctx) {
864                 if (ctx.exception != null) return;
865
866                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
867                 Integer n = (Integer) stack.pop();
868
869                 if (era != null) {
870                         // If the era was explicitly specified, the start and end years
871                         // may be calculated now.
872
873                         stack.push(DateUtils.getMillenniumStartDate(n, era));
874                         stack.push(DateUtils.getMillenniumEndDate(n, era));
875                 }
876                 else {
877                         // If the era was not explicitly specified, the start and end years
878                         // can't be calculated yet. The calculation must be deferred until
879                         // later. For example, this millennium may be the start of a hyphenated
880                         // range, where the era will be inherited from the era of the end of
881                         // the range; this era won't be known until farther up the parse tree,
882                         // when both sides of the range will have been parsed.
883
884                         stack.push(new DeferredMillenniumStartDate(n));
885                         stack.push(new DeferredMillenniumEndDate(n));
886                 }
887         }
888
889         @Override
890         public void exitStrCentury(StrCenturyContext ctx) {
891                 if (ctx.exception != null) return;
892
893                 Integer n = (Integer) stack.pop();
894
895                 // Convert the nth number to a year number,
896                 // and push on the stack.
897
898                 Integer year = DateUtils.nthCenturyToYear(n);
899
900                 stack.push(year);
901         }
902
903         @Override
904         public void exitNumCentury(NumCenturyContext ctx) {
905                 if (ctx.exception != null) return;
906
907                 // Convert the string to a number,
908                 // and push on the stack.
909
910                 Integer year = new Integer(stripEndLetters(ctx.HUNDREDS().getText()));
911
912                 if (year == 0) {
913                         throw new StructuredDateFormatException("unexpected century '" + ctx.HUNDREDS().getText() + "'");
914                 }
915
916                 stack.push(year);
917         }
918
919         @Override
920         public void exitNumDecade(NumDecadeContext ctx) {
921                 if (ctx.exception != null) return;
922
923                 // Convert the string to a number,
924                 // and push on the stack.
925
926                 Integer year = new Integer(stripEndLetters(ctx.TENS().getText()));
927
928                 if (year == 0) {
929                         throw new StructuredDateFormatException("unexpected decade '" + ctx.TENS().getText() + "'");
930                 }
931
932                 stack.push(year);
933         }
934
935         @Override
936         public void exitNumYear(NumYearContext ctx) {
937                 if (ctx.exception != null) return;
938
939                 // Convert the string to a number,
940                 // and push on the stack.
941
942                 Integer year = new Integer(ctx.NUMBER().getText());
943
944                 if (year == 0) {
945                         throw new StructuredDateFormatException("unexpected year '" + ctx.NUMBER().getText() + "'");
946                 }
947
948                 stack.push(year);
949         }
950
951         @Override
952         public void exitNumMonth(NumMonthContext ctx) {
953                 if (ctx.exception != null) return;
954
955                 // Convert the string a number,
956                 // and push on the stack.
957
958                 Integer month = new Integer(ctx.NUMBER().getText());
959
960                 if (month < 1 || month > 12) {
961                         throw new StructuredDateFormatException("unexpected month '" + ctx.NUMBER().getText() + "'");
962                 }
963
964                 stack.push(month);
965         }
966
967         @Override
968         public void exitNthHalf(NthHalfContext ctx) {
969                 if (ctx.exception != null) return;
970
971                 // Convert LAST to a number (the last half
972                 // is the 2nd). If this rule matched the
973                 // alternative with nth instead of LAST,
974                 // the nth handler will already have pushed
975                 // a number on the stack.
976
977                 if (ctx.LAST() != null) {
978                         stack.push(new Integer(2));
979                 }
980
981                 // Check for a valid half.
982
983                 Integer n = (Integer) stack.peek();
984
985                 if (n < 1 || n > 2) {
986                         throw new StructuredDateFormatException("unexpected half '" + n + "'");
987                 }
988         }
989         
990
991         @Override
992         public void exitNthQuarterInYearRange(NthQuarterInYearRangeContext ctx) {
993                 if (ctx.exception != null) return;
994
995                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
996
997                 stack.push(era);
998         }
999
1000         @Override
1001         public void exitStrSeasonInYearRange(StrSeasonInYearRangeContext ctx) {
1002                 if (ctx.exception != null) return;
1003
1004                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
1005
1006                 stack.push(era);
1007
1008         }
1009
1010         @Override
1011         public void exitNthQuarterYear(NthQuarterYearContext ctx) {
1012                 
1013                 Era era = (ctx.era() == null) ? null : (Era) stack.pop();
1014
1015                 stack.push(era);
1016         }
1017
1018         @Override
1019         public void exitNthQuarter(NthQuarterContext ctx) {
1020                 if (ctx.exception != null) return;
1021
1022                 // Convert LAST to a number (the last quarter
1023                 // is the 4th). If this rule matched the
1024                 // alternative with nth instead of LAST,
1025                 // the nth handler will already have pushed
1026                 // a number on the stack.
1027
1028                 if (ctx.LAST() != null) {
1029                         stack.push(new Integer(4));
1030                 }
1031
1032                 // Check for a valid quarter.
1033
1034                 Integer n = (Integer) stack.peek();
1035
1036                 if (n < 1 || n > 4) {
1037                         throw new StructuredDateFormatException("unexpected quarter '" + n + "'");
1038                 }
1039         }
1040
1041         @Override
1042         public void exitNth(NthContext ctx) {
1043                 if (ctx.exception != null) return;
1044
1045                 // Convert the string to a number,
1046                 // and push on the stack.
1047
1048                 Integer n = null;
1049
1050                 if (ctx.NTHSTR() != null) {
1051                         n = new Integer(stripEndLetters(ctx.NTHSTR().getText()));
1052                 }
1053                 else if (ctx.FIRST() != null) {
1054                         n = 1;
1055                 }
1056                 else if (ctx.SECOND() != null) {
1057                         n = 2;
1058                 }
1059                 else if (ctx.THIRD() != null) {
1060                         n = 3;
1061                 }
1062                 else if (ctx.FOURTH() != null) {
1063                         n = 4;
1064                 }
1065
1066                 stack.push(n);
1067         }
1068
1069         @Override
1070         public void exitStrMonth(StrMonthContext ctx) {
1071                 if (ctx.exception != null) return;
1072
1073                 // Convert the month name to a number,
1074                 // and push on the stack.
1075
1076                 TerminalNode monthNode = ctx.MONTH();
1077
1078                 if (monthNode == null) {
1079                         monthNode = ctx.SHORTMONTH();
1080                 }
1081
1082                 String monthStr = monthNode.getText();
1083
1084                 stack.push(DateUtils.getMonthByName(monthStr));
1085         }
1086
1087         @Override
1088         public void exitStrSeason(StrSeasonContext ctx) {
1089                 if (ctx.exception != null) return;
1090
1091                 // Convert the season to a quarter number,
1092                 // and push on the stack.
1093
1094                 Integer quarter = null;
1095
1096                 if (ctx.WINTER() != null) {
1097                         quarter = 1;
1098                 }
1099                 else if (ctx.SPRING() != null) {
1100                         quarter = 2;
1101                 }
1102                 else if (ctx.SUMMER() != null) {
1103                         quarter = 3;
1104                 }
1105                 else if (ctx.FALL() != null) {
1106                         quarter = 4;
1107                 }
1108
1109                 stack.push(quarter);
1110         }
1111
1112         @Override
1113         public void exitAllOrPartOf(AllOrPartOfContext ctx) {
1114                 if (ctx.exception != null) return;
1115
1116                 // If a part was specified, it will have been
1117                 // pushed on the stack in exitPartOf(). If not,
1118                 // push null on the stack.
1119
1120                 if (ctx.partOf() == null) {
1121                         stack.push(null);
1122                 }
1123         }
1124
1125         @Override
1126         public void exitPartOf(PartOfContext ctx) {
1127                 if (ctx.exception != null) return;
1128
1129                 // Convert the token to a Part,
1130                 // and push on the stack.
1131
1132                 Part part = null;
1133
1134                 if (ctx.EARLY() != null) {
1135                         part = Part.EARLY;
1136                 }
1137                 else if (ctx.MIDDLE() != null) {
1138                         part = Part.MIDDLE;
1139                 }
1140                 else if (ctx.LATE() != null) {
1141                         part = Part.LATE;
1142                 }
1143
1144                 stack.push(part);
1145         }
1146
1147         @Override
1148         public void exitEra(EraContext ctx) {
1149                 if (ctx.exception != null) return;
1150
1151                 // Convert the token to an Era,
1152                 // and push on the stack.
1153
1154                 Era era = null;
1155
1156                 if (ctx.BC() != null) {
1157                         era = Era.BCE;
1158                 }
1159                 else if (ctx.AD() != null) {
1160                         era = Era.CE;
1161                 }
1162
1163                 stack.push(era);
1164         }
1165
1166         @Override
1167         public void exitNumDayOfMonth(NumDayOfMonthContext ctx) {
1168                 if (ctx.exception != null) return;
1169
1170                 // Convert the numeric string to an Integer,
1171                 // and push on the stack.
1172
1173                 Integer dayOfMonth = new Integer(ctx.NUMBER().getText());
1174
1175                 if (dayOfMonth == 0 || dayOfMonth > 31) {
1176                         throw new StructuredDateFormatException("unexpected day of month '" + ctx.NUMBER().getText() + "'");
1177                 }
1178
1179                 stack.push(dayOfMonth);
1180         }
1181
1182         @Override
1183         public void exitNum(NumContext ctx) {
1184                 if (ctx.exception != null) return;
1185
1186                 // Convert the numeric string to an Integer,
1187                 // and push on the stack.
1188
1189                 Integer num = new Integer(ctx.NUMBER().getText());
1190
1191                 stack.push(num);
1192         }
1193
1194         protected String getErrorMessage(RecognitionException re) {
1195                 String message = "";
1196
1197                 Parser recognizer = (Parser) re.getRecognizer();
1198                 TokenStream tokens = recognizer.getInputStream();
1199
1200                 if (re instanceof NoViableAltException) {
1201                         NoViableAltException e = (NoViableAltException) re;
1202                         Token startToken = e.getStartToken();
1203                         String input = (startToken.getType() == Token.EOF ) ? "end of text" : quote(tokens.getText(startToken, e.getOffendingToken()));
1204
1205                         message = "no viable date format found at " + input;
1206                 }
1207                 else if (re instanceof InputMismatchException) {
1208                         InputMismatchException e = (InputMismatchException) re;
1209                         message = "did not expect " + getTokenDisplayString(e.getOffendingToken()) + " while looking for " +
1210                                   e.getExpectedTokens().toString(recognizer.getTokenNames());
1211                 }
1212                 else if (re instanceof FailedPredicateException) {
1213                         FailedPredicateException e = (FailedPredicateException) re;
1214                         String ruleName = recognizer.getRuleNames()[recognizer.getContext().getRuleIndex()];
1215
1216                         message = "failed predicate " + ruleName + ": " + e.getMessage();
1217                 }
1218
1219                 return message;
1220         }
1221
1222         protected String quote(String text) {
1223                 return "'" + text + "'";
1224         }
1225
1226         protected String getTokenDisplayString(Token token) {
1227                 String string;
1228
1229                 if (token == null) {
1230                         string = "[no token]";
1231                 }
1232                 else {
1233                         String text = token.getText();
1234
1235                         if (text == null) {
1236                                 if (token.getType() == Token.EOF ) {
1237                                         string = "end of text";
1238                                 }
1239                                 else {
1240                                         string = "[" + token.getType() + "]";
1241                                 }
1242                         }
1243                         else {
1244                                 string = quote(text);
1245                         }
1246                 }
1247
1248                 return string;
1249         }
1250
1251         protected String stripEndLetters(String input) {
1252                 return input.replaceAll("[^\\d]+$", "");
1253         }
1254
1255         public static void main(String[] args) {
1256                 StructuredDateEvaluator evaluator = new ANTLRStructuredDateEvaluator();
1257
1258                 for (String displayDate : args) {
1259                         try {
1260                                 evaluator.evaluate(displayDate);
1261                         } catch (StructuredDateFormatException e) {
1262                                 e.printStackTrace();
1263                         }
1264                 }
1265         }
1266 }