1 /*
2  * hunt-time: A time library for D programming language.
3  *
4  * Copyright (C) 2015-2018 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.time.chrono.Ser;
13 
14 import hunt.stream.Externalizable;
15 import hunt.Exceptions;
16 // import hunt.io.InvalidClassException;
17 import hunt.stream.ObjectInput;
18 import hunt.stream.ObjectOutput;
19 // import hunt.io.StreamCorruptedException;
20 import hunt.time.LocalDate;
21 import hunt.time.LocalDateTime;
22 import hunt.time.chrono.AbstractChronology;
23 import hunt.time.chrono.JapaneseDate;
24 import hunt.time.chrono.JapaneseEra;
25 import hunt.time.chrono.HijrahDate;
26 import hunt.time.chrono.MinguoDate;
27 import hunt.time.chrono.ThaiBuddhistDate;
28 import hunt.time.chrono.ChronoPeriodImpl;
29 import hunt.time.chrono.ChronoLocalDateTimeImpl;
30 import hunt.time.chrono.ChronoZonedDateTimeImpl;
31 import hunt.time.chrono.ChronoLocalDate;
32 /**
33  * The shared serialization delegate for this package.
34  *
35  * @implNote
36  * This class wraps the object being serialized, and takes a byte representing the type of the class to
37  * be serialized.  This byte can also be used for versioning the serialization format.  In this case another
38  * byte flag would be used _in order to specify an alternative version of the type format.
39  * For example {@code CHRONO_TYPE_VERSION_2 = 21}
40  * !(p)
41  * In order to serialize the object it writes its byte and then calls back to the appropriate class where
42  * the serialization is performed.  In order to deserialize the object it read _in the type byte, switching
43  * _in order to select which class to call back into.
44  * !(p)
45  * The serialization format is determined on a per class basis.  In the case of field based classes each
46  * of the fields is written _out with an appropriate size format _in descending order of the field's size.  For
47  * example _in the case of {@link LocalDate} year is written before month.  Composite classes, such as
48  * {@link LocalDateTime} are serialized as one object.  Enum classes are serialized using the index of their
49  * element.
50  * !(p)
51  * This class is mutable and should be created once per serialization.
52  *
53  * @serial include
54  * @since 1.8
55  */
56 final class Ser : Externalizable {
57 
58     /**
59      * Serialization version.
60      */
61     private enum long serialVersionUID = -6103370247208168577L;
62 
63     enum byte CHRONO_TYPE = 1;
64     enum byte CHRONO_LOCAL_DATE_TIME_TYPE = 2;
65     enum byte CHRONO_ZONE_DATE_TIME_TYPE = 3;
66     enum byte JAPANESE_DATE_TYPE = 4;
67     enum byte JAPANESE_ERA_TYPE = 5;
68     enum byte HIJRAH_DATE_TYPE = 6;
69     enum byte MINGUO_DATE_TYPE = 7;
70     enum byte THAIBUDDHIST_DATE_TYPE = 8;
71     enum byte CHRONO_PERIOD_TYPE = 9;
72 
73     /** The type being serialized. */
74     private byte type;
75     /** The object being serialized. */
76     private Object object;
77 
78     /**
79      * Constructor for deserialization.
80      */
81     public this() {
82     }
83 
84     /**
85      * Creates an instance for serialization.
86      *
87      * @param type  the type
88      * @param object  the object
89      */
90     this(byte type, Object object) {
91         this.type = type;
92         this.object = object;
93     }
94 
95     //-----------------------------------------------------------------------
96     /**
97      * Implements the {@code Externalizable} interface to write the object.
98      * @serialData
99      * Each serializable class is mapped to a type that is the first byte
100      * _in the stream.  Refer to each class {@code writeReplace}
101      * serialized form for the value of the type and sequence of values for the type.
102      * !(ul)
103      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.HijrahChronology">HijrahChronology.writeReplace</a>
104      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.IsoChronology">IsoChronology.writeReplace</a>
105      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.JapaneseChronology">JapaneseChronology.writeReplace</a>
106      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.MinguoChronology">MinguoChronology.writeReplace</a>
107      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.ThaiBuddhistChronology">ThaiBuddhistChronology.writeReplace</a>
108      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.ChronoLocalDateTimeImpl">ChronoLocalDateTime.writeReplace</a>
109      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.ChronoZonedDateTimeImpl">ChronoZonedDateTime.writeReplace</a>
110      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.JapaneseDate">JapaneseDate.writeReplace</a>
111      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.JapaneseEra">JapaneseEra.writeReplace</a>
112      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.HijrahDate">HijrahDate.writeReplace</a>
113      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.MinguoDate">MinguoDate.writeReplace</a>
114      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.ThaiBuddhistDate">ThaiBuddhistDate.writeReplace</a>
115      * </ul>
116      *
117      * @param _out  the data stream to write to, not null
118      */
119     override
120     public void writeExternal(ObjectOutput _out) /*throws IOException*/ {
121         writeInternal(type, object, _out);
122     }
123 
124     private static void writeInternal(byte type, Object object, ObjectOutput _out) /*throws IOException*/ {
125         _out.writeByte(type);
126         switch (type) {
127             case CHRONO_TYPE:
128                 (cast(AbstractChronology) object).writeExternal(_out);
129                 break;
130             case CHRONO_LOCAL_DATE_TIME_TYPE:
131                 (cast(ChronoLocalDateTimeImpl!(ChronoLocalDate)) object).writeExternal(_out);
132                 break;
133             case CHRONO_ZONE_DATE_TIME_TYPE:
134                 (cast(ChronoZonedDateTimeImpl!(ChronoLocalDate)) object).writeExternal(_out);
135                 break;
136             // case JAPANESE_DATE_TYPE:
137             //     (cast(JapaneseDate) object).writeExternal(_out);
138             //     break;
139             // case JAPANESE_ERA_TYPE:
140             //     (cast(JapaneseEra) object).writeExternal(_out);
141             //     break;
142             // case HIJRAH_DATE_TYPE:
143             //     (cast(HijrahDate) object).writeExternal(_out);
144             //     break;
145             // case MINGUO_DATE_TYPE:
146             //     (cast(MinguoDate) object).writeExternal(_out);
147             //     break;
148             // case THAIBUDDHIST_DATE_TYPE:
149             //     (cast(ThaiBuddhistDate) object).writeExternal(_out);
150             //     break;
151             case CHRONO_PERIOD_TYPE:
152                 (cast(ChronoPeriodImpl) object).writeExternal(_out);
153                 break;
154             default:
155                 throw new InvalidClassException("Unknown serialized type");
156         }
157     }
158 
159     //-----------------------------------------------------------------------
160     /**
161      * Implements the {@code Externalizable} interface to read the object.
162      * @serialData
163      * The streamed type and parameters defined by the type's {@code writeReplace}
164      * method are read and passed to the corresponding static factory for the type
165      * to create a new instance.  That instance is returned as the de-serialized
166      * {@code Ser} object.
167      *
168      * !(ul)
169      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.HijrahChronology">HijrahChronology</a> - Chronology.of(id)
170      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.IsoChronology">IsoChronology</a> - Chronology.of(id)
171      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.JapaneseChronology">JapaneseChronology</a> - Chronology.of(id)
172      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.MinguoChronology">MinguoChronology</a> - Chronology.of(id)
173      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.ThaiBuddhistChronology">ThaiBuddhistChronology</a> - Chronology.of(id)
174      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.ChronoLocalDateTimeImpl">ChronoLocalDateTime</a> - date.atTime(time)
175      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.ChronoZonedDateTimeImpl">ChronoZonedDateTime</a> - dateTime.atZone(offset).withZoneSameLocal(zone)
176      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.JapaneseDate">JapaneseDate</a> - JapaneseChronology.INSTANCE.date(year, month, dayOfMonth)
177      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.JapaneseEra">JapaneseEra</a> - JapaneseEra.of(eraValue)
178      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.HijrahDate">HijrahDate</a> - HijrahChronology chrono.date(year, month, dayOfMonth)
179      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.MinguoDate">MinguoDate</a> - MinguoChronology.INSTANCE.date(year, month, dayOfMonth)
180      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.chrono.ThaiBuddhistDate">ThaiBuddhistDate</a> - ThaiBuddhistChronology.INSTANCE.date(year, month, dayOfMonth)
181      * </ul>
182      *
183      * @param _in  the data stream to read from, not null
184      */
185     override
186     public void readExternal(ObjectInput _in) /*throws IOException, ClassNotFoundException*/ {
187         type = _in.readByte();
188         object = readInternal(type, _in);
189     }
190 
191     static Object read(ObjectInput _in) /*throws IOException, ClassNotFoundException*/ {
192         byte type = _in.readByte();
193         return readInternal(type, _in);
194     }
195 
196     private static Object readInternal(byte type, ObjectInput _in) /*throws IOException, ClassNotFoundException */{
197         switch (type) {
198             case CHRONO_TYPE: return cast(Object)(AbstractChronology.readExternal(_in));
199             case CHRONO_LOCAL_DATE_TIME_TYPE: return cast(Object)(ChronoLocalDateTimeImpl!(ChronoLocalDate).readExternal(_in));
200             case CHRONO_ZONE_DATE_TIME_TYPE: return cast(Object)(ChronoZonedDateTimeImpl!(ChronoLocalDate).readExternal(_in));
201             // case JAPANESE_DATE_TYPE:  return JapaneseDate.readExternal(_in);
202             // case JAPANESE_ERA_TYPE: return JapaneseEra.readExternal(_in);
203             // case HIJRAH_DATE_TYPE: return HijrahDate.readExternal(_in);
204             // case MINGUO_DATE_TYPE: return MinguoDate.readExternal(_in);
205             // case THAIBUDDHIST_DATE_TYPE: return ThaiBuddhistDate.readExternal(_in);
206             case CHRONO_PERIOD_TYPE: return cast(Object)(ChronoPeriodImpl.readExternal(_in));
207             default: throw new Exception("Unknown serialized type");
208         }
209     }
210 
211     /**
212      * Returns the object that will replace this one.
213      *
214      * @return the read object, should never be null
215      */
216     private Object readResolve() {
217          return object;
218     }
219 
220 }