/* * ----------------------------------------------------------------------- * Copyright © 2013-2016 Meno Hochschild, <http://www.menodata.de/> * ----------------------------------------------------------------------- * This file (SPX.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 net.time4j.PlainDate; import net.time4j.engine.EpochDays; import net.time4j.history.internal.HistoricVariant; import java.io.DataInput; import java.io.DataOutput; import java.io.Externalizable; import java.io.IOException; import java.io.InvalidClassException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.io.ObjectStreamException; import java.io.StreamCorruptedException; /** * <p>Serialisierungsform für {@code ChronoHistory}. </p> * * @author Meno Hochschild * @serial include */ final class SPX implements Externalizable { //~ Statische Felder/Initialisierungen ---------------------------- /** Serialisierungstyp. */ static final int VERSION_1 = 1; /** Serialisierungstyp. */ static final int VERSION_2 = 2; /** Serialisierungstyp. */ static final int VERSION_3 = 3; private static final int[] EMPTY_INT_ARRAY = new int[0]; private static final long serialVersionUID = 1L; //~ Instanzvariablen ---------------------------------------------- private transient Object obj; private transient int type; //~ Konstruktoren ------------------------------------------------- /** * <p>Benutzt in der Deserialisierung gemäß dem Kontrakt * von {@code Externalizable}. </p> */ public SPX() { super(); } /** * <p>Benutzt in der Serialisierung (writeReplace). </p> * * @param obj object to be serialized * @param type serialization type (corresponds to type of obj) */ SPX( Object obj, int type ) { super(); this.obj = obj; this.type = type; } //~ Methoden ------------------------------------------------------ /** * <p>Implementation method of interface {@link Externalizable}. </p> * * <p>The first byte contains within the 4 most-significant bits the type * of the object to be serialized. Then the data bytes follow in a * bit-compressed representation. </p> * * @serialData data layout see {@code writeReplace()}-method of object * to be serialized * @param out output stream * @throws IOException in any case of IO-failures */ /*[deutsch] * <p>Implementierungsmethode des Interface {@link Externalizable}. </p> * * <p>Das erste Byte enthält um 4 Bits nach links verschoben den * Typ des zu serialisierenden Objekts. Danach folgen die Daten-Bits * in einer bit-komprimierten Darstellung. </p> * * @serialData data layout see {@code writeReplace()}-method of object * to be serialized * @param out output stream * @throws IOException in any case of IO-failures */ @Override public void writeExternal(ObjectOutput out) throws IOException { switch (this.type) { case VERSION_1: case VERSION_2: case VERSION_3: this.writeHistory(out); break; default: throw new InvalidClassException("Unknown serialized type."); } } /** * <p>Implementation method of interface {@link Externalizable}. </p> * * @param in input stream * @throws IOException in any case of IO-failures * @throws ClassNotFoundException if class loading fails */ /*[deutsch] * <p>Implementierungsmethode des Interface {@link Externalizable}. </p> * * @param in input stream * @throws IOException in any case of IO-failures * @throws ClassNotFoundException if class loading fails */ @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { ChronoHistory history; AncientJulianLeapYears ajly; byte header = in.readByte(); switch ((header & 0xFF) >> 4) { case VERSION_1: history = this.readHistory(in, header); break; case VERSION_2: history = this.readHistory(in, header); ajly = readTriennalState(in); if (ajly != null) { history = history.with(ajly); } break; case VERSION_3: history = this.readHistory(in, header); ajly = readTriennalState(in); if (ajly != null) { history = history.with(ajly); } history = history.with(NewYearStrategy.readFromStream(in)); history = history.with(EraPreference.readFromStream(in)); break; default: throw new StreamCorruptedException("Unknown serialized type."); } this.obj = history; } private Object readResolve() throws ObjectStreamException { return this.obj; } private void writeHistory(DataOutput out) throws IOException { ChronoHistory history = (ChronoHistory) this.obj; int variant = history.getHistoricVariant().getSerialValue(); int header = this.type; header <<= 4; header |= variant; out.writeByte(header); if (history.getHistoricVariant() == HistoricVariant.SINGLE_CUTOVER_DATE) { out.writeLong(history.getEvents().get(0).start); } int[] sequence = ( history.hasAncientJulianLeapYears() ? history.getAncientJulianLeapYears().getPattern() : EMPTY_INT_ARRAY); out.writeInt(sequence.length); for (int i = 0; i < sequence.length; i++) { out.writeInt(sequence[i]); } history.getNewYearStrategy().writeToStream(out); history.getEraPreference().writeToStream(out); } private ChronoHistory readHistory( DataInput in, byte header ) throws IOException, ClassNotFoundException { int variant = header & 0xF; HistoricVariant hv = getEnum(variant); switch (hv) { case PROLEPTIC_GREGORIAN: return ChronoHistory.PROLEPTIC_GREGORIAN; case PROLEPTIC_JULIAN: return ChronoHistory.PROLEPTIC_JULIAN; case PROLEPTIC_BYZANTINE: return ChronoHistory.PROLEPTIC_BYZANTINE; case SWEDEN: return ChronoHistory.ofSweden(); case INTRODUCTION_ON_1582_10_15: return ChronoHistory.ofFirstGregorianReform(); default: long mjd = in.readLong(); return ChronoHistory.ofGregorianReform(PlainDate.of(mjd, EpochDays.MODIFIED_JULIAN_DATE)); } } private static HistoricVariant getEnum(int variant) throws StreamCorruptedException { for (HistoricVariant hv : HistoricVariant.values()) { if (hv.getSerialValue() == variant) { return hv; } } throw new StreamCorruptedException("Unknown variant of chronological history."); } private static AncientJulianLeapYears readTriennalState(DataInput in) throws IOException { int len = in.readInt(); if (len > 0) { int[] sequence = new int[len]; for (int i = 0; i < len; i++) { sequence[i] = 1 - in.readInt(); } return AncientJulianLeapYears.of(sequence); } return null; } }