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