1 /*
2  * Copyright (c) 2012, 2015, 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  * Copyright (c) 2012, Stephen Colebourne & Michael Nascimento Santos
28  *
29  * All rights reserved.
30  *
31  * Redistribution and use in source and binary forms, with or without
32  * modification, are permitted provided that the following conditions are met:
33  *
34  *  * Redistributions of source code must retain the above copyright notice,
35  *    this list of conditions and the following disclaimer.
36  *
37  *  * Redistributions in binary form must reproduce the above copyright notice,
38  *    this list of conditions and the following disclaimer in the documentation
39  *    and/or other materials provided with the distribution.
40  *
41  *  * Neither the name of JSR-310 nor the names of its contributors
42  *    may be used to endorse or promote products derived from this software
43  *    without specific prior written permission.
44  *
45  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
46  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
47  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
48  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
49  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
50  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
51  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
52  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
53  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
54  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
55  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
56  */
57 package java.time.chrono;
58 
59 import static java.time.temporal.ChronoField.DAY_OF_MONTH;
60 import static java.time.temporal.ChronoField.DAY_OF_YEAR;
61 import static java.time.temporal.ChronoField.ERA;
62 import static java.time.temporal.ChronoField.MONTH_OF_YEAR;
63 import static java.time.temporal.ChronoField.YEAR;
64 import static java.time.temporal.ChronoField.YEAR_OF_ERA;
65 import static java.time.temporal.ChronoUnit.DAYS;
66 import static java.time.temporal.ChronoUnit.MONTHS;
67 
68 import java.io.InvalidObjectException;
69 import java.io.ObjectInputStream;
70 import java.io.Serializable;
71 import java.time.Clock;
72 import java.time.DateTimeException;
73 import java.time.Instant;
74 import java.time.LocalDate;
75 import java.time.Year;
76 import java.time.ZoneId;
77 import java.time.format.ResolverStyle;
78 import java.time.temporal.ChronoField;
79 import java.time.temporal.TemporalAccessor;
80 import java.time.temporal.TemporalAdjusters;
81 import java.time.temporal.TemporalField;
82 import java.time.temporal.UnsupportedTemporalTypeException;
83 import java.time.temporal.ValueRange;
84 import java.util.Arrays;
85 import java.util.Calendar;
86 import java.util.List;
87 import java.util.Locale;
88 import java.util.Map;
89 import java.util.TimeZone;
90 
91 import sun.util.calendar.CalendarSystem;
92 import sun.util.calendar.LocalGregorianCalendar;
93 
94 /**
95  * The Japanese Imperial calendar system.
96  * <p>
97  * This chronology defines the rules of the Japanese Imperial calendar system.
98  * This calendar system is primarily used in Japan.
99  * The Japanese Imperial calendar system is the same as the ISO calendar system
100  * apart from the era-based year numbering.
101  * <p>
102  * Japan introduced the Gregorian calendar starting with Meiji 6.
103  * Only Meiji and later eras are supported;
104  * dates before Meiji 6, January 1 are not supported.
105  * <p>
106  * The supported {@code ChronoField} instances are:
107  * <ul>
108  * <li>{@code DAY_OF_WEEK}
109  * <li>{@code DAY_OF_MONTH}
110  * <li>{@code DAY_OF_YEAR}
111  * <li>{@code EPOCH_DAY}
112  * <li>{@code MONTH_OF_YEAR}
113  * <li>{@code PROLEPTIC_MONTH}
114  * <li>{@code YEAR_OF_ERA}
115  * <li>{@code YEAR}
116  * <li>{@code ERA}
117  * </ul>
118  *
119  * @implSpec
120  * This class is immutable and thread-safe.
121  *
122  * @since 1.8
123  */
124 public final class JapaneseChronology extends AbstractChronology implements Serializable {
125 
126     static final LocalGregorianCalendar JCAL =
127         (LocalGregorianCalendar) CalendarSystem.forName("japanese");
128 
129     // Android-changed: don't use locale to create japanese imperial calendar, as it's not generally
130     // supported on Android. Use Calendar.getJapaneseImperialInstance() instead. See .createCalendar
131     // Locale for creating a JapaneseImpericalCalendar.
132     private static final Locale LOCALE = Locale.forLanguageTag("ja-JP-u-ca-japanese");
133 
createCalendar()134     static Calendar createCalendar() {
135         return Calendar.getJapaneseImperialInstance(TimeZone.getDefault(), LOCALE);
136     }
137 
138     /**
139      * Singleton instance for Japanese chronology.
140      */
141     public static final JapaneseChronology INSTANCE = new JapaneseChronology();
142 
143     /**
144      * Serialization version.
145      */
146     private static final long serialVersionUID = 459996390165777884L;
147 
148     //-----------------------------------------------------------------------
149     /**
150      * Restricted constructor.
151      */
JapaneseChronology()152     private JapaneseChronology() {
153     }
154 
155     //-----------------------------------------------------------------------
156     /**
157      * Gets the ID of the chronology - 'Japanese'.
158      * <p>
159      * The ID uniquely identifies the {@code Chronology}.
160      * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
161      *
162      * @return the chronology ID - 'Japanese'
163      * @see #getCalendarType()
164      */
165     @Override
getId()166     public String getId() {
167         return "Japanese";
168     }
169 
170     /**
171      * Gets the calendar type of the underlying calendar system - 'japanese'.
172      * <p>
173      * The calendar type is an identifier defined by the
174      * <em>Unicode Locale Data Markup Language (LDML)</em> specification.
175      * It can be used to lookup the {@code Chronology} using {@link Chronology#of(String)}.
176      * It can also be used as part of a locale, accessible via
177      * {@link Locale#getUnicodeLocaleType(String)} with the key 'ca'.
178      *
179      * @return the calendar system type - 'japanese'
180      * @see #getId()
181      */
182     @Override
getCalendarType()183     public String getCalendarType() {
184         return "japanese";
185     }
186 
187     //-----------------------------------------------------------------------
188     /**
189      * Obtains a local date in Japanese calendar system from the
190      * era, year-of-era, month-of-year and day-of-month fields.
191      * <p>
192      * The Japanese month and day-of-month are the same as those in the
193      * ISO calendar system. They are not reset when the era changes.
194      * For example:
195      * <pre>
196      *  6th Jan Showa 64 = ISO 1989-01-06
197      *  7th Jan Showa 64 = ISO 1989-01-07
198      *  8th Jan Heisei 1 = ISO 1989-01-08
199      *  9th Jan Heisei 1 = ISO 1989-01-09
200      * </pre>
201      *
202      * @param era  the Japanese era, not null
203      * @param yearOfEra  the year-of-era
204      * @param month  the month-of-year
205      * @param dayOfMonth  the day-of-month
206      * @return the Japanese local date, not null
207      * @throws DateTimeException if unable to create the date
208      * @throws ClassCastException if the {@code era} is not a {@code JapaneseEra}
209      */
210     @Override
date(Era era, int yearOfEra, int month, int dayOfMonth)211     public JapaneseDate date(Era era, int yearOfEra, int month, int dayOfMonth) {
212         if (era instanceof JapaneseEra == false) {
213             throw new ClassCastException("Era must be JapaneseEra");
214         }
215         return JapaneseDate.of((JapaneseEra) era, yearOfEra, month, dayOfMonth);
216     }
217 
218     /**
219      * Obtains a local date in Japanese calendar system from the
220      * proleptic-year, month-of-year and day-of-month fields.
221      * <p>
222      * The Japanese proleptic year, month and day-of-month are the same as those
223      * in the ISO calendar system. They are not reset when the era changes.
224      *
225      * @param prolepticYear  the proleptic-year
226      * @param month  the month-of-year
227      * @param dayOfMonth  the day-of-month
228      * @return the Japanese local date, not null
229      * @throws DateTimeException if unable to create the date
230      */
231     @Override
date(int prolepticYear, int month, int dayOfMonth)232     public JapaneseDate date(int prolepticYear, int month, int dayOfMonth) {
233         return new JapaneseDate(LocalDate.of(prolepticYear, month, dayOfMonth));
234     }
235 
236     /**
237      * Obtains a local date in Japanese calendar system from the
238      * era, year-of-era and day-of-year fields.
239      * <p>
240      * The day-of-year in this factory is expressed relative to the start of the year-of-era.
241      * This definition changes the normal meaning of day-of-year only in those years
242      * where the year-of-era is reset to one due to a change in the era.
243      * For example:
244      * <pre>
245      *  6th Jan Showa 64 = day-of-year 6
246      *  7th Jan Showa 64 = day-of-year 7
247      *  8th Jan Heisei 1 = day-of-year 1
248      *  9th Jan Heisei 1 = day-of-year 2
249      * </pre>
250      *
251      * @param era  the Japanese era, not null
252      * @param yearOfEra  the year-of-era
253      * @param dayOfYear  the day-of-year
254      * @return the Japanese local date, not null
255      * @throws DateTimeException if unable to create the date
256      * @throws ClassCastException if the {@code era} is not a {@code JapaneseEra}
257      */
258     @Override
dateYearDay(Era era, int yearOfEra, int dayOfYear)259     public JapaneseDate dateYearDay(Era era, int yearOfEra, int dayOfYear) {
260         return JapaneseDate.ofYearDay((JapaneseEra) era, yearOfEra, dayOfYear);
261     }
262 
263     /**
264      * Obtains a local date in Japanese calendar system from the
265      * proleptic-year and day-of-year fields.
266      * <p>
267      * The day-of-year in this factory is expressed relative to the start of the proleptic year.
268      * The Japanese proleptic year and day-of-year are the same as those in the ISO calendar system.
269      * They are not reset when the era changes.
270      *
271      * @param prolepticYear  the proleptic-year
272      * @param dayOfYear  the day-of-year
273      * @return the Japanese local date, not null
274      * @throws DateTimeException if unable to create the date
275      */
276     @Override
dateYearDay(int prolepticYear, int dayOfYear)277     public JapaneseDate dateYearDay(int prolepticYear, int dayOfYear) {
278         return new JapaneseDate(LocalDate.ofYearDay(prolepticYear, dayOfYear));
279     }
280 
281     /**
282      * Obtains a local date in the Japanese calendar system from the epoch-day.
283      *
284      * @param epochDay  the epoch day
285      * @return the Japanese local date, not null
286      * @throws DateTimeException if unable to create the date
287      */
288     @Override  // override with covariant return type
dateEpochDay(long epochDay)289     public JapaneseDate dateEpochDay(long epochDay) {
290         return new JapaneseDate(LocalDate.ofEpochDay(epochDay));
291     }
292 
293     @Override
dateNow()294     public JapaneseDate dateNow() {
295         return dateNow(Clock.systemDefaultZone());
296     }
297 
298     @Override
dateNow(ZoneId zone)299     public JapaneseDate dateNow(ZoneId zone) {
300         return dateNow(Clock.system(zone));
301     }
302 
303     @Override
dateNow(Clock clock)304     public JapaneseDate dateNow(Clock clock) {
305         return date(LocalDate.now(clock));
306     }
307 
308     @Override
date(TemporalAccessor temporal)309     public JapaneseDate date(TemporalAccessor temporal) {
310         if (temporal instanceof JapaneseDate) {
311             return (JapaneseDate) temporal;
312         }
313         return new JapaneseDate(LocalDate.from(temporal));
314     }
315 
316     @Override
317     @SuppressWarnings("unchecked")
localDateTime(TemporalAccessor temporal)318     public ChronoLocalDateTime<JapaneseDate> localDateTime(TemporalAccessor temporal) {
319         return (ChronoLocalDateTime<JapaneseDate>)super.localDateTime(temporal);
320     }
321 
322     @Override
323     @SuppressWarnings("unchecked")
zonedDateTime(TemporalAccessor temporal)324     public ChronoZonedDateTime<JapaneseDate> zonedDateTime(TemporalAccessor temporal) {
325         return (ChronoZonedDateTime<JapaneseDate>)super.zonedDateTime(temporal);
326     }
327 
328     @Override
329     @SuppressWarnings("unchecked")
zonedDateTime(Instant instant, ZoneId zone)330     public ChronoZonedDateTime<JapaneseDate> zonedDateTime(Instant instant, ZoneId zone) {
331         return (ChronoZonedDateTime<JapaneseDate>)super.zonedDateTime(instant, zone);
332     }
333 
334     //-----------------------------------------------------------------------
335     /**
336      * Checks if the specified year is a leap year.
337      * <p>
338      * Japanese calendar leap years occur exactly in line with ISO leap years.
339      * This method does not validate the year passed in, and only has a
340      * well-defined result for years in the supported range.
341      *
342      * @param prolepticYear  the proleptic-year to check, not validated for range
343      * @return true if the year is a leap year
344      */
345     @Override
isLeapYear(long prolepticYear)346     public boolean isLeapYear(long prolepticYear) {
347         return IsoChronology.INSTANCE.isLeapYear(prolepticYear);
348     }
349 
350     @Override
prolepticYear(Era era, int yearOfEra)351     public int prolepticYear(Era era, int yearOfEra) {
352         if (era instanceof JapaneseEra == false) {
353             throw new ClassCastException("Era must be JapaneseEra");
354         }
355 
356         JapaneseEra jera = (JapaneseEra) era;
357         int gregorianYear = jera.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
358         if (yearOfEra == 1) {
359             return gregorianYear;
360         }
361         if (gregorianYear >= Year.MIN_VALUE && gregorianYear <= Year.MAX_VALUE) {
362             LocalGregorianCalendar.Date jdate = JCAL.newCalendarDate(null);
363             jdate.setEra(jera.getPrivateEra()).setDate(yearOfEra, 1, 1);
364             if (JapaneseChronology.JCAL.validate(jdate)) {
365                 return gregorianYear;
366             }
367         }
368         throw new DateTimeException("Invalid yearOfEra value");
369     }
370 
371     // Android-changed: Integrate OpenJDK support for Japanese Era Reiwa.
372     /**
373      * Returns the calendar system era object from the given numeric value.
374      *
375      * The numeric values supported by this method are the same as the
376      * numeric values supported by {@link JapaneseEra#of(int)}.
377      *
378      * @param eraValue  the era value
379      * @return the Japanese {@code Era} for the given numeric era value
380      * @throws DateTimeException if {@code eraValue} is invalid
381      */
382     @Override
eraOf(int eraValue)383     public JapaneseEra eraOf(int eraValue) {
384         return JapaneseEra.of(eraValue);
385     }
386 
387     @Override
eras()388     public List<Era> eras() {
389         return Arrays.<Era>asList(JapaneseEra.values());
390     }
391 
getCurrentEra()392     JapaneseEra getCurrentEra() {
393         // Assume that the last JapaneseEra is the current one.
394         JapaneseEra[] eras = JapaneseEra.values();
395         return eras[eras.length - 1];
396     }
397 
398     //-----------------------------------------------------------------------
399     @Override
range(ChronoField field)400     public ValueRange range(ChronoField field) {
401         switch (field) {
402             case ALIGNED_DAY_OF_WEEK_IN_MONTH:
403             case ALIGNED_DAY_OF_WEEK_IN_YEAR:
404             case ALIGNED_WEEK_OF_MONTH:
405             case ALIGNED_WEEK_OF_YEAR:
406                 throw new UnsupportedTemporalTypeException("Unsupported field: " + field);
407             case YEAR_OF_ERA: {
408                 // Android-changed: use #createCalendar() to create calendar.
409                 Calendar jcal = createCalendar();
410                 int startYear = getCurrentEra().getPrivateEra().getSinceDate().getYear();
411                 return ValueRange.of(1, jcal.getGreatestMinimum(Calendar.YEAR),
412                         jcal.getLeastMaximum(Calendar.YEAR) + 1, // +1 due to the different definitions
413                         Year.MAX_VALUE - startYear);
414             }
415             case DAY_OF_YEAR: {
416                 // Android-changed: use #createCalendar() to create calendar.
417                 Calendar jcal = createCalendar();
418                 int fieldIndex = Calendar.DAY_OF_YEAR;
419                 return ValueRange.of(jcal.getMinimum(fieldIndex), jcal.getGreatestMinimum(fieldIndex),
420                         jcal.getLeastMaximum(fieldIndex), jcal.getMaximum(fieldIndex));
421             }
422             case YEAR:
423                 return ValueRange.of(JapaneseDate.MEIJI_6_ISODATE.getYear(), Year.MAX_VALUE);
424             case ERA:
425                 return ValueRange.of(JapaneseEra.MEIJI.getValue(), getCurrentEra().getValue());
426             default:
427                 return field.range();
428         }
429     }
430 
431     //-----------------------------------------------------------------------
432     @Override  // override for return type
resolveDate(Map <TemporalField, Long> fieldValues, ResolverStyle resolverStyle)433     public JapaneseDate resolveDate(Map <TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
434         return (JapaneseDate) super.resolveDate(fieldValues, resolverStyle);
435     }
436 
437     @Override  // override for special Japanese behavior
resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle)438     ChronoLocalDate resolveYearOfEra(Map<TemporalField, Long> fieldValues, ResolverStyle resolverStyle) {
439         // validate era and year-of-era
440         Long eraLong = fieldValues.get(ERA);
441         JapaneseEra era = null;
442         if (eraLong != null) {
443             era = eraOf(range(ERA).checkValidIntValue(eraLong, ERA));  // always validated
444         }
445         Long yoeLong = fieldValues.get(YEAR_OF_ERA);
446         int yoe = 0;
447         if (yoeLong != null) {
448             yoe = range(YEAR_OF_ERA).checkValidIntValue(yoeLong, YEAR_OF_ERA);  // always validated
449         }
450         // if only year-of-era and no year then invent era unless strict
451         if (era == null && yoeLong != null && fieldValues.containsKey(YEAR) == false && resolverStyle != ResolverStyle.STRICT) {
452             era = JapaneseEra.values()[JapaneseEra.values().length - 1];
453         }
454         // if both present, then try to create date
455         if (yoeLong != null && era != null) {
456             if (fieldValues.containsKey(MONTH_OF_YEAR)) {
457                 if (fieldValues.containsKey(DAY_OF_MONTH)) {
458                     return resolveYMD(era, yoe, fieldValues, resolverStyle);
459                 }
460             }
461             if (fieldValues.containsKey(DAY_OF_YEAR)) {
462                 return resolveYD(era, yoe, fieldValues, resolverStyle);
463             }
464         }
465         return null;
466     }
467 
prolepticYearLenient(JapaneseEra era, int yearOfEra)468     private int prolepticYearLenient(JapaneseEra era, int yearOfEra) {
469         return era.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1;
470     }
471 
resolveYMD(JapaneseEra era, int yoe, Map<TemporalField,Long> fieldValues, ResolverStyle resolverStyle)472      private ChronoLocalDate resolveYMD(JapaneseEra era, int yoe, Map<TemporalField,Long> fieldValues, ResolverStyle resolverStyle) {
473          fieldValues.remove(ERA);
474          fieldValues.remove(YEAR_OF_ERA);
475          if (resolverStyle == ResolverStyle.LENIENT) {
476              int y = prolepticYearLenient(era, yoe);
477              long months = Math.subtractExact(fieldValues.remove(MONTH_OF_YEAR), 1);
478              long days = Math.subtractExact(fieldValues.remove(DAY_OF_MONTH), 1);
479              return date(y, 1, 1).plus(months, MONTHS).plus(days, DAYS);
480          }
481          int moy = range(MONTH_OF_YEAR).checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR), MONTH_OF_YEAR);
482          int dom = range(DAY_OF_MONTH).checkValidIntValue(fieldValues.remove(DAY_OF_MONTH), DAY_OF_MONTH);
483          if (resolverStyle == ResolverStyle.SMART) {  // previous valid
484              if (yoe < 1) {
485                  throw new DateTimeException("Invalid YearOfEra: " + yoe);
486              }
487              int y = prolepticYearLenient(era, yoe);
488              JapaneseDate result;
489              try {
490                  result = date(y, moy, dom);
491              } catch (DateTimeException ex) {
492                  result = date(y, moy, 1).with(TemporalAdjusters.lastDayOfMonth());
493              }
494              // handle the era being changed
495              // only allow if the new date is in the same Jan-Dec as the era change
496              // determine by ensuring either original yoe or result yoe is 1
497              if (result.getEra() != era && result.get(YEAR_OF_ERA) > 1 && yoe > 1) {
498                  throw new DateTimeException("Invalid YearOfEra for Era: " + era + " " + yoe);
499              }
500              return result;
501          }
502          return date(era, yoe, moy, dom);
503      }
504 
resolveYD(JapaneseEra era, int yoe, Map <TemporalField,Long> fieldValues, ResolverStyle resolverStyle)505     private ChronoLocalDate resolveYD(JapaneseEra era, int yoe, Map <TemporalField,Long> fieldValues, ResolverStyle resolverStyle) {
506         fieldValues.remove(ERA);
507         fieldValues.remove(YEAR_OF_ERA);
508         if (resolverStyle == ResolverStyle.LENIENT) {
509             int y = prolepticYearLenient(era, yoe);
510             long days = Math.subtractExact(fieldValues.remove(DAY_OF_YEAR), 1);
511             return dateYearDay(y, 1).plus(days, DAYS);
512         }
513         int doy = range(DAY_OF_YEAR).checkValidIntValue(fieldValues.remove(DAY_OF_YEAR), DAY_OF_YEAR);
514         return dateYearDay(era, yoe, doy);  // smart is same as strict
515     }
516 
517     //-----------------------------------------------------------------------
518     /**
519      * Writes the Chronology using a
520      * <a href="../../../serialized-form.html#java.time.chrono.Ser">dedicated serialized form</a>.
521      * @serialData
522      * <pre>
523      *  out.writeByte(1);     // identifies a Chronology
524      *  out.writeUTF(getId());
525      * </pre>
526      *
527      * @return the instance of {@code Ser}, not null
528      */
529     @Override
writeReplace()530     Object writeReplace() {
531         return super.writeReplace();
532     }
533 
534     /**
535      * Defend against malicious streams.
536      *
537      * @param s the stream to read
538      * @throws InvalidObjectException always
539      */
readObject(ObjectInputStream s)540     private void readObject(ObjectInputStream s) throws InvalidObjectException {
541         throw new InvalidObjectException("Deserialization via serialization delegate");
542     }
543 }
544