mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			2143 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			2143 lines
		
	
	
		
			70 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* ObjectInputStream.java -- Class used to read serialized objects
 | |
|    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008
 | |
|    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.Pair;
 | |
| import gnu.classpath.VMStackWalker;
 | |
| 
 | |
| 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.HashMap;
 | |
| import java.util.Hashtable;
 | |
| import java.util.Iterator;
 | |
| import java.util.Map;
 | |
| import java.util.TreeSet;
 | |
| 
 | |
| /**
 | |
|  * @author Tom Tromey (tromey@redhat.com)
 | |
|  * @author Jeroen Frijters (jeroen@frijters.net)
 | |
|  * @author Guilhem Lavaux (guilhem@kaffe.org)
 | |
|  * @author Michael Koch (konqueror@gmx.de)
 | |
|  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
 | |
|  */
 | |
| 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.blockDataPosition = 0;
 | |
|     this.blockDataBytes = 0;
 | |
|     this.blockData = new byte[BUFFER_SIZE];
 | |
|     this.blockDataInput = new DataInputStream(this);
 | |
|     this.realInputStream = new DataInputStream(in);
 | |
|     this.nextOID = baseWireHandle;
 | |
|     handles = new HashMap<Integer,Pair<Boolean,Object>>();
 | |
|     this.classLookupTable = new Hashtable<Class,ObjectStreamClass>();
 | |
|     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. This method can also throw Errors and
 | |
|    * RuntimeExceptions if caused by existing readResolve() user code.
 | |
|    *
 | |
|    * @return The object read from the underlying stream.
 | |
|    *
 | |
|    * @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
 | |
