1/*
2 * Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.util.calendar;
27
28import java.io.File;
29import java.io.FileInputStream;
30import java.io.IOException;
31import java.security.AccessController;
32import java.security.PrivilegedAction;
33import java.security.PrivilegedActionException;
34import java.security.PrivilegedExceptionAction;
35import java.util.ArrayList;
36import java.util.List;
37import java.util.Properties;
38import java.util.StringTokenizer;
39import java.util.TimeZone;
40
41/**
42 *
43 * @author Masayoshi Okutsu
44 * @since 1.6
45 */
46
47public class LocalGregorianCalendar extends BaseCalendar {
48    private String name;
49    private Era[] eras;
50
51    public static class Date extends BaseCalendar.Date {
52
53        protected Date() {
54            super();
55        }
56
57        protected Date(TimeZone zone) {
58            super(zone);
59        }
60
61        private int gregorianYear = FIELD_UNDEFINED;
62
63        public Date setEra(Era era) {
64            if (getEra() != era) {
65                super.setEra(era);
66                gregorianYear = FIELD_UNDEFINED;
67            }
68            return this;
69        }
70
71        public Date addYear(int localYear) {
72            super.addYear(localYear);
73            gregorianYear += localYear;
74            return this;
75        }
76
77        public Date setYear(int localYear) {
78            if (getYear() != localYear) {
79                super.setYear(localYear);
80                gregorianYear = FIELD_UNDEFINED;
81            }
82            return this;
83        }
84
85        public int getNormalizedYear() {
86            return gregorianYear;
87        }
88
89        public void setNormalizedYear(int normalizedYear) {
90            this.gregorianYear = normalizedYear;
91        }
92
93        void setLocalEra(Era era) {
94            super.setEra(era);
95        }
96
97        void setLocalYear(int year) {
98            super.setYear(year);
99        }
100
101        public String toString() {
102            String time = super.toString();
103            time = time.substring(time.indexOf('T'));
104            StringBuffer sb = new StringBuffer();
105            Era era = getEra();
106            if (era != null) {
107                String abbr = era.getAbbreviation();
108                if (abbr != null) {
109                    sb.append(abbr);
110                }
111            }
112            sb.append(getYear()).append('.');
113            CalendarUtils.sprintf0d(sb, getMonth(), 2).append('.');
114            CalendarUtils.sprintf0d(sb, getDayOfMonth(), 2);
115            sb.append(time);
116            return sb.toString();
117        }
118    }
119
120    static LocalGregorianCalendar getLocalGregorianCalendar(String name) {
121        Properties calendarProps = null;
122        try {
123            String homeDir = AccessController.doPrivileged(
124                new sun.security.action.GetPropertyAction("java.home"));
125            final String fname = homeDir + File.separator + "lib" + File.separator
126                                 + "calendars.properties";
127            calendarProps = (Properties) AccessController.doPrivileged(new PrivilegedExceptionAction() {
128                public Object run() throws IOException {
129                    Properties props = new Properties();
130                    try (FileInputStream fis = new FileInputStream(fname)) {
131                        props.load(fis);
132                    }
133                    return props;
134                }
135            });
136        } catch (PrivilegedActionException e) {
137            throw new RuntimeException(e.getException());
138        }
139
140        // Parse calendar.*.eras
141        String props = calendarProps.getProperty("calendar." + name + ".eras");
142        if (props == null) {
143            return null;
144        }
145        List<Era> eras = new ArrayList<Era>();
146        StringTokenizer eraTokens = new StringTokenizer(props, ";");
147        while (eraTokens.hasMoreTokens()) {
148            String items = eraTokens.nextToken().trim();
149            StringTokenizer itemTokens = new StringTokenizer(items, ",");
150            String eraName = null;
151            boolean localTime = true;
152            long since = 0;
153            String abbr = null;
154
155            while (itemTokens.hasMoreTokens()) {
156                String item = itemTokens.nextToken();
157                int index = item.indexOf('=');
158                // it must be in the key=value form.
159                if (index == -1) {
160                    return null;
161                }
162                String key = item.substring(0, index);
163                String value = item.substring(index + 1);
164                if ("name".equals(key)) {
165                    eraName = value;
166                } else if ("since".equals(key)) {
167                    if (value.endsWith("u")) {
168                        localTime = false;
169                        since = Long.parseLong(value.substring(0, value.length() - 1));
170                    } else {
171                        since = Long.parseLong(value);
172                    }
173                } else if ("abbr".equals(key)) {
174                    abbr = value;
175                } else {
176                    throw new RuntimeException("Unknown key word: " + key);
177                }
178            }
179            Era era = new Era(eraName, abbr, since, localTime);
180            eras.add(era);
181        }
182        Era[] eraArray = new Era[eras.size()];
183        eras.toArray(eraArray);
184
185        return new LocalGregorianCalendar(name, eraArray);
186    }
187
188    private LocalGregorianCalendar(String name, Era[] eras) {
189        this.name = name;
190        this.eras = eras;
191        setEras(eras);
192    }
193
194    public String getName() {
195        return name;
196    }
197
198    public Date getCalendarDate() {
199        return getCalendarDate(System.currentTimeMillis(), newCalendarDate());
200    }
201
202    public Date getCalendarDate(long millis) {
203        return getCalendarDate(millis, newCalendarDate());
204    }
205
206    public Date getCalendarDate(long millis, TimeZone zone) {
207        return getCalendarDate(millis, newCalendarDate(zone));
208    }
209
210    public Date getCalendarDate(long millis, CalendarDate date) {
211        Date ldate = (Date) super.getCalendarDate(millis, date);
212        return adjustYear(ldate, millis, ldate.getZoneOffset());
213    }
214
215    private Date adjustYear(Date ldate, long millis, int zoneOffset) {
216        int i;
217        for (i = eras.length - 1; i >= 0; --i) {
218            Era era = eras[i];
219            long since = era.getSince(null);
220            if (era.isLocalTime()) {
221                since -= zoneOffset;
222            }
223            if (millis >= since) {
224                ldate.setLocalEra(era);
225                int y = ldate.getNormalizedYear() - era.getSinceDate().getYear() + 1;
226                ldate.setLocalYear(y);
227                break;
228            }
229        }
230        if (i < 0) {
231            ldate.setLocalEra(null);
232            ldate.setLocalYear(ldate.getNormalizedYear());
233        }
234        ldate.setNormalized(true);
235        return ldate;
236    }
237
238    public Date newCalendarDate() {
239        return new Date();
240    }
241
242    public Date newCalendarDate(TimeZone zone) {
243        return new Date(zone);
244    }
245
246    public boolean validate(CalendarDate date) {
247        Date ldate = (Date) date;
248        Era era = ldate.getEra();
249        if (era != null) {
250            if (!validateEra(era)) {
251                return false;
252            }
253            ldate.setNormalizedYear(era.getSinceDate().getYear() + ldate.getYear());
254        } else {
255            ldate.setNormalizedYear(ldate.getYear());
256        }
257        return super.validate(ldate);
258    }
259
260    private boolean validateEra(Era era) {
261        // Validate the era
262        for (int i = 0; i < eras.length; i++) {
263            if (era == eras[i]) {
264                return true;
265            }
266        }
267        return false;
268    }
269
270    public boolean normalize(CalendarDate date) {
271        if (date.isNormalized()) {
272            return true;
273        }
274
275        normalizeYear(date);
276        Date ldate = (Date) date;
277
278        // Normalize it as a Gregorian date and get its millisecond value
279        super.normalize(ldate);
280
281        boolean hasMillis = false;
282        long millis = 0;
283        int year = ldate.getNormalizedYear();
284        int i;
285        Era era = null;
286        for (i = eras.length - 1; i >= 0; --i) {
287            era = eras[i];
288            if (era.isLocalTime()) {
289                CalendarDate sinceDate = era.getSinceDate();
290                int sinceYear = sinceDate.getYear();
291                if (year > sinceYear) {
292                    break;
293                }
294                if (year == sinceYear) {
295                    int month = ldate.getMonth();
296                    int sinceMonth = sinceDate.getMonth();
297                    if (month > sinceMonth) {
298                        break;
299                    }
300                    if (month == sinceMonth) {
301                        int day = ldate.getDayOfMonth();
302                        int sinceDay = sinceDate.getDayOfMonth();
303                        if (day > sinceDay) {
304                            break;
305                        }
306                        if (day == sinceDay) {
307                            long timeOfDay = ldate.getTimeOfDay();
308                            long sinceTimeOfDay = sinceDate.getTimeOfDay();
309                            if (timeOfDay >= sinceTimeOfDay) {
310                                break;
311                            }
312                            --i;
313                            break;
314                        }
315                    }
316                }
317            } else {
318                if (!hasMillis) {
319                    millis  = super.getTime(date);
320                    hasMillis = true;
321                }
322
323                long since = era.getSince(date.getZone());
324                if (millis >= since) {
325                    break;
326                }
327            }
328        }
329        if (i >= 0) {
330            ldate.setLocalEra(era);
331            int y = ldate.getNormalizedYear() - era.getSinceDate().getYear() + 1;
332            ldate.setLocalYear(y);
333        } else {
334            // Set Gregorian year with no era
335            ldate.setEra(null);
336            ldate.setLocalYear(year);
337            ldate.setNormalizedYear(year);
338        }
339        ldate.setNormalized(true);
340        return true;
341    }
342
343    void normalizeMonth(CalendarDate date) {
344        normalizeYear(date);
345        super.normalizeMonth(date);
346    }
347
348    void normalizeYear(CalendarDate date) {
349        Date ldate = (Date) date;
350        // Set the supposed-to-be-correct Gregorian year first
351        // e.g., Showa 90 becomes 2015 (1926 + 90 - 1).
352        Era era = ldate.getEra();
353        if (era == null || !validateEra(era)) {
354            ldate.setNormalizedYear(ldate.getYear());
355        } else {
356            ldate.setNormalizedYear(era.getSinceDate().getYear() + ldate.getYear() - 1);
357        }
358    }
359
360    /**
361     * Returns whether the specified Gregorian year is a leap year.
362     * @see #isLeapYear(Era, int)
363     */
364    public boolean isLeapYear(int gregorianYear) {
365        return CalendarUtils.isGregorianLeapYear(gregorianYear);
366    }
367
368    public boolean isLeapYear(Era era, int year) {
369        if (era == null) {
370            return isLeapYear(year);
371        }
372        int gyear = era.getSinceDate().getYear() + year - 1;
373        return isLeapYear(gyear);
374    }
375
376    public void getCalendarDateFromFixedDate(CalendarDate date, long fixedDate) {
377        Date ldate = (Date) date;
378        super.getCalendarDateFromFixedDate(ldate, fixedDate);
379        adjustYear(ldate, (fixedDate - EPOCH_OFFSET) * DAY_IN_MILLIS, 0);
380    }
381}
382