OSDN Git Service

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