summaryrefslogtreecommitdiffstats
path: root/libjava/classpath/java/io/ObjectInputStream.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/java/io/ObjectInputStream.java')
-rw-r--r--libjava/classpath/java/io/ObjectInputStream.java1956
1 files changed, 1956 insertions, 0 deletions
diff --git a/libjava/classpath/java/io/ObjectInputStream.java b/libjava/classpath/java/io/ObjectInputStream.java
new file mode 100644
index 00000000000..05776a7fcdd
--- /dev/null
+++ b/libjava/classpath/java/io/ObjectInputStream.java
@@ -0,0 +1,1956 @@
+/* ObjectInputStream.java -- Class used to read serialized objects
+ Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005
+ 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., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package java.io;
+
+import gnu.classpath.Configuration;
+import gnu.java.io.ObjectIdentityWrapper;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Proxy;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Arrays;
+import java.util.Hashtable;
+import java.util.Vector;
+
+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
+ {
+ if (DEBUG)
+ {
+ String val = System.getProperty("gcj.dumpobjects");
+ if (dump == false && val != null && !val.equals(""))
+ {
+ dump = true;
+ System.out.println ("Serialization debugging enabled");
+ }
+ else if (dump == true && (val == null || val.equals("")))
+ {
+ dump = false;
+ System.out.println ("Serialization debugging disabled");
+ }
+ }
+
+ 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();
+ this.classLookupTable = new Hashtable();
+ 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;
+
+ boolean is_consumed = false;
+ boolean old_mode = setBlockDataMode(false);
+
+ this.isDeserializing = true;
+
+ byte marker = this.realInputStream.readByte();
+
+ depth += 2;
+
+ if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " ");
+
+ try
+ {
+ switch (marker)
+ {
+ case TC_ENDBLOCKDATA:
+ {
+ ret_val = null;
+ is_consumed = true;
+ break;
+ }
+
+ case TC_BLOCKDATA:
+ case TC_BLOCKDATALONG:
+ {
+ if (marker == TC_BLOCKDATALONG)
+ { if(dump) dumpElementln("BLOCKDATALONG"); }
+ else
+ { if(dump) dumpElementln("BLOCKDATA"); }
+ readNextBlock(marker);
+ throw new StreamCorruptedException("Unexpected blockData");
+ }
+
+ case TC_NULL:
+ {
+ if(dump) dumpElementln("NULL");
+ ret_val = null;
+ break;
+ }
+
+ case TC_REFERENCE:
+ {
+ if(dump) dumpElement("REFERENCE ");
+ Integer oid = new Integer(this.realInputStream.readInt());
+ if(dump) dumpElementln(Integer.toHexString(oid.intValue()));
+ ret_val = ((ObjectIdentityWrapper)
+ this.objectLookupTable.get(oid)).object;
+ break;
+ }
+
+ case TC_CLASS:
+ {
+ if(dump) dumpElementln("CLASS");
+ ObjectStreamClass osc = (ObjectStreamClass)readObject();
+ Class clazz = osc.forClass();
+ assignNewHandle(clazz);
+ ret_val = clazz;
+ break;
+ }
+
+ case TC_PROXYCLASSDESC:
+ {
+ if(dump) dumpElementln("PROXYCLASS");
+ int n_intf = this.realInputStream.readInt();
+ String[] intfs = new String[n_intf];
+ for (int i = 0; i < n_intf; i++)
+ {
+ intfs[i] = this.realInputStream.readUTF();
+ System.out.println(intfs[i]);
+ }
+
+ boolean oldmode = setBlockDataMode(true);
+ Class cl = resolveProxyClass(intfs);
+ setBlockDataMode(oldmode);
+
+ ObjectStreamClass osc = lookupClass(cl);
+ assignNewHandle(osc);
+
+ if (!is_consumed)
+ {
+ byte b = this.realInputStream.readByte();
+ if (b != TC_ENDBLOCKDATA)
+ throw new IOException("Data annotated to class was not consumed." + b);
+ }
+ else
+ is_consumed = false;
+ ObjectStreamClass superosc = (ObjectStreamClass)readObject();
+ osc.setSuperclass(superosc);
+ ret_val = osc;
+ break;
+ }
+
+ case TC_CLASSDESC:
+ {
+ ObjectStreamClass osc = readClassDescriptor();
+
+ if (!is_consumed)
+ {
+ byte b = this.realInputStream.readByte();
+ if (b != TC_ENDBLOCKDATA)
+ throw new IOException("Data annotated to class was not consumed." + b);
+ }
+ else
+ is_consumed = false;
+
+ osc.setSuperclass ((ObjectStreamClass)readObject());
+ ret_val = osc;
+ break;
+ }
+
+ case TC_STRING:
+ case TC_LONGSTRING:
+ {
+ if(dump) dumpElement("STRING=");
+ String s = this.realInputStream.readUTF();
+ if(dump) dumpElementln(s);
+ ret_val = processResolution(null, s, assignNewHandle(s));
+ break;
+ }
+
+ case TC_ARRAY:
+ {
+ if(dump) dumpElementln("ARRAY");
+ ObjectStreamClass osc = (ObjectStreamClass)readObject();
+ Class componentType = osc.forClass().getComponentType();
+ if(dump) dumpElement("ARRAY LENGTH=");
+ int length = this.realInputStream.readInt();
+ if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType);
+ Object array = Array.newInstance(componentType, length);
+ int handle = assignNewHandle(array);
+ readArrayElements(array, componentType);
+ if(dump)
+ for (int i = 0, len = Array.getLength(array); i < len; i++)
+ dumpElementln(" ELEMENT[" + i + "]=" + Array.get(array, i));
+ ret_val = processResolution(null, array, handle);
+ break;
+ }
+
+ case TC_OBJECT:
+ {
+ if(dump) dumpElementln("OBJECT");
+ ObjectStreamClass osc = (ObjectStreamClass)readObject();
+ Class clazz = osc.forClass();
+
+ if (!osc.realClassIsSerializable)
+ throw new NotSerializableException
+ (clazz + " is not Serializable, and thus cannot be deserialized.");
+
+ if (osc.realClassIsExternalizable)
+ {
+ Externalizable obj = osc.newInstance();
+
+ int handle = assignNewHandle(obj);
+
+ boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0);
+
+ boolean oldmode = this.readDataFromBlock;
+ if (read_from_blocks)
+ setBlockDataMode(true);
+
+ obj.readExternal(this);
+
+ if (read_from_blocks)
+ {
+ setBlockDataMode(oldmode);
+ if (!oldmode)
+ if (this.realInputStream.readByte() != TC_ENDBLOCKDATA)
+ throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method.");
+ }
+
+ ret_val = processResolution(osc, obj, handle);
+ break;
+ } // end if (osc.realClassIsExternalizable)
+
+ Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor);
+
+ int handle = assignNewHandle(obj);
+ Object prevObject = this.currentObject;
+ ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
+
+ this.currentObject = obj;
+ ObjectStreamClass[] hierarchy =
+ inputGetObjectStreamClasses(clazz);
+
+ for (int i = 0; i < hierarchy.length; i++)
+ {
+ this.currentObjectStreamClass = hierarchy[i];
+
+ if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ());
+
+ // 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
+
+ Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod;
+ if (readObjectMethod != null)
+ {
+ fieldsAlreadyRead = false;
+ boolean oldmode = setBlockDataMode(true);
+ callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj);
+ setBlockDataMode(oldmode);
+ }
+ else
+ {
+ readFields(obj, currentObjectStreamClass);
+ }
+
+ if (this.currentObjectStreamClass.hasWriteMethod())
+ {
+ if(dump) dumpElement("ENDBLOCKDATA? ");
+ try
+ {
+ // FIXME: XXX: This try block is to
+ // catch EOF which is thrown for some
+ // objects. That indicates a bug in
+ // the logic.
+
+ if (this.realInputStream.readByte() != TC_ENDBLOCKDATA)
+ throw new IOException
+ ("No end of block data seen for class with readObject (ObjectInputStream) method.");
+ if(dump) dumpElementln("yes");
+ }
+// catch (EOFException e)
+// {
+// if(dump) dumpElementln("no, got EOFException");
+// }
+ catch (IOException e)
+ {
+ if(dump) dumpElementln("no, got IOException");
+ }
+ }
+ }
+
+ this.currentObject = prevObject;
+ this.currentObjectStreamClass = prevObjectStreamClass;
+ ret_val = processResolution(osc, obj, handle);
+
+ break;
+ }
+
+ case TC_RESET:
+ if(dump) dumpElementln("RESET");
+ clearHandles();
+ ret_val = readObject();
+ break;
+
+ case TC_EXCEPTION:
+ {
+ if(dump) dumpElement("EXCEPTION=");
+ Exception e = (Exception)readObject();
+ if(dump) dumpElementln(e.toString());
+ clearHandles();
+ throw new WriteAbortedException("Exception thrown during writing of stream", e);
+ }
+
+ default:
+ throw new IOException("Unknown marker on stream: " + marker);
+ }
+ }
+ finally
+ {
+ setBlockDataMode(old_mode);
+
+ this.isDeserializing = was_deserializing;
+
+ depth -= 2;
+
+ if (! was_deserializing)
+ {
+ if (validators.size() > 0)
+ invokeValidators();
+ }
+ }
+
+ return ret_val;
+ }
+
+ /**
+ * This method makes a partial check of types for the fields
+ * contained given in arguments. It checks primitive types of
+ * fields1 against non primitive types of fields2. This method
+ * assumes the two lists has already been sorted according to
+ * the Java specification.
+ *
+ * @param name Name of the class owning the given fields.
+ * @param fields1 First list to check.
+ * @param fields2 Second list to check.
+ * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present
+ * in the non primitive part in fields2.
+ */
+ private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2)
+ throws InvalidClassException
+ {
+ int nonPrimitive = 0;
+
+ for (nonPrimitive = 0;
+ nonPrimitive < fields1.length
+ && fields1[nonPrimitive].isPrimitive(); nonPrimitive++)
+ {
+ }
+
+ if (nonPrimitive == fields1.length)
+ return;
+
+ int i = 0;
+ ObjectStreamField f1;
+ ObjectStreamField f2;
+
+ while (i < fields2.length
+ && nonPrimitive < fields1.length)
+ {
+ f1 = fields1[nonPrimitive];
+ f2 = fields2[i];
+
+ if (!f2.isPrimitive())
+ break;
+
+ int compVal = f1.getName().compareTo (f2.getName());
+
+ if (compVal < 0)
+ {
+ nonPrimitive++;
+ }
+ else if (compVal > 0)
+ {
+ i++;
+ }
+ else
+ {
+ throw new InvalidClassException
+ ("invalid field type for " + f2.getName() +
+ " in class " + name);
+ }
+ }
+ }
+
+ /**
+ * This method reads a class descriptor from the real input stream
+ * and use these data to create a new instance of ObjectStreamClass.
+ * Fields are sorted and ordered for the real read which occurs for
+ * each instance of the described class. Be aware that if you call that
+ * method you must ensure that the stream is synchronized, in the other
+ * case it may be completely desynchronized.
+ *
+ * @return A new instance of ObjectStreamClass containing the freshly
+ * created descriptor.
+ * @throws ClassNotFoundException if the required class to build the
+ * descriptor has not been found in the system.
+ * @throws IOException An input/output error occured.
+ * @throws InvalidClassException If there was a compatibility problem
+ * between the class present in the system and the serialized class.
+ */
+ protected ObjectStreamClass readClassDescriptor()
+ throws ClassNotFoundException, IOException
+ {
+ if(dump) dumpElement("CLASSDESC NAME=");
+ String name = this.realInputStream.readUTF();
+ if(dump) dumpElement(name + "; UID=");
+ long uid = this.realInputStream.readLong ();
+ if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS=");
+ byte flags = this.realInputStream.readByte ();
+ if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT=");
+ short field_count = this.realInputStream.readShort();
+ if(dump) dumpElementln(Short.toString(field_count));
+ ObjectStreamField[] fields = new ObjectStreamField[field_count];
+ ObjectStreamClass osc = new ObjectStreamClass(name, uid,
+ flags, fields);
+ assignNewHandle(osc);
+
+ if (callersClassLoader == null)
+ callersClassLoader = currentLoader();
+
+ for (int i = 0; i < field_count; i++)
+ {
+ if(dump) dumpElement(" TYPE CODE=");
+ char type_code = (char)this.realInputStream.readByte();
+ if(dump) dumpElement(type_code + "; FIELD NAME=");
+ String field_name = this.realInputStream.readUTF();
+ if(dump) dumpElementln(field_name);
+ String class_name;
+
+ // If the type code is an array or an object we must
+ // decode a String here. In the other case we convert
+ // the type code and pass it to ObjectStreamField.
+ // Type codes are decoded by gnu.java.lang.reflect.TypeSignature.
+ if (type_code == 'L' || type_code == '[')
+ class_name = (String)readObject();
+ else
+ class_name = String.valueOf(type_code);
+
+ fields[i] =
+ new ObjectStreamField(field_name, class_name, callersClassLoader);
+ }
+
+ /* Now that fields have been read we may resolve the class
+ * (and read annotation if needed). */
+ Class clazz;
+ try
+ {
+ clazz = resolveClass(osc);
+ }
+ catch (ClassNotFoundException cnfe)
+ {
+ // Maybe it was an primitive class?
+ if (name.equals("void"))
+ clazz = Void.TYPE;
+ else if (name.equals("boolean"))
+ clazz = Boolean.TYPE;
+ else if (name.equals("byte"))
+ clazz = Byte.TYPE;
+ else if (name.equals("short"))
+ clazz = Short.TYPE;
+ else if (name.equals("char"))
+ clazz = Character.TYPE;
+ else if (name.equals("int"))
+ clazz = Integer.TYPE;
+ else if (name.equals("long"))
+ clazz = Long.TYPE;
+ else if (name.equals("float"))
+ clazz = Float.TYPE;
+ else if (name.equals("double"))
+ clazz = Double.TYPE;
+ else
+ throw cnfe;
+ }
+
+ boolean oldmode = setBlockDataMode(true);
+ osc.setClass(clazz, lookupClass(clazz.getSuperclass()));
+ classLookupTable.put(clazz, osc);
+ setBlockDataMode(oldmode);
+
+ // find the first non-serializable, non-abstract
+ // class in clazz's inheritance hierarchy
+ Class first_nonserial = clazz.getSuperclass();
+ // Maybe it is a primitive class, those don't have a super class,
+ // or Object itself. Otherwise we can keep getting the superclass
+ // till we hit the Object class, or some other non-serializable class.
+
+ if (first_nonserial == null)
+ first_nonserial = clazz;
+ else
+ while (Serializable.class.isAssignableFrom(first_nonserial)
+ || Modifier.isAbstract(first_nonserial.getModifiers()))
+ first_nonserial = first_nonserial.getSuperclass();
+
+ final Class local_constructor_class = first_nonserial;
+
+ osc.firstNonSerializableParentConstructor =
+ (Constructor)AccessController.doPrivileged(new PrivilegedAction()
+ {
+ public Object run()
+ {
+ try
+ {
+ Constructor c = local_constructor_class.
+ getDeclaredConstructor(new Class[0]);
+ if (Modifier.isPrivate(c.getModifiers()))
+ return null;
+ return c;
+ }
+ catch (NoSuchMethodException e)
+ {
+ // error will be reported later, in newObject()
+ return null;
+ }
+ }
+ });
+
+ osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz);
+ osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz);
+
+ ObjectStreamField[] stream_fields = osc.fields;
+ ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields;
+ ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)];
+
+ int stream_idx = 0;
+ int real_idx = 0;
+ int map_idx = 0;
+
+ /*
+ * Check that there is no type inconsistencies between the lists.
+ * A special checking must be done for the two groups: primitive types and
+ * not primitive types.
+ */
+ checkTypeConsistency(name, real_fields, stream_fields);
+ checkTypeConsistency(name, stream_fields, real_fields);
+
+
+ while (stream_idx < stream_fields.length
+ || real_idx < real_fields.length)
+ {
+ ObjectStreamField stream_field = null;
+ ObjectStreamField real_field = null;
+
+ if (stream_idx == stream_fields.length)
+ {
+ real_field = real_fields[real_idx++];
+ }
+ else if (real_idx == real_fields.length)
+ {
+ stream_field = stream_fields[stream_idx++];
+ }
+ else
+ {
+ int comp_val =
+ real_fields[real_idx].compareTo (stream_fields[stream_idx]);
+
+ if (comp_val < 0)
+ {
+ real_field = real_fields[real_idx++];
+ }
+ else if (comp_val > 0)
+ {
+ stream_field = stream_fields[stream_idx++];
+ }
+ else
+ {
+ stream_field = stream_fields[stream_idx++];
+ real_field = real_fields[real_idx++];
+ if (stream_field.getType() != real_field.getType())
+ throw new InvalidClassException
+ ("invalid field type for " + real_field.getName() +
+ " in class " + name);
+ }
+ }
+
+ /* If some of stream_fields does not correspond to any of real_fields,
+ * or the opposite, then fieldmapping will go short.
+ */
+ if (map_idx == fieldmapping.length)
+ {
+ ObjectStreamField[] newfieldmapping =
+ new ObjectStreamField[fieldmapping.length + 2];
+ System.arraycopy(fieldmapping, 0,
+ newfieldmapping, 0, fieldmapping.length);
+ fieldmapping = newfieldmapping;
+ }
+ fieldmapping[map_idx++] = stream_field;
+ fieldmapping[map_idx++] = real_field;
+ }
+ osc.fieldMapping = fieldmapping;
+
+ return osc;
+ }
+
+ /**
+ * 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)");
+
+ boolean oldmode = setBlockDataMode(false);
+ readFields(this.currentObject, this.currentObjectStreamClass);
+ setBlockDataMode(oldmode);
+
+ 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
+ {
+ if (callersClassLoader == null)
+ {
+ callersClassLoader = currentLoader ();
+ if (DEBUG && dump)
+ {
+ dumpElementln ("CallersClassLoader = " + callersClassLoader);
+ }
+ }
+
+ return Class.forName(osc.getName(), true, callersClassLoader);
+ }
+
+ /**
+ * Returns the most recent user defined ClassLoader on the execution stack
+ * or null if none is found.
+ */
+ private ClassLoader currentLoader()
+ {
+ return VMObjectInputStream.currentClassLoader();
+ }
+
+ /**
+ * Lookup a class stored in the local hashtable. If it is not
+ * use the global lookup function in ObjectStreamClass to build
+ * the ObjectStreamClass. This method is requested according to
+ * the behaviour detected in the JDK by Kaffe's team.
+ *
+ * @param clazz Class to lookup in the hash table or for which
+ * we must build a descriptor.
+ * @return A valid instance of ObjectStreamClass corresponding
+ * to the specified class.
+ */
+ private ObjectStreamClass lookupClass(Class clazz)
+ {
+ if (clazz == null)
+ return null;
+
+ ObjectStreamClass oclazz;
+ oclazz = (ObjectStreamClass)classLookupTable.get(clazz);
+ if (oclazz == null)
+ return ObjectStreamClass.lookup(clazz);
+ else
+ return oclazz;
+ }
+
+ /**
+ * Reconstruct class hierarchy the same way
+ * {@link java.io.ObjectStreamClass.getObjectStreamClasses(java.lang.Class)} does
+ * but using lookupClass instead of ObjectStreamClass.lookup. This
+ * dup is necessary localize the lookup table. Hopefully some future
+ * rewritings will be able to prevent this.
+ *
+ * @param clazz This is the class for which we want the hierarchy.
+ *
+ * @return An array of valid {@link java.io.ObjectStreamClass} instances which
+ * represent the class hierarchy for clazz.
+ */
+ private ObjectStreamClass[] inputGetObjectStreamClasses(Class clazz)
+ {
+ ObjectStreamClass osc = lookupClass(clazz);
+
+ if (osc == null)
+ return new ObjectStreamClass[0];
+ else
+ {
+ Vector oscs = new Vector();
+
+ while (osc != null)
+ {
+ oscs.addElement(osc);
+ osc = osc.getSuper();
+ }
+
+ int count = oscs.size();
+ ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[count];
+
+ for (int i = count - 1; i >= 0; i--)
+ sorted_oscs[count - i - 1] = (ObjectStreamClass) oscs.elementAt(i);
+
+ return sorted_oscs;
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+
+ protected Class resolveProxyClass(String[] intfs)
+ throws IOException, ClassNotFoundException
+ {
+ ClassLoader cl = currentLoader();
+
+ Class[] clss = new Class[intfs.length];
+ if(cl == null)
+ {
+ for (int i = 0; i < intfs.length; i++)
+ clss[i] = Class.forName(intfs[i]);
+ cl = ClassLoader.getSystemClassLoader();
+ }
+ else
+ for (int i = 0; i < intfs.length; i++)
+ clss[i] = cl.loadClass(intfs[i]);
+ try
+ {
+ return Proxy.getProxyClass(cl, clss);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new ClassNotFoundException(null, e);
+ }
+ }
+
+ /**
+ * 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)
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if (sm != null)
+ sm.checkPermission(new SerializablePermission("enableSubstitution"));
+ }
+
+ 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
+ {
+ if(dump) dumpElement("STREAM MAGIC ");
+ if (this.realInputStream.readShort() != STREAM_MAGIC)
+ throw new StreamCorruptedException("Invalid stream magic number");
+
+ if(dump) dumpElementln("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++] & 0xff);
+ }
+ 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)
+ {
+ int remain = this.blockDataBytes - this.blockDataPosition;
+ if (remain != 0)
+ {
+ System.arraycopy(this.blockData, this.blockDataPosition,
+ data, offset, remain);
+ offset += remain;
+ length -= remain;
+ }
+ readNextBlock ();
+ }
+
+ System.arraycopy(this.blockData, this.blockDataPosition,
+ data, offset, length);
+ this.blockDataPosition += 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
+ {
+ boolean switchmode = true;
+ boolean oldmode = this.readDataFromBlock;
+ if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
+ switchmode = false;
+ if (switchmode)
+ oldmode = setBlockDataMode (true);
+ boolean value = this.dataInputStream.readBoolean ();
+ if (switchmode)
+ setBlockDataMode (oldmode);
+ return value;
+ }
+
+ public byte readByte() throws IOException
+ {
+ boolean switchmode = true;
+ boolean oldmode = this.readDataFromBlock;
+ if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
+ switchmode = false;
+ if (switchmode)
+ oldmode = setBlockDataMode(true);
+ byte value = this.dataInputStream.readByte();
+ if (switchmode)
+ setBlockDataMode(oldmode);
+ return value;
+ }
+
+ public int readUnsignedByte() throws IOException
+ {
+ boolean switchmode = true;
+ boolean oldmode = this.readDataFromBlock;
+ if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
+ switchmode = false;
+ if (switchmode)
+ oldmode = setBlockDataMode(true);
+ int value = this.dataInputStream.readUnsignedByte();
+ if (switchmode)
+ setBlockDataMode(oldmode);
+ return value;
+ }
+
+ public short readShort() throws IOException
+ {
+ boolean switchmode = true;
+ boolean oldmode = this.readDataFromBlock;
+ if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
+ switchmode = false;
+ if (switchmode)
+ oldmode = setBlockDataMode(true);
+ short value = this.dataInputStream.readShort();
+ if (switchmode)
+ setBlockDataMode(oldmode);
+ return value;
+ }
+
+ public int readUnsignedShort() throws IOException
+ {
+ boolean switchmode = true;
+ boolean oldmode = this.readDataFromBlock;
+ if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
+ switchmode = false;
+ if (switchmode)
+ oldmode = setBlockDataMode(true);
+ int value = this.dataInputStream.readUnsignedShort();
+ if (switchmode)
+ setBlockDataMode(oldmode);
+ return value;
+ }
+
+ public char readChar() throws IOException
+ {
+ boolean switchmode = true;
+ boolean oldmode = this.readDataFromBlock;
+ if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
+ switchmode = false;
+ if (switchmode)
+ oldmode = setBlockDataMode(true);
+ char value = this.dataInputStream.readChar();
+ if (switchmode)
+ setBlockDataMode(oldmode);
+ return value;
+ }
+
+ public int readInt() throws IOException
+ {
+ boolean switchmode = true;
+ boolean oldmode = this.readDataFromBlock;
+ if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4)
+ switchmode = false;
+ if (switchmode)
+ oldmode = setBlockDataMode(true);
+ int value = this.dataInputStream.readInt();
+ if (switchmode)
+ setBlockDataMode(oldmode);
+ return value;
+ }
+
+ public long readLong() throws IOException
+ {
+ boolean switchmode = true;
+ boolean oldmode = this.readDataFromBlock;
+ if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8)
+ switchmode = false;
+ if (switchmode)
+ oldmode = setBlockDataMode(true);
+ long value = this.dataInputStream.readLong();
+ if (switchmode)
+ setBlockDataMode(oldmode);
+ return value;
+ }
+
+ public float readFloat() throws IOException
+ {
+ boolean switchmode = true;
+ boolean oldmode = this.readDataFromBlock;
+ if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4)
+ switchmode = false;
+ if (switchmode)
+ oldmode = setBlockDataMode(true);
+ float value = this.dataInputStream.readFloat();
+ if (switchmode)
+ setBlockDataMode(oldmode);
+ return value;
+ }
+
+ public double readDouble() throws IOException
+ {
+ boolean switchmode = true;
+ boolean oldmode = this.readDataFromBlock;
+ if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8)
+ switchmode = false;
+ if (switchmode)
+ oldmode = setBlockDataMode(true);
+ double value = this.dataInputStream.readDouble();
+ if (switchmode)
+ setBlockDataMode(oldmode);
+ return value;
+ }
+
+ 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 abstract static 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;
+ }
+
+ /**
+ * This method should be called by a method called 'readObject' in the
+ * deserializing class (if present). It cannot (and should not)be called
+ * outside of it. Its goal is to read all fields in the real input stream
+ * and keep them accessible through the {@link #GetField} class. Calling
+ * this method will not alter the deserializing object.
+ *
+ * @return A valid freshly created 'GetField' instance to get access to
+ * the deserialized stream.
+ * @throws IOException An input/output exception occured.
+ * @throws ClassNotFoundException
+ * @throws NotActiveException
+ */
+ 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 (prereadFields != null)
+ return prereadFields;
+
+ 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];
+
+ // Apparently Block data is not used with GetField as per
+ // empirical evidence against JDK 1.2. Also see Mauve test
+ // java.io.ObjectInputOutput.Test.GetPutField.
+ boolean oldmode = setBlockDataMode(false);
+ readFully(prim_field_data);
+ for (int i = 0; i < objs.length; ++ i)
+ objs[i] = readObject();
+ setBlockDataMode(oldmode);
+
+ prereadFields = new GetField()
+ {
+ public ObjectStreamClass getObjectStreamClass()
+ {
+ return clazz;
+ }
+
+ public boolean defaulted(String name)
+ throws IOException, IllegalArgumentException
+ {
+ ObjectStreamField f = clazz.getField(name);
+
+ /* First if we have a serialized field use the descriptor */
+ if (f != null)
+ {
+ /* It is in serialPersistentFields but setClass tells us
+ * it should not be set. This value is defaulted.
+ */
+ if (f.isPersistent() && !f.isToSet())
+ return true;
+
+ return false;
+ }
+
+ /* This is not a serialized field. There should be
+ * a default value only if the field really exists.
+ */
+ try
+ {
+ return (clazz.forClass().getDeclaredField (name) != null);
+ }
+ catch (NoSuchFieldException e)
+ {
+ throw new IllegalArgumentException(e.getMessage());
+ }
+ }
+
+ 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++] & 0xFFL) << 56)
+ | ((prim_field_data[off++] & 0xFFL) << 48)
+ | ((prim_field_data[off++] & 0xFFL) << 40)
+ | ((prim_field_data[off++] & 0xFFL) << 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++] & 0xFFL) << 56)
+ | ((prim_field_data[off++] & 0xFFL) << 48)
+ | ((prim_field_data[off++] & 0xFFL) << 40)
+ | ((prim_field_data[off++] & 0xFFL) << 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, defvalue == null ? null : defvalue.getClass ());
+
+ if (field == null)
+ return defvalue;
+
+ return objs[field.getOffset()];
+ }
+
+ private ObjectStreamField getField(String name, Class type)
+ throws IllegalArgumentException
+ {
+ ObjectStreamField field = clazz.getField(name);
+ boolean illegal = false;
+
+ try
+ {
+ try
+ {
+ Class field_type = field.getType();
+
+ if (type == field_type ||
+ (type == null && !field_type.isPrimitive()))
+ {
+ /* See defaulted */
+ return field;
+ }
+
+ illegal = true;
+ throw new IllegalArgumentException
+ ("Field requested is of type "
+ + field_type.getName()
+ + ", but requested type was "
+ + (type == null ? "Object" : type.getName()));
+ }
+ catch (NullPointerException _)
+ {
+ /* Here we catch NullPointerException, because it may
+ only come from the call 'field.getType()'. If field
+ is null, we have to return null and classpath ethic
+ say we must try to avoid 'if (xxx == null)'.
+ */
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw e;
+ }
+
+ return null;
+ }
+ finally
+ {
+ /* If this is an unassigned field we should return
+ * the default value.
+ */
+ if (!illegal && field != null && !field.isToSet() && field.isPersistent())
+ return null;
+
+ /* We do not want to modify transient fields. They should
+ * be left to 0.
+ */
+ try
+ {
+ Field f = clazz.forClass().getDeclaredField(name);
+ if (Modifier.isTransient(f.getModifiers()))
+ throw new IllegalArgumentException
+ ("no such field (non transient) " + name);
+ if (field == null && f.getType() != type)
+ throw new IllegalArgumentException
+ ("Invalid requested type for field " + name);
+ }
+ catch (NoSuchFieldException e)
+ {
+ if (field == null)
+ throw new IllegalArgumentException(e.getMessage());
+ }
+
+ }
+ }
+ };
+
+ fieldsAlreadyRead = true;
+ return prereadFields;
+ }
+
+ /**
+ * 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()
+ */
+ 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 their constructor.
+ *
+ * @see #ObjectInputStream()
+ */
+ protected Object readObjectOverride()
+ throws ClassNotFoundException, IOException, OptionalDataException
+ {
+ throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride");
+ }
+
+ /**
+ * Assigns the next available handle to <code>obj</code>.
+ *
+ * @param obj The object for which we want a new handle.
+ * @return A valid handle for the specified object.
+ */
+ private int assignNewHandle(Object obj)
+ {
+ this.objectLookupTable.put(new Integer(this.nextOID),
+ new ObjectIdentityWrapper(obj));
+ return this.nextOID++;
+ }
+
+ private Object processResolution(ObjectStreamClass osc, Object obj, int handle)
+ throws IOException
+ {
+ if (osc != null && obj instanceof Serializable)
+ {
+ try
+ {
+ Method m = osc.readResolveMethod;
+ if(m != null)
+ {
+ obj = m.invoke(obj, new Object[] {});
+ }
+ }
+ catch (IllegalAccessException ignore)
+ {
+ }
+ catch (InvocationTargetException ignore)
+ {
+ }
+ }
+
+ 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
+ {
+ readNextBlock(this.realInputStream.readByte());
+ }
+
+ private void readNextBlock(byte marker) throws IOException
+ {
+ if (marker == TC_BLOCKDATA)
+ {
+ if(dump) dumpElement("BLOCK DATA SIZE=");
+ this.blockDataBytes = this.realInputStream.readUnsignedByte();
+ if(dump) dumpElementln (Integer.toString(this.blockDataBytes));
+ }
+ else if (marker == TC_BLOCKDATALONG)
+ {
+ if(dump) dumpElement("BLOCK DATA LONG SIZE=");
+ this.blockDataBytes = this.realInputStream.readInt();
+ if(dump) dumpElementln (Integer.toString(this.blockDataBytes));
+ }
+ 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, ObjectStreamClass stream_osc)
+ throws ClassNotFoundException, IOException
+ {
+ ObjectStreamField[] fields = stream_osc.fieldMapping;
+
+ for (int i = 0; i < fields.length; i += 2)
+ {
+ ObjectStreamField stream_field = fields[i];
+ ObjectStreamField real_field = fields[i + 1];
+ boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet());
+ boolean set_value = (real_field != null && real_field.isToSet());
+ String field_name;
+ char type;
+
+ if (stream_field != null)
+ {
+ field_name = stream_field.getName();
+ type = stream_field.getTypeCode();
+ }
+ else
+ {
+ field_name = real_field.getName();
+ type = real_field.getTypeCode();
+ }
+
+ switch(type)
+ {
+ case 'Z':
+ {
+ boolean value =
+ read_value ? this.realInputStream.readBoolean() : false;
+ if (dump && read_value && set_value)
+ dumpElementln(" " + field_name + ": " + value);
+ if (set_value)
+ real_field.setBooleanField(obj, value);
+ break;
+ }
+ case 'B':
+ {
+ byte value =
+ read_value ? this.realInputStream.readByte() : 0;
+ if (dump && read_value && set_value)
+ dumpElementln(" " + field_name + ": " + value);
+ if (set_value)
+ real_field.setByteField(obj, value);
+ break;
+ }
+ case 'C':
+ {
+ char value =
+ read_value ? this.realInputStream.readChar(): 0;
+ if (dump && read_value && set_value)
+ dumpElementln(" " + field_name + ": " + value);
+ if (set_value)
+ real_field.setCharField(obj, value);
+ break;
+ }
+ case 'D':
+ {
+ double value =
+ read_value ? this.realInputStream.readDouble() : 0;
+ if (dump && read_value && set_value)
+ dumpElementln(" " + field_name + ": " + value);
+ if (set_value)
+ real_field.setDoubleField(obj, value);
+ break;
+ }
+ case 'F':
+ {
+ float value =
+ read_value ? this.realInputStream.readFloat() : 0;
+ if (dump && read_value && set_value)
+ dumpElementln(" " + field_name + ": " + value);
+ if (set_value)
+ real_field.setFloatField(obj, value);
+ break;
+ }
+ case 'I':
+ {
+ int value =
+ read_value ? this.realInputStream.readInt() : 0;
+ if (dump && read_value && set_value)
+ dumpElementln(" " + field_name + ": " + value);
+ if (set_value)
+ real_field.setIntField(obj, value);
+ break;
+ }
+ case 'J':
+ {
+ long value =
+ read_value ? this.realInputStream.readLong() : 0;
+ if (dump && read_value && set_value)
+ dumpElementln(" " + field_name + ": " + value);
+ if (set_value)
+ real_field.setLongField(obj, value);
+ break;
+ }
+ case 'S':
+ {
+ short value =
+ read_value ? this.realInputStream.readShort() : 0;
+ if (dump && read_value && set_value)
+ dumpElementln(" " + field_name + ": " + value);
+ if (set_value)
+ real_field.setShortField(obj, value);
+ break;
+ }
+ case 'L':
+ case '[':
+ {
+ Object value =
+ read_value ? readObject() : null;
+ if (set_value)
+ real_field.setObjectField(obj, value);
+ break;
+ }
+ default:
+ throw new InternalError("Invalid type code: " + type);
+ }
+ }
+ }
+
+ // Toggles writing primitive data to block-data buffer.
+ private boolean setBlockDataMode (boolean on)
+ {
+ boolean oldmode = this.readDataFromBlock;
+ this.readDataFromBlock = on;
+
+ if (on)
+ this.dataInputStream = this.blockDataInput;
+ else
+ this.dataInputStream = this.realInputStream;
+ return oldmode;
+ }
+
+ // returns a new instance of REAL_CLASS that has been constructed
+ // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS)
+ private Object newObject (Class real_class, Constructor constructor)
+ throws ClassNotFoundException, IOException
+ {
+ if (constructor == null)
+ throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName());
+ try
+ {
+ return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor);
+ }
+ catch (InstantiationException e)
+ {
+ throw new ClassNotFoundException
+ ("Instance of " + real_class + " could not be created");
+ }
+ }
+
+ // 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();
+ }
+ }
+
+ private void callReadMethod (Method readObject, Class klass, Object obj)
+ throws ClassNotFoundException, IOException
+ {
+ try
+ {
+ readObject.invoke(obj, new Object[] { this });
+ }
+ catch (InvocationTargetException x)
+ {
+ /* Rethrow if possible. */
+ Throwable exception = x.getTargetException();
+ if (exception instanceof RuntimeException)
+ throw (RuntimeException) exception;
+ if (exception instanceof IOException)
+ throw (IOException) exception;
+ if (exception instanceof ClassNotFoundException)
+ throw (ClassNotFoundException) exception;
+
+ throw new IOException("Exception thrown from readObject() on " +
+ klass + ": " + exception.getClass().getName());
+ }
+ catch (Exception x)
+ {
+ throw new IOException("Failure invoking readObject() on " +
+ klass + ": " + x.getClass().getName());
+ }
+
+ // Invalidate fields which has been read through readFields.
+ prereadFields = null;
+ }
+
+ private static final int BUFFER_SIZE = 1024;
+
+ 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;
+ private Hashtable classLookupTable;
+ private GetField prereadFields;
+
+ private ClassLoader callersClassLoader;
+ private static boolean dump;
+
+ // The nesting depth for debugging output
+ private int depth = 0;
+
+ private static final boolean DEBUG = false;
+
+ private void dumpElement (String msg)
+ {
+ System.out.print(msg);
+ }
+
+ private void dumpElementln (String msg)
+ {
+ System.out.println(msg);
+ for (int i = 0; i < depth; i++)
+ System.out.print (" ");
+ System.out.print (Thread.currentThread() + ": ");
+ }
+
+ static
+ {
+ if (Configuration.INIT_LOAD_LIBRARY)
+ {
+ System.loadLibrary ("javaio");
+ }
+ }
+
+ // used to keep a prioritized list of object validators
+ private static final 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;
+ }
+ }
+}
+
OpenPOWER on IntegriCloud