|   {
 | |
|     return readObject(true);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * <p>
 | |
|    * Returns the next deserialized object read from the
 | |
|    * underlying stream in an unshared manner.  Any object
 | |
|    * returned by this method will not be returned by
 | |
|    * subsequent calls to either this method or {@link #readObject()}.
 | |
|    * </p>
 | |
|    * <p>
 | |
|    * This behaviour is achieved by:
 | |
|    * </p>
 | |
|    * <ul>
 | |
|    * <li>Marking the handles created by successful calls to this
 | |
|    * method, so that future calls to {@link #readObject()} or
 | |
|    * {@link #readUnshared()} will throw an {@link ObjectStreamException}
 | |
|    * rather than returning the same object reference.</li>
 | |
|    * <li>Throwing an {@link ObjectStreamException} if the next
 | |
|    * element in the stream is a reference to an earlier object.</li>
 | |
|    * </ul>
 | |
|    *
 | |
|    * @return a reference to the deserialized object.
 | |
|    * @throws ClassNotFoundException if the class of the object being
 | |
|    *                                deserialized can not be found.
 | |
|    * @throws StreamCorruptedException if information in the stream
 | |
|    *                                  is inconsistent.
 | |
|    * @throws ObjectStreamException if the next object has already been
 | |
|    *                               returned by an earlier call to this
 | |
|    *                               method or {@link #readObject()}.
 | |
|    * @throws OptionalDataException if primitive data occurs next in the stream.
 | |
|    * @throws IOException if an I/O error occurs from the stream.
 | |
|    * @since 1.4
 | |
|    * @see #readObject()
 | |
|    */
 | |
|   public Object readUnshared()
 | |
|     throws IOException, ClassNotFoundException
 | |
|   {
 | |
|     return readObject(false);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * 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. This method can also throw Errors and
 | |
|    * RuntimeExceptions if caused by existing readResolve() user code.
 | |
|    *
 | |
|    * @param shared true if handles created by this call should be shared
 | |
|    *               with later calls.
 | |
|    * @return The object read from the underlying stream.
 | |
|    *
 | |
|    * @exception ClassNotFoundException The class that an object being
 | |
|    * read in belongs to cannot be found.
 | |
|    *
 | |
|    * @exception IOException Exception from underlying
 | |
|    * <code>InputStream</code>.
 | |
|    */
 | |
|   private final Object readObject(boolean shared)
 | |
|     throws ClassNotFoundException, IOException
 | |
|   {
 | |
|     if (this.useSubclassMethod)
 | |
|       return readObjectOverride();
 | |
| 
 | |
|     Object ret_val;
 | |
|     boolean old_mode = setBlockDataMode(false);
 | |
|     byte marker = this.realInputStream.readByte();
 | |
| 
 | |
|     if (DEBUG)
 | |
|       depth += 2;
 | |
| 
 | |
|     if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " ");
 | |
| 
 | |
|     try
 | |
|       {
 | |
|         ret_val = parseContent(marker, shared);
 | |
|       }
 | |
|     finally
 | |
|       {
 | |
|         setBlockDataMode(old_mode);
 | |
|         if (DEBUG)
 | |
|           depth -= 2;
 | |
|       }
 | |
| 
 | |
|     return ret_val;
 | |
|   }
 | |
| 
 | |
|    /**
 | |
|     * Handles a content block within the stream, which begins with a marker
 | |
|     * byte indicating its type.
 | |
|     *
 | |
|     * @param marker the byte marker.
 | |
|     * @param shared true if handles created by this call should be shared
 | |
|     *               with later calls.
 | |
|     * @return an object which represents the parsed content.
 | |
|     * @throws ClassNotFoundException if the class of an object being
 | |
|     *                                read in cannot be found.
 | |
|     * @throws IOException if invalid data occurs or one is thrown by the
 | |
|     *                     underlying <code>InputStream</code>.
 | |
|     */
 | |
|    private Object parseContent(byte marker, boolean shared)
 | |
|      throws ClassNotFoundException, IOException
 | |
|    {
 | |
|      Object ret_val;
 | |
|      boolean is_consumed = false;
 | |
| 
 | |
|      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);
 | |
|         }
 | |
| 
 | |
|        case TC_NULL:
 | |
|         {
 | |
|           if(dump) dumpElementln("NULL");
 | |
|           ret_val = null;
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|        case TC_REFERENCE:
 | |
|         {
 | |
|           if(dump) dumpElement("REFERENCE ");
 | |
|           int oid = realInputStream.readInt();
 | |
|           if(dump) dumpElementln(Integer.toHexString(oid));
 | |
|           ret_val = lookupHandle(oid);
 | |
|           if (!shared)
 | |
|             throw new
 | |
|               InvalidObjectException("References can not be read unshared.");
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|        case TC_CLASS:
 | |
|         {
 | |
|           if(dump) dumpElementln("CLASS");
 | |
|           ObjectStreamClass osc = (ObjectStreamClass)readObject();
 | |
|           Class clazz = osc.forClass();
 | |
|           assignNewHandle(clazz,shared);
 | |
|           ret_val = clazz;
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|        case TC_PROXYCLASSDESC:
 | |
|         {
 | |
|           if(dump) dumpElementln("PROXYCLASS");
 | |
| 
 | |
| /* GCJ LOCAL */
 | |
|           // The grammar at this point is
 | |
|           //   TC_PROXYCLASSDESC newHandle proxyClassDescInfo
 | |
|           // i.e. we have to assign the handle immediately after
 | |
|           // reading the marker.
 | |
|           int handle = assignNewHandle("Dummy proxy",shared);
 | |
| /* END GCJ LOCAL */
 | |
| 
 | |
|           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();
 | |
|             }
 | |
| 
 | |
|           boolean oldmode = setBlockDataMode(true);
 | |
|           Class cl = resolveProxyClass(intfs);
 | |
|           setBlockDataMode(oldmode);
 | |
| 
 | |
|           ObjectStreamClass osc = lookupClass(cl);
 | |
|           if (osc.firstNonSerializableParentConstructor == null)
 | |
|             {
 | |
|               osc.realClassIsSerializable = true;
 | |
|               osc.fields = osc.fieldMapping = new ObjectStreamField[0];
 | |
|               try
 | |
|                 {
 | |
|                   osc.firstNonSerializableParentConstructor =
 | |
|                     Object.class.getConstructor(new Class[0]);
 | |
|                 }
 | |
|               catch (NoSuchMethodException x)
 | |
|                 {
 | |
|                   throw (InternalError)
 | |
|                     new InternalError("Object ctor missing").initCause(x);
 | |
|                 }
 | |
|             }
 | |
| /* GCJ LOCAL */
 | |
|           rememberHandle(osc,shared,handle);
 | |
| /* END GCJ LOCAL */
 | |
| 
 | |
|           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:
 | |
|         {
 | |
|           if(dump) dumpElement("STRING=");
 | |
|           String s = this.realInputStream.readUTF();
 | |
|           if(dump) dumpElementln(s);
 | |
|           ret_val = processResolution(null, s, assignNewHandle(s,shared),
 | |
|                                       shared);
 | |
|           break;
 | |
|         }
 | |
| 
 | |
|        case TC_LONGSTRING:
 | |
|         {
 | |
|           if(dump) dumpElement("STRING=");
 | |
|           String s = this.realInputStream.readUTFLong();
 | |
|           if(dump) dumpElementln(s);
 | |
|           ret_val = processResolution(null, s, assignNewHandle(s,shared),
 | |
|                                       shared);
 | |
|           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,shared);
 | |
|           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, shared);
 | |
|           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,shared);
 | |
| 
 | |
|               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,shared);
 | |
|               break;
 | |
| 
 | |
|             } // end if (osc.realClassIsExternalizable)
 | |
| 
 | |
|           Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor);
 | |
| 
 | |
|           int handle = assignNewHandle(obj,shared);
 | |
|           Object prevObject = this.currentObject;
 | |
|           ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
 | |
|           TreeSet<ValidatorAndPriority> prevObjectValidators =
 | |
|             this.currentObjectValidators;
 | |
| 
 | |
|           this.currentObject = obj;
 | |
|           this.currentObjectValidators = null;
 | |
|           ObjectStreamClass[] hierarchy = hierarchy(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
 | |
|                     {
 | |
|                       /* Read blocks until an end marker */
 | |
|                       byte writeMarker = this.realInputStream.readByte();
 | |
|                       while (writeMarker != TC_ENDBLOCKDATA)
 | |
|                         {
 | |
|                           parseContent(writeMarker, shared);
 | |
|                           writeMarker = this.realInputStream.readByte();
 | |
|                         }
 | |
|                       if(dump) dumpElementln("yes");
 | |
|                     }
 | |
|                   catch (EOFException e)
 | |
|                     {
 | |
|                       throw (IOException) new IOException
 | |
|                         ("No end of block data seen for class with readObject (ObjectInputStream) method.").initCause(e);
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|           this.currentObject = prevObject;
 | |
|           this.currentObjectStreamClass = prevObjectStreamClass;
 | |
|           ret_val = processResolution(osc, obj, handle, shared);
 | |
|           if (currentObjectValidators != null)
 | |
|             invokeValidators();
 | |
|           this.currentObjectValidators = prevObjectValidators;
 | |
| 
 | |
|           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);
 | |
|         }
 | |
| 
 | |
|        case TC_ENUM:
 | |
|          {
 | |
|            /* TC_ENUM classDesc newHandle enumConstantName */
 | |
|            if (dump)
 | |
|              dumpElementln("ENUM=");
 | |
|            ObjectStreamClass osc = (ObjectStreamClass) readObject();
 | |
|            String constantName = (String) readObject();
 | |
|            if (dump)
 | |
|              dumpElementln("CONSTANT NAME = " + constantName);
 | |
|            Class clazz = osc.forClass();
 | |
|            Enum instance = Enum.valueOf(clazz, constantName);
 | |
|            assignNewHandle(instance,shared);
 | |
|            ret_val = instance;
 | |
|            break;
 | |
|          }
 | |
| 
 | |
|        default:
 | |
|         throw new IOException("Unknown marker on stream: " + marker);
 | |
|       }
 | |
|     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,true);
 | |
