/* * ----------------------------------------------------------------------- * Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (CompositeTransitionModel.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.tz.model; import net.time4j.base.GregorianDate; import net.time4j.base.UnixTime; import net.time4j.base.WallTime; import net.time4j.tz.ZonalOffset; import net.time4j.tz.ZonalTransition; import java.io.IOException; import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; /** * <p>Übergangsmodell als Komposition eines {@code ArrayTransitionModel} * mit einem {@code RuleBasedTransitionModel}. </p> * * @author Meno Hochschild * @since 2.2 * @serial include * @doctags.concurrency {immutable} */ final class CompositeTransitionModel extends TransitionModel { //~ Statische Felder/Initialisierungen -------------------------------- private static final long serialVersionUID = 1749643877954103721L; //~ Instanzvariablen -------------------------------------------------- private transient final int size; private transient final ArrayTransitionModel arrayModel; private transient final RuleBasedTransitionModel ruleModel; private transient final ZonalTransition last; // Cache private transient int hash = 0; //~ Konstruktoren ----------------------------------------------------- CompositeTransitionModel( int size, List<ZonalTransition> transitions, List<DaylightSavingRule> rules, boolean create, boolean sanityCheck ) { super(); this.size = size; this.arrayModel = new ArrayTransitionModel(transitions, create, sanityCheck); this.last = this.arrayModel.getLastTransition(); this.ruleModel = new RuleBasedTransitionModel(this.last, rules, create); } //~ Methoden ---------------------------------------------------------- @Override public ZonalOffset getInitialOffset() { return this.arrayModel.getInitialOffset(); } @Override public ZonalTransition getStartTransition(UnixTime ut) { if (ut.getPosixTime() < this.last.getPosixTime()) { return this.arrayModel.getStartTransition(ut); } else { ZonalTransition result = this.ruleModel.getStartTransition(ut); return ((result == null) ? this.last : result); } } @Override public ZonalTransition getConflictTransition( GregorianDate localDate, WallTime localTime ) { return this.arrayModel.getConflictTransition( localDate, localTime, this.ruleModel ); } @Override public Optional<ZonalTransition> findNextTransition(UnixTime ut) { Optional<ZonalTransition> result = this.arrayModel.findNextTransition(ut); if (!result.isPresent()) { result = this.ruleModel.findNextTransition(ut); } return result; } @Override public List<ZonalOffset> getValidOffsets( GregorianDate localDate, WallTime localTime ) { return this.arrayModel.getValidOffsets( localDate, localTime, this.ruleModel); } @Override public List<ZonalTransition> getStdTransitions() { // condition: std-transitions built by enhanced array cache return this.arrayModel.getStdTransitions(); } @Override public List<ZonalTransition> getTransitions( UnixTime start, UnixTime end ) { List<ZonalTransition> result = new ArrayList<>(); result.addAll(this.arrayModel.getTransitions(start, end)); result.addAll(this.ruleModel.getTransitions(start, end)); return Collections.unmodifiableList(result); } @Override public void dump(Appendable buffer) throws IOException { this.arrayModel.dump(this.size, buffer); this.ruleModel.dump(buffer); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (obj instanceof CompositeTransitionModel) { CompositeTransitionModel that = (CompositeTransitionModel) obj; return ( this.arrayModel.equals(that.arrayModel, this.size, that.size) && this.ruleModel.getRules().equals(that.ruleModel.getRules())); } else { return false; } } @Override public int hashCode() { int h = this.hash; if (h == 0) { h = this.arrayModel.hashCode(this.size); h += (37 * this.ruleModel.getRules().hashCode()); this.hash = h; } return h; } @Override public String toString() { StringBuilder sb = new StringBuilder(32); sb.append(this.getClass().getName()); sb.append("[transition-count="); sb.append(this.size); sb.append(",hash="); sb.append(this.hashCode()); sb.append(",last-rules="); sb.append(this.ruleModel.getRules()); sb.append(']'); return sb.toString(); } /** * <p>Benutzt in der Serialisierung. </p> * * @param out serialization stream */ void writeTransitions(ObjectOutput out) throws IOException { this.arrayModel.writeTransitions(this.size, out); } /** * <p>Benutzt in der Serialisierung. </p> * * @return list of daylight saving rules */ List<DaylightSavingRule> getRules() { return this.ruleModel.getRules(); } /** * @serialData Uses a specialized serialisation form as proxy. The format * is bit-compressed. The first byte contains the type id * {@code 127}. Then the data bytes for the internal * transitions and rules follow. The complex algorithm * exploits the fact that allmost all transitions happen * at full hours around midnight in local standard time. * Insight in details see source code. * * @return replacement object in serialization graph */ private Object writeReplace() { return new SPX(this, SPX.COMPOSITE_TRANSITION_MODEL_TYPE); } /** * @serialData Blocks because a serialization proxy is required. * @param in object input stream * @throws InvalidObjectException (always) */ private void readObject(ObjectInputStream in) throws IOException { throw new InvalidObjectException("Serialization proxy required."); } }