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.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.Duration;
21 // import hunt.time.Instant;
22 // import hunt.time.LocalDate;
23 // import hunt.time.LocalDateTime;
24 // import hunt.time.LocalTime;
25 // import hunt.time.ZoneRegion;
26 // import hunt.time.ZoneOffset;
27 // import hunt.time.ZonedDateTime;
28 // import hunt.time.OffsetTime;
29 // import hunt.time.OffsetDateTime;
30 // import hunt.time.Year;
31 // import hunt.time.YearMonth;
32 // import hunt.time.MonthDay;
33 // import hunt.time.Period;
34 
35 /**
36  * The shared serialization delegate for this package.
37  *
38  * @implNote
39  * This class wraps the object being serialized, and takes a byte representing the type of the class to
40  * be serialized.  This byte can also be used for versioning the serialization format.  In this case another
41  * byte flag would be used _in order to specify an alternative version of the type format.
42  * For example {@code LOCAL_DATE_TYPE_VERSION_2 = 21}.
43  * !(p)
44  * In order to serialize the object it writes its byte and then calls back to the appropriate class where
45  * the serialization is performed.  In order to deserialize the object it read _in the type byte, switching
46  * _in order to select which class to call back into.
47  * !(p)
48  * The serialization format is determined on a per class basis.  In the case of field based classes each
49  * of the fields is written _out with an appropriate size format _in descending order of the field's size.  For
50  * example _in the case of {@link LocalDate} year is written before month.  Composite classes, such as
51  * {@link LocalDateTime} are serialized as one object.
52  * !(p)
53  * This class is mutable and should be created once per serialization.
54  *
55  * @serial include
56  * @since 1.8
57  */
58 final class Ser : Externalizable {
59 
60     /**
61      * Serialization version.
62      */
63     private enum long serialVersionUID = -7683839454370182990L;
64 
65     enum byte DURATION_TYPE = 1;
66     enum byte INSTANT_TYPE = 2;
67     enum byte LOCAL_DATE_TYPE = 3;
68     enum byte LOCAL_TIME_TYPE = 4;
69     enum byte LOCAL_DATE_TIME_TYPE = 5;
70     enum byte ZONE_DATE_TIME_TYPE = 6;
71     enum byte ZONE_REGION_TYPE = 7;
72     enum byte ZONE_OFFSET_TYPE = 8;
73     enum byte OFFSET_TIME_TYPE = 9;
74     enum byte OFFSET_DATE_TIME_TYPE = 10;
75     enum byte YEAR_TYPE = 11;
76     enum byte YEAR_MONTH_TYPE = 12;
77     enum byte MONTH_DAY_TYPE = 13;
78     enum byte PERIOD_TYPE = 14;
79 
80     /** The type being serialized. */
81     private byte type;
82     /** The object being serialized. */
83     private Object object;
84 
85     /**
86      * Constructor for deserialization.
87      */
88     public this() {
89     }
90 
91     /**
92      * Creates an instance for serialization.
93      *
94      * @param type  the type
95      * @param object  the object
96      */
97     this(byte type, Object object) {
98         this.type = type;
99         this.object = object;
100     }
101 
102     //-----------------------------------------------------------------------
103     /**
104      * Implements the {@code Externalizable} interface to write the object.
105      * @serialData
106      *
107      * Each serializable class is mapped to a type that is the first byte
108      * _in the stream.  Refer to each class {@code writeReplace}
109      * serialized form for the value of the type and sequence of values for the type.
110      * !(ul)
111      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.Duration">Duration.writeReplace</a>
112      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.Instant">Instant.writeReplace</a>
113      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.LocalDate">LocalDate.writeReplace</a>
114      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.LocalDateTime">LocalDateTime.writeReplace</a>
115      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.LocalTime">LocalTime.writeReplace</a>
116      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.MonthDay">MonthDay.writeReplace</a>
117      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.OffsetTime">OffsetTime.writeReplace</a>
118      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.OffsetDateTime">OffsetDateTime.writeReplace</a>
119      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.Period">Period.writeReplace</a>
120      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.Year">Year.writeReplace</a>
121      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.YearMonth">YearMonth.writeReplace</a>
122      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.ZoneId">ZoneId.writeReplace</a>
123      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.ZoneOffset">ZoneOffset.writeReplace</a>
124      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.ZonedDateTime">ZonedDateTime.writeReplace</a>
125      * </ul>
126      *
127      * @param _out  the data stream to write to, not null
128      */
129     override
130     public void writeExternal(ObjectOutput _out) /*throws IOException*/ {
131         writeInternal(type, object, _out);
132     }
133 
134     static void writeInternal(byte type, Object object, ObjectOutput _out) /*throws IOException*/ {
135         // _out.writeByte(type);
136         // switch (type) {
137         //     case DURATION_TYPE:
138         //         (cast(Duration) object).writeExternal(_out);
139         //         break;
140         //     case INSTANT_TYPE:
141         //         (cast(Instant) object).writeExternal(_out);
142         //         break;
143         //     case LOCAL_DATE_TYPE:
144         //         (cast(LocalDate) object).writeExternal(_out);
145         //         break;
146         //     case LOCAL_DATE_TIME_TYPE:
147         //         (cast(LocalDateTime) object).writeExternal(_out);
148         //         break;
149         //     case LOCAL_TIME_TYPE:
150         //         (cast(LocalTime) object).writeExternal(_out);
151         //         break;
152         //     case ZONE_REGION_TYPE:
153         //         (cast(ZoneRegion) object).writeExternal(_out);
154         //         break;
155         //     case ZONE_OFFSET_TYPE:
156         //         (cast(ZoneOffset) object).writeExternal(_out);
157         //         break;
158         //     case ZONE_DATE_TIME_TYPE:
159         //         (cast(ZonedDateTime) object).writeExternal(_out);
160         //         break;
161         //     case OFFSET_TIME_TYPE:
162         //         (cast(OffsetTime) object).writeExternal(_out);
163         //         break;
164         //     case OFFSET_DATE_TIME_TYPE:
165         //         (cast(OffsetDateTime) object).writeExternal(_out);
166         //         break;
167         //     case YEAR_TYPE:
168         //         (cast(Year) object).writeExternal(_out);
169         //         break;
170         //     case YEAR_MONTH_TYPE:
171         //         (cast(YearMonth) object).writeExternal(_out);
172         //         break;
173         //     case MONTH_DAY_TYPE:
174         //         (cast(MonthDay) object).writeExternal(_out);
175         //         break;
176         //     case PERIOD_TYPE:
177         //         (cast(Period) object).writeExternal(_out);
178         //         break;
179         //     default:
180         //         throw new InvalidClassException("Unknown serialized type");
181         // }
182     }
183 
184     //-----------------------------------------------------------------------
185     /**
186      * Implements the {@code Externalizable} interface to read the object.
187      * @serialData
188      *
189      * The streamed type and parameters defined by the type's {@code writeReplace}
190      * method are read and passed to the corresponding static factory for the type
191      * to create a new instance.  That instance is returned as the de-serialized
192      * {@code Ser} object.
193      *
194      * !(ul)
195      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.Duration">Duration</a> - {@code Duration.ofSeconds(seconds, nanos);}
196      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.Instant">Instant</a> - {@code Instant.ofEpochSecond(seconds, nanos);}
197      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.LocalDate">LocalDate</a> - {@code LocalDate.of(year, month, day);}
198      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.LocalDateTime">LocalDateTime</a> - {@code LocalDateTime.of(date, time);}
199      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.LocalTime">LocalTime</a> - {@code LocalTime.of(hour, minute, second, nano);}
200      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.MonthDay">MonthDay</a> - {@code MonthDay.of(month, day);}
201      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.OffsetTime">OffsetTime</a> - {@code OffsetTime.of(time, offset);}
202      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.OffsetDateTime">OffsetDateTime</a> - {@code OffsetDateTime.of(dateTime, offset);}
203      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.Period">Period</a> - {@code Period.of(years, months, days);}
204      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.Year">Year</a> - {@code Year.of(year);}
205      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.YearMonth">YearMonth</a> - {@code YearMonth.of(year, month);}
206      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.ZonedDateTime">ZonedDateTime</a> - {@code ZonedDateTime.ofLenient(dateTime, offset, zone);}
207      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.ZoneId">ZoneId</a> - {@code ZoneId.of(id);}
208      * !(li)<a href="{@docRoot}/serialized-form.html#hunt.time.ZoneOffset">ZoneOffset</a> - {@code (offsetByte == 127 ? ZoneOffset.ofTotalSeconds(_in.readInt()) : ZoneOffset.ofTotalSeconds(offsetByte * 900));}
209      * </ul>
210      *
211      * @param _in  the data to read, not null
212      */
213     public void readExternal(ObjectInput _in) /*throws IOException, ClassNotFoundException*/ {
214         type = _in.readByte();
215         object = readInternal(type, _in);
216     }
217 
218     static Object read(ObjectInput _in) /*throws IOException, ClassNotFoundException*/{
219         byte type = _in.readByte();
220         return readInternal(type, _in);
221     }
222 
223     private static Object readInternal(byte type, ObjectInput _in) /*throws IOException, ClassNotFoundException*/ {
224         // switch (type) {
225         //     case DURATION_TYPE: return Duration.readExternal(_in);
226         //     case INSTANT_TYPE: return Instant.readExternal(_in);
227         //     case LOCAL_DATE_TYPE: return LocalDate.readExternal(_in);
228         //     case LOCAL_DATE_TIME_TYPE: return LocalDateTime.readExternal(_in);
229         //     case LOCAL_TIME_TYPE: return LocalTime.readExternal(_in);
230         //     case ZONE_DATE_TIME_TYPE: return ZonedDateTime.readExternal(_in);
231         //     case ZONE_OFFSET_TYPE: return ZoneOffset.readExternal(_in);
232         //     case ZONE_REGION_TYPE: return ZoneRegion.readExternal(_in);
233         //     case OFFSET_TIME_TYPE: return OffsetTime.readExternal(_in);
234         //     case OFFSET_DATE_TIME_TYPE: return OffsetDateTime.readExternal(_in);
235         //     case YEAR_TYPE: return Year.readExternal(_in);
236         //     case YEAR_MONTH_TYPE: return YearMonth.readExternal(_in);
237         //     case MONTH_DAY_TYPE: return MonthDay.readExternal(_in);
238         //     case PERIOD_TYPE: return Period.readExternal(_in);
239         //     default:
240         //         throw new Exception("Unknown serialized type");
241         // }
242         return null;
243     }
244 
245     /**
246      * Returns the object that will replace this one.
247      *
248      * @return the read object, should never be null
249      */
250     private Object readResolve() {
251          return object;
252     }
253 
254 }