Home | History | Annotate | Download | only in zone
      1 /*
      2  * Copyright (c) 2012, 2013, 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 /*
     27  * This file is available under and governed by the GNU General Public
     28  * License version 2 only, as published by the Free Software Foundation.
     29  * However, the following notice accompanied the original version of this
     30  * file:
     31  *
     32  * Copyright (c) 2011-2012, Stephen Colebourne & Michael Nascimento Santos
     33  *
     34  * All rights reserved.
     35  *
     36  * Redistribution and use in source and binary forms, with or without
     37  * modification, are permitted provided that the following conditions are met:
     38  *
     39  *  * Redistributions of source code must retain the above copyright notice,
     40  *    this list of conditions and the following disclaimer.
     41  *
     42  *  * Redistributions in binary form must reproduce the above copyright notice,
     43  *    this list of conditions and the following disclaimer in the documentation
     44  *    and/or other materials provided with the distribution.
     45  *
     46  *  * Neither the name of JSR-310 nor the names of its contributors
     47  *    may be used to endorse or promote products derived from this software
     48  *    without specific prior written permission.
     49  *
     50  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     51  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     52  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     53  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     54  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     55  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     56  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     57  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     58  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     59  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     60  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     61  */
     62 package java.time.zone;
     63 
     64 import java.io.DataInput;
     65 import java.io.DataOutput;
     66 import java.io.Externalizable;
     67 import java.io.IOException;
     68 import java.io.InvalidClassException;
     69 import java.io.ObjectInput;
     70 import java.io.ObjectOutput;
     71 import java.io.StreamCorruptedException;
     72 import java.time.ZoneOffset;
     73 
     74 /**
     75  * The shared serialization delegate for this package.
     76  *
     77  * @implNote
     78  * This class is mutable and should be created once per serialization.
     79  *
     80  * @serial include
     81  * @since 1.8
     82  */
     83 final class Ser implements Externalizable {
     84 
     85     /**
     86      * Serialization version.
     87      */
     88     private static final long serialVersionUID = -8885321777449118786L;
     89 
     90     /** Type for ZoneRules. */
     91     static final byte ZRULES = 1;
     92     /** Type for ZoneOffsetTransition. */
     93     static final byte ZOT = 2;
     94     /** Type for ZoneOffsetTransition. */
     95     static final byte ZOTRULE = 3;
     96 
     97     /** The type being serialized. */
     98     private byte type;
     99     /** The object being serialized. */
    100     private Object object;
    101 
    102     /**
    103      * Constructor for deserialization.
    104      */
    105     public Ser() {
    106     }
    107 
    108     /**
    109      * Creates an instance for serialization.
    110      *
    111      * @param type  the type
    112      * @param object  the object
    113      */
    114     Ser(byte type, Object object) {
    115         this.type = type;
    116         this.object = object;
    117     }
    118 
    119     //-----------------------------------------------------------------------
    120     /**
    121      * Implements the {@code Externalizable} interface to write the object.
    122      * @serialData
    123      * Each serializable class is mapped to a type that is the first byte
    124      * in the stream.  Refer to each class {@code writeReplace}
    125      * serialized form for the value of the type and sequence of values for the type.
    126      *
    127      * <ul>
    128      * <li><a href="../../../serialized-form.html#java.time.zone.ZoneRules">ZoneRules.writeReplace</a>
    129      * <li><a href="../../../serialized-form.html#java.time.zone.ZoneOffsetTransition">ZoneOffsetTransition.writeReplace</a>
    130      * <li><a href="../../../serialized-form.html#java.time.zone.ZoneOffsetTransitionRule">ZoneOffsetTransitionRule.writeReplace</a>
    131      * </ul>
    132      *
    133      * @param out  the data stream to write to, not null
    134      */
    135     @Override
    136     public void writeExternal(ObjectOutput out) throws IOException {
    137         writeInternal(type, object, out);
    138     }
    139 
    140     static void write(Object object, DataOutput out) throws IOException {
    141         writeInternal(ZRULES, object, out);
    142     }
    143 
    144     private static void writeInternal(byte type, Object object, DataOutput out) throws IOException {
    145         out.writeByte(type);
    146         switch (type) {
    147             case ZRULES:
    148                 ((ZoneRules) object).writeExternal(out);
    149                 break;
    150             case ZOT:
    151                 ((ZoneOffsetTransition) object).writeExternal(out);
    152                 break;
    153             case ZOTRULE:
    154                 ((ZoneOffsetTransitionRule) object).writeExternal(out);
    155                 break;
    156             default:
    157                 throw new InvalidClassException("Unknown serialized type");
    158         }
    159     }
    160 
    161     //-----------------------------------------------------------------------
    162     /**
    163      * Implements the {@code Externalizable} interface to read the object.
    164      * @serialData
    165      * The streamed type and parameters defined by the type's {@code writeReplace}
    166      * method are read and passed to the corresponding static factory for the type
    167      * to create a new instance.  That instance is returned as the de-serialized
    168      * {@code Ser} object.
    169      *
    170      * <ul>
    171      * <li><a href="../../../serialized-form.html#java.time.zone.ZoneRules">ZoneRules</a>
    172      * - {@code ZoneRules.of(standardTransitions, standardOffsets, savingsInstantTransitions, wallOffsets, lastRules);}
    173      * <li><a href="../../../serialized-form.html#java.time.zone.ZoneOffsetTransition">ZoneOffsetTransition</a>
    174      * - {@code ZoneOffsetTransition of(LocalDateTime.ofEpochSecond(epochSecond), offsetBefore, offsetAfter);}
    175      * <li><a href="../../../serialized-form.html#java.time.zone.ZoneOffsetTransitionRule">ZoneOffsetTransitionRule</a>
    176      * - {@code ZoneOffsetTransitionRule.of(month, dom, dow, time, timeEndOfDay, timeDefinition, standardOffset, offsetBefore, offsetAfter);}
    177      * </ul>
    178      * @param in  the data to read, not null
    179      */
    180     @Override
    181     public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    182         type = in.readByte();
    183         object = readInternal(type, in);
    184     }
    185 
    186     static Object read(DataInput in) throws IOException, ClassNotFoundException {
    187         byte type = in.readByte();
    188         return readInternal(type, in);
    189     }
    190 
    191     private static Object readInternal(byte type, DataInput in) throws IOException, ClassNotFoundException {
    192         switch (type) {
    193             case ZRULES:
    194                 return ZoneRules.readExternal(in);
    195             case ZOT:
    196                 return ZoneOffsetTransition.readExternal(in);
    197             case ZOTRULE:
    198                 return ZoneOffsetTransitionRule.readExternal(in);
    199             default:
    200                 throw new StreamCorruptedException("Unknown serialized type");
    201         }
    202     }
    203 
    204     /**
    205      * Returns the object that will replace this one.
    206      *
    207      * @return the read object, should never be null
    208      */
    209     private Object readResolve() {
    210          return object;
    211     }
    212 
    213     //-----------------------------------------------------------------------
    214     /**
    215      * Writes the state to the stream.
    216      *
    217      * @param offset  the offset, not null
    218      * @param out  the output stream, not null
    219      * @throws IOException if an error occurs
    220      */
    221     static void writeOffset(ZoneOffset offset, DataOutput out) throws IOException {
    222         final int offsetSecs = offset.getTotalSeconds();
    223         int offsetByte = offsetSecs % 900 == 0 ? offsetSecs / 900 : 127;  // compress to -72 to +72
    224         out.writeByte(offsetByte);
    225         if (offsetByte == 127) {
    226             out.writeInt(offsetSecs);
    227         }
    228     }
    229 
    230     /**
    231      * Reads the state from the stream.
    232      *
    233      * @param in  the input stream, not null
    234      * @return the created object, not null
    235      * @throws IOException if an error occurs
    236      */
    237     static ZoneOffset readOffset(DataInput in) throws IOException {
    238         int offsetByte = in.readByte();
    239         return (offsetByte == 127 ? ZoneOffset.ofTotalSeconds(in.readInt()) : ZoneOffset.ofTotalSeconds(offsetByte * 900));
    240     }
    241 
    242     //-----------------------------------------------------------------------
    243     /**
    244      * Writes the state to the stream.
    245      *
    246      * @param epochSec  the epoch seconds, not null
    247      * @param out  the output stream, not null
    248      * @throws IOException if an error occurs
    249      */
    250     static void writeEpochSec(long epochSec, DataOutput out) throws IOException {
    251         if (epochSec >= -4575744000L && epochSec < 10413792000L && epochSec % 900 == 0) {  // quarter hours between 1825 and 2300
    252             int store = (int) ((epochSec + 4575744000L) / 900);
    253             out.writeByte((store >>> 16) & 255);
    254             out.writeByte((store >>> 8) & 255);
    255             out.writeByte(store & 255);
    256         } else {
    257             out.writeByte(255);
    258             out.writeLong(epochSec);
    259         }
    260     }
    261 
    262     /**
    263      * Reads the state from the stream.
    264      *
    265      * @param in  the input stream, not null
    266      * @return the epoch seconds, not null
    267      * @throws IOException if an error occurs
    268      */
    269     static long readEpochSec(DataInput in) throws IOException {
    270         int hiByte = in.readByte() & 255;
    271         if (hiByte == 255) {
    272             return in.readLong();
    273         } else {
    274             int midByte = in.readByte() & 255;
    275             int loByte = in.readByte() & 255;
    276             long tot = ((hiByte << 16) + (midByte << 8) + loByte);
    277             return (tot * 900) - 4575744000L;
    278         }
    279     }
    280 
    281 }
    282