/* * ----------------------------------------------------------------------- * Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (NewYearStrategy.java) is part of project Time4J. * * Time4J is free software: You can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 2.1 of the License, or * (at your option) any later version. * * Time4J is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Time4J. If not, see <http://www.gnu.org/licenses/>. * ----------------------------------------------------------------------- */ package net.time4j.history; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; /** * <p>Determines the begin of a given historic year. </p> * * @author Meno Hochschild * @see NewYearRule#until(int) * @since 3.14/4.11 * @doctags.concurrency {immutable} */ /*[deutsch] * <p>Bestimmt den Beginn eines historischen Jahres. </p> * * @author Meno Hochschild * @see NewYearRule#until(int) * @since 3.14/4.11 * @doctags.concurrency {immutable} */ public final class NewYearStrategy { //~ Statische Felder/Initialisierungen -------------------------------- static final NewYearStrategy DEFAULT = new NewYearStrategy(NewYearRule.BEGIN_OF_JANUARY, Integer.MAX_VALUE); private static final Comparator<NewYearStrategy> STD_ORDER = new NYSComparator(); //~ Instanzvariablen -------------------------------------------------- private final List<NewYearStrategy> strategies; private final NewYearRule lastRule; private final int lastAnnoDomini; //~ Konstruktoren ----------------------------------------------------- NewYearStrategy( NewYearRule lastRule, int lastAnnoDomini ) { super(); this.strategies = Collections.emptyList(); this.lastRule = lastRule; this.lastAnnoDomini = lastAnnoDomini; } NewYearStrategy(List<NewYearStrategy> strategies) { super(); Collections.sort(strategies, STD_ORDER); NewYearStrategy prev = null; Iterator<NewYearStrategy> iter = strategies.iterator(); while (iter.hasNext()) { NewYearStrategy nys = iter.next(); if ((prev != null) && (nys.lastAnnoDomini == prev.lastAnnoDomini)) { if (nys.lastRule == prev.lastRule) { iter.remove(); } else { throw new IllegalArgumentException( "Multiple strategies with overlapping validity range: " + strategies); } } else { prev = nys; } } assert (strategies.size() >= 2); this.strategies = Collections.unmodifiableList(strategies); this.lastRule = NewYearRule.BEGIN_OF_JANUARY; this.lastAnnoDomini = Integer.MAX_VALUE; } //~ Methoden ---------------------------------------------------------- /** * <p>Combines this and given strategy to a new strategy. </p> * * @param next strategy which chronologically follows after this strategy * @return combined stragegy * @throws IllegalArgumentException in case of multiple overlapping rules * @since 3.14/4.11 */ /*[deutsch] * <p>Kombiniert diese und die angegebene Strategie zu einer neuen Strategie. </p> * * @param next strategy which chronologically follows after this strategy * @return combined stragegy * @throws IllegalArgumentException in case of multiple overlapping rules * @since 3.14/4.11 */ public NewYearStrategy and(NewYearStrategy next) { List<NewYearStrategy> list = new ArrayList<>(); list.addAll(this.strategies); if (list.isEmpty()) { list.add(this); } if (next.strategies.isEmpty()) { list.add(next); } else { list.addAll(next.strategies); } return new NewYearStrategy(list); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj instanceof NewYearStrategy) { NewYearStrategy that = (NewYearStrategy) obj; return ( this.strategies.equals(that.strategies) && (this.lastRule == that.lastRule) && (this.lastAnnoDomini == that.lastAnnoDomini)); } else { return false; } } @Override public int hashCode() { return 17 * this.strategies.hashCode() + 37 * this.lastRule.hashCode() + this.lastAnnoDomini; } /** * <p>For debugging purposes. </p> * * @return description of content */ /*[deutsch] * <p>Für Debugging-Zwecke. </p> * * @return description of content */ @Override public String toString() { StringBuilder sb = new StringBuilder(); if (this.strategies.isEmpty()) { sb.append("[new-year-rule="); sb.append(this.lastRule); if (this.lastAnnoDomini != Integer.MAX_VALUE) { sb.append("->"); sb.append(this.lastAnnoDomini); } } else { boolean first = true; for (NewYearStrategy nys : this.strategies) { if (first) { sb.append('['); first = false; } else { sb.append(','); } sb.append(nys.lastRule); sb.append("->"); sb.append(nys.lastAnnoDomini); } } sb.append(']'); return sb.toString(); } /** * <p>Determines the date of New Year. </p> * * @param era historic era * @param yearOfDisplay historic year of era as displayed (deviating from standard calendar year) * @return historic date of New Year * @since 3.14/4.11 */ HistoricDate newYear( HistoricEra era, int yearOfDisplay ) { return rule(era, yearOfDisplay).newYear(era, yearOfDisplay); } /** * <p>Determines the appropriate rule for New Year. </p> * * @param era historic era * @param yearOfDisplay historic year of era as displayed (deviating from standard calendar year) * @return NewYearRule * @since 3.17/4.14 */ NewYearRule rule( HistoricEra era, int yearOfDisplay ) { int ad = era.annoDomini(yearOfDisplay); int previous = Integer.MIN_VALUE; NewYearRule prevRule = null; for (int i = 0, n = this.strategies.size(); i < n; i++) { NewYearStrategy strategy = this.strategies.get(i); if ((ad >= previous) && (ad < strategy.lastAnnoDomini)) { return strategy.lastRule; } previous = strategy.lastAnnoDomini; prevRule = strategy.lastRule; } if ((ad == previous) && (era == HistoricEra.BYZANTINE) && (prevRule == NewYearRule.BEGIN_OF_SEPTEMBER)) { return prevRule; // see Russia in byzantine year 7208 } return this.lastRule; } /** * <p>Determines the displayed year for given historic date. </p> * * <p>The displayed year can deviate from year of era depending on the concrete new year strategy. </p> * * @param date historic date as reference for the calculation of the displayed year * @return displayed historic year * @since 3.14/4.11 */ int displayedYear(HistoricDate date) { HistoricEra era = date.getEra(); int yearOfEra = date.getYearOfEra(); int annoDomini = era.annoDomini(yearOfEra); int previous = Integer.MIN_VALUE; for (int i = 0, n = this.strategies.size(); i < n; i++) { NewYearStrategy strategy = this.strategies.get(i); if ((annoDomini >= previous) && (annoDomini < strategy.lastAnnoDomini)) { return strategy.lastRule.displayedYear(this, date); } previous = strategy.lastAnnoDomini; } return this.lastRule.displayedYear(this, date); } // used in serialization void writeToStream(DataOutput out) throws IOException { int n = this.strategies.size(); out.writeInt(n); if (n == 0) { out.writeUTF(this.lastRule.name()); out.writeInt(this.lastAnnoDomini); return; } for (int i = 0; i < n; i++) { NewYearStrategy strategy = this.strategies.get(i); out.writeUTF(strategy.lastRule.name()); out.writeInt(strategy.lastAnnoDomini); } } // used in deserialization static NewYearStrategy readFromStream(DataInput in) throws IOException { int n = in.readInt(); if (n == 0) { NewYearRule rule = NewYearRule.valueOf(in.readUTF()); int annoDomini = in.readInt(); if ((annoDomini == Integer.MAX_VALUE) && (rule == NewYearRule.BEGIN_OF_JANUARY)) { return NewYearStrategy.DEFAULT; } else { return new NewYearStrategy(rule, annoDomini); } } List<NewYearStrategy> strategies = new ArrayList<>(n); for (int i = 0; i < n; i++) { NewYearRule rule = NewYearRule.valueOf(in.readUTF()); int annoDomini = in.readInt(); strategies.add(new NewYearStrategy(rule, annoDomini)); } return new NewYearStrategy(strategies); } //~ Innere Klassen ---------------------------------------------------- private static class NYSComparator implements Comparator<NewYearStrategy> { //~ Methoden ------------------------------------------------------ @Override public int compare( NewYearStrategy o1, NewYearStrategy o2 ) { return ( (o1.lastAnnoDomini < o2.lastAnnoDomini) ? -1 : (o1.lastAnnoDomini > o2.lastAnnoDomini ? 1 : 0) ); } } }