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