diff options
Diffstat (limited to 'libjava/java/io/ObjectInputStream.java')
-rw-r--r-- | libjava/java/io/ObjectInputStream.java | 1467 |
1 files changed, 1467 insertions, 0 deletions
diff --git a/libjava/java/io/ObjectInputStream.java b/libjava/java/io/ObjectInputStream.java new file mode 100644 index 00000000000..7855480acb3 --- /dev/null +++ b/libjava/java/io/ObjectInputStream.java @@ -0,0 +1,1467 @@ +/* ObjectInputStream.java -- Class used to read serialized objects + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +import java.lang.reflect.Array; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Vector; + +import gnu.java.io.ObjectIdentityWrapper; +import gnu.java.lang.reflect.TypeSignature; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + + + +public class ObjectInputStream extends InputStream + implements ObjectInput, ObjectStreamConstants +{ + /** + Creates a new <code>ObjectInputStream</code> that will do all of + its reading from <code>in</code>. This method also checks + the stream by reading the header information (stream magic number + and stream version). + + @exception IOException Reading stream header from underlying + stream cannot be completed. + + @exception StreamCorruptedException An invalid stream magic + number or stream version was read from the stream. + + @see readStreamHeader () + */ + public ObjectInputStream (InputStream in) + throws IOException, StreamCorruptedException + { + this.resolveEnabled = false; + this.isDeserializing = false; + this.blockDataPosition = 0; + this.blockDataBytes = 0; + this.blockData = new byte[BUFFER_SIZE]; + this.blockDataInput = new DataInputStream (this); + this.realInputStream = new DataInputStream (in); + this.nextOID = baseWireHandle; + this.objectLookupTable = new Hashtable (); + this.validators = new Vector (); + setBlockDataMode (true); + readStreamHeader (); + } + + + /** + Returns the next deserialized object read from the underlying stream. + + This method can be overriden by a class by implementing + <code>private void readObject (ObjectInputStream)</code>. + + If an exception is thrown from this method, the stream is left in + an undefined state. + + @exception ClassNotFoundException The class that an object being + read in belongs to cannot be found. + + @exception IOException Exception from underlying + <code>InputStream</code>. + */ + public final Object readObject () throws ClassNotFoundException, IOException + { + if (this.useSubclassMethod) + return readObjectOverride (); + + boolean was_deserializing; + + Object ret_val; + was_deserializing = this.isDeserializing; + + if (! was_deserializing) + setBlockDataMode (false); + + this.isDeserializing = true; + +// DEBUG ("MARKER "); + byte marker = this.realInputStream.readByte (); + + switch (marker) + { + case TC_BLOCKDATA: + case TC_BLOCKDATALONG: + readNextBlock (marker); + throw new BlockDataException (this.blockDataBytes); + + case TC_NULL: + ret_val = null; + break; + + case TC_REFERENCE: + { +// DEBUG ("REFERENCE "); + Integer oid = new Integer (this.realInputStream.readInt ()); + ret_val = ((ObjectIdentityWrapper) + this.objectLookupTable.get (oid)).object; + break; + } + + case TC_CLASS: + { + ObjectStreamClass osc = (ObjectStreamClass)readObject (); + Class clazz = osc.forClass (); + assignNewHandle (clazz); + ret_val = clazz; + break; + } + + case TC_CLASSDESC: + { +// DEBUG ("CLASSDESC NAME "); + String name = this.realInputStream.readUTF (); +// DEBUG ("UID "); + long uid = this.realInputStream.readLong (); +// DEBUG ("FLAGS "); + byte flags = this.realInputStream.readByte (); +// DEBUG ("FIELD COUNT "); + short field_count = this.realInputStream.readShort (); + ObjectStreamField[] fields = new ObjectStreamField[field_count]; + + ObjectStreamClass osc = new ObjectStreamClass (name, uid, + flags, fields); + assignNewHandle (osc); + + for (int i=0; i < field_count; i++) + { +// DEBUG ("TYPE CODE "); + char type_code = (char)this.realInputStream.readByte (); +// DEBUG ("FIELD NAME "); + String field_name = this.realInputStream.readUTF (); + String class_name; + + if (type_code == 'L' || type_code == '[') + class_name = (String)readObject (); + else + class_name = String.valueOf (type_code); + + fields[i] = + new ObjectStreamField (field_name, + TypeSignature.getClassForEncoding + (class_name)); + } + + setBlockDataMode (true); + osc.setClass (resolveClass (osc)); + setBlockDataMode (false); + +// DEBUG ("ENDBLOCKDATA "); + if (this.realInputStream.readByte () != TC_ENDBLOCKDATA) + throw new IOException ("Data annotated to class was not consumed."); + + osc.setSuperclass ((ObjectStreamClass)readObject ()); + ret_val = osc; + break; + } + + case TC_STRING: + { +// DEBUG ("STRING "); + String s = this.realInputStream.readUTF (); + ret_val = processResoultion (s, assignNewHandle (s)); + break; + } + + case TC_ARRAY: + { + ObjectStreamClass osc = (ObjectStreamClass)readObject (); + Class componenetType = osc.forClass ().getComponentType (); +// DEBUG ("ARRAY LENGTH "); + int length = this.realInputStream.readInt (); + Object array = Array.newInstance (componenetType, length); + int handle = assignNewHandle (array); + readArrayElements (array, componenetType); + ret_val = processResoultion (array, handle); + break; + } + + case TC_OBJECT: + { + ObjectStreamClass osc = (ObjectStreamClass)readObject (); + Class clazz = osc.forClass (); + + if (!Serializable.class.isAssignableFrom (clazz)) + throw new NotSerializableException (clazz + " is not Serializable, and thus cannot be deserialized."); + + if (Externalizable.class.isAssignableFrom (clazz)) + { + Externalizable obj = null; + + try + { + obj = (Externalizable)clazz.newInstance (); + } + catch (InstantiationException e) + { + throw new ClassNotFoundException ("Instance of " + clazz + + " could not be created"); + } + catch (IllegalAccessException e) + { + throw new ClassNotFoundException ("Instance of " + clazz + + " could not be created because class or zero-argument constructor is not accessible"); + } + catch (NoSuchMethodError e) + { + throw new ClassNotFoundException ("Instance of " + clazz + + " could not be created because zero-argument constructor is not defined"); + } + + int handle = assignNewHandle (obj); + + boolean read_from_blocks = ((osc.getFlags () & SC_BLOCK_DATA) != 0); + + if (read_from_blocks) + setBlockDataMode (true); + + obj.readExternal (this); + + if (read_from_blocks) + setBlockDataMode (false); + + ret_val = processResoultion (obj, handle); + break; + } // end if (Externalizable.class.isAssignableFrom (clazz)) + + // find the first non-serializable, non-abstract + // class in clazz's inheritance hierarchy + Class first_nonserial = clazz.getSuperclass (); + while (Serializable.class.isAssignableFrom (first_nonserial) + || Modifier.isAbstract (first_nonserial.getModifiers ())) + first_nonserial = first_nonserial.getSuperclass (); + +// DEBUGln ("Using " + first_nonserial +// + " as starting point for constructing " + clazz); + + Object obj = null; + obj = newObject (clazz, first_nonserial); + + if (obj == null) + throw new ClassNotFoundException ("Instance of " + clazz + + " could not be created"); + + int handle = assignNewHandle (obj); + this.currentObject = obj; + ObjectStreamClass[] hierarchy = + ObjectStreamClass.getObjectStreamClasses (clazz); + +// DEBUGln ("Got class hierarchy of depth " + hierarchy.length); + + boolean has_read; + for (int i=0; i < hierarchy.length; i++) + { + this.currentObjectStreamClass = hierarchy[i]; + +// DEBUGln ("Reading fields of " +// + this.currentObjectStreamClass.getName ()); + + has_read = true; + + try + { + this.currentObjectStreamClass.forClass (). + getDeclaredMethod ("readObject", readObjectParams); + } + catch (NoSuchMethodException e) + { + has_read = false; + } + + // XXX: should initialize fields in classes in the hierarchy + // that aren't in the stream + // should skip over classes in the stream that aren't in the + // real classes hierarchy + readFields (obj, this.currentObjectStreamClass.fields, + has_read, this.currentObjectStreamClass); + + if (has_read) + { +// DEBUG ("ENDBLOCKDATA? "); + if (this.realInputStream.readByte () != TC_ENDBLOCKDATA) + throw new IOException ("No end of block data seen for class with readObject (ObjectInputStream) method."); + } + } + + this.currentObject = null; + this.currentObjectStreamClass = null; + ret_val = processResoultion (obj, handle); + break; + } + + case TC_RESET: + clearHandles (); + ret_val = readObject (); + break; + + case TC_EXCEPTION: + { + Exception e = (Exception)readObject (); + clearHandles (); + throw new WriteAbortedException ("Exception thrown during writing of stream", e); + } + + default: + throw new IOException ("Unknown marker on stream"); + } + + this.isDeserializing = was_deserializing; + + if (! was_deserializing) + { + setBlockDataMode (true); + + if (validators.size () > 0) + invokeValidators (); + } + + return ret_val; + } + + + /** + Reads the current objects non-transient, non-static fields from + the current class from the underlying output stream. + + This method is intended to be called from within a object's + <code>private void readObject (ObjectInputStream)</code> + method. + + @exception ClassNotFoundException The class that an object being + read in belongs to cannot be found. + + @exception NotActiveException This method was called from a + context other than from the current object's and current class's + <code>private void readObject (ObjectInputStream)</code> + method. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + */ + public void defaultReadObject () + throws ClassNotFoundException, IOException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException ("defaultReadObject called by non-active class and/or object"); + + if (fieldsAlreadyRead) + throw new NotActiveException ("defaultReadObject called but fields already read from stream (by defaultReadObject or readFields)"); + + readFields (this.currentObject, + this.currentObjectStreamClass.fields, + false, this.currentObjectStreamClass); + + fieldsAlreadyRead = true; + } + + + /** + Registers a <code>ObjectInputValidation</code> to be carried out + on the object graph currently being deserialized before it is + returned to the original caller of <code>readObject ()</code>. + The order of validation for multiple + <code>ObjectInputValidation</code>s can be controled using + <code>priority</code>. Validators with higher priorities are + called first. + + @see java.io.ObjectInputValidation + + @exception InvalidObjectException <code>validator</code> is + <code>null</code> + + @exception NotActiveException an attempt was made to add a + validator outside of the <code>readObject</code> method of the + object currently being deserialized + */ + public void registerValidation (ObjectInputValidation validator, + int priority) + throws InvalidObjectException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException ("registerValidation called by non-active class and/or object"); + + if (validator == null) + throw new InvalidObjectException ("attempt to add a null ObjectInputValidation object"); + + this.validators.addElement (new ValidatorAndPriority (validator, + priority)); + } + + + /** + Called when a class is being deserialized. This is a hook to + allow subclasses to read in information written by the + <code>annotateClass (Class)</code> method of an + <code>ObjectOutputStream</code>. + + This implementation looks up the active call stack for a + <code>ClassLoader</code>; if a <code>ClassLoader</code> is found, + it is used to load the class associated with <code>osc</code>, + otherwise, the default system <code>ClassLoader</code> is used. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + + @see java.io.ObjectOutputStream#annotateClass (java.lang.Class) + */ + protected Class resolveClass (ObjectStreamClass osc) + throws ClassNotFoundException, IOException + { +// DEBUGln ("Resolving " + osc); + + SecurityManager sm = System.getSecurityManager (); + + if (sm == null) + sm = new SecurityManager () {}; + + ClassLoader cl = currentClassLoader (sm); + + if (cl == null) + { +// DEBUGln ("No class loader found"); + return Class.forName (osc.getName ()); + } + else + { +// DEBUGln ("Using " + cl); + return cl.loadClass (osc.getName ()); + } + } + + + /** + Allows subclasses to resolve objects that are read from the + stream with other objects to be returned in their place. This + method is called the first time each object is encountered. + + This method must be enabled before it will be called in the + serialization process. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + + @see enableResolveObject (boolean) + */ + protected Object resolveObject (Object obj) throws IOException + { + return obj; + } + + + /** + If <code>enable</code> is <code>true</code> and this object is + trusted, then <code>resolveObject (Object)</code> will be called + in subsequent calls to <code>readObject (Object)</code>. + Otherwise, <code>resolveObject (Object)</code> will not be called. + + @exception SecurityException This class is not trusted. + */ + protected boolean enableResolveObject (boolean enable) + throws SecurityException + { + if (enable) + if (getClass ().getClassLoader () != null) + throw new SecurityException ("Untrusted ObjectInputStream subclass attempted to enable object resolution"); + + boolean old_val = this.resolveEnabled; + this.resolveEnabled = enable; + return old_val; + } + + + /** + Reads stream magic and stream version information from the + underlying stream. + + @exception IOException Exception from underlying stream. + + @exception StreamCorruptedException An invalid stream magic + number or stream version was read from the stream. + */ + protected void readStreamHeader () + throws IOException, StreamCorruptedException + { +// DEBUG ("STREAM MAGIC "); + if (this.realInputStream.readShort () != STREAM_MAGIC) + throw new StreamCorruptedException ("Invalid stream magic number"); + +// DEBUG ("STREAM VERSION "); + if (this.realInputStream.readShort () != STREAM_VERSION) + throw new StreamCorruptedException ("Invalid stream version number"); + } + + + public int read () throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock (); + return this.blockData[this.blockDataPosition++]; + } + else + return this.realInputStream.read (); + } + + public int read (byte data[], int offset, int length) throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition + length >= this.blockDataBytes) + readNextBlock (); + + System.arraycopy (this.blockData, this.blockDataPosition, + data, offset, length); + return length; + } + else + return this.realInputStream.read (data, offset, length); + } + + public int available () throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock (); + + return this.blockDataBytes - this.blockDataPosition; + } + else + return this.realInputStream.available (); + } + + public void close () throws IOException + { + this.realInputStream.close (); + } + + public boolean readBoolean () throws IOException + { + return this.dataInputStream.readBoolean (); + } + + public byte readByte () throws IOException + { + return this.dataInputStream.readByte (); + } + + public int readUnsignedByte () throws IOException + { + return this.dataInputStream.readUnsignedByte (); + } + + public short readShort () throws IOException + { + return this.dataInputStream.readShort (); + } + + public int readUnsignedShort () throws IOException + { + return this.dataInputStream.readUnsignedShort (); + } + + public char readChar () throws IOException + { + return this.dataInputStream.readChar (); + } + + public int readInt () throws IOException + { + return this.dataInputStream.readInt (); + } + + public long readLong () throws IOException + { + return this.dataInputStream.readLong (); + } + + public float readFloat () throws IOException + { + return this.dataInputStream.readFloat (); + } + + public double readDouble () throws IOException + { + return this.dataInputStream.readDouble (); + } + + public void readFully (byte data[]) throws IOException + { + this.dataInputStream.readFully (data); + } + + public void readFully (byte data[], int offset, int size) + throws IOException + { + this.dataInputStream.readFully (data, offset, size); + } + + public int skipBytes (int len) throws IOException + { + return this.dataInputStream.skipBytes (len); + } + + /** + @deprecated + @see java.io.DataInputStream#readLine () + */ + public String readLine () throws IOException + { + return this.dataInputStream.readLine (); + } + + public String readUTF () throws IOException + { + return this.dataInputStream.readUTF (); + } + + + /** + This class allows a class to specify exactly which fields should + be read, and what values should be read for these fields. + + XXX: finish up comments + */ + public static abstract class GetField + { + public abstract ObjectStreamClass getObjectStreamClass (); + + public abstract boolean defaulted (String name) + throws IOException, IllegalArgumentException; + + public abstract boolean get (String name, boolean defvalue) + throws IOException, IllegalArgumentException; + + public abstract char get (String name, char defvalue) + throws IOException, IllegalArgumentException; + + public abstract byte get (String name, byte defvalue) + throws IOException, IllegalArgumentException; + + public abstract short get (String name, short defvalue) + throws IOException, IllegalArgumentException; + + public abstract int get (String name, int defvalue) + throws IOException, IllegalArgumentException; + + public abstract long get (String name, long defvalue) + throws IOException, IllegalArgumentException; + + public abstract float get (String name, float defvalue) + throws IOException, IllegalArgumentException; + + public abstract double get (String name, double defvalue) + throws IOException, IllegalArgumentException; + + public abstract Object get (String name, Object defvalue) + throws IOException, IllegalArgumentException; + } + + public GetField readFields () + throws IOException, ClassNotFoundException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException ("readFields called by non-active class and/or object"); + + if (fieldsAlreadyRead) + throw new NotActiveException ("readFields called but fields already read from stream (by defaultReadObject or readFields)"); + + final ObjectStreamClass clazz = this.currentObjectStreamClass; + final byte[] prim_field_data = new byte[clazz.primFieldSize]; + final Object[] objs = new Object[clazz.objectFieldCount]; + readFully (prim_field_data); + for (int i = 0; i < objs.length; ++ i) + objs[i] = readObject (); + + return new GetField () + { + public ObjectStreamClass getObjectStreamClass () + { + return clazz; + } + + public boolean defaulted (String name) + throws IOException, IllegalArgumentException + { + return clazz.getField (name) == null; + } + + public boolean get (String name, boolean defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Boolean.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset ()] == 0 ? false : true; + } + + public char get (String name, char defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Character.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return (char)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public byte get (String name, byte defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Byte.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset ()]; + } + + public short get (String name, short defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Short.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return (short)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public int get (String name, int defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Integer.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF); + } + + public long get (String name, long defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Long.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return (long)(((prim_field_data[off++] & 0xFF) << 56) + | ((prim_field_data[off++] & 0xFF) << 48) + | ((prim_field_data[off++] & 0xFF) << 40) + | ((prim_field_data[off++] & 0xFF) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public float get (String name, float defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Float.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return Float.intBitsToFloat (((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public double get (String name, double defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Double.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return Double.longBitsToDouble ( + (long)(((prim_field_data[off++] & 0xFF) << 56) + | ((prim_field_data[off++] & 0xFF) << 48) + | ((prim_field_data[off++] & 0xFF) << 40) + | ((prim_field_data[off++] & 0xFF) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF))); + } + + public Object get (String name, Object defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, null); + + if (field == null) + return defvalue; + + return objs[field.getOffset ()]; + } + + private ObjectStreamField getField (String name, Class type) + throws IllegalArgumentException + { + ObjectStreamField field = clazz.getField (name); + + if (field == null) + return null; + + Class field_type = field.getType (); + + if (type == field_type || + (type != null && field_type.isPrimitive ())) + return field; + + throw new IllegalArgumentException ("Field requested is of type " + + field_type.getName () + + ", but requested type was " + + (type == null ? + "Object" : type.getName ())); + } + }; + + } + + + /** + Protected constructor that allows subclasses to override + deserialization. This constructor should be called by subclasses + that wish to override <code>readObject (Object)</code>. This + method does a security check <i>NOTE: currently not + implemented</i>, then sets a flag that informs + <code>readObject (Object)</code> to call the subclasses + <code>readObjectOverride (Object)</code> method. + + @see readObjectOverride (Object) + */ + protected ObjectInputStream () + throws IOException, SecurityException + { + SecurityManager sec_man = System.getSecurityManager (); + if (sec_man != null) + sec_man.checkPermission (SUBCLASS_IMPLEMENTATION_PERMISSION); + this.useSubclassMethod = true; + } + + + /** + This method allows subclasses to override the default + de serialization mechanism provided by + <code>ObjectInputStream</code>. To make this method be used for + writing objects, subclasses must invoke the 0-argument + constructor on this class from there constructor. + + @see ObjectInputStream () + */ + protected Object readObjectOverride () + throws ClassNotFoundException, IOException, OptionalDataException + { + throw new IOException ("Subclass of ObjectInputStream must implement readObjectOverride"); + } + + + // assigns the next availible handle to OBJ + private int assignNewHandle (Object obj) + { + this.objectLookupTable.put (new Integer (this.nextOID), + new ObjectIdentityWrapper (obj)); + +// try +// { +// DEBUG ("Assigning handle " + this.nextOID); +// DEBUGln (" to " + obj); +// } +// catch (Throwable t) {} + + return this.nextOID++; + } + + + private Object processResoultion (Object obj, int handle) + throws IOException + { + if (obj instanceof Resolvable) + obj = ((Resolvable)obj).readResolve (); + + if (this.resolveEnabled) + obj = resolveObject (obj); + + this.objectLookupTable.put (new Integer (handle), + new ObjectIdentityWrapper (obj)); + + return obj; + } + + + private void clearHandles () + { + this.objectLookupTable.clear (); + this.nextOID = baseWireHandle; + } + + + private void readNextBlock () throws IOException + { +// DEBUG ("MARKER "); + readNextBlock (this.realInputStream.readByte ()); + } + + + private void readNextBlock (byte marker) throws IOException + { + if (marker == TC_BLOCKDATA) + { +// DEBUG ("BLOCK DATA SIZE "); + this.blockDataBytes = this.realInputStream.readUnsignedByte (); + } + else if (marker == TC_BLOCKDATALONG) + { +// DEBUG ("BLOCK DATA LONG SIZE "); + this.blockDataBytes = this.realInputStream.readInt (); + } + else + { + throw new EOFException ("Attempt to read primitive data, but no data block is active."); + } + + if (this.blockData.length < this.blockDataBytes) + this.blockData = new byte[this.blockDataBytes]; + + this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes); + this.blockDataPosition = 0; + } + + + private void readArrayElements (Object array, Class clazz) + throws ClassNotFoundException, IOException + { + if (clazz.isPrimitive ()) + { + if (clazz == Boolean.TYPE) + { + boolean[] cast_array = (boolean[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readBoolean (); + return; + } + if (clazz == Byte.TYPE) + { + byte[] cast_array = (byte[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readByte (); + return; + } + if (clazz == Character.TYPE) + { + char[] cast_array = (char[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readChar (); + return; + } + if (clazz == Double.TYPE) + { + double[] cast_array = (double[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readDouble (); + return; + } + if (clazz == Float.TYPE) + { + float[] cast_array = (float[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readFloat (); + return; + } + if (clazz == Integer.TYPE) + { + int[] cast_array = (int[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readInt (); + return; + } + if (clazz == Long.TYPE) + { + long[] cast_array = (long[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readLong (); + return; + } + if (clazz == Short.TYPE) + { + short[] cast_array = (short[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readShort (); + return; + } + } + else + { + Object[] cast_array = (Object[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = readObject (); + } + } + + + private void readFields (Object obj, ObjectStreamField[] stream_fields, + boolean call_read_method, + ObjectStreamClass stream_osc) + throws ClassNotFoundException, IOException + { + if (call_read_method) + { + fieldsAlreadyRead = false; + setBlockDataMode (true); + callReadMethod (obj, stream_osc.forClass ()); + setBlockDataMode (false); + return; + } + + ObjectStreamField[] real_fields = + ObjectStreamClass.lookup (stream_osc.forClass ()).fields; + + boolean default_initialize, set_value; + String field_name = null; + Class type = null; + ObjectStreamField stream_field = null; + ObjectStreamField real_field = null; + int stream_idx = 0; + int real_idx = 0; + + while (stream_idx < stream_fields.length + && real_idx < real_fields.length) + { + default_initialize = false; + set_value = true; + + if (stream_idx == stream_fields.length) + default_initialize = true; + else + { + stream_field = stream_fields[stream_idx]; + type = stream_field.getType (); + } + + if (real_idx == real_fields.length) + set_value = false; + else + { + real_field = real_fields[real_idx]; + type = real_field.getType (); + field_name = real_field.getName (); + } + + if (set_value && !default_initialize) + { + int comp_val = + real_field.compareTo (stream_field); + + if (comp_val < 0) + { + default_initialize = true; + real_idx++; + } + else if (comp_val > 0) + { + set_value = false; + stream_idx++; + } + else + { + real_idx++; + stream_idx++; + } + } + + if (type == Boolean.TYPE) + { + boolean value = + default_initialize ? false : this.realInputStream.readBoolean (); + if (set_value) + setBooleanField (obj, field_name, value); + } + else if (type == Byte.TYPE) + { + byte value = + default_initialize ? 0 : this.realInputStream.readByte (); + if (set_value) + setByteField (obj, field_name, value); + } + else if (type == Character.TYPE) + { + char value = + default_initialize ? (char)0 : this.realInputStream.readChar (); + if (set_value) + setCharField (obj, field_name, value); + } + else if (type == Double.TYPE) + { + double value = + default_initialize ? 0 : this.realInputStream.readDouble (); + if (set_value) + setDoubleField (obj, field_name, value); + } + else if (type == Float.TYPE) + { + float value = + default_initialize ? 0 : this.realInputStream.readFloat (); + if (set_value) + setFloatField (obj, field_name, value); + } + else if (type == Integer.TYPE) + { + int value = + default_initialize ? 0 : this.realInputStream.readInt (); + if (set_value) + setIntField (obj, field_name, value); + } + else if (type == Long.TYPE) + { + long value = + default_initialize ? 0 : this.realInputStream.readLong (); + if (set_value) + setLongField (obj, field_name, value); + } + else if (type == Short.TYPE) + { + short value = + default_initialize ? (short)0 : this.realInputStream.readShort (); + if (set_value) + setShortField (obj, field_name, value); + } + else + { + Object value = + default_initialize ? null : readObject (); + if (set_value) + setObjectField (obj, field_name, + real_field.getTypeString (), value); + } + } + } + + + // Toggles writing primitive data to block-data buffer. + private void setBlockDataMode (boolean on) + { +// DEBUGln ("Setting block data mode to " + on); + + this.readDataFromBlock = on; + + if (on) + this.dataInputStream = this.blockDataInput; + else + this.dataInputStream = this.realInputStream; + } + + + // returns a new instance of REAL_CLASS that has been constructed + // only to th level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS) + private Object newObject (Class real_class, Class constructor_class) + { + try + { + Object obj = allocateObject (real_class); + callConstructor (constructor_class, obj); + return obj; + } + catch (InstantiationException e) + { + return null; + } + } + + + // runs all registered ObjectInputValidations in prioritized order + // on OBJ + private void invokeValidators () throws InvalidObjectException + { + Object[] validators = new Object[this.validators.size ()]; + this.validators.copyInto (validators); + Arrays.sort (validators); + + try + { + for (int i=0; i < validators.length; i++) + ((ObjectInputValidation)validators[i]).validateObject (); + } + finally + { + this.validators.removeAllElements (); + } + } + + + // this native method is used to get access to the protected method + // of the same name in SecurityManger + private static ClassLoader currentClassLoader (SecurityManager sm) + { + // FIXME: This is too simple. + return ClassLoader.getSystemClassLoader (); + } + + private static native Field getField (Class klass, String name) + throws java.lang.NoSuchFieldException; + + private static native Method getMethod (Class klass, String name, Class args[]) + throws java.lang.NoSuchMethodException; + + private void callReadMethod (Object obj, Class klass) throws IOException + { + try + { + Class classArgs[] = {Class.forName ("java.io.ObjectInputStream")}; + Method m = getMethod (klass, "readObject", classArgs); + if (m == null) + return; + Object args[] = {this}; + m.invoke (obj, args); + } + catch (Exception _) + { + throw new IOException (); + } + } + + private native Object allocateObject (Class clazz) + throws InstantiationException; + + private native void callConstructor (Class clazz, Object obj); + + private void setBooleanField (Object obj, String field_name, + boolean val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setBoolean (obj, val); + } + catch (Exception _) + { + } + } + + private void setByteField (Object obj, String field_name, + byte val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setByte (obj, val); + } + catch (Exception _) + { + } + } + + private void setCharField (Object obj, String field_name, + char val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setChar (obj, val); + } + catch (Exception _) + { + } + } + + private void setDoubleField (Object obj, String field_name, + double val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setDouble (obj, val); + } + catch (Exception _) + { + } + } + + private void setFloatField (Object obj, String field_name, + float val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setFloat (obj, val); + } + catch (Exception _) + { + } + } + + private void setIntField (Object obj, String field_name, + int val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setInt (obj, val); + } + catch (Exception _) + { + } + } + + + private void setLongField (Object obj, String field_name, + long val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setLong (obj, val); + } + catch (Exception _) + { + } + } + + + private void setShortField (Object obj, String field_name, + short val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setShort (obj, val); + } + catch (Exception _) + { + } + } + + + private void setObjectField (Object obj, String field_name, String type_code, + Object val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + // FIXME: We should check the type_code here + f.set (obj, val); + } + catch (Exception _) + { + } + } + + private static final int BUFFER_SIZE = 1024; + private static final Class[] readObjectParams = { ObjectInputStream.class }; + + private DataInputStream realInputStream; + private DataInputStream dataInputStream; + private DataInputStream blockDataInput; + private int blockDataPosition; + private int blockDataBytes; + private byte[] blockData; + private boolean useSubclassMethod; + private int nextOID; + private boolean resolveEnabled; + private Hashtable objectLookupTable; + private Object currentObject; + private ObjectStreamClass currentObjectStreamClass; + private boolean readDataFromBlock; + private boolean isDeserializing; + private boolean fieldsAlreadyRead; + private Vector validators; + + +/* FIXME: These 2 methods cause a build error on i686-pc-linux-gnu. + private void DEBUG (String msg) + { + System.out.print (msg); + } + + + private void DEBUGln (String msg) + { + System.out.println (msg); + } +* end FIXME */ +} + + +// used to keep a prioritized list of object validators +class ValidatorAndPriority implements Comparable +{ + int priority; + ObjectInputValidation validator; + + ValidatorAndPriority (ObjectInputValidation validator, int priority) + { + this.priority = priority; + this.validator = validator; + } + + public int compareTo (Object o) + { + ValidatorAndPriority vap = (ValidatorAndPriority)o; + return this.priority - vap.priority; + } +} |