| 
 | |
|     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);
 | |
|       }
 | |
| 
 | |
|     /* Now that fields have been read we may resolve the class
 | |
|      * (and read annotation if needed). */
 | |
|     Class clazz = resolveClass(osc);
 | |
|     ClassLoader loader = clazz.getClassLoader();
 | |
|     for (int i = 0; i < field_count; i++)
 | |
|       {
 | |
|         fields[i].resolveType(loader);
 | |
|       }
 | |
|     boolean oldmode = setBlockDataMode(true);
 | |
|     osc.setClass(clazz, lookupClass(clazz.getSuperclass()));
 | |
|     classLookupTable.put(clazz, osc);
 | |
|     setBlockDataMode(oldmode);
 | |
| 
 | |
|     // find the first non-serializable 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))
 | |
|         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");
 | |
| 
 | |
|     if (currentObjectValidators == null)
 | |
|       currentObjectValidators = new TreeSet<ValidatorAndPriority>();
 | |
| 
 | |
|     currentObjectValidators.add(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
 | |
|   {
 | |
|     String name = osc.getName();
 | |
|     try
 | |
|       {
 | |
|         return Class.forName(name, true, currentLoader());
 | |
|       }
 | |
|     catch(ClassNotFoundException x)
 | |
|       {
 | |
|         if (name.equals("void"))
 | |
|           return Void.TYPE;
 | |
|         else if (name.equals("boolean"))
 | |
|           return Boolean.TYPE;
 | |
|         else if (name.equals("byte"))
 | |
|           return Byte.TYPE;
 | |
|         else if (name.equals("char"))
 | |
|           return Character.TYPE;
 | |
|         else if (name.equals("short"))
 | |
|           return Short.TYPE;
 | |
|         else if (name.equals("int"))
 | |
|           return Integer.TYPE;
 | |
|         else if (name.equals("long"))
 | |
|           return Long.TYPE;
 | |
|         else if (name.equals("float"))
 | |
|           return Float.TYPE;
 | |
|         else if (name.equals("double"))
 | |
|           return Double.TYPE;
 | |
|         else
 | |
|           throw x;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Returns the most recent user defined ClassLoader on the execution stack
 | |
|    * or null if none is found.
 | |
|    */
 | |
|   private ClassLoader currentLoader()
 | |
|   {
 | |
|     return VMStackWalker.firstNonNullClassLoader();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * 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 = classLookupTable.get(clazz);
 | |
|     if (oclazz == null)
 | |
|       return ObjectStreamClass.lookup(clazz);
 | |
|     else
 | |
|       return oclazz;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Reconstruct class hierarchy the same way {@link
 | |
|    * java.io.ObjectStreamClass#hierarchy} does but using lookupClass
 | |
|    * instead of ObjectStreamClass.lookup.
 | |
|    *
 | |
|    * @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[] hierarchy(Class clazz)
 | |
|   {
 | |
|     ObjectStreamClass osc = lookupClass(clazz);
 | |
| 
 | |
|     return osc == null ? new ObjectStreamClass[0] : osc.hierarchy();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * 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] = Class.forName(intfs[i], false, cl);
 | |
|     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)
 | |
|       {
 | |
|         int remain = this.blockDataBytes - this.blockDataPosition;
 | |
|         if (remain == 0)
 | |
|           {
 | |
|             readNextBlock();
 | |
|             remain = this.blockDataBytes - this.blockDataPosition;
 | |
|           }
 | |
|         length = Math.min(length, remain);
 | |
|         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);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         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;
 | |
| 
 | |
|           // XXX This code is horrible and needs to be rewritten!
 | |
|           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);
 | |
|                 }
 | |
| 
 | |
|             }
 | |
|         }
 | |
