OSDN Git Service

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