]> gitweb.fperrin.net Git - Dictionary.git/blob - jars/icu4j-4_8_1_1/demos/src/com/ibm/icu/dev/demo/holiday/HolidayCalendarDemo.java
Added flags.
[Dictionary.git] / jars / icu4j-4_8_1_1 / demos / src / com / ibm / icu / dev / demo / holiday / HolidayCalendarDemo.java
1 /*
2  *******************************************************************************
3  * Copyright (C) 1996-2007, International Business Machines Corporation and    *
4  * others. All Rights Reserved.                                                *
5  *******************************************************************************
6  */
7
8 package com.ibm.icu.dev.demo.holiday;
9
10 import java.awt.BorderLayout;
11 import java.awt.Button;
12 import java.awt.Canvas;
13 import java.awt.Choice;
14 import java.awt.Color;
15 import java.awt.Component;
16 import java.awt.Container;
17 import java.awt.Dimension;
18 import java.awt.Font;
19 import java.awt.FontMetrics;
20 import java.awt.Frame;
21 import java.awt.Graphics;
22 import java.awt.GridBagConstraints;
23 import java.awt.GridBagLayout;
24 import java.awt.Label;
25 import java.awt.Panel;
26 import java.awt.Point;
27 import java.awt.event.ActionEvent;
28 import java.awt.event.ActionListener;
29 import java.awt.event.ItemEvent;
30 import java.awt.event.ItemListener;
31 import java.awt.event.WindowEvent;
32 import java.text.DateFormatSymbols;
33 import java.util.Date;
34 import java.util.Locale;
35 import java.util.Vector;
36
37 import com.ibm.icu.dev.demo.impl.DemoApplet;
38 import com.ibm.icu.dev.demo.impl.DemoTextBox;
39 import com.ibm.icu.dev.demo.impl.DemoUtility;
40 import com.ibm.icu.text.SimpleDateFormat;
41 import com.ibm.icu.util.Calendar;
42 import com.ibm.icu.util.Holiday;
43 import com.ibm.icu.util.SimpleTimeZone;
44
45 /**
46  * CalendarDemo demonstrates how Calendar works.
47  */
48 public class HolidayCalendarDemo extends DemoApplet 
49 {
50     /**
51      * For serialization
52      */
53     private static final long serialVersionUID = 4546085430817359372L;
54
55     /**
56      * The main function which defines the behavior of the CalendarDemo
57      * applet when an applet is started.
58      */
59     public static void main(String argv[]) {
60
61         new HolidayCalendarDemo().showDemo();
62     }
63
64     /* This creates a CalendarFrame for the demo applet. */
65     public Frame createDemoFrame(DemoApplet applet) {
66         return new CalendarFrame(applet);
67     }
68
69     /**
70     * A Frame is a top-level window with a title. The default layout for a frame
71     * is BorderLayout.  The CalendarFrame class defines the window layout of
72     * CalendarDemo.
73     */
74     private static class CalendarFrame extends Frame implements ActionListener,
75                                                                 ItemListener
76     {
77         /**
78          * For serialization
79          */
80         private static final long serialVersionUID = -7023296782393042761L;
81
82         private static final boolean DEBUG = false;
83
84         //private Locale curLocale = Locale.US; // unused
85
86         private DemoApplet applet;
87
88         private static final Locale[] calendars = {
89             //new Locale("de","AT"),
90             Locale.CANADA,
91             Locale.CANADA_FRENCH,
92             Locale.FRANCE,
93             Locale.GERMANY,
94             new Locale("iw","IL"),
95             new Locale("el","GR"),
96             //new Locale("es","MX"),
97             Locale.UK,
98             Locale.US,
99         };
100         private static final Locale[] displays = {
101             Locale.CANADA,
102             Locale.UK,
103             Locale.US,
104             Locale.FRANCE,
105             Locale.CANADA_FRENCH,
106             //new Locale("de","AT"),
107             Locale.GERMAN,
108             new Locale("el","GR"),
109             //new Locale("iw","IL"),
110             new Locale("es","MX"),
111         };
112
113         /**
114         * Constructs a new CalendarFrame that is initially invisible.
115         */
116         public CalendarFrame(DemoApplet applet)
117         {
118             super("Calendar Demo");
119             this.applet = applet;
120             init();
121             start();
122             enableEvents(WindowEvent.WINDOW_CLOSING);
123         }
124
125         /**
126         * Initializes the applet. You never need to call this directly, it
127         * is called automatically by the system once the applet is created.
128         */
129         public void init()
130         {
131             // Get G7 locales only for demo purpose. To get all the locales
132             // supported, switch to calling Calendar.getAvailableLocales().
133             // commented
134             locales = displays;
135
136             buildGUI();
137         }
138
139         //------------------------------------------------------------
140         // package private
141         //------------------------------------------------------------
142         void addWithFont(Container container, Component foo, Font font) {
143             if (font != null)
144                 foo.setFont(font);
145             container.add(foo);
146         }
147
148         /**
149         * Called to start the applet. You never need to call this method
150         * directly, it is called when the applet's document is visited.
151         */
152         public void start()
153         {
154             // do nothing
155         }
156
157         private Choice          localeMenu;
158         private Choice          displayMenu;
159         private Locale[]        locales;
160
161         private Label           monthLabel;
162         private Button          prevYear;
163         private Button          prevMonth;
164         private Button          gotoToday;
165         private Button          nextMonth;
166         private Button          nextYear;
167         private CalendarPanel   calendarPanel;
168
169         private static final Locale kFirstLocale = Locale.US;
170
171         private static void add(Container container, Component component,
172                                 GridBagLayout g, GridBagConstraints c)
173         {
174             g.setConstraints(component, c);
175             container.add(component);
176         }
177
178         public void buildGUI()
179         {
180             setBackground(DemoUtility.bgColor);
181             setLayout(new BorderLayout(10,10));
182
183             // Label for the demo's title
184             Label titleLabel = new Label("Calendar Demo", Label.CENTER);
185             titleLabel.setFont(DemoUtility.titleFont);
186
187             // Label for the current month name
188             monthLabel = new Label("", Label.LEFT);
189             monthLabel.setFont(new Font(DemoUtility.titleFont.getName(),
190                                         DemoUtility.titleFont.getStyle(),
191                                         (DemoUtility.titleFont.getSize() * 3)/2));
192
193             // Make the locale popup menus
194             localeMenu= new Choice();
195             localeMenu.addItemListener(this);
196             int selectMe = 0;
197             
198             for (int i = 0; i < calendars.length; i++) {
199                 if (i > 0 &&
200                         calendars[i].getCountry().equals(calendars[i-1].getCountry()) ||
201                     i < calendars.length - 1 &&
202                         calendars[i].getCountry().equals(calendars[i+1].getCountry()))
203                 {
204                     localeMenu.addItem(calendars[i].getDisplayCountry() + " (" +
205                                     calendars[i].getDisplayLanguage() + ")");
206                 } else {
207                     localeMenu.addItem( calendars[i].getDisplayCountry() );
208                 }
209                 
210                 if (calendars[i].equals(kFirstLocale)) {
211                     selectMe = i;
212                 }
213             }
214             
215             localeMenu.setBackground(DemoUtility.choiceColor);
216             localeMenu.select(selectMe);
217
218             displayMenu = new Choice();
219             displayMenu.addItemListener(this);
220             
221             selectMe = 0;
222             for (int i = 0; i < locales.length; i++) {
223                 if (i > 0 &&
224                         locales[i].getLanguage().equals(locales[i-1].getLanguage()) ||
225                     i < locales.length - 1 &&
226                         locales[i].getLanguage().equals(locales[i+1].getLanguage()))
227                 {
228                     displayMenu.addItem( locales[i].getDisplayName() );
229                 } else {
230                     displayMenu.addItem( locales[i].getDisplayLanguage());
231                 }
232                 
233                 if (locales[i].equals(kFirstLocale)) {
234                     selectMe = i;
235                 }
236             }
237             
238             displayMenu.setBackground(DemoUtility.choiceColor);
239             displayMenu.select(selectMe);
240
241             // Make all the next/previous/today buttons
242             prevYear = new Button("<<");
243             prevYear.addActionListener(this);
244             prevMonth = new Button("<");
245             prevMonth.addActionListener(this);
246             gotoToday = new Button("Today");
247             gotoToday.addActionListener(this);
248             nextMonth = new Button(">");
249             nextMonth.addActionListener(this);
250             nextYear = new Button(">>");
251             nextYear.addActionListener(this);
252
253             // The month name and the control buttons are bunched together
254             Panel monthPanel = new Panel();
255             {
256                 GridBagLayout g = new GridBagLayout();
257                 GridBagConstraints c = new GridBagConstraints();
258                 monthPanel.setLayout(g);
259
260                 c.weightx = 1;
261                 c.weighty = 1;
262
263                 c.gridwidth = 1;
264                 c.fill = GridBagConstraints.HORIZONTAL;
265                 c.gridwidth = GridBagConstraints.REMAINDER;
266                 add(monthPanel, monthLabel, g, c);
267
268                 c.gridwidth = 1;
269                 add(monthPanel, prevYear, g, c);
270                 add(monthPanel, prevMonth, g, c);
271                 add(monthPanel, gotoToday, g, c);
272                 add(monthPanel, nextMonth, g, c);
273                 c.gridwidth = GridBagConstraints.REMAINDER;
274                 add(monthPanel, nextYear, g, c);
275             }
276
277             // Stick the menu and buttons in a little "control panel"
278             Panel menuPanel = new Panel();
279             {
280                 GridBagLayout g = new GridBagLayout();
281                 GridBagConstraints c = new GridBagConstraints();
282                 menuPanel.setLayout(g);
283
284                 c.weightx = 1;
285                 c.weighty = 1;
286
287                 c.fill = GridBagConstraints.HORIZONTAL;
288
289                 c.gridwidth = GridBagConstraints.RELATIVE;
290                 Label l1 = new Label("Holidays");
291                 l1.setFont(DemoUtility.labelFont);
292                 add(menuPanel, l1, g, c);
293
294                 c.gridwidth = GridBagConstraints.REMAINDER;
295                 add(menuPanel, localeMenu, g, c);
296
297                 c.gridwidth = GridBagConstraints.RELATIVE;
298                 Label l2 = new Label("Display:");
299                 l2.setFont(DemoUtility.labelFont);
300                 add(menuPanel, l2, g, c);
301
302                 c.gridwidth = GridBagConstraints.REMAINDER;
303                 add(menuPanel, displayMenu, g, c);
304             }
305
306             // The title, buttons, etc. go in a panel at the top of the window
307             Panel topPanel = new Panel();
308             {
309                 topPanel.setLayout(new BorderLayout());
310
311                 //topPanel.add("North", titleLabel);
312                 topPanel.add("Center", monthPanel);
313                 topPanel.add("East", menuPanel);
314             }
315             add("North", topPanel);
316
317             // The copyright notice goes at the bottom of the window
318             Label copyright = new Label(DemoUtility.copyright1, Label.LEFT);
319             copyright.setFont(DemoUtility.creditFont);
320             add("South", copyright);
321
322             // Now create the big calendar panel and stick it in the middle
323             calendarPanel = new CalendarPanel( kFirstLocale );
324             add("Center", calendarPanel);
325
326             updateMonthName();
327         }
328
329         private void updateMonthName()
330         {
331             SimpleDateFormat f = new SimpleDateFormat("MMMM yyyyy",
332                                                         calendarPanel.getDisplayLocale());
333             f.setCalendar(calendarPanel.getCalendar());
334             f.setTimeZone(new SimpleTimeZone(0, "UTC"));        // JDK 1.1.2 workaround
335             monthLabel.setText( f.format( calendarPanel.firstOfMonth() ));
336         }
337         
338         /**
339         * Handles the event. Returns true if the event is handled and should not
340         * be passed to the parent of this component. The default event handler
341         * calls some helper methods to make life easier on the programmer.
342         */
343         public void actionPerformed(ActionEvent e)
344         {
345             Object obj = e.getSource();
346             
347             // *** Button events are handled here.
348             if (obj instanceof Button) {
349                 if (obj == nextMonth) {
350                     calendarPanel.add(Calendar.MONTH, +1);
351                 }
352                 else
353                 if (obj == prevMonth) {
354                     calendarPanel.add(Calendar.MONTH, -1);
355                 }
356                 else
357                 if (obj == prevYear) {
358                     calendarPanel.add(Calendar.YEAR, -1);
359                 }
360                 else
361                 if (obj == nextYear) {
362                     calendarPanel.add(Calendar.YEAR, +1);
363                 }
364                 else
365                 if (obj == gotoToday) {
366                     calendarPanel.set( new Date() );
367                 }
368                 updateMonthName();
369             }
370         }
371         
372         public void itemStateChanged(ItemEvent e)
373         {
374             Object obj = e.getSource();
375             if (obj == localeMenu) {
376                 calendarPanel.setCalendarLocale(calendars[localeMenu.getSelectedIndex()]);
377                 updateMonthName();
378             }
379             else 
380                 if (obj == displayMenu) {
381                     calendarPanel.setDisplayLocale(locales[displayMenu.getSelectedIndex()]);
382                     updateMonthName();
383                 }
384         }
385         
386         /**
387         * Print out the error message while debugging this program.
388         */
389         public void errorText(String s)
390         {
391             if (DEBUG)
392             {
393                 System.out.println(s);
394             }
395         }
396         
397         protected void processWindowEvent(WindowEvent e)
398         {
399             System.out.println("event " + e);
400             if (e.getID() == WindowEvent.WINDOW_CLOSING) {
401                 this.hide();
402                 this.dispose();
403
404                 if (applet != null) {
405                     applet.demoClosed();
406                 } else {
407                     System.exit(0);
408                 }
409             }
410         }
411     }
412
413
414     private static class CalendarPanel extends Canvas {
415
416         /**
417          * For serialization
418          */
419         private static final long serialVersionUID = 1521099412250120821L;
420
421         public CalendarPanel( Locale locale ) {
422             set(locale, locale, new Date());
423         }
424
425         public void setCalendarLocale(Locale locale) {
426             set(locale, fDisplayLocale, fCalendar.getTime());
427         }
428
429         public void setDisplayLocale(Locale locale) {
430             set(fCalendarLocale, locale, fCalendar.getTime());
431         }
432
433         public void set(Date date) {
434             set(fCalendarLocale, fDisplayLocale, date);
435         }
436
437         public void set(Locale loc, Locale display, Date date)
438         {
439             if (fCalendarLocale == null || !loc.equals(fCalendarLocale)) {
440                 fCalendarLocale = loc;
441                 fCalendar = Calendar.getInstance(fCalendarLocale);
442                 fAllHolidays = Holiday.getHolidays(fCalendarLocale);
443             }
444             if (fDisplayLocale == null || !display.equals(fDisplayLocale)) {
445                 fDisplayLocale = display;
446                 fSymbols = new DateFormatSymbols(fDisplayLocale);
447             }
448
449             fStartOfMonth = date;
450
451             dirty = true;
452             repaint();
453         }
454
455         public void add(int field, int delta)
456         {
457             synchronized(fCalendar) {
458                 fCalendar.setTime(fStartOfMonth);
459                 fCalendar.add(field, delta);
460                 fStartOfMonth = fCalendar.getTime();
461             }
462             dirty = true;
463             repaint();
464         }
465
466         public com.ibm.icu.util.Calendar getCalendar() {
467             return fCalendar;
468         }
469
470         public Locale getCalendarLocale() {
471             return fCalendarLocale;
472         }
473
474         public Locale getDisplayLocale() {
475             return fDisplayLocale;
476         }
477
478
479         public Date firstOfMonth() {
480             return fStartOfMonth;
481         }
482
483         private Date startOfMonth(Date dateInMonth)
484         {
485             synchronized(fCalendar) {
486                 fCalendar.setTime(dateInMonth);             // TODO: synchronization
487
488                 int era = fCalendar.get(Calendar.ERA);
489                 int year = fCalendar.get(Calendar.YEAR);
490                 int month = fCalendar.get(Calendar.MONTH);
491
492                 fCalendar.clear();
493                 fCalendar.set(Calendar.ERA, era);
494                 fCalendar.set(Calendar.YEAR, year);
495                 fCalendar.set(Calendar.MONTH, month);
496                 fCalendar.set(Calendar.DATE, 1);
497
498                 return fCalendar.getTime();
499             }
500         }
501
502         private void calculate()
503         {
504             //
505             // As a workaround for JDK 1.1.3 and below, where Calendars and time
506             // zones are a bit goofy, always set my calendar's time zone to UTC.
507             // You would think I would want to do this in the "set" function above,
508             // but if I do that, the program hangs when this class is loaded,
509             // perhaps due to some sort of static initialization ordering problem.
510             // So I do it here instead.
511             //
512             fCalendar.setTimeZone(new SimpleTimeZone(0, "UTC"));
513
514             Calendar c = (Calendar)fCalendar.clone(); // Temporary copy
515
516             fStartOfMonth = startOfMonth(fStartOfMonth);
517
518             // Stash away a few useful constants for this calendar and display
519             minDay = c.getMinimum(Calendar.DAY_OF_WEEK);
520             daysInWeek = c.getMaximum(Calendar.DAY_OF_WEEK) - minDay + 1;
521
522             firstDayOfWeek = Calendar.getInstance(fDisplayLocale).getFirstDayOfWeek();
523
524             // Stash away a Date for the start of this month
525
526             // Find the day of week of the first day in this month
527             c.setTime(fStartOfMonth);
528             firstDayInMonth = c.get(Calendar.DAY_OF_WEEK);
529
530             // Now find the # of days in the month
531             c.roll(Calendar.DATE, false);
532             daysInMonth = c.get(Calendar.DATE);
533
534             // Finally, find the end of the month, i.e. the start of the next one
535             c.roll(Calendar.DATE, true);
536             c.add(Calendar.MONTH, 1);
537             c.getTime();        // JDK 1.1.2 bug workaround
538             c.add(Calendar.SECOND, -1);
539             Date endOfMonth = c.getTime();
540
541             //
542             // Calculate the number of full or partial weeks in this month.
543             // To do this I can just reuse the code that calculates which
544             // calendar cell contains a given date.
545             //
546             numWeeks = dateToCell(daysInMonth).y - dateToCell(1).y + 1;
547
548             // Remember which holidays fall on which days in this month,
549             // to save the trouble of having to do it later
550             fHolidays.setSize(0);
551
552             for (int h = 0; h < fAllHolidays.length; h++)
553             {
554                 Date d = fStartOfMonth;
555                 while ( (d = fAllHolidays[h].firstBetween(d, endOfMonth) ) != null)
556                 {
557                     c.setTime(d);
558                     fHolidays.addElement( new HolidayInfo(c.get(Calendar.DATE),
559                                             fAllHolidays[h],
560                                             fAllHolidays[h].getDisplayName(fDisplayLocale) ));
561
562                     d.setTime( d.getTime() + 1000 );    // "d++"
563                 }
564             }
565             dirty = false;
566         }
567
568         static final int INSET = 2;
569
570         /*
571         * Convert from the day number within a month (1-based)
572         * to the cell coordinates on the calendar (0-based)
573         */
574         private void dateToCell(int date, Point pos)
575         {
576             int cell = (date + firstDayInMonth - firstDayOfWeek - minDay);
577             if (firstDayInMonth < firstDayOfWeek) {
578                 cell += daysInWeek;
579             }
580
581             pos.x = cell % daysInWeek;
582             pos.y = cell / daysInWeek;
583         }
584         private Point dateToCell(int date) {
585             Point p = new Point(0,0);
586             dateToCell(date, p);
587             return p;
588         }
589
590         public void paint(Graphics g) {
591
592             if (dirty) {
593                 calculate();
594             }
595
596             Point cellPos = new Point(0,0);     // Temporary variable
597             Dimension d = getSize();
598
599             g.setColor(DemoUtility.bgColor);
600             g.fillRect(0,0,d.width,d.height);
601
602             // Draw the day names at the top
603             g.setColor(Color.black);
604             g.setFont(DemoUtility.labelFont);
605             FontMetrics fm = g.getFontMetrics();
606             int labelHeight = fm.getHeight() + INSET * 2;
607
608             int v = fm.getAscent() + INSET;
609             for (int i = 0; i < daysInWeek; i++) {
610                 int dayNum = (i + minDay + firstDayOfWeek - 2) % daysInWeek + 1;
611                 String dayName = fSymbols.getWeekdays()[dayNum];
612
613                 int h = (int) (d.width * (i + 0.5)) / daysInWeek;
614                 h -= fm.stringWidth(dayName) / 2;
615
616                 g.drawString(dayName, h, v);
617             }
618
619             double cellHeight = (d.height - labelHeight - 1) / numWeeks;
620             double cellWidth = (double)(d.width - 1) / daysInWeek;
621
622             // Draw a white background in the part of the calendar
623             // that displays this month.
624             // First figure out how much of the first week should be shaded.
625             {
626                 g.setColor(Color.white);
627                 dateToCell(1, cellPos);
628                 int width = (int)(cellPos.x*cellWidth);  // Width of unshaded area
629
630                 g.fillRect((int)(width), labelHeight ,
631                         (int)(d.width - width), (int)cellHeight);
632
633                 // All of the intermediate weeks get shaded completely
634                 g.fillRect(0, (int)(labelHeight + cellHeight),
635                             d.width, (int)(cellHeight * (numWeeks - 2)));
636
637                 // Now figure out the last week.
638                 dateToCell(daysInMonth, cellPos);
639                 width = (int)((cellPos.x+1)*cellWidth);  // Width of shaded area
640
641                 g.fillRect(0, (int)(labelHeight + (numWeeks-1) * cellHeight),
642                             width, (int)(cellHeight));
643
644             }
645             // Draw the X/Y grid lines
646             g.setColor(Color.black);
647             for (int i = 0; i <= numWeeks; i++) {
648                 int y = (int)(labelHeight + i * cellHeight);
649                 g.drawLine(0, y, d.width - 1, y);
650             }
651             for (int i = 0; i <= daysInWeek; i++) {
652                 int x = (int)(i * cellWidth);
653                 g.drawLine(x, labelHeight, x, d.height - 1);
654             }
655
656             // Now loop through all of the days in the month, figure out where
657             // they go in the grid, and draw the day # for each one
658             Font numberFont = new Font("Helvetica",Font.PLAIN,12);
659             // not used Font holidayFont = DemoUtility.creditFont;
660
661             Calendar c = (Calendar)fCalendar.clone();
662             c.setTime(fStartOfMonth);
663
664             for (int i = 1, h = 0; i <= daysInMonth; i++) {
665                 g.setFont(numberFont);
666                 g.setColor(Color.black);
667                 fm = g.getFontMetrics();
668
669                 dateToCell(i, cellPos);
670                 int x = (int)((cellPos.x + 1) * cellWidth);
671                 int y = (int)(cellPos.y * cellHeight + labelHeight);
672
673                 StringBuffer buffer = new StringBuffer();
674                 buffer.append(i);
675                 String dayNum = buffer.toString();
676
677                 x = x - INSET - fm.stringWidth(dayNum);
678                 y = y + fm.getAscent() + INSET;
679
680                 g.drawString(dayNum, x, y);
681
682                 // See if any of the holidays land on this day....
683                 HolidayInfo info = null;
684                 int count = 0;
685
686                 // Coordinates of lower-left corner of cell.
687                 x = (int)((cellPos.x) * cellWidth);
688                 y = (int)((cellPos.y+1) * cellHeight) + labelHeight;
689
690                 while (h < fHolidays.size() &&
691                         (info = (HolidayInfo)fHolidays.elementAt(h)).date <= i)
692                 {
693                     if (info.date == i) {
694                         // Draw the holiday here.
695                         g.setFont(numberFont);
696                         g.setColor(Color.red);
697
698                         DemoTextBox box = new DemoTextBox(g, info.name, (int)(cellWidth - INSET));
699                         box.draw(g, x + INSET, y - INSET - box.getHeight());
700
701                         y -= (box.getHeight() + INSET);
702                         count++;
703                     }
704                     h++;
705                 }
706             }
707         }
708
709         // Important state variables
710         private Locale              fCalendarLocale;    // Whose calendar
711         private Calendar            fCalendar;          // Calendar for calculations
712
713         private Locale              fDisplayLocale;     // How to display it
714         private DateFormatSymbols   fSymbols;           // Symbols for drawing
715
716         private Date                fStartOfMonth;      // 00:00:00 on first day of month
717
718         // Cached calculations to make drawing faster.
719         private transient int minDay;           // Minimum legal day #
720         private transient int daysInWeek;       // # of days in a week
721         private transient int firstDayOfWeek;   // First day to display in week
722         private transient int numWeeks;         // # full or partial weeks in month
723         private transient int daysInMonth;      // # days in this month
724         private transient int firstDayInMonth;  // Day of week of first day in month
725
726         private transient Holiday[] fAllHolidays;
727         private transient Vector    fHolidays = new Vector(5,5);
728
729         private transient boolean dirty = true;
730     }
731
732     private static class HolidayInfo {
733         public HolidayInfo(int date, Holiday holiday, String name) {
734             this.date = date;
735             this.holiday = holiday;
736             this.name = name;
737         }
738
739         public Holiday holiday;
740         public int date;
741         public String name;
742     }
743 }
744