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