OSDN Git Service

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