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