|       };
 | |
| 
 | |
|     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.
 | |
|    * @param shared True if the handle should be shared
 | |
|    *               with later calls.
 | |
|    * @return A valid handle for the specified object.
 | |
|    */
 | |
|   private int assignNewHandle(Object obj, boolean shared)
 | |
|   {
 | |
|     int handle = this.nextOID;
 | |
|     this.nextOID = handle + 1;
 | |
|     rememberHandle(obj,shared,handle);
 | |
|     return handle;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Remember the object associated with the given handle.
 | |
|    *
 | |
|    * @param obj an object
 | |
|    * @param shared true if the reference should be shared
 | |
|    *               with later calls.
 | |
|    * @param handle a handle, must be >= baseWireHandle
 | |
|    *
 | |
|    * @see #lookupHandle
 | |
|    */
 | |
|   private void rememberHandle(Object obj, boolean shared,
 | |
|                               int handle)
 | |
|   {
 | |
|     handles.put(handle, new Pair<Boolean,Object>(shared, obj));
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Look up the object associated with a given handle.
 | |
|    *
 | |
|    * @param handle a handle, must be >= baseWireHandle
 | |
|    * @return the object remembered for handle or null if none.
 | |
|    * @throws StreamCorruptedException if the handle is invalid.
 | |
|    * @throws InvalidObjectException if the reference is not shared.
 | |
|    * @see #rememberHandle
 | |
|    */
 | |
|   private Object lookupHandle(int handle)
 | |
|     throws ObjectStreamException
 | |
|   {
 | |
|     Pair<Boolean,Object> result = handles.get(handle);
 | |
|     if (result == null)
 | |
|       throw new StreamCorruptedException("The handle, " +
 | |
|                                          Integer.toHexString(handle) +
 | |
|                                          ", is invalid.");
 | |
|     if (!result.getLeft())
 | |
|       throw new InvalidObjectException("The handle, " +
 | |
|                                        Integer.toHexString(handle) +
 | |
|                                        ", is not shared.");
 | |
|     return result.getRight();
 | |
|   }
 | |
| 
 | |
|   private Object processResolution(ObjectStreamClass osc, Object obj, int handle,
 | |
|                                    boolean shared)
 | |
|     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 exception)
 | |
|           {
 | |
|             Throwable cause = exception.getCause();
 | |
|             if (cause instanceof ObjectStreamException)
 | |
|               throw (ObjectStreamException) cause;
 | |
|             else if (cause instanceof RuntimeException)
 | |
|               throw (RuntimeException) cause;
 | |
|             else if (cause instanceof Error)
 | |
|               throw (Error) cause;
 | |
|           }
 | |
|       }
 | |
