OSDN Git Service

Initial revision
[pf3gnuchains/gcc-fork.git] / libjava / classpath / java / io / ObjectInputStream.java
1 /* ObjectInputStream.java -- Class used to read serialized objects
2    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA.
21
22 Linking this library statically or dynamically with other modules is
23 making a combined work based on this library.  Thus, the terms and
24 conditions of the GNU General Public License cover the whole
25 combination.
26
27 As a special exception, the copyright holders of this library give you
28 permission to link this library with independent modules to produce an
29 executable, regardless of the license terms of these independent
30 modules, and to copy and distribute the resulting executable under
31 terms of your choice, provided that you also meet, for each linked
32 independent module, the terms and conditions of the license of that
33 module.  An independent module is a module which is not derived from
34 or based on this library.  If you modify this library, you may extend
35 this exception to your version of the library, but you are not
36 obligated to do so.  If you do not wish to do so, delete this
37 exception statement from your version. */
38
39
40 package java.io;
41
42 import gnu.classpath.Configuration;
43 import gnu.java.io.ObjectIdentityWrapper;
44
45 import java.lang.reflect.Array;
46 import java.lang.reflect.Constructor;
47 import java.lang.reflect.Field;
48 import java.lang.reflect.InvocationTargetException;
49 import java.lang.reflect.Method;
50 import java.lang.reflect.Modifier;
51 import java.lang.reflect.Proxy;
52 import java.security.AccessController;
53 import java.security.PrivilegedAction;
54 import java.util.Arrays;
55 import java.util.Hashtable;
56 import java.util.Vector;
57
58 public class ObjectInputStream extends InputStream
59   implements ObjectInput, ObjectStreamConstants
60 {
61   /**
62    * Creates a new <code>ObjectInputStream</code> that will do all of
63    * its reading from <code>in</code>.  This method also checks
64    * the stream by reading the header information (stream magic number
65    * and stream version).
66    *
67    * @exception IOException Reading stream header from underlying
68    * stream cannot be completed.
69    *
70    * @exception StreamCorruptedException An invalid stream magic
71    * number or stream version was read from the stream.
72    *
73    * @see #readStreamHeader()
74    */
75   public ObjectInputStream(InputStream in)
76     throws IOException, StreamCorruptedException
77   {
78     if (DEBUG)
79       {
80         String val = System.getProperty("gcj.dumpobjects");
81         if (dump == false && val != null && !val.equals(""))
82           {
83             dump = true;
84             System.out.println ("Serialization debugging enabled");
85           }
86         else if (dump == true && (val == null || val.equals("")))
87           {
88             dump = false;
89             System.out.println ("Serialization debugging disabled");
90           }
91       }
92
93     this.resolveEnabled = false;
94     this.isDeserializing = false;
95     this.blockDataPosition = 0;
96     this.blockDataBytes = 0;
97     this.blockData = new byte[BUFFER_SIZE];
98     this.blockDataInput = new DataInputStream(this);
99     this.realInputStream = new DataInputStream(in);
100     this.nextOID = baseWireHandle;
101     this.objectLookupTable = new Hashtable();
102     this.validators = new Vector();
103     this.classLookupTable = new Hashtable();
104     setBlockDataMode(true);
105     readStreamHeader();
106   }
107
108
109   /**
110    * Returns the next deserialized object read from the underlying stream.
111    *
112    * This method can be overriden by a class by implementing
113    * <code>private void readObject (ObjectInputStream)</code>.
114    *
115    * If an exception is thrown from this method, the stream is left in
116    * an undefined state.
117    *
118    * @exception ClassNotFoundException The class that an object being
119    * read in belongs to cannot be found.
120    *
121    * @exception IOException Exception from underlying
122    * <code>InputStream</code>.
123    */
124   public final Object readObject() throws ClassNotFoundException, IOException
125   {
126     if (this.useSubclassMethod)
127       return readObjectOverride();
128
129     boolean was_deserializing;
130
131     Object ret_val;
132     was_deserializing = this.isDeserializing;
133
134     boolean is_consumed = false;
135     boolean old_mode = setBlockDataMode(false);
136
137     this.isDeserializing = true;
138
139     byte marker = this.realInputStream.readByte();
140
141     depth += 2;
142
143     if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " ");
144
145     try
146       {
147         switch (marker)
148           {
149           case TC_ENDBLOCKDATA:
150             {
151               ret_val = null;
152               is_consumed = true;
153               break;
154             }
155
156           case TC_BLOCKDATA:
157           case TC_BLOCKDATALONG:
158             {
159               if (marker == TC_BLOCKDATALONG)
160                 { if(dump) dumpElementln("BLOCKDATALONG"); }
161               else
162                 { if(dump) dumpElementln("BLOCKDATA"); }
163               readNextBlock(marker);
164               throw new StreamCorruptedException("Unexpected blockData");
165             }
166
167           case TC_NULL:
168             {
169               if(dump) dumpElementln("NULL");
170               ret_val = null;
171               break;
172             }
173
174           case TC_REFERENCE:
175             {
176               if(dump) dumpElement("REFERENCE ");
177               Integer oid = new Integer(this.realInputStream.readInt());
178               if(dump) dumpElementln(Integer.toHexString(oid.intValue()));
179               ret_val = ((ObjectIdentityWrapper)
180                          this.objectLookupTable.get(oid)).object;
181               break;
182             }
183
184           case TC_CLASS:
185             {
186               if(dump) dumpElementln("CLASS");
187               ObjectStreamClass osc = (ObjectStreamClass)readObject();
188               Class clazz = osc.forClass();
189               assignNewHandle(clazz);
190               ret_val = clazz;
191               break;
192             }
193
194           case TC_PROXYCLASSDESC:
195             {
196               if(dump) dumpElementln("PROXYCLASS");
197               int n_intf = this.realInputStream.readInt();
198               String[] intfs = new String[n_intf];
199               for (int i = 0; i < n_intf; i++)
200                 {
201                   intfs[i] = this.realInputStream.readUTF();
202                   System.out.println(intfs[i]);
203                 }
204               
205               boolean oldmode = setBlockDataMode(true);
206               Class cl = resolveProxyClass(intfs);
207               setBlockDataMode(oldmode);
208               
209               ObjectStreamClass osc = lookupClass(cl);
210               assignNewHandle(osc);
211               
212               if (!is_consumed)
213                 {
214                   byte b = this.realInputStream.readByte();
215                   if (b != TC_ENDBLOCKDATA)
216                     throw new IOException("Data annotated to class was not consumed." + b);
217                 }
218               else
219                 is_consumed = false;
220               ObjectStreamClass superosc = (ObjectStreamClass)readObject();
221               osc.setSuperclass(superosc);
222               ret_val = osc;
223               break;
224             }
225
226           case TC_CLASSDESC:
227             {
228               ObjectStreamClass osc = readClassDescriptor();
229               
230               if (!is_consumed)
231                 {
232                   byte b = this.realInputStream.readByte();
233                   if (b != TC_ENDBLOCKDATA)
234                     throw new IOException("Data annotated to class was not consumed." + b);
235                 }
236               else
237                 is_consumed = false;
238               
239               osc.setSuperclass ((ObjectStreamClass)readObject());
240               ret_val = osc;
241               break;
242             }
243
244           case TC_STRING:
245           case TC_LONGSTRING:
246             {
247               if(dump) dumpElement("STRING=");
248               String s = this.realInputStream.readUTF();
249               if(dump) dumpElementln(s);
250               ret_val = processResolution(null, s, assignNewHandle(s));
251               break;
252             }
253
254           case TC_ARRAY:
255             {
256               if(dump) dumpElementln("ARRAY");
257               ObjectStreamClass osc = (ObjectStreamClass)readObject();
258               Class componentType = osc.forClass().getComponentType();
259               if(dump) dumpElement("ARRAY LENGTH=");
260               int length = this.realInputStream.readInt();
261               if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType);
262               Object array = Array.newInstance(componentType, length);
263               int handle = assignNewHandle(array);
264               readArrayElements(array, componentType);
265               if(dump)
266                 for (int i = 0, len = Array.getLength(array); i < len; i++)
267                   dumpElementln("  ELEMENT[" + i + "]=" + Array.get(array, i));
268               ret_val = processResolution(null, array, handle);
269               break;
270             }
271
272           case TC_OBJECT:
273             {
274               if(dump) dumpElementln("OBJECT");
275               ObjectStreamClass osc = (ObjectStreamClass)readObject();
276               Class clazz = osc.forClass();
277               
278               if (!osc.realClassIsSerializable)
279                 throw new NotSerializableException
280                   (clazz + " is not Serializable, and thus cannot be deserialized.");
281               
282               if (osc.realClassIsExternalizable)
283                 {
284                   Externalizable obj = osc.newInstance();
285                   
286                   int handle = assignNewHandle(obj);
287                   
288                   boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0);
289                   
290                   boolean oldmode = this.readDataFromBlock;
291                   if (read_from_blocks)
292                     setBlockDataMode(true);
293                   
294                   obj.readExternal(this);
295                   
296                   if (read_from_blocks)
297                     {
298                       setBlockDataMode(oldmode);
299                       if (!oldmode)
300                         if (this.realInputStream.readByte() != TC_ENDBLOCKDATA)
301                             throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method.");
302                     }
303                   
304                   ret_val = processResolution(osc, obj, handle);
305                   break;
306                 } // end if (osc.realClassIsExternalizable)
307
308               Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor);
309               
310               int handle = assignNewHandle(obj);
311               Object prevObject = this.currentObject;
312               ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
313               
314               this.currentObject = obj;
315               ObjectStreamClass[] hierarchy =
316                 inputGetObjectStreamClasses(clazz);
317               
318               for (int i = 0; i < hierarchy.length; i++)
319                 {
320                   this.currentObjectStreamClass = hierarchy[i];
321                   
322                   if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ());
323
324                   // XXX: should initialize fields in classes in the hierarchy
325                   // that aren't in the stream
326                   // should skip over classes in the stream that aren't in the
327                   // real classes hierarchy
328                   
329                   Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod;
330                   if (readObjectMethod != null)
331                     {
332                       fieldsAlreadyRead = false;
333                       boolean oldmode = setBlockDataMode(true);
334                       callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj);
335                       setBlockDataMode(oldmode);
336                     }
337                   else
338                     {
339                       readFields(obj, currentObjectStreamClass);
340                     }
341
342                   if (this.currentObjectStreamClass.hasWriteMethod())
343                     {
344                       if(dump) dumpElement("ENDBLOCKDATA? ");
345                       try
346                         {
347                           // FIXME: XXX: This try block is to
348                           // catch EOF which is thrown for some
349                           // objects.  That indicates a bug in
350                           // the logic.
351
352                           if (this.realInputStream.readByte() != TC_ENDBLOCKDATA)
353                             throw new IOException
354                               ("No end of block data seen for class with readObject (ObjectInputStream) method.");
355                           if(dump) dumpElementln("yes");
356                         }
357 //                    catch (EOFException e)
358 //                      {
359 //                        if(dump) dumpElementln("no, got EOFException");
360 //                      }
361                       catch (IOException e)
362                         {
363                           if(dump) dumpElementln("no, got IOException");
364                         }
365                     }
366                 }
367
368               this.currentObject = prevObject;
369               this.currentObjectStreamClass = prevObjectStreamClass;
370               ret_val = processResolution(osc, obj, handle);
371                   
372               break;
373             }
374
375           case TC_RESET:
376             if(dump) dumpElementln("RESET");
377             clearHandles();
378             ret_val = readObject();
379             break;
380
381           case TC_EXCEPTION:
382             {
383               if(dump) dumpElement("EXCEPTION=");
384               Exception e = (Exception)readObject();
385               if(dump) dumpElementln(e.toString());
386               clearHandles();
387               throw new WriteAbortedException("Exception thrown during writing of stream", e);
388             }
389
390           default:
391             throw new IOException("Unknown marker on stream: " + marker);
392           }
393       }
394     finally
395       {
396         setBlockDataMode(old_mode);
397         
398         this.isDeserializing = was_deserializing;
399         
400         depth -= 2;
401         
402         if (! was_deserializing)
403           {
404             if (validators.size() > 0)
405               invokeValidators();
406           }
407       }
408     
409     return ret_val;
410   }
411
412   /**
413    * This method makes a partial check of types for the fields
414    * contained given in arguments. It checks primitive types of
415    * fields1 against non primitive types of fields2. This method 
416    * assumes the two lists has already been sorted according to 
417    * the Java specification.
418    *
419    * @param name Name of the class owning the given fields.
420    * @param fields1 First list to check.
421    * @param fields2 Second list to check.
422    * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present
423    * in the non primitive part in fields2.
424    */
425   private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2)
426     throws InvalidClassException
427   {
428     int nonPrimitive = 0;
429     
430     for (nonPrimitive = 0; 
431          nonPrimitive < fields1.length
432            && fields1[nonPrimitive].isPrimitive(); nonPrimitive++)
433       {
434       }
435
436     if (nonPrimitive == fields1.length)
437       return;
438     
439     int i = 0;
440     ObjectStreamField f1;
441     ObjectStreamField f2;
442     
443     while (i < fields2.length
444            && nonPrimitive < fields1.length)
445       {
446         f1 = fields1[nonPrimitive];
447         f2 = fields2[i];
448         
449         if (!f2.isPrimitive())
450           break;
451
452         int compVal = f1.getName().compareTo (f2.getName());
453
454         if (compVal < 0)
455           {
456             nonPrimitive++;
457           }
458         else if (compVal > 0)
459           {
460             i++;
461           }
462         else
463           {
464             throw new InvalidClassException
465               ("invalid field type for " + f2.getName() +
466                " in class " + name);
467           }
468       }
469   }
470
471   /**
472    * This method reads a class descriptor from the real input stream
473    * and use these data to create a new instance of ObjectStreamClass.
474    * Fields are sorted and ordered for the real read which occurs for
475    * each instance of the described class. Be aware that if you call that
476    * method you must ensure that the stream is synchronized, in the other
477    * case it may be completely desynchronized.
478    *
479    * @return A new instance of ObjectStreamClass containing the freshly
480    * created descriptor.
481    * @throws ClassNotFoundException if the required class to build the
482    * descriptor has not been found in the system.
483    * @throws IOException An input/output error occured.
484    * @throws InvalidClassException If there was a compatibility problem
485    * between the class present in the system and the serialized class.
486    */
487   protected ObjectStreamClass readClassDescriptor()
488     throws ClassNotFoundException, IOException
489   {
490     if(dump) dumpElement("CLASSDESC NAME=");
491     String name = this.realInputStream.readUTF();
492     if(dump) dumpElement(name + "; UID=");
493     long uid = this.realInputStream.readLong ();
494     if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS=");
495     byte flags = this.realInputStream.readByte ();
496     if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT=");
497     short field_count = this.realInputStream.readShort();
498     if(dump) dumpElementln(Short.toString(field_count));
499     ObjectStreamField[] fields = new ObjectStreamField[field_count];
500     ObjectStreamClass osc = new ObjectStreamClass(name, uid,
501                                                   flags, fields);
502     assignNewHandle(osc);
503
504     if (callersClassLoader == null)
505       callersClassLoader = currentLoader();
506               
507     for (int i = 0; i < field_count; i++)
508       {
509         if(dump) dumpElement("  TYPE CODE=");
510         char type_code = (char)this.realInputStream.readByte();
511         if(dump) dumpElement(type_code + "; FIELD NAME=");
512         String field_name = this.realInputStream.readUTF();
513         if(dump) dumpElementln(field_name);
514         String class_name;
515                   
516         // If the type code is an array or an object we must
517         // decode a String here. In the other case we convert
518         // the type code and pass it to ObjectStreamField.
519         // Type codes are decoded by gnu.java.lang.reflect.TypeSignature.
520         if (type_code == 'L' || type_code == '[')
521           class_name = (String)readObject();
522         else
523           class_name = String.valueOf(type_code);
524                   
525         fields[i] =
526           new ObjectStreamField(field_name, class_name, callersClassLoader);
527       }
528               
529     /* Now that fields have been read we may resolve the class
530      * (and read annotation if needed). */
531     Class clazz;
532     try
533       {
534         clazz = resolveClass(osc);
535       }
536     catch (ClassNotFoundException cnfe)
537       {
538         // Maybe it was an primitive class?
539         if (name.equals("void"))
540           clazz = Void.TYPE;
541         else if (name.equals("boolean"))
542           clazz = Boolean.TYPE;
543         else if (name.equals("byte"))
544           clazz = Byte.TYPE;
545         else if (name.equals("short"))
546           clazz = Short.TYPE;
547         else if (name.equals("char"))
548           clazz = Character.TYPE;
549         else if (name.equals("int"))
550           clazz = Integer.TYPE;
551         else if (name.equals("long"))
552           clazz = Long.TYPE;
553         else if (name.equals("float"))
554           clazz = Float.TYPE;
555         else if (name.equals("double"))
556           clazz = Double.TYPE;
557         else
558           throw cnfe;
559       }
560
561     boolean oldmode = setBlockDataMode(true);
562     osc.setClass(clazz, lookupClass(clazz.getSuperclass()));
563     classLookupTable.put(clazz, osc);
564     setBlockDataMode(oldmode);
565
566     // find the first non-serializable, non-abstract
567     // class in clazz's inheritance hierarchy
568     Class first_nonserial = clazz.getSuperclass();
569     // Maybe it is a primitive class, those don't have a super class,
570     // or Object itself.  Otherwise we can keep getting the superclass
571     // till we hit the Object class, or some other non-serializable class.
572
573     if (first_nonserial == null)
574       first_nonserial = clazz;
575     else
576       while (Serializable.class.isAssignableFrom(first_nonserial)
577              || Modifier.isAbstract(first_nonserial.getModifiers()))
578         first_nonserial = first_nonserial.getSuperclass();
579
580     final Class local_constructor_class = first_nonserial;
581
582     osc.firstNonSerializableParentConstructor =
583         (Constructor)AccessController.doPrivileged(new PrivilegedAction()
584           {
585             public Object run()
586             {
587               try
588                 {
589                   Constructor c = local_constructor_class.
590                                     getDeclaredConstructor(new Class[0]);
591                   if (Modifier.isPrivate(c.getModifiers()))
592                     return null;
593                   return c;
594                 }
595               catch (NoSuchMethodException e)
596                 {
597                   // error will be reported later, in newObject()
598                   return null;
599                 }
600             }
601           });
602
603     osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz);
604     osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz);
605
606     ObjectStreamField[] stream_fields = osc.fields;
607     ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields;
608     ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)];
609
610     int stream_idx = 0;
611     int real_idx = 0;
612     int map_idx = 0;
613
614     /*
615      * Check that there is no type inconsistencies between the lists.
616      * A special checking must be done for the two groups: primitive types and
617      * not primitive types. 
618      */
619     checkTypeConsistency(name, real_fields, stream_fields);
620     checkTypeConsistency(name, stream_fields, real_fields);
621
622     
623     while (stream_idx < stream_fields.length
624            || real_idx < real_fields.length)
625       {
626         ObjectStreamField stream_field = null;
627         ObjectStreamField real_field = null;
628
629         if (stream_idx == stream_fields.length)
630           {
631             real_field = real_fields[real_idx++];
632           }
633         else if (real_idx == real_fields.length)
634           {
635             stream_field = stream_fields[stream_idx++];
636           }
637         else
638           {
639             int comp_val =
640               real_fields[real_idx].compareTo (stream_fields[stream_idx]);
641
642             if (comp_val < 0)
643               {
644                 real_field = real_fields[real_idx++];
645               }
646             else if (comp_val > 0)
647               {
648                 stream_field = stream_fields[stream_idx++];
649               }
650             else
651               {
652                 stream_field = stream_fields[stream_idx++];
653                 real_field = real_fields[real_idx++];
654                 if (stream_field.getType() != real_field.getType())
655                   throw new InvalidClassException
656                     ("invalid field type for " + real_field.getName() +
657                      " in class " + name);
658               }
659           }
660
661         /* If some of stream_fields does not correspond to any of real_fields,
662          * or the opposite, then fieldmapping will go short.
663          */
664         if (map_idx == fieldmapping.length)
665           {
666             ObjectStreamField[] newfieldmapping =
667               new ObjectStreamField[fieldmapping.length + 2];
668             System.arraycopy(fieldmapping, 0,
669                              newfieldmapping, 0, fieldmapping.length);
670             fieldmapping = newfieldmapping;
671           }
672         fieldmapping[map_idx++] = stream_field;
673         fieldmapping[map_idx++] = real_field;
674       }
675     osc.fieldMapping = fieldmapping;
676
677     return osc;
678   }
679
680   /**
681    * Reads the current objects non-transient, non-static fields from
682    * the current class from the underlying output stream.
683    *
684    * This method is intended to be called from within a object's
685    * <code>private void readObject (ObjectInputStream)</code>
686    * method.
687    *
688    * @exception ClassNotFoundException The class that an object being
689    * read in belongs to cannot be found.
690    *
691    * @exception NotActiveException This method was called from a
692    * context other than from the current object's and current class's
693    * <code>private void readObject (ObjectInputStream)</code>
694    * method.
695    *
696    * @exception IOException Exception from underlying
697    * <code>OutputStream</code>.
698    */
699   public void defaultReadObject()
700     throws ClassNotFoundException, IOException, NotActiveException
701   {
702     if (this.currentObject == null || this.currentObjectStreamClass == null)
703       throw new NotActiveException("defaultReadObject called by non-active"
704                                    + " class and/or object");
705
706     if (fieldsAlreadyRead)
707       throw new NotActiveException("defaultReadObject called but fields "
708                                    + "already read from stream (by "
709                                    + "defaultReadObject or readFields)");
710
711     boolean oldmode = setBlockDataMode(false);
712     readFields(this.currentObject, this.currentObjectStreamClass);
713     setBlockDataMode(oldmode);
714
715     fieldsAlreadyRead = true;
716   }
717
718
719   /**
720    * Registers a <code>ObjectInputValidation</code> to be carried out
721    * on the object graph currently being deserialized before it is
722    * returned to the original caller of <code>readObject ()</code>.
723    * The order of validation for multiple
724    * <code>ObjectInputValidation</code>s can be controled using
725    * <code>priority</code>.  Validators with higher priorities are
726    * called first.
727    *
728    * @see java.io.ObjectInputValidation
729    *
730    * @exception InvalidObjectException <code>validator</code> is
731    * <code>null</code>
732    *
733    * @exception NotActiveException an attempt was made to add a
734    * validator outside of the <code>readObject</code> method of the
735    * object currently being deserialized
736    */
737   public void registerValidation(ObjectInputValidation validator,
738                                  int priority)
739     throws InvalidObjectException, NotActiveException
740   {
741     if (this.currentObject == null || this.currentObjectStreamClass == null)
742       throw new NotActiveException("registerValidation called by non-active "
743                                    + "class and/or object");
744
745     if (validator == null)
746       throw new InvalidObjectException("attempt to add a null "
747                                        + "ObjectInputValidation object");
748
749     this.validators.addElement(new ValidatorAndPriority (validator,
750                                                          priority));
751   }
752
753
754   /**
755    * Called when a class is being deserialized.  This is a hook to
756    * allow subclasses to read in information written by the
757    * <code>annotateClass (Class)</code> method of an
758    * <code>ObjectOutputStream</code>.
759    *
760    * This implementation looks up the active call stack for a
761    * <code>ClassLoader</code>; if a <code>ClassLoader</code> is found,
762    * it is used to load the class associated with <code>osc</code>,
763    * otherwise, the default system <code>ClassLoader</code> is used.
764    *
765    * @exception IOException Exception from underlying
766    * <code>OutputStream</code>.
767    *
768    * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class)
769    */
770   protected Class resolveClass(ObjectStreamClass osc)
771     throws ClassNotFoundException, IOException
772   {
773     if (callersClassLoader == null)
774       {
775         callersClassLoader = currentLoader ();
776         if (DEBUG && dump)
777           {
778             dumpElementln ("CallersClassLoader = " + callersClassLoader);
779           }
780       }
781
782     return Class.forName(osc.getName(), true, callersClassLoader);
783   }
784
785   /**
786    * Returns the most recent user defined ClassLoader on the execution stack
787    * or null if none is found.
788    */
789   private ClassLoader currentLoader()
790   {
791     return VMObjectInputStream.currentClassLoader();
792   }
793
794   /**
795    * Lookup a class stored in the local hashtable. If it is not
796    * use the global lookup function in ObjectStreamClass to build
797    * the ObjectStreamClass. This method is requested according to
798    * the behaviour detected in the JDK by Kaffe's team.
799    *
800    * @param clazz Class to lookup in the hash table or for which
801    * we must build a descriptor.
802    * @return A valid instance of ObjectStreamClass corresponding
803    * to the specified class.
804    */
805   private ObjectStreamClass lookupClass(Class clazz)
806   {
807     if (clazz == null)
808       return null;
809
810     ObjectStreamClass oclazz;
811     oclazz = (ObjectStreamClass)classLookupTable.get(clazz);
812     if (oclazz == null)
813       return ObjectStreamClass.lookup(clazz);
814     else
815       return oclazz;
816   }
817
818   /**
819    * Reconstruct class hierarchy the same way
820    * {@link java.io.ObjectStreamClass.getObjectStreamClasses(java.lang.Class)} does
821    * but using lookupClass instead of ObjectStreamClass.lookup. This
822    * dup is necessary localize the lookup table. Hopefully some future
823    * rewritings will be able to prevent this.
824    *
825    * @param clazz This is the class for which we want the hierarchy.
826    *
827    * @return An array of valid {@link java.io.ObjectStreamClass} instances which
828    * represent the class hierarchy for clazz.
829    */
830   private ObjectStreamClass[] inputGetObjectStreamClasses(Class clazz)
831   {
832     ObjectStreamClass osc = lookupClass(clazz);
833
834     if (osc == null)
835       return new ObjectStreamClass[0];
836     else
837       {
838         Vector oscs = new Vector();
839
840         while (osc != null)
841           {
842             oscs.addElement(osc);
843             osc = osc.getSuper();
844           }
845
846         int count = oscs.size();
847         ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[count];
848
849         for (int i = count - 1; i >= 0; i--)
850           sorted_oscs[count - i - 1] = (ObjectStreamClass) oscs.elementAt(i);
851
852         return sorted_oscs;
853       }
854   }
855
856   /**
857    * Allows subclasses to resolve objects that are read from the
858    * stream with other objects to be returned in their place.  This
859    * method is called the first time each object is encountered.
860    *
861    * This method must be enabled before it will be called in the
862    * serialization process.
863    *
864    * @exception IOException Exception from underlying
865    * <code>OutputStream</code>.
866    *
867    * @see #enableResolveObject(boolean)
868    */
869   protected Object resolveObject(Object obj) throws IOException
870   {
871     return obj;
872   }
873
874
875   protected Class resolveProxyClass(String[] intfs)
876     throws IOException, ClassNotFoundException
877   {
878     ClassLoader cl = currentLoader();
879     
880     Class[] clss = new Class[intfs.length];
881     if(cl == null)
882       {
883         for (int i = 0; i < intfs.length; i++)
884           clss[i] = Class.forName(intfs[i]);
885         cl = ClassLoader.getSystemClassLoader();
886       }
887     else
888       for (int i = 0; i < intfs.length; i++)
889         clss[i] = cl.loadClass(intfs[i]);
890     try 
891       {
892         return Proxy.getProxyClass(cl, clss);
893       } 
894     catch (IllegalArgumentException e) 
895       {
896         throw new ClassNotFoundException(null, e);
897       }
898   }
899   
900   /**
901    * If <code>enable</code> is <code>true</code> and this object is
902    * trusted, then <code>resolveObject (Object)</code> will be called
903    * in subsequent calls to <code>readObject (Object)</code>.
904    * Otherwise, <code>resolveObject (Object)</code> will not be called.
905    *
906    * @exception SecurityException This class is not trusted.
907    */
908   protected boolean enableResolveObject (boolean enable)
909     throws SecurityException
910   {
911     if (enable)
912       {
913         SecurityManager sm = System.getSecurityManager();
914         if (sm != null)
915           sm.checkPermission(new SerializablePermission("enableSubstitution"));
916       }
917
918     boolean old_val = this.resolveEnabled;
919     this.resolveEnabled = enable;
920     return old_val;
921   }
922
923   /**
924    * Reads stream magic and stream version information from the
925    * underlying stream.
926    *
927    * @exception IOException Exception from underlying stream.
928    *
929    * @exception StreamCorruptedException An invalid stream magic
930    * number or stream version was read from the stream.
931    */
932   protected void readStreamHeader()
933     throws IOException, StreamCorruptedException
934   {
935     if(dump) dumpElement("STREAM MAGIC ");
936     if (this.realInputStream.readShort() != STREAM_MAGIC)
937       throw new StreamCorruptedException("Invalid stream magic number");
938
939     if(dump) dumpElementln("STREAM VERSION ");
940     if (this.realInputStream.readShort() != STREAM_VERSION)
941       throw new StreamCorruptedException("Invalid stream version number");
942   }
943
944   public int read() throws IOException
945   {
946     if (this.readDataFromBlock)
947       {
948         if (this.blockDataPosition >= this.blockDataBytes)
949           readNextBlock();
950         return (this.blockData[this.blockDataPosition++] & 0xff);
951       }
952     else
953       return this.realInputStream.read();
954   }
955
956   public int read(byte[] data, int offset, int length) throws IOException
957   {
958     if (this.readDataFromBlock)
959       {
960         if (this.blockDataPosition + length > this.blockDataBytes)
961           {
962             int remain = this.blockDataBytes - this.blockDataPosition;
963             if (remain != 0)
964               {
965                 System.arraycopy(this.blockData, this.blockDataPosition,
966                                  data, offset, remain);
967                 offset += remain;
968                 length -= remain;
969               }
970             readNextBlock ();
971           }
972
973         System.arraycopy(this.blockData, this.blockDataPosition,
974                          data, offset, length);
975         this.blockDataPosition += length;
976
977         return length;
978       }
979     else
980       return this.realInputStream.read(data, offset, length);
981   }
982
983   public int available() throws IOException
984   {
985     if (this.readDataFromBlock)
986       {
987         if (this.blockDataPosition >= this.blockDataBytes)
988           readNextBlock ();
989
990         return this.blockDataBytes - this.blockDataPosition;
991       }
992     else
993       return this.realInputStream.available();
994   }
995
996   public void close() throws IOException
997   {
998     this.realInputStream.close();
999   }
1000
1001   public boolean readBoolean() throws IOException
1002   {
1003     boolean switchmode = true;
1004     boolean oldmode = this.readDataFromBlock;
1005     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1006       switchmode = false;
1007     if (switchmode)
1008       oldmode = setBlockDataMode (true);
1009     boolean value = this.dataInputStream.readBoolean ();
1010     if (switchmode)
1011       setBlockDataMode (oldmode);
1012     return value;
1013   }
1014
1015   public byte readByte() throws IOException
1016   {
1017     boolean switchmode = true;
1018     boolean oldmode = this.readDataFromBlock;
1019     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1020       switchmode = false;
1021     if (switchmode)
1022       oldmode = setBlockDataMode(true);
1023     byte value = this.dataInputStream.readByte();
1024     if (switchmode)
1025       setBlockDataMode(oldmode);
1026     return value;
1027   }
1028
1029   public int readUnsignedByte() throws IOException
1030   {
1031     boolean switchmode = true;
1032     boolean oldmode = this.readDataFromBlock;
1033     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1034       switchmode = false;
1035     if (switchmode)
1036       oldmode = setBlockDataMode(true);
1037     int value = this.dataInputStream.readUnsignedByte();
1038     if (switchmode)
1039       setBlockDataMode(oldmode);
1040     return value;
1041   }
1042
1043   public short readShort() throws IOException
1044   {
1045     boolean switchmode = true;
1046     boolean oldmode = this.readDataFromBlock;
1047     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1048       switchmode = false;
1049     if (switchmode)
1050       oldmode = setBlockDataMode(true);
1051     short value = this.dataInputStream.readShort();
1052     if (switchmode)
1053       setBlockDataMode(oldmode);
1054     return value;
1055   }
1056
1057   public int readUnsignedShort() throws IOException
1058   {
1059     boolean switchmode = true;
1060     boolean oldmode = this.readDataFromBlock;
1061     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1062       switchmode = false;
1063     if (switchmode)
1064       oldmode = setBlockDataMode(true);
1065     int value = this.dataInputStream.readUnsignedShort();
1066     if (switchmode)
1067       setBlockDataMode(oldmode);
1068     return value;
1069   }
1070
1071   public char readChar() throws IOException
1072   {
1073     boolean switchmode = true;
1074     boolean oldmode = this.readDataFromBlock;
1075     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1076       switchmode = false;
1077     if (switchmode)
1078       oldmode = setBlockDataMode(true);
1079     char value = this.dataInputStream.readChar();
1080     if (switchmode)
1081       setBlockDataMode(oldmode);
1082     return value;
1083   }
1084
1085   public int readInt() throws IOException
1086   {
1087     boolean switchmode = true;
1088     boolean oldmode = this.readDataFromBlock;
1089     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4)
1090       switchmode = false;
1091     if (switchmode)
1092       oldmode = setBlockDataMode(true);
1093     int value = this.dataInputStream.readInt();
1094     if (switchmode)
1095       setBlockDataMode(oldmode);
1096     return value;
1097   }
1098
1099   public long readLong() throws IOException
1100   {
1101     boolean switchmode = true;
1102     boolean oldmode = this.readDataFromBlock;
1103     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8)
1104       switchmode = false;
1105     if (switchmode)
1106       oldmode = setBlockDataMode(true);
1107     long value = this.dataInputStream.readLong();
1108     if (switchmode)
1109       setBlockDataMode(oldmode);
1110     return value;
1111   }
1112
1113   public float readFloat() throws IOException
1114   {
1115     boolean switchmode = true;
1116     boolean oldmode = this.readDataFromBlock;
1117     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4)
1118       switchmode = false;
1119     if (switchmode)
1120       oldmode = setBlockDataMode(true);
1121     float value = this.dataInputStream.readFloat();
1122     if (switchmode)
1123       setBlockDataMode(oldmode);
1124     return value;
1125   }
1126
1127   public double readDouble() throws IOException
1128   {
1129     boolean switchmode = true;
1130     boolean oldmode = this.readDataFromBlock;
1131     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8)
1132       switchmode = false;
1133     if (switchmode)
1134       oldmode = setBlockDataMode(true);
1135     double value = this.dataInputStream.readDouble();
1136     if (switchmode)
1137       setBlockDataMode(oldmode);
1138     return value;
1139   }
1140
1141   public void readFully(byte data[]) throws IOException
1142   {
1143     this.dataInputStream.readFully(data);
1144   }
1145
1146   public void readFully(byte data[], int offset, int size)
1147     throws IOException
1148   {
1149     this.dataInputStream.readFully(data, offset, size);
1150   }
1151
1152   public int skipBytes(int len) throws IOException
1153   {
1154     return this.dataInputStream.skipBytes(len);
1155   }
1156
1157   /**
1158    * @deprecated
1159    * @see java.io.DataInputStream#readLine ()
1160    */
1161   public String readLine() throws IOException
1162   {
1163     return this.dataInputStream.readLine();
1164   }
1165
1166   public String readUTF() throws IOException
1167   {
1168     return this.dataInputStream.readUTF();
1169   }
1170
1171   /**
1172    * This class allows a class to specify exactly which fields should
1173    * be read, and what values should be read for these fields.
1174    *
1175    * XXX: finish up comments
1176    */
1177   public abstract static class GetField
1178   {
1179     public abstract ObjectStreamClass getObjectStreamClass();
1180
1181     public abstract boolean defaulted(String name)
1182       throws IOException, IllegalArgumentException;
1183
1184     public abstract boolean get(String name, boolean defvalue)
1185       throws IOException, IllegalArgumentException;
1186
1187     public abstract char get(String name, char defvalue)
1188       throws IOException, IllegalArgumentException;
1189
1190     public abstract byte get(String name, byte defvalue)
1191       throws IOException, IllegalArgumentException;
1192
1193     public abstract short get(String name, short defvalue)
1194       throws IOException, IllegalArgumentException;
1195
1196     public abstract int get(String name, int defvalue)
1197       throws IOException, IllegalArgumentException;
1198
1199     public abstract long get(String name, long defvalue)
1200       throws IOException, IllegalArgumentException;
1201
1202     public abstract float get(String name, float defvalue)
1203       throws IOException, IllegalArgumentException;
1204
1205     public abstract double get(String name, double defvalue)
1206       throws IOException, IllegalArgumentException;
1207
1208     public abstract Object get(String name, Object defvalue)
1209       throws IOException, IllegalArgumentException;
1210   }
1211
1212   /**
1213    * This method should be called by a method called 'readObject' in the
1214    * deserializing class (if present). It cannot (and should not)be called
1215    * outside of it. Its goal is to read all fields in the real input stream
1216    * and keep them accessible through the {@link #GetField} class. Calling
1217    * this method will not alter the deserializing object.
1218    *
1219    * @return A valid freshly created 'GetField' instance to get access to
1220    * the deserialized stream.
1221    * @throws IOException An input/output exception occured. 
1222    * @throws ClassNotFoundException 
1223    * @throws NotActiveException
1224    */
1225   public GetField readFields()
1226     throws IOException, ClassNotFoundException, NotActiveException
1227   {
1228     if (this.currentObject == null || this.currentObjectStreamClass == null)
1229       throw new NotActiveException("readFields called by non-active class and/or object");
1230
1231     if (prereadFields != null)
1232       return prereadFields;
1233
1234     if (fieldsAlreadyRead)
1235       throw new NotActiveException("readFields called but fields already read from"
1236                                    + " stream (by defaultReadObject or readFields)");
1237
1238     final ObjectStreamClass clazz = this.currentObjectStreamClass;
1239     final byte[] prim_field_data = new byte[clazz.primFieldSize];
1240     final Object[] objs = new Object[clazz.objectFieldCount];
1241
1242     // Apparently Block data is not used with GetField as per
1243     // empirical evidence against JDK 1.2.  Also see Mauve test
1244     // java.io.ObjectInputOutput.Test.GetPutField.
1245     boolean oldmode = setBlockDataMode(false);
1246     readFully(prim_field_data);
1247     for (int i = 0; i < objs.length; ++ i)
1248       objs[i] = readObject();
1249     setBlockDataMode(oldmode);
1250
1251     prereadFields = new GetField()
1252       {
1253         public ObjectStreamClass getObjectStreamClass()
1254         {
1255           return clazz;
1256         }
1257
1258         public boolean defaulted(String name)
1259           throws IOException, IllegalArgumentException
1260         {
1261           ObjectStreamField f = clazz.getField(name);
1262           
1263           /* First if we have a serialized field use the descriptor */
1264           if (f != null)
1265             {
1266               /* It is in serialPersistentFields but setClass tells us
1267                * it should not be set. This value is defaulted.
1268                */
1269               if (f.isPersistent() && !f.isToSet())
1270                 return true;
1271               
1272               return false;
1273             }
1274
1275           /* This is not a serialized field. There should be
1276            * a default value only if the field really exists.
1277            */
1278           try
1279             {
1280               return (clazz.forClass().getDeclaredField (name) != null);
1281             }
1282           catch (NoSuchFieldException e)
1283             {
1284               throw new IllegalArgumentException(e.getMessage());
1285             }
1286         }
1287
1288         public boolean get(String name, boolean defvalue)
1289           throws IOException, IllegalArgumentException
1290         {
1291           ObjectStreamField field = getField(name, Boolean.TYPE);
1292
1293           if (field == null)
1294             return defvalue;
1295
1296           return prim_field_data[field.getOffset()] == 0 ? false : true;
1297         }
1298
1299         public char get(String name, char defvalue)
1300           throws IOException, IllegalArgumentException
1301         {
1302           ObjectStreamField field = getField(name, Character.TYPE);
1303
1304           if (field == null)
1305             return defvalue;
1306
1307           int off = field.getOffset();
1308
1309           return (char)(((prim_field_data[off++] & 0xFF) << 8)
1310                         | (prim_field_data[off] & 0xFF));
1311         }
1312
1313         public byte get(String name, byte defvalue)
1314           throws IOException, IllegalArgumentException
1315         {
1316           ObjectStreamField field = getField(name, Byte.TYPE);
1317
1318           if (field == null)
1319             return defvalue;
1320
1321           return prim_field_data[field.getOffset()];
1322         }
1323
1324         public short get(String name, short defvalue)
1325           throws IOException, IllegalArgumentException
1326         {
1327           ObjectStreamField field = getField(name, Short.TYPE);
1328
1329           if (field == null)
1330             return defvalue;
1331
1332           int off = field.getOffset();
1333
1334           return (short)(((prim_field_data[off++] & 0xFF) << 8)
1335                          | (prim_field_data[off] & 0xFF));
1336         }
1337
1338         public int get(String name, int defvalue)
1339           throws IOException, IllegalArgumentException
1340         {
1341           ObjectStreamField field = getField(name, Integer.TYPE);
1342
1343           if (field == null)
1344             return defvalue;
1345
1346           int off = field.getOffset();
1347
1348           return ((prim_field_data[off++] & 0xFF) << 24)
1349             | ((prim_field_data[off++] & 0xFF) << 16)
1350             | ((prim_field_data[off++] & 0xFF) << 8)
1351             | (prim_field_data[off] & 0xFF);
1352         }
1353
1354         public long get(String name, long defvalue)
1355           throws IOException, IllegalArgumentException
1356         {
1357           ObjectStreamField field = getField(name, Long.TYPE);
1358
1359           if (field == null)
1360             return defvalue;
1361
1362           int off = field.getOffset();
1363
1364           return (long)(((prim_field_data[off++] & 0xFFL) << 56)
1365                         | ((prim_field_data[off++] & 0xFFL) << 48)
1366                         | ((prim_field_data[off++] & 0xFFL) << 40)
1367                         | ((prim_field_data[off++] & 0xFFL) << 32)
1368                         | ((prim_field_data[off++] & 0xFF) << 24)
1369                         | ((prim_field_data[off++] & 0xFF) << 16)
1370                         | ((prim_field_data[off++] & 0xFF) << 8)
1371                         | (prim_field_data[off] & 0xFF));
1372         }
1373
1374         public float get(String name, float defvalue)
1375           throws IOException, IllegalArgumentException
1376         {
1377           ObjectStreamField field = getField(name, Float.TYPE);
1378
1379           if (field == null)
1380             return defvalue;
1381
1382           int off = field.getOffset();
1383
1384           return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24)
1385                                       | ((prim_field_data[off++] & 0xFF) << 16)
1386                                       | ((prim_field_data[off++] & 0xFF) << 8)
1387                                       | (prim_field_data[off] & 0xFF));
1388         }
1389
1390         public double get(String name, double defvalue)
1391           throws IOException, IllegalArgumentException
1392         {
1393           ObjectStreamField field = getField(name, Double.TYPE);
1394
1395           if (field == null)
1396             return defvalue;
1397
1398           int off = field.getOffset();
1399
1400           return Double.longBitsToDouble
1401             ( (long) (((prim_field_data[off++] & 0xFFL) << 56)
1402                       | ((prim_field_data[off++] & 0xFFL) << 48)
1403                       | ((prim_field_data[off++] & 0xFFL) << 40)
1404                       | ((prim_field_data[off++] & 0xFFL) << 32)
1405                       | ((prim_field_data[off++] & 0xFF) << 24)
1406                       | ((prim_field_data[off++] & 0xFF) << 16)
1407                       | ((prim_field_data[off++] & 0xFF) << 8)
1408                       | (prim_field_data[off] & 0xFF)));
1409         }
1410
1411         public Object get(String name, Object defvalue)
1412           throws IOException, IllegalArgumentException
1413         {
1414           ObjectStreamField field =
1415             getField(name, defvalue == null ? null : defvalue.getClass ());
1416
1417           if (field == null)
1418             return defvalue;
1419
1420           return objs[field.getOffset()];
1421         }
1422
1423         private ObjectStreamField getField(String name, Class type)
1424           throws IllegalArgumentException
1425         {
1426           ObjectStreamField field = clazz.getField(name);
1427           boolean illegal = false;
1428
1429           try
1430             {
1431               try
1432                 {
1433                   Class field_type = field.getType();
1434                   
1435                   if (type == field_type ||
1436                       (type == null && !field_type.isPrimitive()))
1437                     {
1438                       /* See defaulted */
1439                       return field;
1440                     }
1441          
1442                   illegal = true;
1443                   throw new IllegalArgumentException
1444                     ("Field requested is of type "
1445                      + field_type.getName()
1446                      + ", but requested type was "
1447                      + (type == null ?  "Object" : type.getName()));
1448                 }
1449               catch (NullPointerException _)
1450                 {
1451                   /* Here we catch NullPointerException, because it may
1452                      only come from the call 'field.getType()'. If field
1453                      is null, we have to return null and classpath ethic
1454                      say we must try to avoid 'if (xxx == null)'.
1455                   */
1456                 }
1457               catch (IllegalArgumentException e)
1458                 {
1459                   throw e;
1460                 }
1461               
1462               return null;
1463             }
1464           finally
1465             {
1466               /* If this is an unassigned field we should return
1467                * the default value.
1468                */
1469               if (!illegal && field != null && !field.isToSet() && field.isPersistent())
1470                 return null;
1471
1472               /* We do not want to modify transient fields. They should
1473                * be left to 0.
1474                */
1475               try
1476                 {
1477                   Field f = clazz.forClass().getDeclaredField(name);
1478                   if (Modifier.isTransient(f.getModifiers()))
1479                     throw new IllegalArgumentException
1480                       ("no such field (non transient) " + name);
1481                   if (field == null && f.getType() != type)
1482                     throw new IllegalArgumentException
1483                       ("Invalid requested type for field " + name);
1484                 }
1485               catch (NoSuchFieldException e)
1486                 {
1487                   if (field == null)
1488                     throw new IllegalArgumentException(e.getMessage());
1489                 }
1490                
1491             }
1492         }
1493       };
1494
1495     fieldsAlreadyRead = true;
1496     return prereadFields;
1497   }
1498
1499   /**
1500    * Protected constructor that allows subclasses to override
1501    * deserialization.  This constructor should be called by subclasses
1502    * that wish to override <code>readObject (Object)</code>.  This
1503    * method does a security check <i>NOTE: currently not
1504    * implemented</i>, then sets a flag that informs
1505    * <code>readObject (Object)</code> to call the subclasses
1506    * <code>readObjectOverride (Object)</code> method.
1507    *
1508    * @see #readObjectOverride()
1509    */
1510   protected ObjectInputStream()
1511     throws IOException, SecurityException
1512   {
1513     SecurityManager sec_man = System.getSecurityManager();
1514     if (sec_man != null)
1515       sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
1516     this.useSubclassMethod = true;
1517   }
1518
1519   /**
1520    * This method allows subclasses to override the default
1521    * de serialization mechanism provided by
1522    * <code>ObjectInputStream</code>.  To make this method be used for
1523    * writing objects, subclasses must invoke the 0-argument
1524    * constructor on this class from their constructor.
1525    *
1526    * @see #ObjectInputStream()
1527    */
1528   protected Object readObjectOverride()
1529     throws ClassNotFoundException, IOException, OptionalDataException
1530   {
1531     throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride");
1532   }
1533
1534   /**
1535    * Assigns the next available handle to <code>obj</code>.
1536    *
1537    * @param obj The object for which we want a new handle.
1538    * @return A valid handle for the specified object.
1539    */
1540   private int assignNewHandle(Object obj)
1541   {
1542     this.objectLookupTable.put(new Integer(this.nextOID),
1543                                new ObjectIdentityWrapper(obj));
1544     return this.nextOID++;
1545   }
1546
1547   private Object processResolution(ObjectStreamClass osc, Object obj, int handle)
1548     throws IOException
1549   {
1550     if (osc != null && obj instanceof Serializable)
1551       {
1552         try
1553           {
1554             Method m = osc.readResolveMethod; 
1555             if(m != null)
1556             {
1557                 obj = m.invoke(obj, new Object[] {});
1558             }
1559           }
1560         catch (IllegalAccessException ignore)
1561           {
1562           }
1563         catch (InvocationTargetException ignore)
1564           {
1565           }
1566       }
1567
1568     if (this.resolveEnabled)
1569       obj = resolveObject(obj);
1570
1571     this.objectLookupTable.put(new Integer(handle),
1572                                new ObjectIdentityWrapper(obj));
1573
1574     return obj;
1575   }
1576
1577   private void clearHandles()
1578   {
1579     this.objectLookupTable.clear();
1580     this.nextOID = baseWireHandle;
1581   }
1582
1583   private void readNextBlock() throws IOException
1584   {
1585     readNextBlock(this.realInputStream.readByte());
1586   }
1587
1588   private void readNextBlock(byte marker) throws IOException
1589   {
1590     if (marker == TC_BLOCKDATA)
1591       {
1592         if(dump) dumpElement("BLOCK DATA SIZE=");
1593         this.blockDataBytes = this.realInputStream.readUnsignedByte();
1594         if(dump) dumpElementln (Integer.toString(this.blockDataBytes));
1595       }
1596     else if (marker == TC_BLOCKDATALONG)
1597       {
1598         if(dump) dumpElement("BLOCK DATA LONG SIZE=");
1599         this.blockDataBytes = this.realInputStream.readInt();
1600         if(dump) dumpElementln (Integer.toString(this.blockDataBytes));
1601       }
1602     else
1603       {
1604         throw new EOFException("Attempt to read primitive data, but no data block is active.");
1605       }
1606
1607     if (this.blockData.length < this.blockDataBytes)
1608       this.blockData = new byte[this.blockDataBytes];
1609
1610     this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes);
1611     this.blockDataPosition = 0;
1612   }
1613
1614   private void readArrayElements (Object array, Class clazz)
1615     throws ClassNotFoundException, IOException
1616   {
1617     if (clazz.isPrimitive())
1618       {
1619         if (clazz == Boolean.TYPE)
1620           {
1621             boolean[] cast_array = (boolean[])array;
1622             for (int i=0; i < cast_array.length; i++)
1623               cast_array[i] = this.realInputStream.readBoolean();
1624             return;
1625           }
1626         if (clazz == Byte.TYPE)
1627           {
1628             byte[] cast_array = (byte[])array;
1629             for (int i=0; i < cast_array.length; i++)
1630               cast_array[i] = this.realInputStream.readByte();
1631             return;
1632           }
1633         if (clazz == Character.TYPE)
1634           {
1635             char[] cast_array = (char[])array;
1636             for (int i=0; i < cast_array.length; i++)
1637               cast_array[i] = this.realInputStream.readChar();
1638             return;
1639           }
1640         if (clazz == Double.TYPE)
1641           {
1642             double[] cast_array = (double[])array;
1643             for (int i=0; i < cast_array.length; i++)
1644               cast_array[i] = this.realInputStream.readDouble();
1645             return;
1646           }
1647         if (clazz == Float.TYPE)
1648           {
1649             float[] cast_array = (float[])array;
1650             for (int i=0; i < cast_array.length; i++)
1651               cast_array[i] = this.realInputStream.readFloat();
1652             return;
1653           }
1654         if (clazz == Integer.TYPE)
1655           {
1656             int[] cast_array = (int[])array;
1657             for (int i=0; i < cast_array.length; i++)
1658               cast_array[i] = this.realInputStream.readInt();
1659             return;
1660           }
1661         if (clazz == Long.TYPE)
1662           {
1663             long[] cast_array = (long[])array;
1664             for (int i=0; i < cast_array.length; i++)
1665               cast_array[i] = this.realInputStream.readLong();
1666             return;
1667           }
1668         if (clazz == Short.TYPE)
1669           {
1670             short[] cast_array = (short[])array;
1671             for (int i=0; i < cast_array.length; i++)
1672               cast_array[i] = this.realInputStream.readShort();
1673             return;
1674           }
1675       }
1676     else
1677       {
1678         Object[] cast_array = (Object[])array;
1679         for (int i=0; i < cast_array.length; i++)
1680           cast_array[i] = readObject();
1681       }
1682   }
1683
1684   private void readFields (Object obj, ObjectStreamClass stream_osc)
1685     throws ClassNotFoundException, IOException
1686   {
1687     ObjectStreamField[] fields = stream_osc.fieldMapping;
1688
1689     for (int i = 0; i < fields.length; i += 2)
1690       {
1691         ObjectStreamField stream_field = fields[i];
1692         ObjectStreamField real_field = fields[i + 1];
1693         boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet());
1694         boolean set_value = (real_field != null && real_field.isToSet());
1695         String field_name;
1696         char type;
1697
1698         if (stream_field != null)
1699           {
1700             field_name = stream_field.getName();
1701             type = stream_field.getTypeCode();
1702           }
1703         else
1704           {
1705             field_name = real_field.getName();
1706             type = real_field.getTypeCode();
1707           }
1708         
1709         switch(type)
1710           {
1711           case 'Z':
1712             {
1713               boolean value =
1714                 read_value ? this.realInputStream.readBoolean() : false;
1715               if (dump && read_value && set_value)
1716                 dumpElementln("  " + field_name + ": " + value);
1717               if (set_value)
1718                 real_field.setBooleanField(obj, value);
1719               break;
1720             }
1721           case 'B':
1722             {
1723               byte value =
1724                 read_value ? this.realInputStream.readByte() : 0;
1725               if (dump && read_value && set_value)
1726                 dumpElementln("  " + field_name + ": " + value);
1727               if (set_value)
1728                 real_field.setByteField(obj, value);
1729               break;
1730             }
1731           case 'C':
1732             {
1733               char value =
1734                 read_value ? this.realInputStream.readChar(): 0;
1735               if (dump && read_value && set_value)
1736                 dumpElementln("  " + field_name + ": " + value);
1737               if (set_value)
1738                 real_field.setCharField(obj, value);
1739               break;
1740             }
1741           case 'D':
1742             {
1743               double value =
1744                 read_value ? this.realInputStream.readDouble() : 0;
1745               if (dump && read_value && set_value)
1746                 dumpElementln("  " + field_name + ": " + value);
1747               if (set_value)
1748                 real_field.setDoubleField(obj, value);
1749               break;
1750             }
1751           case 'F':
1752             {
1753               float value =
1754                 read_value ? this.realInputStream.readFloat() : 0;
1755               if (dump && read_value && set_value)
1756                 dumpElementln("  " + field_name + ": " + value);
1757               if (set_value)
1758                 real_field.setFloatField(obj, value);
1759               break;
1760             }
1761           case 'I':
1762             {
1763               int value =
1764                 read_value ? this.realInputStream.readInt() : 0;
1765               if (dump && read_value && set_value)
1766                 dumpElementln("  " + field_name + ": " + value);
1767               if (set_value)
1768                 real_field.setIntField(obj, value);
1769               break;
1770             }
1771           case 'J':
1772             {
1773               long value =
1774                 read_value ? this.realInputStream.readLong() : 0;
1775               if (dump && read_value && set_value)
1776                 dumpElementln("  " + field_name + ": " + value);
1777               if (set_value)
1778                 real_field.setLongField(obj, value);
1779               break;
1780             }
1781           case 'S':
1782             {
1783               short value =
1784                 read_value ? this.realInputStream.readShort() : 0;
1785               if (dump && read_value && set_value)
1786                 dumpElementln("  " + field_name + ": " + value);
1787               if (set_value)
1788                 real_field.setShortField(obj, value);
1789               break;
1790             }
1791           case 'L':
1792           case '[':
1793             {
1794               Object value =
1795                 read_value ? readObject() : null;
1796               if (set_value)
1797                 real_field.setObjectField(obj, value);
1798               break;
1799             }
1800           default:
1801             throw new InternalError("Invalid type code: " + type);
1802           }
1803       }
1804   }
1805   
1806   // Toggles writing primitive data to block-data buffer.
1807   private boolean setBlockDataMode (boolean on)
1808   {
1809     boolean oldmode = this.readDataFromBlock;
1810     this.readDataFromBlock = on;
1811
1812     if (on)
1813       this.dataInputStream = this.blockDataInput;
1814     else
1815       this.dataInputStream = this.realInputStream;
1816     return oldmode;
1817   }
1818
1819   // returns a new instance of REAL_CLASS that has been constructed
1820   // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS)
1821   private Object newObject (Class real_class, Constructor constructor)
1822     throws ClassNotFoundException, IOException
1823   {
1824     if (constructor == null)
1825         throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName()); 
1826     try
1827       {
1828         return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor);
1829       }
1830     catch (InstantiationException e)
1831       {
1832         throw new ClassNotFoundException
1833                 ("Instance of " + real_class + " could not be created");
1834       }
1835   }
1836
1837   // runs all registered ObjectInputValidations in prioritized order
1838   // on OBJ
1839   private void invokeValidators() throws InvalidObjectException
1840   {
1841     Object[] validators = new Object[this.validators.size()];
1842     this.validators.copyInto (validators);
1843     Arrays.sort (validators);
1844
1845     try
1846       {
1847         for (int i=0; i < validators.length; i++)
1848           ((ObjectInputValidation)validators[i]).validateObject();
1849       }
1850     finally
1851       {
1852         this.validators.removeAllElements();
1853       }
1854   }
1855
1856   private void callReadMethod (Method readObject, Class klass, Object obj)
1857     throws ClassNotFoundException, IOException
1858   {
1859     try
1860       {
1861         readObject.invoke(obj, new Object[] { this });
1862       }
1863     catch (InvocationTargetException x)
1864       {
1865         /* Rethrow if possible. */
1866         Throwable exception = x.getTargetException();
1867         if (exception instanceof RuntimeException)
1868           throw (RuntimeException) exception;
1869         if (exception instanceof IOException)
1870           throw (IOException) exception;
1871         if (exception instanceof ClassNotFoundException)
1872           throw (ClassNotFoundException) exception;
1873
1874         throw new IOException("Exception thrown from readObject() on " +
1875                                klass + ": " + exception.getClass().getName());
1876       }
1877     catch (Exception x)
1878       {
1879         throw new IOException("Failure invoking readObject() on " +
1880                                klass + ": " + x.getClass().getName());
1881       }
1882
1883     // Invalidate fields which has been read through readFields.
1884     prereadFields = null;
1885   }
1886     
1887   private static final int BUFFER_SIZE = 1024;
1888
1889   private DataInputStream realInputStream;
1890   private DataInputStream dataInputStream;
1891   private DataInputStream blockDataInput;
1892   private int blockDataPosition;
1893   private int blockDataBytes;
1894   private byte[] blockData;
1895   private boolean useSubclassMethod;
1896   private int nextOID;
1897   private boolean resolveEnabled;
1898   private Hashtable objectLookupTable;
1899   private Object currentObject;
1900   private ObjectStreamClass currentObjectStreamClass;
1901   private boolean readDataFromBlock;
1902   private boolean isDeserializing;
1903   private boolean fieldsAlreadyRead;
1904   private Vector validators;
1905   private Hashtable classLookupTable;
1906   private GetField prereadFields;
1907
1908   private ClassLoader callersClassLoader;
1909   private static boolean dump;
1910
1911   // The nesting depth for debugging output
1912   private int depth = 0;
1913
1914   private static final boolean DEBUG = false;
1915
1916   private void dumpElement (String msg)
1917   {
1918     System.out.print(msg);
1919   }
1920   
1921   private void dumpElementln (String msg)
1922   {
1923     System.out.println(msg);
1924     for (int i = 0; i < depth; i++)
1925       System.out.print (" ");
1926     System.out.print (Thread.currentThread() + ": ");
1927   }
1928
1929   static
1930   {
1931     if (Configuration.INIT_LOAD_LIBRARY)
1932       {
1933         System.loadLibrary ("javaio");
1934       }
1935   }
1936
1937   // used to keep a prioritized list of object validators
1938   private static final class ValidatorAndPriority implements Comparable
1939   {
1940     int priority;
1941     ObjectInputValidation validator;
1942
1943     ValidatorAndPriority (ObjectInputValidation validator, int priority)
1944     {
1945       this.priority = priority;
1946       this.validator = validator;
1947     }
1948
1949     public int compareTo (Object o)
1950     {
1951       ValidatorAndPriority vap = (ValidatorAndPriority)o;
1952       return this.priority - vap.priority;
1953     }
1954   }
1955 }
1956