OSDN Git Service

Jumbo patch:
[pf3gnuchains/gcc-fork.git] / libjava / java / io / ObjectInputStream.java
1 /* ObjectInputStream.java -- Class used to read serialized objects
2    Copyright (C) 1998, 1999 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10  
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 USA.
20
21 As a special exception, if you link this library with other files to
22 produce an executable, this library does not by itself cause the
23 resulting executable to be covered by the GNU General Public License.
24 This exception does not however invalidate any other reasons why the
25 executable file might be covered by the GNU General Public License. */
26
27
28 package java.io;
29
30 import java.lang.reflect.Array;
31 import java.lang.reflect.Modifier;
32 import java.util.Arrays;
33 import java.util.Hashtable;
34 import java.util.Vector;
35
36 import gnu.java.io.ObjectIdentityWrapper;
37 import gnu.java.lang.reflect.TypeSignature;
38 import java.lang.reflect.Field;
39 import java.lang.reflect.Method;
40
41
42
43 public class ObjectInputStream extends InputStream
44   implements ObjectInput, ObjectStreamConstants
45 {
46   /**
47      Creates a new <code>ObjectInputStream</code> that will do all of
48      its reading from <code>in</code>.  This method also checks
49      the stream by reading the header information (stream magic number
50      and stream version).
51
52      @exception IOException Reading stream header from underlying
53      stream cannot be completed.
54
55      @exception StreamCorruptedException An invalid stream magic
56      number or stream version was read from the stream.
57
58      @see readStreamHeader ()
59   */
60   public ObjectInputStream (InputStream in)
61     throws IOException, StreamCorruptedException
62   {
63     this.resolveEnabled = false;
64     this.isDeserializing = false;
65     this.blockDataPosition = 0;
66     this.blockDataBytes = 0;
67     this.blockData = new byte[BUFFER_SIZE];
68     this.blockDataInput = new DataInputStream (this);
69     this.realInputStream = new DataInputStream (in);
70     this.nextOID = baseWireHandle;
71     this.objectLookupTable = new Hashtable ();
72     this.validators = new Vector ();
73     setBlockDataMode (true);
74     readStreamHeader ();
75   }
76
77
78   /**
79      Returns the next deserialized object read from the underlying stream.
80
81      This method can be overriden by a class by implementing
82      <code>private void readObject (ObjectInputStream)</code>.
83
84      If an exception is thrown from this method, the stream is left in
85      an undefined state.
86
87      @exception ClassNotFoundException The class that an object being
88      read in belongs to cannot be found.
89
90      @exception IOException Exception from underlying
91      <code>InputStream</code>.
92   */
93   public final Object readObject () throws ClassNotFoundException, IOException
94   {
95     if (this.useSubclassMethod)
96       return readObjectOverride ();
97
98     boolean was_deserializing;
99
100     Object ret_val;
101     was_deserializing = this.isDeserializing;
102
103     if (! was_deserializing)
104       setBlockDataMode (false);
105
106     this.isDeserializing = true;
107
108 //    DEBUG ("MARKER ");
109     byte marker = this.realInputStream.readByte ();
110
111     switch (marker)
112     {
113       case TC_BLOCKDATA:
114       case TC_BLOCKDATALONG:
115         readNextBlock (marker);
116         throw new BlockDataException (this.blockDataBytes);
117
118       case TC_NULL:
119         ret_val = null;
120         break;
121
122       case TC_REFERENCE:
123       {
124 //      DEBUG ("REFERENCE ");
125         Integer oid = new Integer (this.realInputStream.readInt ());
126         ret_val = ((ObjectIdentityWrapper)
127                    this.objectLookupTable.get (oid)).object;
128         break;
129       }
130
131       case TC_CLASS:
132       {
133         ObjectStreamClass osc = (ObjectStreamClass)readObject ();
134         Class clazz = osc.forClass ();
135         assignNewHandle (clazz);
136         ret_val = clazz;
137         break;
138       }
139
140       case TC_CLASSDESC:
141       {
142 //      DEBUG ("CLASSDESC NAME ");
143         String name = this.realInputStream.readUTF ();
144 //      DEBUG ("UID ");
145         long uid = this.realInputStream.readLong ();
146 //      DEBUG ("FLAGS ");
147         byte flags = this.realInputStream.readByte ();
148 //      DEBUG ("FIELD COUNT ");
149         short field_count = this.realInputStream.readShort ();
150         ObjectStreamField[] fields = new ObjectStreamField[field_count];
151
152         ObjectStreamClass osc = new ObjectStreamClass (name, uid,
153                                                        flags, fields);
154         assignNewHandle (osc);
155
156         for (int i=0; i < field_count; i++)
157         {
158 //        DEBUG ("TYPE CODE ");
159           char type_code = (char)this.realInputStream.readByte ();
160 //        DEBUG ("FIELD NAME ");
161           String field_name = this.realInputStream.readUTF ();
162           String class_name;
163
164           if (type_code == 'L' || type_code == '[')
165             class_name = (String)readObject ();
166           else
167             class_name = String.valueOf (type_code);
168
169           fields[i] =
170             new ObjectStreamField (field_name,
171                                    TypeSignature.getClassForEncoding
172                                    (class_name));
173         }
174
175         setBlockDataMode (true);
176         osc.setClass (resolveClass (osc));
177         setBlockDataMode (false);
178
179 //      DEBUG ("ENDBLOCKDATA ");
180         if (this.realInputStream.readByte () != TC_ENDBLOCKDATA)
181           throw new IOException ("Data annotated to class was not consumed.");
182
183         osc.setSuperclass ((ObjectStreamClass)readObject ());
184         ret_val = osc;
185         break;
186       }
187
188       case TC_STRING:
189       {
190 //      DEBUG ("STRING ");
191         String s = this.realInputStream.readUTF ();
192         ret_val = processResoultion (s, assignNewHandle (s));
193         break;
194       }
195
196       case TC_ARRAY:
197       {
198         ObjectStreamClass osc = (ObjectStreamClass)readObject ();
199         Class componenetType = osc.forClass ().getComponentType ();
200 //      DEBUG ("ARRAY LENGTH ");
201         int length = this.realInputStream.readInt ();
202         Object array = Array.newInstance (componenetType, length);
203         int handle = assignNewHandle (array);
204         readArrayElements (array, componenetType);
205         ret_val = processResoultion (array, handle);
206         break;
207       }
208
209       case TC_OBJECT:
210       {
211         ObjectStreamClass osc = (ObjectStreamClass)readObject ();
212         Class clazz = osc.forClass ();
213
214         if (!Serializable.class.isAssignableFrom (clazz))
215           throw new NotSerializableException (clazz + " is not Serializable, and thus cannot be deserialized.");
216
217         if (Externalizable.class.isAssignableFrom (clazz))
218         {
219           Externalizable obj = null;
220
221           try
222           {
223             obj = (Externalizable)clazz.newInstance ();
224           }
225           catch (InstantiationException e)
226           {
227             throw new ClassNotFoundException ("Instance of " + clazz
228                                               + " could not be created");
229           }
230           catch (IllegalAccessException e)
231           {
232             throw new ClassNotFoundException ("Instance of " + clazz
233                                               + " could not be created because class or zero-argument constructor is not accessible");
234           }
235           catch (NoSuchMethodError e)
236           {
237             throw new ClassNotFoundException ("Instance of " + clazz
238                                               + " could not be created because zero-argument constructor is not defined");
239           }
240
241           int handle = assignNewHandle (obj);
242
243           boolean read_from_blocks = ((osc.getFlags () & SC_BLOCK_DATA) != 0);
244
245           if (read_from_blocks)
246             setBlockDataMode (true);
247
248           obj.readExternal (this);
249
250           if (read_from_blocks)
251             setBlockDataMode (false);
252
253           ret_val = processResoultion (obj, handle);
254           break;
255         } // end if (Externalizable.class.isAssignableFrom (clazz))
256
257         // find the first non-serializable, non-abstract
258         // class in clazz's inheritance hierarchy
259         Class first_nonserial = clazz.getSuperclass ();
260         while (Serializable.class.isAssignableFrom (first_nonserial)
261                || Modifier.isAbstract (first_nonserial.getModifiers ()))
262           first_nonserial = first_nonserial.getSuperclass ();
263
264 //      DEBUGln ("Using " + first_nonserial
265 //               + " as starting point for constructing " + clazz);
266
267         Object obj = null;
268         obj = newObject (clazz, first_nonserial);
269
270         if (obj == null)
271           throw new ClassNotFoundException ("Instance of " + clazz +
272                                             " could not be created");
273
274         int handle = assignNewHandle (obj);
275         this.currentObject = obj;
276         ObjectStreamClass[] hierarchy =
277           ObjectStreamClass.getObjectStreamClasses (clazz);
278
279 //      DEBUGln ("Got class hierarchy of depth " + hierarchy.length);
280
281         boolean has_read;
282         for (int i=0; i < hierarchy.length; i++)
283         {
284           this.currentObjectStreamClass = hierarchy[i];
285
286 //        DEBUGln ("Reading fields of "
287 //                 + this.currentObjectStreamClass.getName ());
288
289           has_read = true;
290
291           try
292           {
293             this.currentObjectStreamClass.forClass ().
294               getDeclaredMethod ("readObject", readObjectParams);
295           }
296           catch (NoSuchMethodException e)
297           {
298             has_read = false;
299           }
300
301           // XXX: should initialize fields in classes in the hierarchy
302           // that aren't in the stream
303           // should skip over classes in the stream that aren't in the
304           // real classes hierarchy
305           readFields (obj, this.currentObjectStreamClass.fields,
306                       has_read, this.currentObjectStreamClass);
307
308           if (has_read)
309           {
310 //          DEBUG ("ENDBLOCKDATA? ");
311             if (this.realInputStream.readByte () != TC_ENDBLOCKDATA)
312               throw new IOException ("No end of block data seen for class with readObject (ObjectInputStream) method.");
313           }
314         }
315
316         this.currentObject = null;
317         this.currentObjectStreamClass = null;
318         ret_val = processResoultion (obj, handle);
319         break;
320       }
321
322       case TC_RESET:
323         clearHandles ();
324         ret_val = readObject ();
325         break;
326
327       case TC_EXCEPTION:
328       {
329         Exception e = (Exception)readObject ();
330         clearHandles ();
331         throw new WriteAbortedException ("Exception thrown during writing of stream", e);
332       }
333
334       default:
335         throw new IOException ("Unknown marker on stream");
336     }
337
338     this.isDeserializing = was_deserializing;
339
340     if (! was_deserializing)
341     {
342       setBlockDataMode (true);
343
344       if (validators.size () > 0)
345         invokeValidators ();
346     }
347
348     return ret_val;
349   }
350
351
352   /**
353      Reads the current objects non-transient, non-static fields from
354      the current class from the underlying output stream.
355
356      This method is intended to be called from within a object's
357      <code>private void readObject (ObjectInputStream)</code>
358      method.
359
360      @exception ClassNotFoundException The class that an object being
361      read in belongs to cannot be found.
362
363      @exception NotActiveException This method was called from a
364      context other than from the current object's and current class's
365      <code>private void readObject (ObjectInputStream)</code>
366      method.
367
368      @exception IOException Exception from underlying
369      <code>OutputStream</code>.
370   */
371   public void defaultReadObject ()
372     throws ClassNotFoundException, IOException, NotActiveException
373   {
374     if (this.currentObject == null || this.currentObjectStreamClass == null)
375       throw new NotActiveException ("defaultReadObject called by non-active class and/or object");
376
377     if (fieldsAlreadyRead)
378       throw new NotActiveException ("defaultReadObject called but fields already read from stream (by defaultReadObject or readFields)");
379
380     readFields (this.currentObject,
381                 this.currentObjectStreamClass.fields,
382                 false, this.currentObjectStreamClass);
383
384     fieldsAlreadyRead = true;
385   }
386
387
388   /**
389      Registers a <code>ObjectInputValidation</code> to be carried out
390      on the object graph currently being deserialized before it is
391      returned to the original caller of <code>readObject ()</code>.
392      The order of validation for multiple
393      <code>ObjectInputValidation</code>s can be controled using
394      <code>priority</code>.  Validators with higher priorities are
395      called first.
396
397      @see java.io.ObjectInputValidation
398
399      @exception InvalidObjectException <code>validator</code> is
400      <code>null</code>
401
402      @exception NotActiveException an attempt was made to add a
403      validator outside of the <code>readObject</code> method of the
404      object currently being deserialized
405   */
406   public void registerValidation (ObjectInputValidation validator,
407                                   int priority)
408     throws InvalidObjectException, NotActiveException
409   {
410     if (this.currentObject == null || this.currentObjectStreamClass == null)
411       throw new NotActiveException ("registerValidation called by non-active class and/or object");
412
413     if (validator == null)
414       throw new InvalidObjectException ("attempt to add a null ObjectInputValidation object");
415
416     this.validators.addElement (new ValidatorAndPriority (validator,
417                                                           priority));
418   }
419
420
421   /**
422      Called when a class is being deserialized.  This is a hook to
423      allow subclasses to read in information written by the
424      <code>annotateClass (Class)</code> method of an
425      <code>ObjectOutputStream</code>.
426
427      This implementation looks up the active call stack for a
428      <code>ClassLoader</code>; if a <code>ClassLoader</code> is found,
429      it is used to load the class associated with <code>osc</code>,
430      otherwise, the default system <code>ClassLoader</code> is used.
431
432      @exception IOException Exception from underlying
433      <code>OutputStream</code>.
434
435      @see java.io.ObjectOutputStream#annotateClass (java.lang.Class)
436   */
437   protected Class resolveClass (ObjectStreamClass osc)
438     throws ClassNotFoundException, IOException
439   {
440 //    DEBUGln ("Resolving " + osc);
441
442     SecurityManager sm = System.getSecurityManager ();
443
444     if (sm == null)
445       sm = new SecurityManager () {};
446
447     ClassLoader cl = currentClassLoader (sm);
448
449     if (cl == null)
450     {
451 //      DEBUGln ("No class loader found");
452       return Class.forName (osc.getName ());
453     }
454     else
455     {
456 //      DEBUGln ("Using " + cl);
457       return cl.loadClass (osc.getName ());
458     }
459   }
460
461
462   /**
463      Allows subclasses to resolve objects that are read from the
464      stream with other objects to be returned in their place.  This
465      method is called the first time each object is encountered.
466
467      This method must be enabled before it will be called in the
468      serialization process.
469
470      @exception IOException Exception from underlying
471      <code>OutputStream</code>.
472
473      @see enableResolveObject (boolean)
474   */
475   protected Object resolveObject (Object obj) throws IOException
476   {
477     return obj;
478   }
479
480
481   /**
482      If <code>enable</code> is <code>true</code> and this object is
483      trusted, then <code>resolveObject (Object)</code> will be called
484      in subsequent calls to <code>readObject (Object)</code>.
485      Otherwise, <code>resolveObject (Object)</code> will not be called.
486
487      @exception SecurityException This class is not trusted.
488   */
489   protected boolean enableResolveObject (boolean enable)
490     throws SecurityException
491   {
492     if (enable)
493       if (getClass ().getClassLoader () != null)
494         throw new SecurityException ("Untrusted ObjectInputStream subclass attempted to enable object resolution");
495
496     boolean old_val = this.resolveEnabled;
497     this.resolveEnabled = enable;
498     return old_val;
499   }
500
501
502   /**
503      Reads stream magic and stream version information from the
504      underlying stream.
505
506      @exception IOException Exception from underlying stream.
507
508      @exception StreamCorruptedException An invalid stream magic
509      number or stream version was read from the stream.
510   */
511   protected void readStreamHeader ()
512     throws IOException, StreamCorruptedException
513   {
514 //    DEBUG ("STREAM MAGIC ");
515     if (this.realInputStream.readShort () != STREAM_MAGIC)
516       throw new StreamCorruptedException ("Invalid stream magic number");
517
518 //    DEBUG ("STREAM VERSION ");
519     if (this.realInputStream.readShort () != STREAM_VERSION)
520       throw new StreamCorruptedException ("Invalid stream version number");
521   }
522
523
524   public int read () throws IOException
525   {
526     if (this.readDataFromBlock)
527     {
528       if (this.blockDataPosition >= this.blockDataBytes)
529         readNextBlock ();
530       return this.blockData[this.blockDataPosition++];
531     }
532     else
533       return this.realInputStream.read ();
534   }
535
536   public int read (byte data[], int offset, int length) throws IOException
537   {
538     if (this.readDataFromBlock)
539     {
540       if (this.blockDataPosition + length >= this.blockDataBytes)
541         readNextBlock ();
542
543       System.arraycopy (this.blockData, this.blockDataPosition,
544                         data, offset, length);
545       return length;
546     }
547     else
548       return this.realInputStream.read (data, offset, length);
549   }
550
551   public int available () throws IOException
552   {
553     if (this.readDataFromBlock)
554     {
555       if (this.blockDataPosition >= this.blockDataBytes)
556         readNextBlock ();
557
558       return this.blockDataBytes - this.blockDataPosition;
559     }
560     else
561       return this.realInputStream.available ();
562   }
563
564   public void close () throws IOException
565   {
566     this.realInputStream.close ();
567   }
568
569   public boolean readBoolean () throws IOException
570   {
571     return this.dataInputStream.readBoolean ();
572   }
573
574   public byte readByte () throws IOException
575   {
576     return this.dataInputStream.readByte ();
577   }
578
579   public int readUnsignedByte () throws IOException
580   {
581     return this.dataInputStream.readUnsignedByte ();
582   }
583
584   public short readShort () throws IOException
585   {
586     return this.dataInputStream.readShort ();
587   }
588
589   public int readUnsignedShort () throws IOException
590   {
591     return this.dataInputStream.readUnsignedShort ();
592   }
593
594   public char readChar () throws IOException
595   {
596     return this.dataInputStream.readChar ();
597   }
598
599   public int readInt () throws IOException
600   {
601     return this.dataInputStream.readInt ();
602   }
603
604   public long readLong () throws IOException
605   {
606     return this.dataInputStream.readLong ();
607   }
608
609   public float readFloat () throws IOException
610   {
611     return this.dataInputStream.readFloat ();
612   }
613
614   public double readDouble () throws IOException
615   {
616     return this.dataInputStream.readDouble ();
617   }
618
619   public void readFully (byte data[]) throws IOException
620   {
621     this.dataInputStream.readFully (data);
622   }
623
624   public void readFully (byte data[], int offset, int size)
625     throws IOException
626   {
627     this.dataInputStream.readFully (data, offset, size);
628   }
629
630   public int skipBytes (int len) throws IOException
631   {
632     return this.dataInputStream.skipBytes (len);
633   }
634
635   /**
636      @deprecated
637      @see java.io.DataInputStream#readLine ()
638   */
639   public String readLine () throws IOException
640   {
641     return this.dataInputStream.readLine ();
642   }
643
644   public String readUTF () throws IOException
645   {
646     return this.dataInputStream.readUTF ();
647   }
648
649
650   /**
651      This class allows a class to specify exactly which fields should
652      be read, and what values should be read for these fields.
653
654      XXX: finish up comments
655   */
656   public static abstract class GetField
657   {
658     public abstract ObjectStreamClass getObjectStreamClass ();
659
660     public abstract boolean defaulted (String name)
661       throws IOException, IllegalArgumentException;
662
663     public abstract boolean get (String name, boolean defvalue)
664       throws IOException, IllegalArgumentException;
665
666     public abstract char get (String name, char defvalue)
667       throws IOException, IllegalArgumentException;
668
669     public abstract byte get (String name, byte defvalue)
670       throws IOException, IllegalArgumentException;
671
672     public abstract short get (String name, short defvalue)
673       throws IOException, IllegalArgumentException;
674
675     public abstract int get (String name, int defvalue)
676       throws IOException, IllegalArgumentException;
677
678     public abstract long get (String name, long defvalue)
679       throws IOException, IllegalArgumentException;
680
681     public abstract float get (String name, float defvalue)
682       throws IOException, IllegalArgumentException;
683
684     public abstract double get (String name, double defvalue)
685       throws IOException, IllegalArgumentException;
686
687     public abstract Object get (String name, Object defvalue)
688       throws IOException, IllegalArgumentException;
689   }
690
691   public GetField readFields ()
692     throws IOException, ClassNotFoundException, NotActiveException
693   {
694     if (this.currentObject == null || this.currentObjectStreamClass == null)
695       throw new NotActiveException ("readFields called by non-active class and/or object");
696
697     if (fieldsAlreadyRead)
698       throw new NotActiveException ("readFields called but fields already read from stream (by defaultReadObject or readFields)");
699
700     final ObjectStreamClass clazz = this.currentObjectStreamClass;
701     final byte[] prim_field_data = new byte[clazz.primFieldSize];
702     final Object[] objs = new Object[clazz.objectFieldCount];
703     readFully (prim_field_data);
704     for (int i = 0; i < objs.length; ++ i)
705       objs[i] = readObject ();
706
707     return new GetField ()
708     {
709       public ObjectStreamClass getObjectStreamClass ()
710       {
711         return clazz;
712       }
713
714       public boolean defaulted (String name)
715         throws IOException, IllegalArgumentException
716       {
717         return clazz.getField (name) == null;
718       }
719
720       public boolean get (String name, boolean defvalue)
721         throws IOException, IllegalArgumentException
722       {
723         ObjectStreamField field = getField (name, Boolean.TYPE);
724
725         if (field == null)
726           return defvalue;
727
728         return prim_field_data[field.getOffset ()] == 0 ? false : true;
729       }
730
731       public char get (String name, char defvalue)
732         throws IOException, IllegalArgumentException
733       {
734         ObjectStreamField field = getField (name, Character.TYPE);
735
736         if (field == null)
737           return defvalue;
738
739         int off = field.getOffset ();
740
741         return (char)(((prim_field_data[off++] & 0xFF) << 8)
742                       | (prim_field_data[off] & 0xFF));
743       }
744
745       public byte get (String name, byte defvalue)
746         throws IOException, IllegalArgumentException
747       {
748         ObjectStreamField field = getField (name, Byte.TYPE);
749
750         if (field == null)
751           return defvalue;
752
753         return prim_field_data[field.getOffset ()];
754       }
755
756       public short get (String name, short defvalue)
757         throws IOException, IllegalArgumentException
758       {
759         ObjectStreamField field = getField (name, Short.TYPE);
760
761         if (field == null)
762           return defvalue;
763
764         int off = field.getOffset ();
765
766         return (short)(((prim_field_data[off++] & 0xFF) << 8)
767                        | (prim_field_data[off] & 0xFF));
768       }
769
770       public int get (String name, int defvalue)
771         throws IOException, IllegalArgumentException
772       {
773         ObjectStreamField field = getField (name, Integer.TYPE);
774
775         if (field == null)
776           return defvalue;
777
778         int off = field.getOffset ();
779
780         return ((prim_field_data[off++] & 0xFF) << 24)
781           | ((prim_field_data[off++] & 0xFF) << 16)
782           | ((prim_field_data[off++] & 0xFF) << 8)
783           | (prim_field_data[off] & 0xFF);
784       }
785
786       public long get (String name, long defvalue)
787         throws IOException, IllegalArgumentException
788       {
789         ObjectStreamField field = getField (name, Long.TYPE);
790
791         if (field == null)
792           return defvalue;
793
794         int off = field.getOffset ();
795
796         return (long)(((prim_field_data[off++] & 0xFF) << 56)
797                       | ((prim_field_data[off++] & 0xFF) << 48)
798                       | ((prim_field_data[off++] & 0xFF) << 40)
799                       | ((prim_field_data[off++] & 0xFF) << 32)
800                       | ((prim_field_data[off++] & 0xFF) << 24)
801                       | ((prim_field_data[off++] & 0xFF) << 16)
802                       | ((prim_field_data[off++] & 0xFF) << 8)
803                       | (prim_field_data[off] & 0xFF));
804       }
805
806       public float get (String name, float defvalue)
807         throws IOException, IllegalArgumentException
808       {
809         ObjectStreamField field = getField (name, Float.TYPE);
810
811         if (field == null)
812           return defvalue;
813
814         int off = field.getOffset ();
815
816         return Float.intBitsToFloat (((prim_field_data[off++] & 0xFF) << 24)
817                                     | ((prim_field_data[off++] & 0xFF) << 16)
818                                     | ((prim_field_data[off++] & 0xFF) << 8)
819                                     | (prim_field_data[off] & 0xFF));
820       }
821
822       public double get (String name, double defvalue)
823         throws IOException, IllegalArgumentException
824       {
825         ObjectStreamField field = getField (name, Double.TYPE);
826
827         if (field == null)
828           return defvalue;
829
830         int off = field.getOffset ();
831
832         return Double.longBitsToDouble (
833           (long)(((prim_field_data[off++] & 0xFF) << 56)
834                  | ((prim_field_data[off++] & 0xFF) << 48)
835                  | ((prim_field_data[off++] & 0xFF) << 40)
836                  | ((prim_field_data[off++] & 0xFF) << 32)
837                  | ((prim_field_data[off++] & 0xFF) << 24)
838                  | ((prim_field_data[off++] & 0xFF) << 16)
839                  | ((prim_field_data[off++] & 0xFF) << 8)
840                  | (prim_field_data[off] & 0xFF)));
841       }
842
843       public Object get (String name, Object defvalue)
844         throws IOException, IllegalArgumentException
845       {
846         ObjectStreamField field = getField (name, null);
847
848         if (field == null)
849           return defvalue;
850
851         return objs[field.getOffset ()];
852       }
853
854       private ObjectStreamField getField (String name, Class type)
855         throws IllegalArgumentException
856       {
857         ObjectStreamField field = clazz.getField (name);
858
859         if (field == null)
860           return null;
861
862         Class field_type = field.getType ();
863
864         if (type == field_type ||
865             (type != null && field_type.isPrimitive ()))
866           return field;
867
868         throw new IllegalArgumentException ("Field requested is of type "
869                                             + field_type.getName ()
870                                             + ", but requested type was "
871                                             + (type == null ?
872                                                "Object" : type.getName ()));
873       }
874     };
875
876   }
877
878
879   /**
880      Protected constructor that allows subclasses to override
881      deserialization.  This constructor should be called by subclasses
882      that wish to override <code>readObject (Object)</code>.  This
883      method does a security check <i>NOTE: currently not
884      implemented</i>, then sets a flag that informs
885      <code>readObject (Object)</code> to call the subclasses
886      <code>readObjectOverride (Object)</code> method.
887
888      @see readObjectOverride (Object)
889   */
890   protected ObjectInputStream ()
891     throws IOException, SecurityException
892   {
893     SecurityManager sec_man = System.getSecurityManager ();
894     if (sec_man != null)
895       sec_man.checkPermission (SUBCLASS_IMPLEMENTATION_PERMISSION);
896     this.useSubclassMethod = true;
897   }
898
899
900   /**
901      This method allows subclasses to override the default
902      de serialization mechanism provided by
903      <code>ObjectInputStream</code>.  To make this method be used for
904      writing objects, subclasses must invoke the 0-argument
905      constructor on this class from there constructor.
906
907      @see ObjectInputStream ()
908   */
909   protected Object readObjectOverride ()
910     throws ClassNotFoundException, IOException, OptionalDataException
911   {
912     throw new IOException ("Subclass of ObjectInputStream must implement readObjectOverride");
913   }
914
915
916   // assigns the next availible handle to OBJ
917   private int assignNewHandle (Object obj)
918   {
919     this.objectLookupTable.put (new Integer (this.nextOID),
920                              new ObjectIdentityWrapper (obj));
921
922 //    try
923 //    {
924 //      DEBUG ("Assigning handle " + this.nextOID);
925 //      DEBUGln (" to " + obj);
926 //    }
927 //    catch (Throwable t) {}
928
929     return this.nextOID++;
930   }
931
932
933   private Object processResoultion (Object obj, int handle)
934     throws IOException
935   {
936     if (obj instanceof Resolvable)
937       obj = ((Resolvable)obj).readResolve ();
938
939     if (this.resolveEnabled)
940       obj = resolveObject (obj);
941
942     this.objectLookupTable.put (new Integer (handle),
943                                 new ObjectIdentityWrapper (obj));
944
945     return obj;
946   }
947
948
949   private void clearHandles ()
950   {
951     this.objectLookupTable.clear ();
952     this.nextOID = baseWireHandle;
953   }
954
955
956   private void readNextBlock () throws IOException
957   {
958 //    DEBUG ("MARKER ");
959     readNextBlock (this.realInputStream.readByte ());
960   }
961
962
963   private void readNextBlock (byte marker) throws IOException
964   {
965     if (marker == TC_BLOCKDATA)
966     {
967 //      DEBUG ("BLOCK DATA SIZE ");
968       this.blockDataBytes = this.realInputStream.readUnsignedByte ();
969     }
970     else if (marker == TC_BLOCKDATALONG)
971     {
972 //      DEBUG ("BLOCK DATA LONG SIZE ");
973       this.blockDataBytes = this.realInputStream.readInt ();
974     }
975     else
976     {
977       throw new EOFException ("Attempt to read primitive data, but no data block is active.");
978     }
979
980     if (this.blockData.length < this.blockDataBytes)
981       this.blockData = new byte[this.blockDataBytes];
982
983     this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes);
984     this.blockDataPosition = 0;
985   }
986
987
988   private void readArrayElements (Object array, Class clazz)
989     throws ClassNotFoundException, IOException
990   {
991     if (clazz.isPrimitive ())
992     {
993       if (clazz == Boolean.TYPE)
994       {
995         boolean[] cast_array = (boolean[])array;
996         for (int i=0; i < cast_array.length; i++)
997           cast_array[i] = this.realInputStream.readBoolean ();
998         return;
999       }
1000       if (clazz == Byte.TYPE)
1001       {
1002         byte[] cast_array = (byte[])array;
1003         for (int i=0; i < cast_array.length; i++)
1004           cast_array[i] = this.realInputStream.readByte ();
1005         return;
1006       }
1007       if (clazz == Character.TYPE)
1008       {
1009         char[] cast_array = (char[])array;
1010         for (int i=0; i < cast_array.length; i++)
1011           cast_array[i] = this.realInputStream.readChar ();
1012         return;
1013       }
1014       if (clazz == Double.TYPE)
1015       {
1016         double[] cast_array = (double[])array;
1017         for (int i=0; i < cast_array.length; i++)
1018           cast_array[i] = this.realInputStream.readDouble ();
1019         return;
1020       }
1021       if (clazz == Float.TYPE)
1022       {
1023         float[] cast_array = (float[])array;
1024         for (int i=0; i < cast_array.length; i++)
1025           cast_array[i] = this.realInputStream.readFloat ();
1026         return;
1027       }
1028       if (clazz == Integer.TYPE)
1029       {
1030         int[] cast_array = (int[])array;
1031         for (int i=0; i < cast_array.length; i++)
1032           cast_array[i] = this.realInputStream.readInt ();
1033         return;
1034       }
1035       if (clazz == Long.TYPE)
1036       {
1037         long[] cast_array = (long[])array;
1038         for (int i=0; i < cast_array.length; i++)
1039           cast_array[i] = this.realInputStream.readLong ();
1040         return;
1041       }
1042       if (clazz == Short.TYPE)
1043       {
1044         short[] cast_array = (short[])array;
1045         for (int i=0; i < cast_array.length; i++)
1046           cast_array[i] = this.realInputStream.readShort ();
1047         return;
1048       }
1049     }
1050     else
1051     {
1052       Object[] cast_array = (Object[])array;
1053       for (int i=0; i < cast_array.length; i++)
1054           cast_array[i] = readObject ();
1055     }
1056   }
1057
1058
1059   private void readFields (Object obj, ObjectStreamField[] stream_fields,
1060                            boolean call_read_method,
1061                            ObjectStreamClass stream_osc)
1062     throws ClassNotFoundException, IOException
1063   {
1064     if (call_read_method)
1065     {
1066       fieldsAlreadyRead = false;
1067       setBlockDataMode (true);
1068       callReadMethod (obj, stream_osc.forClass ());
1069       setBlockDataMode (false);
1070       return;
1071     }
1072
1073     ObjectStreamField[] real_fields =
1074       ObjectStreamClass.lookup (stream_osc.forClass ()).fields;
1075
1076     boolean default_initialize, set_value;
1077     String field_name = null;
1078     Class type = null;
1079     ObjectStreamField stream_field = null;
1080     ObjectStreamField real_field = null;
1081     int stream_idx = 0;
1082     int real_idx = 0;
1083
1084     while (stream_idx < stream_fields.length
1085            && real_idx < real_fields.length)
1086     {
1087       default_initialize = false;
1088       set_value = true;
1089
1090       if (stream_idx == stream_fields.length)
1091         default_initialize = true;
1092       else
1093       {
1094         stream_field = stream_fields[stream_idx];
1095         type = stream_field.getType ();
1096       }
1097
1098       if (real_idx == real_fields.length)
1099         set_value = false;
1100       else
1101       {
1102         real_field = real_fields[real_idx];
1103         type = real_field.getType ();
1104         field_name = real_field.getName ();
1105       }
1106
1107       if (set_value && !default_initialize)
1108       {
1109         int comp_val =
1110           real_field.compareTo (stream_field);
1111
1112         if (comp_val < 0)
1113         {
1114           default_initialize = true;
1115           real_idx++;
1116         }
1117         else if (comp_val > 0)
1118         {
1119           set_value = false;
1120           stream_idx++;
1121         }
1122         else
1123         {
1124           real_idx++;
1125           stream_idx++;
1126         }
1127       }
1128
1129       if (type == Boolean.TYPE)
1130       {
1131         boolean value =
1132           default_initialize ? false : this.realInputStream.readBoolean ();
1133         if (set_value)
1134           setBooleanField (obj, field_name, value);
1135       }
1136       else if (type == Byte.TYPE)
1137       {
1138         byte value =
1139           default_initialize ? 0 : this.realInputStream.readByte ();
1140         if (set_value)
1141           setByteField (obj, field_name, value);
1142       }
1143       else if (type == Character.TYPE)
1144       {
1145         char value =
1146           default_initialize ? (char)0 : this.realInputStream.readChar ();
1147         if (set_value)
1148           setCharField (obj, field_name, value);
1149       }
1150       else if (type == Double.TYPE)
1151       {
1152         double value =
1153           default_initialize ? 0 : this.realInputStream.readDouble ();
1154         if (set_value)
1155           setDoubleField (obj, field_name, value);
1156       }
1157       else if (type == Float.TYPE)
1158       {
1159         float value =
1160           default_initialize ? 0 : this.realInputStream.readFloat ();
1161         if (set_value)
1162           setFloatField (obj, field_name, value);
1163       }
1164       else if (type == Integer.TYPE)
1165       {
1166         int value =
1167           default_initialize ? 0 : this.realInputStream.readInt ();
1168         if (set_value)
1169           setIntField (obj, field_name, value);
1170       }
1171       else if (type == Long.TYPE)
1172       {
1173         long value =
1174           default_initialize ? 0 : this.realInputStream.readLong ();
1175         if (set_value)
1176           setLongField (obj, field_name, value);
1177       }
1178       else if (type == Short.TYPE)
1179       {
1180         short value =
1181           default_initialize ? (short)0 : this.realInputStream.readShort ();
1182         if (set_value)
1183           setShortField (obj, field_name, value);
1184       }
1185       else
1186       {
1187         Object value =
1188           default_initialize ? null : readObject ();
1189         if (set_value)
1190           setObjectField (obj, field_name,
1191                           real_field.getTypeString (), value);
1192       }
1193     }
1194   }
1195
1196
1197   // Toggles writing primitive data to block-data buffer.
1198   private void setBlockDataMode (boolean on)
1199   {
1200 //    DEBUGln ("Setting block data mode to " + on);
1201
1202     this.readDataFromBlock = on;
1203
1204     if (on)
1205       this.dataInputStream = this.blockDataInput;
1206     else
1207       this.dataInputStream = this.realInputStream;
1208   }
1209
1210
1211   // returns a new instance of REAL_CLASS that has been constructed
1212   // only to th level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS)
1213   private Object newObject (Class real_class, Class constructor_class)
1214   {
1215     try
1216     {
1217       Object obj = allocateObject (real_class);
1218       callConstructor (constructor_class, obj);
1219       return obj;
1220     }
1221     catch (InstantiationException e)
1222     {
1223       return null;
1224     }
1225   }
1226
1227
1228   // runs all registered ObjectInputValidations in prioritized order
1229   // on OBJ
1230   private void invokeValidators () throws InvalidObjectException
1231   {
1232     Object[] validators = new Object[this.validators.size ()];
1233     this.validators.copyInto (validators);
1234     Arrays.sort (validators);
1235
1236     try
1237     {
1238       for (int i=0; i < validators.length; i++)
1239         ((ObjectInputValidation)validators[i]).validateObject ();
1240     }
1241     finally
1242     {
1243       this.validators.removeAllElements ();
1244     }
1245   }
1246
1247
1248   // this native method is used to get access to the protected method
1249   // of the same name in SecurityManger
1250   private static ClassLoader currentClassLoader (SecurityManager sm)
1251   {
1252     // FIXME: This is too simple.
1253     return ClassLoader.getSystemClassLoader ();
1254   }
1255
1256   private static native Field getField (Class klass, String name)
1257     throws java.lang.NoSuchFieldException;
1258
1259   private static native Method getMethod (Class klass, String name, Class args[])
1260     throws java.lang.NoSuchMethodException;
1261
1262   private void callReadMethod (Object obj, Class klass) throws IOException
1263   {
1264     try
1265       {
1266         Class classArgs[] = {Class.forName ("java.io.ObjectInputStream")};
1267         Method m = getMethod (klass, "readObject", classArgs);
1268         if (m == null)
1269           return;
1270         Object args[] = {this};
1271         m.invoke (obj, args);   
1272       }
1273     catch (Exception _)
1274       {
1275         throw new IOException ();
1276       }
1277   }
1278     
1279   private native Object allocateObject (Class clazz)
1280     throws InstantiationException;
1281
1282   private native void callConstructor (Class clazz, Object obj);
1283
1284   private void setBooleanField (Object obj, String field_name,
1285                                 boolean val)
1286   {
1287     try
1288       {
1289         Class klass = obj.getClass ();
1290         Field f = getField (klass, field_name);
1291         f.setBoolean (obj, val);
1292       }
1293     catch (Exception _)
1294       {
1295       }    
1296   }
1297
1298   private void setByteField (Object obj, String field_name,
1299                                 byte val)
1300   {
1301     try
1302       {
1303         Class klass = obj.getClass ();
1304         Field f = getField (klass, field_name);
1305         f.setByte (obj, val);
1306       }
1307     catch (Exception _)
1308       {
1309       }    
1310   }
1311
1312   private void setCharField (Object obj, String field_name,
1313                              char val)
1314   {
1315     try
1316       {
1317         Class klass = obj.getClass ();
1318         Field f = getField (klass, field_name);
1319         f.setChar (obj, val);
1320       }
1321     catch (Exception _)
1322       {
1323       }    
1324   }
1325
1326   private void setDoubleField (Object obj, String field_name,
1327                                double val)
1328   {
1329     try
1330       {
1331         Class klass = obj.getClass ();
1332         Field f = getField (klass, field_name);
1333         f.setDouble (obj, val);
1334       }
1335     catch (Exception _)
1336       {
1337       }    
1338   }
1339
1340   private void setFloatField (Object obj, String field_name,
1341                               float val)
1342   {
1343     try
1344       {
1345         Class klass = obj.getClass ();
1346         Field f = getField (klass, field_name);
1347         f.setFloat (obj, val);
1348       }
1349     catch (Exception _)
1350       {
1351       }    
1352   }
1353
1354   private void setIntField (Object obj, String field_name,
1355                               int val)
1356   {
1357     try
1358       {
1359         Class klass = obj.getClass ();
1360         Field f = getField (klass, field_name);
1361         f.setInt (obj, val);
1362       }
1363     catch (Exception _)
1364       {
1365       }    
1366   }
1367
1368
1369   private void setLongField (Object obj, String field_name,
1370                               long val)
1371   {
1372     try
1373       {
1374         Class klass = obj.getClass ();
1375         Field f = getField (klass, field_name);
1376         f.setLong (obj, val);
1377       }
1378     catch (Exception _)
1379       {
1380       }    
1381   }
1382
1383
1384   private void setShortField (Object obj, String field_name,
1385                               short val)
1386   {
1387     try
1388       {
1389         Class klass = obj.getClass ();
1390         Field f = getField (klass, field_name);
1391         f.setShort (obj, val);
1392       }
1393     catch (Exception _)
1394       {
1395       }    
1396   }
1397
1398
1399   private void setObjectField (Object obj, String field_name, String type_code,
1400                                Object val)
1401   {
1402     try
1403       {
1404         Class klass = obj.getClass ();
1405         Field f = getField (klass, field_name);
1406         // FIXME: We should check the type_code here
1407         f.set (obj, val);
1408       }
1409     catch (Exception _)
1410       {
1411       }    
1412   }
1413
1414   private static final int BUFFER_SIZE = 1024;
1415   private static final Class[] readObjectParams = { ObjectInputStream.class };
1416
1417   private DataInputStream realInputStream;
1418   private DataInputStream dataInputStream;
1419   private DataInputStream blockDataInput;
1420   private int blockDataPosition;
1421   private int blockDataBytes;
1422   private byte[] blockData;
1423   private boolean useSubclassMethod;
1424   private int nextOID;
1425   private boolean resolveEnabled;
1426   private Hashtable objectLookupTable;
1427   private Object currentObject;
1428   private ObjectStreamClass currentObjectStreamClass;
1429   private boolean readDataFromBlock;
1430   private boolean isDeserializing;
1431   private boolean fieldsAlreadyRead;
1432   private Vector validators;
1433
1434
1435 /* FIXME: These 2 methods cause a build error on i686-pc-linux-gnu.
1436   private void DEBUG (String msg)
1437   {
1438     System.out.print (msg);
1439   }
1440
1441
1442   private void DEBUGln (String msg)
1443   {
1444     System.out.println (msg);
1445   }
1446 * end FIXME */
1447 }
1448
1449
1450 // used to keep a prioritized list of object validators
1451 class ValidatorAndPriority implements Comparable
1452 {
1453   int priority;
1454   ObjectInputValidation validator;
1455
1456   ValidatorAndPriority (ObjectInputValidation validator, int priority)
1457   {
1458     this.priority = priority;
1459     this.validator = validator;
1460   }
1461
1462   public int compareTo (Object o)
1463   {
1464     ValidatorAndPriority vap = (ValidatorAndPriority)o;
1465     return this.priority - vap.priority;
1466   }
1467 }