| 
 | |
|     if (this.resolveEnabled)
 | |
|       obj = resolveObject(obj);
 | |
| 
 | |
|     rememberHandle(obj, shared, handle);
 | |
|     if (!shared)
 | |
|       {
 | |
|         if (obj instanceof byte[])
 | |
|           return ((byte[]) obj).clone();
 | |
|         if (obj instanceof short[])
 | |
|           return ((short[]) obj).clone();
 | |
|         if (obj instanceof int[])
 | |
|           return ((int[]) obj).clone();
 | |
|         if (obj instanceof long[])
 | |
|           return ((long[]) obj).clone();
 | |
|         if (obj instanceof char[])
 | |
|           return ((char[]) obj).clone();
 | |
|         if (obj instanceof boolean[])
 | |
|           return ((boolean[]) obj).clone();
 | |
|         if (obj instanceof float[])
 | |
|           return ((float[]) obj).clone();
 | |
|         if (obj instanceof double[])
 | |
|           return ((double[]) obj).clone();
 | |
|         if (obj instanceof Object[])
 | |
|           return ((Object[]) obj).clone();
 | |
|       }
 | |
|     return obj;
 | |
|   }
 | |
| 
 | |
|   private void clearHandles()
 | |
|   {
 | |
|     handles.clear();
 | |
|     this.nextOID = baseWireHandle;
 | |
|   }
 | |
| 
 | |
|   private void readNextBlock() throws IOException
 | |
|   {
 | |
|     byte marker = this.realInputStream.readByte();
 | |
|     while (marker == TC_RESET)
 | |
|       {
 | |
|         if(dump) dumpElementln("RESET");
 | |
|         clearHandles();
 | |
|         marker = this.realInputStream.readByte();
 | |
|       }
 | |
|     readNextBlock(marker);
 | |
|   }
 | |
| 
 | |
|   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 (ClassNotFoundException) new ClassNotFoundException
 | |
|           ("Instance of " + real_class + " could not be created").initCause(e);
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   // runs all registered ObjectInputValidations in prioritized order
 | |
|   // on OBJ
 | |
|   private void invokeValidators() throws InvalidObjectException
 | |
|   {
 | |
|     try
 | |
|       {
 | |
|         Iterator<ValidatorAndPriority> it = currentObjectValidators.iterator();
 | |
|         while(it.hasNext())
 | |
|           {
 | |
|             ValidatorAndPriority vap = it.next();
 | |
|             ObjectInputValidation validator = vap.validator;
 | |
|             validator.validateObject();
 | |
|           }
 | |
|       }
 | |
|     finally
 | |
|       {
 | |
|         currentObjectValidators = null;
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   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 (IOException) new IOException(
 | |
|           "Exception thrown from readObject() on " + klass).initCause(x);
 | |
|       }
 | |
|     catch (Exception x)
 | |
|       {
 | |
|         throw (IOException) new IOException(
 | |
|           "Failure invoking readObject() on " + klass).initCause(x);
 | |
|       }
 | |
| 
 | |
|     // 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 Map<Integer,Pair<Boolean,Object>> handles;
 | |
|   private Object currentObject;
 | |
|   private ObjectStreamClass currentObjectStreamClass;
 | |
|   private TreeSet<ValidatorAndPriority> currentObjectValidators;
 | |
|   private boolean readDataFromBlock;
 | |
|   private boolean fieldsAlreadyRead;
 | |
|   private Hashtable<Class,ObjectStreamClass> classLookupTable;
 | |
|   private GetField prereadFields;
 | |
| 
 | |
|   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() + ": ");
 | |
|   }
 | |
| 
 | |
|   private void dumpElementln (String msg, Object obj)
 | |
|   {
 | |
|     try
 | |
|       {
 | |
|         System.out.print(msg);
 | |
|         if (java.lang.reflect.Proxy.isProxyClass(obj.getClass()))
 | |
|           System.out.println(obj.getClass());
 | |
|         else
 | |
|         System.out.println(obj);
 | |
|       }
 | |
|     catch (Exception _)
 | |
|       {
 | |
|       }
 | |
|     for (int i = 0; i < depth; i++)
 | |
|       System.out.print (" ");
 | |
|     System.out.print (Thread.currentThread() + ": ");
 | |
|   }
 | |
| 
 | |
|   // 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;
 | |
|     }
 | |
|   }
 | |
| }
 |