1 /*
   2  * Copyright (c) 2000, 2018, 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.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 import java.util.ArrayList;
  25 import java.util.Arrays;
  26 import java.util.Comparator;
  27 import java.util.List;
  28 import java.util.StringTokenizer;
  29 
  30 /**
  31  * Rule manipulates Rule records.
  32  *
  33  * @since 1.4
  34  */
  35 class Rule {
  36 
  37     private List<RuleRec> list;
  38     private String name;
  39 
  40     /**
  41      * Constructs a Rule which consists of a Rule record list. The
  42      * specified name is given to this Rule.
  43      * @param name the Rule name
  44      */
  45     Rule(String name) {
  46         this.name = name;
  47         list = new ArrayList<RuleRec>();
  48     }
  49 
  50     /**
  51      * Added a RuleRec to the Rule record list.
  52      */
  53     void add(RuleRec rec) {
  54         list.add(rec);
  55     }
  56 
  57     /**
  58      * @return the Rule name
  59      */
  60     String getName() {
  61         return name;
  62     }
  63 
  64     /**
  65      * Gets all rule records that cover the given year.
  66      *
  67      * @param year the year number for which the rule is applicable.
  68      * @return rules in List that are collated in time. If no rule is found, an empty
  69      * List is returned.
  70      */
  71     List<RuleRec> getRules(int year) {
  72         List<RuleRec> rules = new ArrayList<RuleRec>(3);
  73         for (RuleRec rec : list) {
  74             if (year >= rec.getFromYear() && year <= rec.getToYear()) {
  75                 if ((rec.isOdd() && year % 2 == 0) || (rec.isEven() && year % 2 == 1))
  76                     continue;
  77                 rules.add(rec);
  78             }
  79         }
  80         int n = rules.size();
  81         if (n <= 1) {
  82             return rules;
  83         }
  84         if (n == 2) {
  85             RuleRec rec1 = rules.get(0);
  86             RuleRec rec2 = rules.get(1);
  87             if (rec1.getMonthNum() > rec2.getMonthNum()) {
  88                 rules.set(0, rec2);
  89                 rules.set(1, rec1);
  90             } else if (rec1.getMonthNum() == rec2.getMonthNum()) {
  91                 // TODO: it's not accurate to ignore time types (STD, WALL, UTC)
  92                 long t1 = Time.getLocalTime(year, rec1.getMonth(),
  93                                             rec1.getDay(), rec1.getTime().getTime());
  94                 long t2 = Time.getLocalTime(year, rec2.getMonth(),
  95                                             rec2.getDay(), rec2.getTime().getTime());
  96                 if (t1 > t2) {
  97                     rules.set(0, rec2);
  98                     rules.set(1, rec1);
  99                 }
 100             }
 101             return rules;
 102         }
 103 
 104         final int y = year;
 105         RuleRec[] recs = new RuleRec[rules.size()];
 106         rules.toArray(recs);
 107 
 108         Arrays.sort(recs, new Comparator<RuleRec>() {
 109                 public int compare(RuleRec r1, RuleRec r2) {
 110                     int n = r1.getMonthNum() - r2.getMonthNum();
 111                     if (n != 0) {
 112                         return n;
 113                     }
 114                     // TODO: it's not accurate to ignore time types (STD, WALL, UTC)
 115                     long t1 = Time.getLocalTime(y, r1.getMonth(),
 116                                                 r1.getDay(), r1.getTime().getTime());
 117                     long t2 = Time.getLocalTime(y, r2.getMonth(),
 118                                                 r2.getDay(), r2.getTime().getTime());
 119                     return Long.compare(t1, t2);
 120                 }
 121                 public boolean equals(Object o) {
 122                     return this == o;
 123                 }
 124             });
 125         rules.clear();
 126         for (int i = 0; i < n; i++) {
 127             if (i != 0 && recs[i -1].getSave() == recs[i].getSave()) {
 128                 // we have two recs back to back with same saving for the same year.
 129                 if (recs[i].isLastRule()) {
 130                     continue;
 131                 } else if (recs[i - 1].isLastRule()) {
 132                     rules.remove(rules.size() - 1);
 133                 }
 134             }
 135             rules.add(recs[i]);
 136         }
 137         return rules;
 138     }
 139 
 140     /**
 141      * Gets rule records that have either "max" or cover the endYear
 142      * value in its DST schedule.
 143      *
 144      * @return rules that contain last DST schedule. An empty
 145      * ArrayList is returned if no last rules are found.
 146      */
 147     List<RuleRec> getLastRules() {
 148         RuleRec start = null;
 149         RuleRec end = null;
 150 
 151         for (int i = 0; i < list.size(); i++) {
 152             RuleRec rec = list.get(i);
 153             if (rec.isLastRule()) {
 154                 if (rec.getSave() > 0) {
 155                     start = rec;
 156                 } else {
 157                     end = rec;
 158                 }
 159             }
 160         }
 161         if (start == null || end == null) {
 162             int endYear = Zoneinfo.getEndYear();
 163             for (int i  = 0; i < list.size(); i++) {
 164                 RuleRec rec = list.get(i);
 165                 if (endYear >= rec.getFromYear() && endYear <= rec.getToYear()) {
 166                     if (start == null && rec.getSave() > 0) {
 167                         start = rec;
 168                     } else {
 169                         if (end == null && rec.getSave() == 0) {
 170                             end = rec;
 171                         }
 172                     }
 173                 }
 174             }
 175         }
 176 
 177         List<RuleRec> r = new ArrayList<RuleRec>(2);
 178         if (start == null || end == null) {
 179             if (start != null || end != null) {
 180                 Main.warning("found last rules for "+name+" inconsistent.");
 181             }
 182             return r;
 183         }
 184 
 185         r.add(start);
 186         r.add(end);
 187         return r;
 188     }
 189 }