diff options
Diffstat (limited to 'libjava/java/io')
20 files changed, 4807 insertions, 0 deletions
diff --git a/libjava/java/io/BlockDataException.java b/libjava/java/io/BlockDataException.java new file mode 100644 index 0000000..ef70f54 --- /dev/null +++ b/libjava/java/io/BlockDataException.java @@ -0,0 +1,39 @@ +/* BlockDataException.java -- Class used to store name and class of fields + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +//TODO: check 1.2 API to make sure this mathces + +class BlockDataException extends IOException +{ + public BlockDataException( int bytes ) + { + super( bytes + " bytes are available in the next data block" ); + } +} + diff --git a/libjava/java/io/Externalizable.java b/libjava/java/io/Externalizable.java new file mode 100644 index 0000000..045df86 --- /dev/null +++ b/libjava/java/io/Externalizable.java @@ -0,0 +1,98 @@ +/* Externalizable.java -- Interface for saving and restoring object data + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * This interface provides a way that classes can completely control how + * the data of their object instances are written and read to and from + * streams. It has two methods which are used to write the data to a stream + * and to read the data from a stream. The read method must read the data + * in exactly the way it was written by the write method. + * <p> + * Note that classes which implement this interface must take into account + * that all superclass data must also be written to the stream as well. + * The class implementing this interface must figure out how to make that + * happen. + * <p> + * This interface can be used to provide object persistence. When an + * object is to be stored externally, the <code>writeExternal</code> method is + * called to save state. When the object is restored, an instance is + * created using the default no-argument constructor and the + * <code>readExternal</code> method is used to restore the state. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract interface Externalizable extends Serializable +{ + +/** + * This method restores an object's state by reading in the instance data + * for the object from the passed in stream. Note that this stream is not + * a subclass of <code>InputStream</code>, but rather is a class that implements + * the <code>ObjectInput</code> interface. That interface provides a mechanism for + * reading in Java data types from a stream. + * <p> + * Note that this method must be compatible with <code>writeExternal</code>. + * It must read back the exact same types that were written by that + * method in the exact order they were written. + * <p> + * If this method needs to read back an object instance, then the class + * for that object must be found and loaded. If that operation fails, + * then this method throws a <code>ClassNotFoundException</code> + * + * @param in An <code>ObjectInput</code> instance for reading in the object state + * + * @exception ClassNotFoundException If the class of an object being restored cannot be found + * @exception IOException If any other error occurs + */ +public abstract void +readExternal(ObjectInput in) throws ClassNotFoundException, IOException; + +/*************************************************************************/ + +/** + * This method is responsible for writing the instance data of an object + * to the passed in stream. Note that this stream is not a subclass of + * <code>OutputStream</code>, but rather is a class that implements the + * <code>ObjectOutput</code> interface. That interface provides a number of methods + * for writing Java data values to a stream. + * <p> + * Not that the implementation of this method must be coordinated with + * the implementation of <code>readExternal</code>. + * + * @param out An <code>ObjectOutput</code> instance for writing the object state + * + * @exception IOException If an error occurs + */ +public abstract void +writeExternal(ObjectOutput out) throws IOException; + +} // interface Externalizable + diff --git a/libjava/java/io/InvalidClassException.java b/libjava/java/io/InvalidClassException.java new file mode 100644 index 0000000..fd03154 --- /dev/null +++ b/libjava/java/io/InvalidClassException.java @@ -0,0 +1,110 @@ +/* InvalidClassException.java -- An I/O operation was interrupted. + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * This exception is thrown when there is some sort of problem with a + * class during a serialization operation. This could be that the + * versions don't match, that there are unknown datatypes in the class + * or that the class doesn't have a default no-arg constructor. + * <p> + * The field <code>classname</code> will contain the name of the + * class that caused the problem if known. The getMessage() method + * for this exception will always include the name of that class + * if known. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class InvalidClassException extends ObjectStreamException +{ + +/* + * Instance Variables + */ + +/** + * The name of the class which encountered the error. + */ +public String classname; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Create a new InvalidClassException with a descriptive error message String + * + * @param message The descriptive error message + */ +public +InvalidClassException(String message) +{ + super(message); +} + +/*************************************************************************/ + +/** + * Create a new InvalidClassException with a descriptive error message + * String, and the name of the class that caused the problem. + * + * @param classname The number of bytes tranferred before the interruption + * @param message The descriptive error message + */ +public +InvalidClassException(String classname, String message) +{ + super(message); + this.classname = classname; +} + +/*************************************************************************/ + +/* + * Instance Methods + */ + +/** + * Returns the descriptive error message for this exception. It will + * include the class name that caused the problem if known. This method + * overrides Throwable.getMessage() + * + * @return A descriptive error message + */ +public String +getMessage() +{ + return(super.getMessage() + ": " + classname); +} + +} // class InvalidClassException + diff --git a/libjava/java/io/InvalidObjectException.java b/libjava/java/io/InvalidObjectException.java new file mode 100644 index 0000000..705082a --- /dev/null +++ b/libjava/java/io/InvalidObjectException.java @@ -0,0 +1,57 @@ +/* InvalidObjectException.java -- An I/O operation was interrupted. + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * This exception is thrown when an object fails a validation test + * during serialization. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class InvalidObjectException extends ObjectStreamException +{ + +/* + * Constructors + */ + +/** + * Create a new InvalidObjectException with a descriptive error message String + * + * @param message The descriptive error message + */ +public +InvalidObjectException(String message) +{ + super(message); +} + +} // class InvalidObjectException + diff --git a/libjava/java/io/NotActiveException.java b/libjava/java/io/NotActiveException.java new file mode 100644 index 0000000..f628a3b --- /dev/null +++ b/libjava/java/io/NotActiveException.java @@ -0,0 +1,68 @@ +/* NotActiveException.java -- Unexpected end of file exception + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * This exception is thrown when a problem occurs due to the fact that + * serialization is not active. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class NotActiveException extends ObjectStreamException +{ + +/* + * Constructors + */ + +/** + * Create a new NotActiveException without a descriptive error message + */ +public +NotActiveException() +{ + super(); +} + +/*************************************************************************/ + +/** + * Create a new NotActiveException with a descriptive error message String + * + * @param message The descriptive error message + */ +public +NotActiveException(String message) +{ + super(message); +} + +} // class NotActiveException + diff --git a/libjava/java/io/NotSerializableException.java b/libjava/java/io/NotSerializableException.java new file mode 100644 index 0000000..d1e0bd2 --- /dev/null +++ b/libjava/java/io/NotSerializableException.java @@ -0,0 +1,69 @@ +/* NotSerializableException.java -- Unexpected end of file exception + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * This exception is thrown when a class may not be serialized. The + * descriptive message will consist of the name of the class in question. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class NotSerializableException extends ObjectStreamException +{ + +/* + * Constructors + */ + +/** + * Create a new NotSerializableException without a descriptive error message + */ +public +NotSerializableException() +{ + super(); +} + +/*************************************************************************/ + +/** + * Create a new NotSerializableException with a descriptive error message String + * This should be the name of the class that cannot be serialized. + * + * @param message The descriptive error message + */ +public +NotSerializableException(String message) +{ + super(message); +} + +} // class NotSerializableException + diff --git a/libjava/java/io/ObjectInput.java b/libjava/java/io/ObjectInput.java new file mode 100644 index 0000000..ef23fa9 --- /dev/null +++ b/libjava/java/io/ObjectInput.java @@ -0,0 +1,147 @@ +/* ObjectInput.java -- Read object data from a stream + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * This interface extends the <code>DataInput</code> interface to provide a + * facility to read objects as well as primitive types from a stream. It + * also has methods that allow input to be done in a manner similar to + * <code>InputStream</code> + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract interface ObjectInput extends DataInput +{ + +/** + * This method returns the number of bytes that can be read without + * blocking. + * + * @return The number of bytes available before blocking + * + * @exception IOException If an error occurs + */ +public abstract int +available() throws IOException; + +/*************************************************************************/ + +/** + * This method reading a byte of data from a stream. It returns that byte + * as an int. This method blocks if no data is available to be read. + * + * @return The byte of data read + * + * @exception IOException If an error occurs + */ +public abstract int +read() throws IOException; + +/*************************************************************************/ + +/** + * This method reads raw bytes and stores them them a byte array buffer. + * Note that this method will block if no data is available. However, + * it will not necessarily block until it fills the entire buffer. That is, + * a "short count" is possible. + * + * @param buf The byte array to receive the data read + * + * @return The actual number fo bytes read or -1 if end of stream + * + * @exception IOException If an error occurs + */ +public abstract int +read(byte[] buf) throws IOException; + +/*************************************************************************/ + +/** + * This method reads raw bytes and stores them in a byte array buffer + * <code>buf</code> starting at position <code>offset</code> into the buffer. A + * maximum of <code>len</code> bytes will be read. Note that this method + * blocks if no data is available, but will not necessarily block until + * it can read <code>len</code> bytes of data. That is, a "short count" is + * possible. + * + * @param buf The byte array to receive the data read + * @param offset The offset into @code{buf} to start storing data + * @param len The maximum number of bytes to read + * + * @return The actual number fo bytes read or -1 if end of stream + * + * @exception IOException If an error occurs + */ +public abstract int +read(byte[] buf, int offset, int len) throws IOException; + +/*************************************************************************/ + +/** + * Reads an object instance and returns it. If the class for the object + * being read cannot be found, then a ClassNotFoundException will + * be thrown. + * + * @return The object instance that was read + * + * @exception ClassNotFoundException If a class for the object cannot be found + * @exception IOException If an error occurs + */ +public abstract Object +readObject() throws ClassNotFoundException, IOException; + +/*************************************************************************/ + +/** + * This method causes the specified number of bytes to be read and + * discarded. It is possible that fewer than the requested number of bytes + * will actually be skipped. + * + * @param num_bytes The number of bytes to skip + * + * @return The actual number of bytes skipped + * + * @exception IOException If an error occurs + */ +public abstract long +skip(long num_bytes) throws IOException; + +/*************************************************************************/ + +/** + * This method closes the input source + * + * @exception IOException If an error occurs + */ +public abstract void +close() throws IOException; + +} // interface ObjectInput + diff --git a/libjava/java/io/ObjectInputStream.java b/libjava/java/io/ObjectInputStream.java new file mode 100644 index 0000000..7855480 --- /dev/null +++ b/libjava/java/io/ObjectInputStream.java @@ -0,0 +1,1467 @@ +/* ObjectInputStream.java -- Class used to read serialized objects + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +import java.lang.reflect.Array; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Hashtable; +import java.util.Vector; + +import gnu.java.io.ObjectIdentityWrapper; +import gnu.java.lang.reflect.TypeSignature; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + + + +public class ObjectInputStream extends InputStream + implements ObjectInput, ObjectStreamConstants +{ + /** + Creates a new <code>ObjectInputStream</code> that will do all of + its reading from <code>in</code>. This method also checks + the stream by reading the header information (stream magic number + and stream version). + + @exception IOException Reading stream header from underlying + stream cannot be completed. + + @exception StreamCorruptedException An invalid stream magic + number or stream version was read from the stream. + + @see readStreamHeader () + */ + public ObjectInputStream (InputStream in) + throws IOException, StreamCorruptedException + { + this.resolveEnabled = false; + this.isDeserializing = false; + this.blockDataPosition = 0; + this.blockDataBytes = 0; + this.blockData = new byte[BUFFER_SIZE]; + this.blockDataInput = new DataInputStream (this); + this.realInputStream = new DataInputStream (in); + this.nextOID = baseWireHandle; + this.objectLookupTable = new Hashtable (); + this.validators = new Vector (); + setBlockDataMode (true); + readStreamHeader (); + } + + + /** + Returns the next deserialized object read from the underlying stream. + + This method can be overriden by a class by implementing + <code>private void readObject (ObjectInputStream)</code>. + + If an exception is thrown from this method, the stream is left in + an undefined state. + + @exception ClassNotFoundException The class that an object being + read in belongs to cannot be found. + + @exception IOException Exception from underlying + <code>InputStream</code>. + */ + public final Object readObject () throws ClassNotFoundException, IOException + { + if (this.useSubclassMethod) + return readObjectOverride (); + + boolean was_deserializing; + + Object ret_val; + was_deserializing = this.isDeserializing; + + if (! was_deserializing) + setBlockDataMode (false); + + this.isDeserializing = true; + +// DEBUG ("MARKER "); + byte marker = this.realInputStream.readByte (); + + switch (marker) + { + case TC_BLOCKDATA: + case TC_BLOCKDATALONG: + readNextBlock (marker); + throw new BlockDataException (this.blockDataBytes); + + case TC_NULL: + ret_val = null; + break; + + case TC_REFERENCE: + { +// DEBUG ("REFERENCE "); + Integer oid = new Integer (this.realInputStream.readInt ()); + ret_val = ((ObjectIdentityWrapper) + this.objectLookupTable.get (oid)).object; + break; + } + + case TC_CLASS: + { + ObjectStreamClass osc = (ObjectStreamClass)readObject (); + Class clazz = osc.forClass (); + assignNewHandle (clazz); + ret_val = clazz; + break; + } + + case TC_CLASSDESC: + { +// DEBUG ("CLASSDESC NAME "); + String name = this.realInputStream.readUTF (); +// DEBUG ("UID "); + long uid = this.realInputStream.readLong (); +// DEBUG ("FLAGS "); + byte flags = this.realInputStream.readByte (); +// DEBUG ("FIELD COUNT "); + short field_count = this.realInputStream.readShort (); + ObjectStreamField[] fields = new ObjectStreamField[field_count]; + + ObjectStreamClass osc = new ObjectStreamClass (name, uid, + flags, fields); + assignNewHandle (osc); + + for (int i=0; i < field_count; i++) + { +// DEBUG ("TYPE CODE "); + char type_code = (char)this.realInputStream.readByte (); +// DEBUG ("FIELD NAME "); + String field_name = this.realInputStream.readUTF (); + String class_name; + + if (type_code == 'L' || type_code == '[') + class_name = (String)readObject (); + else + class_name = String.valueOf (type_code); + + fields[i] = + new ObjectStreamField (field_name, + TypeSignature.getClassForEncoding + (class_name)); + } + + setBlockDataMode (true); + osc.setClass (resolveClass (osc)); + setBlockDataMode (false); + +// DEBUG ("ENDBLOCKDATA "); + if (this.realInputStream.readByte () != TC_ENDBLOCKDATA) + throw new IOException ("Data annotated to class was not consumed."); + + osc.setSuperclass ((ObjectStreamClass)readObject ()); + ret_val = osc; + break; + } + + case TC_STRING: + { +// DEBUG ("STRING "); + String s = this.realInputStream.readUTF (); + ret_val = processResoultion (s, assignNewHandle (s)); + break; + } + + case TC_ARRAY: + { + ObjectStreamClass osc = (ObjectStreamClass)readObject (); + Class componenetType = osc.forClass ().getComponentType (); +// DEBUG ("ARRAY LENGTH "); + int length = this.realInputStream.readInt (); + Object array = Array.newInstance (componenetType, length); + int handle = assignNewHandle (array); + readArrayElements (array, componenetType); + ret_val = processResoultion (array, handle); + break; + } + + case TC_OBJECT: + { + ObjectStreamClass osc = (ObjectStreamClass)readObject (); + Class clazz = osc.forClass (); + + if (!Serializable.class.isAssignableFrom (clazz)) + throw new NotSerializableException (clazz + " is not Serializable, and thus cannot be deserialized."); + + if (Externalizable.class.isAssignableFrom (clazz)) + { + Externalizable obj = null; + + try + { + obj = (Externalizable)clazz.newInstance (); + } + catch (InstantiationException e) + { + throw new ClassNotFoundException ("Instance of " + clazz + + " could not be created"); + } + catch (IllegalAccessException e) + { + throw new ClassNotFoundException ("Instance of " + clazz + + " could not be created because class or zero-argument constructor is not accessible"); + } + catch (NoSuchMethodError e) + { + throw new ClassNotFoundException ("Instance of " + clazz + + " could not be created because zero-argument constructor is not defined"); + } + + int handle = assignNewHandle (obj); + + boolean read_from_blocks = ((osc.getFlags () & SC_BLOCK_DATA) != 0); + + if (read_from_blocks) + setBlockDataMode (true); + + obj.readExternal (this); + + if (read_from_blocks) + setBlockDataMode (false); + + ret_val = processResoultion (obj, handle); + break; + } // end if (Externalizable.class.isAssignableFrom (clazz)) + + // find the first non-serializable, non-abstract + // class in clazz's inheritance hierarchy + Class first_nonserial = clazz.getSuperclass (); + while (Serializable.class.isAssignableFrom (first_nonserial) + || Modifier.isAbstract (first_nonserial.getModifiers ())) + first_nonserial = first_nonserial.getSuperclass (); + +// DEBUGln ("Using " + first_nonserial +// + " as starting point for constructing " + clazz); + + Object obj = null; + obj = newObject (clazz, first_nonserial); + + if (obj == null) + throw new ClassNotFoundException ("Instance of " + clazz + + " could not be created"); + + int handle = assignNewHandle (obj); + this.currentObject = obj; + ObjectStreamClass[] hierarchy = + ObjectStreamClass.getObjectStreamClasses (clazz); + +// DEBUGln ("Got class hierarchy of depth " + hierarchy.length); + + boolean has_read; + for (int i=0; i < hierarchy.length; i++) + { + this.currentObjectStreamClass = hierarchy[i]; + +// DEBUGln ("Reading fields of " +// + this.currentObjectStreamClass.getName ()); + + has_read = true; + + try + { + this.currentObjectStreamClass.forClass (). + getDeclaredMethod ("readObject", readObjectParams); + } + catch (NoSuchMethodException e) + { + has_read = false; + } + + // XXX: should initialize fields in classes in the hierarchy + // that aren't in the stream + // should skip over classes in the stream that aren't in the + // real classes hierarchy + readFields (obj, this.currentObjectStreamClass.fields, + has_read, this.currentObjectStreamClass); + + if (has_read) + { +// DEBUG ("ENDBLOCKDATA? "); + if (this.realInputStream.readByte () != TC_ENDBLOCKDATA) + throw new IOException ("No end of block data seen for class with readObject (ObjectInputStream) method."); + } + } + + this.currentObject = null; + this.currentObjectStreamClass = null; + ret_val = processResoultion (obj, handle); + break; + } + + case TC_RESET: + clearHandles (); + ret_val = readObject (); + break; + + case TC_EXCEPTION: + { + Exception e = (Exception)readObject (); + clearHandles (); + throw new WriteAbortedException ("Exception thrown during writing of stream", e); + } + + default: + throw new IOException ("Unknown marker on stream"); + } + + this.isDeserializing = was_deserializing; + + if (! was_deserializing) + { + setBlockDataMode (true); + + if (validators.size () > 0) + invokeValidators (); + } + + return ret_val; + } + + + /** + Reads the current objects non-transient, non-static fields from + the current class from the underlying output stream. + + This method is intended to be called from within a object's + <code>private void readObject (ObjectInputStream)</code> + method. + + @exception ClassNotFoundException The class that an object being + read in belongs to cannot be found. + + @exception NotActiveException This method was called from a + context other than from the current object's and current class's + <code>private void readObject (ObjectInputStream)</code> + method. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + */ + public void defaultReadObject () + throws ClassNotFoundException, IOException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException ("defaultReadObject called by non-active class and/or object"); + + if (fieldsAlreadyRead) + throw new NotActiveException ("defaultReadObject called but fields already read from stream (by defaultReadObject or readFields)"); + + readFields (this.currentObject, + this.currentObjectStreamClass.fields, + false, this.currentObjectStreamClass); + + fieldsAlreadyRead = true; + } + + + /** + Registers a <code>ObjectInputValidation</code> to be carried out + on the object graph currently being deserialized before it is + returned to the original caller of <code>readObject ()</code>. + The order of validation for multiple + <code>ObjectInputValidation</code>s can be controled using + <code>priority</code>. Validators with higher priorities are + called first. + + @see java.io.ObjectInputValidation + + @exception InvalidObjectException <code>validator</code> is + <code>null</code> + + @exception NotActiveException an attempt was made to add a + validator outside of the <code>readObject</code> method of the + object currently being deserialized + */ + public void registerValidation (ObjectInputValidation validator, + int priority) + throws InvalidObjectException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException ("registerValidation called by non-active class and/or object"); + + if (validator == null) + throw new InvalidObjectException ("attempt to add a null ObjectInputValidation object"); + + this.validators.addElement (new ValidatorAndPriority (validator, + priority)); + } + + + /** + Called when a class is being deserialized. This is a hook to + allow subclasses to read in information written by the + <code>annotateClass (Class)</code> method of an + <code>ObjectOutputStream</code>. + + This implementation looks up the active call stack for a + <code>ClassLoader</code>; if a <code>ClassLoader</code> is found, + it is used to load the class associated with <code>osc</code>, + otherwise, the default system <code>ClassLoader</code> is used. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + + @see java.io.ObjectOutputStream#annotateClass (java.lang.Class) + */ + protected Class resolveClass (ObjectStreamClass osc) + throws ClassNotFoundException, IOException + { +// DEBUGln ("Resolving " + osc); + + SecurityManager sm = System.getSecurityManager (); + + if (sm == null) + sm = new SecurityManager () {}; + + ClassLoader cl = currentClassLoader (sm); + + if (cl == null) + { +// DEBUGln ("No class loader found"); + return Class.forName (osc.getName ()); + } + else + { +// DEBUGln ("Using " + cl); + return cl.loadClass (osc.getName ()); + } + } + + + /** + Allows subclasses to resolve objects that are read from the + stream with other objects to be returned in their place. This + method is called the first time each object is encountered. + + This method must be enabled before it will be called in the + serialization process. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + + @see enableResolveObject (boolean) + */ + protected Object resolveObject (Object obj) throws IOException + { + return obj; + } + + + /** + If <code>enable</code> is <code>true</code> and this object is + trusted, then <code>resolveObject (Object)</code> will be called + in subsequent calls to <code>readObject (Object)</code>. + Otherwise, <code>resolveObject (Object)</code> will not be called. + + @exception SecurityException This class is not trusted. + */ + protected boolean enableResolveObject (boolean enable) + throws SecurityException + { + if (enable) + if (getClass ().getClassLoader () != null) + throw new SecurityException ("Untrusted ObjectInputStream subclass attempted to enable object resolution"); + + boolean old_val = this.resolveEnabled; + this.resolveEnabled = enable; + return old_val; + } + + + /** + Reads stream magic and stream version information from the + underlying stream. + + @exception IOException Exception from underlying stream. + + @exception StreamCorruptedException An invalid stream magic + number or stream version was read from the stream. + */ + protected void readStreamHeader () + throws IOException, StreamCorruptedException + { +// DEBUG ("STREAM MAGIC "); + if (this.realInputStream.readShort () != STREAM_MAGIC) + throw new StreamCorruptedException ("Invalid stream magic number"); + +// DEBUG ("STREAM VERSION "); + if (this.realInputStream.readShort () != STREAM_VERSION) + throw new StreamCorruptedException ("Invalid stream version number"); + } + + + public int read () throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock (); + return this.blockData[this.blockDataPosition++]; + } + else + return this.realInputStream.read (); + } + + public int read (byte data[], int offset, int length) throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition + length >= this.blockDataBytes) + readNextBlock (); + + System.arraycopy (this.blockData, this.blockDataPosition, + data, offset, length); + return length; + } + else + return this.realInputStream.read (data, offset, length); + } + + public int available () throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock (); + + return this.blockDataBytes - this.blockDataPosition; + } + else + return this.realInputStream.available (); + } + + public void close () throws IOException + { + this.realInputStream.close (); + } + + public boolean readBoolean () throws IOException + { + return this.dataInputStream.readBoolean (); + } + + public byte readByte () throws IOException + { + return this.dataInputStream.readByte (); + } + + public int readUnsignedByte () throws IOException + { + return this.dataInputStream.readUnsignedByte (); + } + + public short readShort () throws IOException + { + return this.dataInputStream.readShort (); + } + + public int readUnsignedShort () throws IOException + { + return this.dataInputStream.readUnsignedShort (); + } + + public char readChar () throws IOException + { + return this.dataInputStream.readChar (); + } + + public int readInt () throws IOException + { + return this.dataInputStream.readInt (); + } + + public long readLong () throws IOException + { + return this.dataInputStream.readLong (); + } + + public float readFloat () throws IOException + { + return this.dataInputStream.readFloat (); + } + + public double readDouble () throws IOException + { + return this.dataInputStream.readDouble (); + } + + public void readFully (byte data[]) throws IOException + { + this.dataInputStream.readFully (data); + } + + public void readFully (byte data[], int offset, int size) + throws IOException + { + this.dataInputStream.readFully (data, offset, size); + } + + public int skipBytes (int len) throws IOException + { + return this.dataInputStream.skipBytes (len); + } + + /** + @deprecated + @see java.io.DataInputStream#readLine () + */ + public String readLine () throws IOException + { + return this.dataInputStream.readLine (); + } + + public String readUTF () throws IOException + { + return this.dataInputStream.readUTF (); + } + + + /** + This class allows a class to specify exactly which fields should + be read, and what values should be read for these fields. + + XXX: finish up comments + */ + public static abstract class GetField + { + public abstract ObjectStreamClass getObjectStreamClass (); + + public abstract boolean defaulted (String name) + throws IOException, IllegalArgumentException; + + public abstract boolean get (String name, boolean defvalue) + throws IOException, IllegalArgumentException; + + public abstract char get (String name, char defvalue) + throws IOException, IllegalArgumentException; + + public abstract byte get (String name, byte defvalue) + throws IOException, IllegalArgumentException; + + public abstract short get (String name, short defvalue) + throws IOException, IllegalArgumentException; + + public abstract int get (String name, int defvalue) + throws IOException, IllegalArgumentException; + + public abstract long get (String name, long defvalue) + throws IOException, IllegalArgumentException; + + public abstract float get (String name, float defvalue) + throws IOException, IllegalArgumentException; + + public abstract double get (String name, double defvalue) + throws IOException, IllegalArgumentException; + + public abstract Object get (String name, Object defvalue) + throws IOException, IllegalArgumentException; + } + + public GetField readFields () + throws IOException, ClassNotFoundException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException ("readFields called by non-active class and/or object"); + + if (fieldsAlreadyRead) + throw new NotActiveException ("readFields called but fields already read from stream (by defaultReadObject or readFields)"); + + final ObjectStreamClass clazz = this.currentObjectStreamClass; + final byte[] prim_field_data = new byte[clazz.primFieldSize]; + final Object[] objs = new Object[clazz.objectFieldCount]; + readFully (prim_field_data); + for (int i = 0; i < objs.length; ++ i) + objs[i] = readObject (); + + return new GetField () + { + public ObjectStreamClass getObjectStreamClass () + { + return clazz; + } + + public boolean defaulted (String name) + throws IOException, IllegalArgumentException + { + return clazz.getField (name) == null; + } + + public boolean get (String name, boolean defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Boolean.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset ()] == 0 ? false : true; + } + + public char get (String name, char defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Character.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return (char)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public byte get (String name, byte defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Byte.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset ()]; + } + + public short get (String name, short defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Short.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return (short)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public int get (String name, int defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Integer.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF); + } + + public long get (String name, long defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Long.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return (long)(((prim_field_data[off++] & 0xFF) << 56) + | ((prim_field_data[off++] & 0xFF) << 48) + | ((prim_field_data[off++] & 0xFF) << 40) + | ((prim_field_data[off++] & 0xFF) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public float get (String name, float defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Float.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return Float.intBitsToFloat (((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public double get (String name, double defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, Double.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset (); + + return Double.longBitsToDouble ( + (long)(((prim_field_data[off++] & 0xFF) << 56) + | ((prim_field_data[off++] & 0xFF) << 48) + | ((prim_field_data[off++] & 0xFF) << 40) + | ((prim_field_data[off++] & 0xFF) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF))); + } + + public Object get (String name, Object defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField (name, null); + + if (field == null) + return defvalue; + + return objs[field.getOffset ()]; + } + + private ObjectStreamField getField (String name, Class type) + throws IllegalArgumentException + { + ObjectStreamField field = clazz.getField (name); + + if (field == null) + return null; + + Class field_type = field.getType (); + + if (type == field_type || + (type != null && field_type.isPrimitive ())) + return field; + + throw new IllegalArgumentException ("Field requested is of type " + + field_type.getName () + + ", but requested type was " + + (type == null ? + "Object" : type.getName ())); + } + }; + + } + + + /** + Protected constructor that allows subclasses to override + deserialization. This constructor should be called by subclasses + that wish to override <code>readObject (Object)</code>. This + method does a security check <i>NOTE: currently not + implemented</i>, then sets a flag that informs + <code>readObject (Object)</code> to call the subclasses + <code>readObjectOverride (Object)</code> method. + + @see readObjectOverride (Object) + */ + protected ObjectInputStream () + throws IOException, SecurityException + { + SecurityManager sec_man = System.getSecurityManager (); + if (sec_man != null) + sec_man.checkPermission (SUBCLASS_IMPLEMENTATION_PERMISSION); + this.useSubclassMethod = true; + } + + + /** + This method allows subclasses to override the default + de serialization mechanism provided by + <code>ObjectInputStream</code>. To make this method be used for + writing objects, subclasses must invoke the 0-argument + constructor on this class from there constructor. + + @see ObjectInputStream () + */ + protected Object readObjectOverride () + throws ClassNotFoundException, IOException, OptionalDataException + { + throw new IOException ("Subclass of ObjectInputStream must implement readObjectOverride"); + } + + + // assigns the next availible handle to OBJ + private int assignNewHandle (Object obj) + { + this.objectLookupTable.put (new Integer (this.nextOID), + new ObjectIdentityWrapper (obj)); + +// try +// { +// DEBUG ("Assigning handle " + this.nextOID); +// DEBUGln (" to " + obj); +// } +// catch (Throwable t) {} + + return this.nextOID++; + } + + + private Object processResoultion (Object obj, int handle) + throws IOException + { + if (obj instanceof Resolvable) + obj = ((Resolvable)obj).readResolve (); + + if (this.resolveEnabled) + obj = resolveObject (obj); + + this.objectLookupTable.put (new Integer (handle), + new ObjectIdentityWrapper (obj)); + + return obj; + } + + + private void clearHandles () + { + this.objectLookupTable.clear (); + this.nextOID = baseWireHandle; + } + + + private void readNextBlock () throws IOException + { +// DEBUG ("MARKER "); + readNextBlock (this.realInputStream.readByte ()); + } + + + private void readNextBlock (byte marker) throws IOException + { + if (marker == TC_BLOCKDATA) + { +// DEBUG ("BLOCK DATA SIZE "); + this.blockDataBytes = this.realInputStream.readUnsignedByte (); + } + else if (marker == TC_BLOCKDATALONG) + { +// DEBUG ("BLOCK DATA LONG SIZE "); + this.blockDataBytes = this.realInputStream.readInt (); + } + else + { + throw new EOFException ("Attempt to read primitive data, but no data block is active."); + } + + if (this.blockData.length < this.blockDataBytes) + this.blockData = new byte[this.blockDataBytes]; + + this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes); + this.blockDataPosition = 0; + } + + + private void readArrayElements (Object array, Class clazz) + throws ClassNotFoundException, IOException + { + if (clazz.isPrimitive ()) + { + if (clazz == Boolean.TYPE) + { + boolean[] cast_array = (boolean[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readBoolean (); + return; + } + if (clazz == Byte.TYPE) + { + byte[] cast_array = (byte[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readByte (); + return; + } + if (clazz == Character.TYPE) + { + char[] cast_array = (char[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readChar (); + return; + } + if (clazz == Double.TYPE) + { + double[] cast_array = (double[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readDouble (); + return; + } + if (clazz == Float.TYPE) + { + float[] cast_array = (float[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readFloat (); + return; + } + if (clazz == Integer.TYPE) + { + int[] cast_array = (int[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readInt (); + return; + } + if (clazz == Long.TYPE) + { + long[] cast_array = (long[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readLong (); + return; + } + if (clazz == Short.TYPE) + { + short[] cast_array = (short[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readShort (); + return; + } + } + else + { + Object[] cast_array = (Object[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = readObject (); + } + } + + + private void readFields (Object obj, ObjectStreamField[] stream_fields, + boolean call_read_method, + ObjectStreamClass stream_osc) + throws ClassNotFoundException, IOException + { + if (call_read_method) + { + fieldsAlreadyRead = false; + setBlockDataMode (true); + callReadMethod (obj, stream_osc.forClass ()); + setBlockDataMode (false); + return; + } + + ObjectStreamField[] real_fields = + ObjectStreamClass.lookup (stream_osc.forClass ()).fields; + + boolean default_initialize, set_value; + String field_name = null; + Class type = null; + ObjectStreamField stream_field = null; + ObjectStreamField real_field = null; + int stream_idx = 0; + int real_idx = 0; + + while (stream_idx < stream_fields.length + && real_idx < real_fields.length) + { + default_initialize = false; + set_value = true; + + if (stream_idx == stream_fields.length) + default_initialize = true; + else + { + stream_field = stream_fields[stream_idx]; + type = stream_field.getType (); + } + + if (real_idx == real_fields.length) + set_value = false; + else + { + real_field = real_fields[real_idx]; + type = real_field.getType (); + field_name = real_field.getName (); + } + + if (set_value && !default_initialize) + { + int comp_val = + real_field.compareTo (stream_field); + + if (comp_val < 0) + { + default_initialize = true; + real_idx++; + } + else if (comp_val > 0) + { + set_value = false; + stream_idx++; + } + else + { + real_idx++; + stream_idx++; + } + } + + if (type == Boolean.TYPE) + { + boolean value = + default_initialize ? false : this.realInputStream.readBoolean (); + if (set_value) + setBooleanField (obj, field_name, value); + } + else if (type == Byte.TYPE) + { + byte value = + default_initialize ? 0 : this.realInputStream.readByte (); + if (set_value) + setByteField (obj, field_name, value); + } + else if (type == Character.TYPE) + { + char value = + default_initialize ? (char)0 : this.realInputStream.readChar (); + if (set_value) + setCharField (obj, field_name, value); + } + else if (type == Double.TYPE) + { + double value = + default_initialize ? 0 : this.realInputStream.readDouble (); + if (set_value) + setDoubleField (obj, field_name, value); + } + else if (type == Float.TYPE) + { + float value = + default_initialize ? 0 : this.realInputStream.readFloat (); + if (set_value) + setFloatField (obj, field_name, value); + } + else if (type == Integer.TYPE) + { + int value = + default_initialize ? 0 : this.realInputStream.readInt (); + if (set_value) + setIntField (obj, field_name, value); + } + else if (type == Long.TYPE) + { + long value = + default_initialize ? 0 : this.realInputStream.readLong (); + if (set_value) + setLongField (obj, field_name, value); + } + else if (type == Short.TYPE) + { + short value = + default_initialize ? (short)0 : this.realInputStream.readShort (); + if (set_value) + setShortField (obj, field_name, value); + } + else + { + Object value = + default_initialize ? null : readObject (); + if (set_value) + setObjectField (obj, field_name, + real_field.getTypeString (), value); + } + } + } + + + // Toggles writing primitive data to block-data buffer. + private void setBlockDataMode (boolean on) + { +// DEBUGln ("Setting block data mode to " + on); + + this.readDataFromBlock = on; + + if (on) + this.dataInputStream = this.blockDataInput; + else + this.dataInputStream = this.realInputStream; + } + + + // returns a new instance of REAL_CLASS that has been constructed + // only to th level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS) + private Object newObject (Class real_class, Class constructor_class) + { + try + { + Object obj = allocateObject (real_class); + callConstructor (constructor_class, obj); + return obj; + } + catch (InstantiationException e) + { + return null; + } + } + + + // runs all registered ObjectInputValidations in prioritized order + // on OBJ + private void invokeValidators () throws InvalidObjectException + { + Object[] validators = new Object[this.validators.size ()]; + this.validators.copyInto (validators); + Arrays.sort (validators); + + try + { + for (int i=0; i < validators.length; i++) + ((ObjectInputValidation)validators[i]).validateObject (); + } + finally + { + this.validators.removeAllElements (); + } + } + + + // this native method is used to get access to the protected method + // of the same name in SecurityManger + private static ClassLoader currentClassLoader (SecurityManager sm) + { + // FIXME: This is too simple. + return ClassLoader.getSystemClassLoader (); + } + + private static native Field getField (Class klass, String name) + throws java.lang.NoSuchFieldException; + + private static native Method getMethod (Class klass, String name, Class args[]) + throws java.lang.NoSuchMethodException; + + private void callReadMethod (Object obj, Class klass) throws IOException + { + try + { + Class classArgs[] = {Class.forName ("java.io.ObjectInputStream")}; + Method m = getMethod (klass, "readObject", classArgs); + if (m == null) + return; + Object args[] = {this}; + m.invoke (obj, args); + } + catch (Exception _) + { + throw new IOException (); + } + } + + private native Object allocateObject (Class clazz) + throws InstantiationException; + + private native void callConstructor (Class clazz, Object obj); + + private void setBooleanField (Object obj, String field_name, + boolean val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setBoolean (obj, val); + } + catch (Exception _) + { + } + } + + private void setByteField (Object obj, String field_name, + byte val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setByte (obj, val); + } + catch (Exception _) + { + } + } + + private void setCharField (Object obj, String field_name, + char val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setChar (obj, val); + } + catch (Exception _) + { + } + } + + private void setDoubleField (Object obj, String field_name, + double val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setDouble (obj, val); + } + catch (Exception _) + { + } + } + + private void setFloatField (Object obj, String field_name, + float val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setFloat (obj, val); + } + catch (Exception _) + { + } + } + + private void setIntField (Object obj, String field_name, + int val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setInt (obj, val); + } + catch (Exception _) + { + } + } + + + private void setLongField (Object obj, String field_name, + long val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setLong (obj, val); + } + catch (Exception _) + { + } + } + + + private void setShortField (Object obj, String field_name, + short val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + f.setShort (obj, val); + } + catch (Exception _) + { + } + } + + + private void setObjectField (Object obj, String field_name, String type_code, + Object val) + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + // FIXME: We should check the type_code here + f.set (obj, val); + } + catch (Exception _) + { + } + } + + private static final int BUFFER_SIZE = 1024; + private static final Class[] readObjectParams = { ObjectInputStream.class }; + + private DataInputStream realInputStream; + private DataInputStream dataInputStream; + private DataInputStream blockDataInput; + private int blockDataPosition; + private int blockDataBytes; + private byte[] blockData; + private boolean useSubclassMethod; + private int nextOID; + private boolean resolveEnabled; + private Hashtable objectLookupTable; + private Object currentObject; + private ObjectStreamClass currentObjectStreamClass; + private boolean readDataFromBlock; + private boolean isDeserializing; + private boolean fieldsAlreadyRead; + private Vector validators; + + +/* FIXME: These 2 methods cause a build error on i686-pc-linux-gnu. + private void DEBUG (String msg) + { + System.out.print (msg); + } + + + private void DEBUGln (String msg) + { + System.out.println (msg); + } +* end FIXME */ +} + + +// used to keep a prioritized list of object validators +class ValidatorAndPriority implements Comparable +{ + int priority; + ObjectInputValidation validator; + + ValidatorAndPriority (ObjectInputValidation validator, int priority) + { + this.priority = priority; + this.validator = validator; + } + + public int compareTo (Object o) + { + ValidatorAndPriority vap = (ValidatorAndPriority)o; + return this.priority - vap.priority; + } +} diff --git a/libjava/java/io/ObjectInputValidation.java b/libjava/java/io/ObjectInputValidation.java new file mode 100644 index 0000000..cf3508e --- /dev/null +++ b/libjava/java/io/ObjectInputValidation.java @@ -0,0 +1,50 @@ +/* ObjectInputValidation.java -- Validate an object + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * What does this interface really do? + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract interface ObjectInputValidation +{ + +/** + * This method is called to validate an object. If the object is invalid + * an exception is thrown. + * + * @exception InvalidObjectException If the object is invalid + */ +public abstract void +validateObject() throws InvalidObjectException; + +} // interface ObjectInputValidation + diff --git a/libjava/java/io/ObjectOutput.java b/libjava/java/io/ObjectOutput.java new file mode 100644 index 0000000..56b3390 --- /dev/null +++ b/libjava/java/io/ObjectOutput.java @@ -0,0 +1,116 @@ +/* ObjectOutput.java -- Interface for writing objects to a stream + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * This interface extends <code>DataOutput</code> to provide the additional + * facility of writing object instances to a stream. It also adds some + * additional methods to make the interface more <code>OutputStream</code> like. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public abstract interface ObjectOutput extends DataOutput +{ + + +/** + * This method writes the specified byte to the output stream. + * + * @param b The byte to write. + * + * @exception IOException If an error occurs. + */ +public abstract void +write(int b) throws IOException; + +/*************************************************************************/ + +/** + * This method writes all the bytes in the specified byte array to the + * output stream. + * + * @param buf The array of bytes to write. + * + * @exception IOException If an error occurs. + */ +public abstract void +write(byte[] buf) throws IOException; + +/*************************************************************************/ + +/** + * This method writes <code>len</code> bytes from the specified array + * starting at index <code>offset</code> into that array. + * + * @param buf The byte array to write from. + * @param offset The index into the byte array to start writing from. + * @param len The number of bytes to write. + * + * @exception IOException If an error occurs. + */ +public abstract void +write(byte[] buf, int offset, int len) throws IOException; + +/*************************************************************************/ + +/** + * This method writes a object instance to a stream. The format of the + * data written is determined by the actual implementation of this method + * + * @param obj The object to write + * + * @exception IOException If an error occurs + */ +public abstract void +writeObject(Object obj) throws IOException; + +/*************************************************************************/ + +/** + * This method causes any buffered data to be flushed out to the underlying + * stream + * + * @exception IOException If an error occurs + */ +public abstract void +flush() throws IOException; + +/*************************************************************************/ + +/** + * This method closes the underlying stream. + * + * @exception IOException If an error occurs + */ +public abstract void +close() throws IOException; + +} // interface ObjectOutput + diff --git a/libjava/java/io/ObjectOutputStream.java b/libjava/java/io/ObjectOutputStream.java new file mode 100644 index 0000000..a98b55ba --- /dev/null +++ b/libjava/java/io/ObjectOutputStream.java @@ -0,0 +1,1335 @@ +/* ObjectOutputStream.java -- Class used to write serialized objects + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.Hashtable; + +import gnu.java.io.ObjectIdentityWrapper; +import gnu.java.lang.reflect.TypeSignature; + +/** + An <code>ObjectOutputStream</code> can be used to write objects + as well as primitive data in a platform-independent manner to an + <code>OutputStream</code>. + + The data produced by an <code>ObjectOutputStream</code> can be read + and reconstituted by an <code>ObjectInputStream</code>. + + <code>writeObject (Object)</code> is used to write Objects, the + <code>write<type></code> methods are used to write primitive + data (as in <code>DataOutputStream</code>). Strings can be written + as objects or as primitive data. + + Not all objects can be written out using an + <code>ObjectOutputStream</code>. Only those objects that are an + instance of <code>java.io.Serializable</code> can be written. + + Using default serialization, information about the class of an + object is written, all of the non-transient, non-static fields of + the object are written, if any of these fields are objects, the are + written out in the same manner. + + An object is only written out the first time it is encountered. If + the object is encountered later, a reference to it is written to + the underlying stream. Thus writing circular object graphs + does not present a problem, nor are relationships between objects + in a graph lost. + + Example usage: + <pre> + Hashtable map = new Hashtable (); + map.put ("one", new Integer (1)); + map.put ("two", new Integer (2)); + + ObjectOutputStream oos = + new ObjectOutputStream (new FileOutputStream ("numbers")); + oos.writeObject (map); + oos.close (); + + ObjectInputStream ois = + new ObjectInputStream (new FileInputStream ("numbers")); + Hashtable newmap = (Hashtable)ois.readObject (); + + System.out.println (newmap); + </pre> + + The default serialization can be overriden in two ways. + + By defining a method <code>private void + writeObject (ObjectOutputStream)</code>, a class can dictate exactly + how information about itself is written. + <code>defaultWriteObject ()</code> may be called from this method to + carry out default serialization. This method is not + responsible for dealing with fields of super-classes or subclasses. + + By implementing <code>java.io.Externalizable</code>. This gives + the class complete control over the way it is written to the + stream. If this approach is used the burden of writing superclass + and subclass data is transfered to the class implementing + <code>java.io.Externalizable</code>. + + @see java.io.DataOutputStream + @see java.io.Externalizable + @see java.io.ObjectInputStream + @see java.io.Serializable + @see XXX: java serialization spec +*/ +public class ObjectOutputStream extends OutputStream + implements ObjectOutput, ObjectStreamConstants +{ + /** + Creates a new <code>ObjectOutputStream</code> that will do all of + its writing onto <code>out</code>. This method also initializes + the stream by writing the header information (stream magic number + and stream version). + + @exception IOException Writing stream header to underlying + stream cannot be completed. + + @see writeStreamHeader () + */ + public ObjectOutputStream (OutputStream out) throws IOException + { + realOutput = new DataOutputStream (out); + blockData = new byte[ BUFFER_SIZE ]; + blockDataCount = 0; + blockDataOutput = new DataOutputStream (this); + setBlockDataMode (true); + replacementEnabled = false; + isSerializing = false; + nextOID = baseWireHandle; + OIDLookupTable = new Hashtable (); + protocolVersion = defaultProtocolVersion; + useSubclassMethod = false; + writeStreamHeader (); + } + + + /** + Writes a representation of <code>obj</code> to the underlying + output stream by writing out information about its class, then + writing out each of the objects non-transient, non-static + fields. If any of these fields are other objects, + the are written out in the same manner. + + This method can be overriden by a class by implementing + <code>private void writeObject (ObjectOutputStream)</code>. + + If an exception is thrown from this method, the stream is left in + an undefined state. + + @exception NotSerializableException An attempt was made to + serialize an <code>Object</code> that is not serializable. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + */ + public final void writeObject (Object obj) throws IOException + { + if (useSubclassMethod) + { + writeObjectOverride (obj); + return; + } + + boolean was_serializing = isSerializing; + + if (! was_serializing) + setBlockDataMode (false); + + try + { + isSerializing = true; + boolean replaceDone = false; + + drain (); + + while (true) + { + if (obj == null) + { + realOutput.writeByte (TC_NULL); + break; + } + + Integer handle = findHandle (obj); + if (handle != null) + { + realOutput.writeByte (TC_REFERENCE); + realOutput.writeInt (handle.intValue ()); + break; + } + + if (obj instanceof Class) + { + realOutput.writeByte (TC_CLASS); + writeObject (ObjectStreamClass.lookup ((Class)obj)); + assignNewHandle (obj); + break; + } + + if (obj instanceof ObjectStreamClass) + { + ObjectStreamClass osc = (ObjectStreamClass)obj; + realOutput.writeByte (TC_CLASSDESC); + realOutput.writeUTF (osc.getName ()); + realOutput.writeLong (osc.getSerialVersionUID ()); + assignNewHandle (obj); + + int flags = osc.getFlags (); + + if (protocolVersion == PROTOCOL_VERSION_2 + && osc.isExternalizable ()) + flags |= SC_BLOCK_DATA; + + realOutput.writeByte (flags); + + ObjectStreamField[] fields = osc.fields; + realOutput.writeShort (fields.length); + + ObjectStreamField field; + for (int i=0; i < fields.length; i++) + { + field = fields[i]; + realOutput.writeByte (field.getTypeCode ()); + realOutput.writeUTF (field.getName ()); + + if (! field.isPrimitive ()) + writeObject (field.getTypeString ()); + } + + setBlockDataMode (true); + annotateClass (osc.forClass ()); + setBlockDataMode (false); + realOutput.writeByte (TC_ENDBLOCKDATA); + + if (osc.isSerializable ()) + writeObject (osc.getSuper ()); + else + writeObject (null); + break; + } + + + Object replacedObject = null; + + if ((replacementEnabled || obj instanceof Replaceable) + && ! replaceDone) + { + replacedObject = obj; + + if (obj instanceof Replaceable) + obj = ((Replaceable)obj).writeReplace (); + + if (replacementEnabled) + obj = replaceObject (obj); + + replaceDone = true; + continue; + } + + if (obj instanceof String) + { + realOutput.writeByte (TC_STRING); + assignNewHandle (obj); + realOutput.writeUTF ((String)obj); + break; + } + + Class clazz = obj.getClass (); + ObjectStreamClass osc = ObjectStreamClass.lookup (clazz); + if (osc == null) + throw new NotSerializableException ("The class " + + clazz.getName () + + " is not Serializable"); + + if (clazz.isArray ()) + { + realOutput.writeByte (TC_ARRAY); + writeObject (osc); + assignNewHandle (obj); + writeArraySizeAndElements (obj, clazz); + break; + } + + realOutput.writeByte (TC_OBJECT); + writeObject (osc); + + if (replaceDone) + assignNewHandle (replacedObject); + else + assignNewHandle (obj); + + if (obj instanceof Externalizable) + { + if (protocolVersion == PROTOCOL_VERSION_2) + setBlockDataMode (true); + + ((Externalizable)obj).writeExternal (this); + + if (protocolVersion == PROTOCOL_VERSION_2) + { + setBlockDataMode (false); + drain (); + } + + break; + } + + if (obj instanceof Serializable) + { + currentObject = obj; + ObjectStreamClass[] hierarchy = + ObjectStreamClass.getObjectStreamClasses (clazz); + + boolean has_write; + for (int i=0; i < hierarchy.length; i++) + { + currentObjectStreamClass = hierarchy[i]; + + has_write = currentObjectStreamClass.hasWriteMethod (); + writeFields (obj, currentObjectStreamClass.fields, + has_write); + + fieldsAlreadyWritten = false; + + if (has_write) + { + drain (); + realOutput.writeByte (TC_ENDBLOCKDATA); + } + } + + currentObject = null; + currentObjectStreamClass = null; + currentPutField = null; + break; + } + + throw new NotSerializableException ("Instances of the class " + + clazz.getName () + + " are not Serializable"); + } // end pseudo-loop + } + catch (IOException e) + { + realOutput.writeByte (TC_EXCEPTION); + reset (true); + + try + { + writeObject (e); + } + catch (IOException ioe) + { + throw new StreamCorruptedException ("Exception " + ioe + " thrown while exception was being written to stream."); + } + + reset (true); + } + finally + { + isSerializing = was_serializing; + + if (! was_serializing) + setBlockDataMode (true); + } + } + + + /** + Writes the current objects non-transient, non-static fields from + the current class to the underlying output stream. + + This method is intended to be called from within a object's + <code>private void writeObject (ObjectOutputStream)</code> + method. + + @exception NotActiveException This method was called from a + context other than from the current object's and current class's + <code>private void writeObject (ObjectOutputStream)</code> + method. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + */ + public void defaultWriteObject () + throws IOException, NotActiveException + { + markFieldsWritten (); + writeFields (currentObject, currentObjectStreamClass.fields, false); + } + + + private void markFieldsWritten () throws IOException + { + if (currentObject == null || currentObjectStreamClass == null) + throw new NotActiveException ("defaultWriteObject called by non-active class and/or object"); + + if (fieldsAlreadyWritten) + throw new IOException ("Only one of putFields and defalutWriteObject may be called, and it may only be called once"); + + fieldsAlreadyWritten = true; + } + + + /** + Resets stream to state equivalent to the state just after it was + constructed. + + Causes all objects previously written to the stream to be + forgotten. A notification of this reset is also written to the + underlying stream. + + @exception IOException Exception from underlying + <code>OutputStream</code> or reset called while serialization is + in progress. + */ + public void reset () throws IOException + { + reset (false); + } + + + private void reset (boolean internal) throws IOException + { + if (!internal) + { + if (isSerializing) + throw new IOException ("Reset called while serialization in progress"); + + realOutput.writeByte (TC_RESET); + } + + clearHandles (); + } + + + /** + Informs this <code>ObjectOutputStream</code> to write data + according to the specified protocol. There are currently two + different protocols, specified by <code>PROTOCOL_VERSION_1</code> + and <code>PROTOCOL_VERSION_2</code>. This implementation writes + data using <code>PROTOCOL_VERSION_1</code> by default, as is done + by the JDK 1.1. + + A non-portable method, <code>setDefaultProtocolVersion (int + version)</code> is provided to change the default protocol + version. + + For an explination of the differences beween the two protocols + see XXX: the Java ObjectSerialization Specification. + + @exception IOException if <code>version</code> is not a valid + protocol + + @see setDefaultProtocolVersion (int) + */ + public void useProtocolVersion (int version) throws IOException + { + if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2) + throw new IOException ("Invalid protocol version requested."); + + protocolVersion = version; + } + + + /** + <em>GNU $classpath specific</em> + + Changes the default stream protocol used by all + <code>ObjectOutputStream</code>s. There are currently two + different protocols, specified by <code>PROTOCOL_VERSION_1</code> + and <code>PROTOCOL_VERSION_2</code>. The default default is + <code>PROTOCOL_VERSION_1</code>. + + @exception IOException if <code>version</code> is not a valid + protocol + + @see useProtocolVersion (int) + */ + public static void setDefaultProtocolVersion (int version) + throws IOException + { + if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2) + throw new IOException ("Invalid protocol version requested."); + + defaultProtocolVersion = version; + } + + + /** + An empty hook that allows subclasses to write extra information + about classes to the stream. This method is called the first + time each class is seen, and after all of the standard + information about the class has been written. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + + @see java.io.ObjectInputStream#resolveClass (java.io.ObjectStreamClass) + */ + protected void annotateClass (Class cl) throws IOException + {} + + + /** + Allows subclasses to replace objects that are written to the + stream with other objects to be written in their place. This + method is called the first time each object is encountered + (modulo reseting of the stream). + + This method must be enabled before it will be called in the + serialization process. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + + @see enableReplaceObject (boolean) + */ + protected Object replaceObject (Object obj) throws IOException + { + return obj; + } + + + /** + If <code>enable</code> is <code>true</code> and this object is + trusted, then <code>replaceObject (Object)</code> will be called + in subsequent calls to <code>writeObject (Object)</code>. + Otherwise, <code>replaceObject (Object)</code> will not be called. + + @exception SecurityException This class is not trusted. + */ + protected boolean enableReplaceObject (boolean enable) + throws SecurityException + { + if (enable) + if (getClass ().getClassLoader () != null) + throw new SecurityException ("Untrusted ObjectOutputStream subclass attempted to enable object replacement"); + + boolean old_val = replacementEnabled; + replacementEnabled = enable; + return old_val; + } + + + /** + Writes stream magic and stream version information to the + underlying stream. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + */ + protected void writeStreamHeader () throws IOException + { + realOutput.writeShort (STREAM_MAGIC); + realOutput.writeShort (STREAM_VERSION); + } + + + + /** + Protected constructor that allows subclasses to override + serialization. This constructor should be called by subclasses + that wish to override <code>writeObject (Object)</code>. This + method does a security check <i>NOTE: currently not + implemented</i>, then sets a flag that informs + <code>writeObject (Object)</code> to call the subclasses + <code>writeObjectOverride (Object)</code> method. + + @see writeObjectOverride (Object) + */ + protected ObjectOutputStream () throws IOException, SecurityException + { + SecurityManager sec_man = System.getSecurityManager (); + if (sec_man != null) + sec_man.checkPermission (SUBCLASS_IMPLEMENTATION_PERMISSION); + useSubclassMethod = true; + } + + + /** + This method allows subclasses to override the default + serialization mechanism provided by + <code>ObjectOutputStream</code>. To make this method be used for + writing objects, subclasses must invoke the 0-argument + constructor on this class from there constructor. + + @see ObjectOutputStream () + + @exception NotActiveException Subclass has arranged for this + method to be called, but did not implement this method. + */ + protected void writeObjectOverride (Object obj) throws NotActiveException, + IOException + { + throw new NotActiveException ("Subclass of ObjectOutputStream must implement writeObjectOverride"); + } + + + /** + @see java.io.DataOutputStream#write (int) + */ + public void write (int data) throws IOException + { + if (writeDataAsBlocks) + { + if (blockDataCount == BUFFER_SIZE) + drain (); + + blockData[ blockDataCount++ ] = (byte)data; + } + else + realOutput.write (data); + } + + + /** + @see java.io.DataOutputStream#write (byte[]) + */ + public void write (byte b[]) throws IOException + { + write (b, 0, b.length); + } + + + /** + @see java.io.DataOutputStream#write (byte[],int,int) + */ + public void write (byte b[], int off, int len) throws IOException + { + if (writeDataAsBlocks) + { + if (len < 0) + throw new IndexOutOfBoundsException (); + + if (blockDataCount + len < BUFFER_SIZE) + { + System.arraycopy (b, off, blockData, blockDataCount, len); + blockDataCount += len; + } + else + { + drain (); + writeBlockDataHeader (len); + realOutput.write (b, off, len); + } + } + else + realOutput.write (b, off, len); + } + + + /** + @see java.io.DataOutputStream#flush () + */ + public void flush () throws IOException + { + drain (); + realOutput.flush (); + } + + + /** + Causes the block-data buffer to be written to the underlying + stream, but does not flush underlying stream. + + @exception IOException Exception from underlying + <code>OutputStream</code>. + */ + protected void drain () throws IOException + { + if (blockDataCount == 0) + return; + + writeBlockDataHeader (blockDataCount); + realOutput.write (blockData, 0, blockDataCount); + blockDataCount = 0; + } + + + /** + @see java.io.DataOutputStream#close () + */ + public void close () throws IOException + { + drain (); + realOutput.close (); + } + + + /** + @see java.io.DataOutputStream#writeBoolean (boolean) + */ + public void writeBoolean (boolean data) throws IOException + { + dataOutput.writeBoolean (data); + } + + + /** + @see java.io.DataOutputStream#writeByte (int) + */ + public void writeByte (int data) throws IOException + { + dataOutput.writeByte (data); + } + + + /** + @see java.io.DataOutputStream#writeShort (int) + */ + public void writeShort (int data) throws IOException + { + dataOutput.writeShort (data); + } + + + /** + @see java.io.DataOutputStream#writeChar (int) + */ + public void writeChar (int data) throws IOException + { + dataOutput.writeChar (data); + } + + + /** + @see java.io.DataOutputStream#writeInt (int) + */ + public void writeInt (int data) throws IOException + { + dataOutput.writeInt (data); + } + + + /** + @see java.io.DataOutputStream#writeLong (long) + */ + public void writeLong (long data) throws IOException + { + dataOutput.writeLong (data); + } + + + /** + @see java.io.DataOutputStream#writeFloat (float) + */ + public void writeFloat (float data) throws IOException + { + dataOutput.writeFloat (data); + } + + + /** + @see java.io.DataOutputStream#writeDouble (double) + */ + public void writeDouble (double data) throws IOException + { + dataOutput.writeDouble (data); + } + + + /** + @see java.io.DataOutputStream#writeBytes (java.lang.String) + */ + public void writeBytes (String data) throws IOException + { + dataOutput.writeBytes (data); + } + + + /** + @see java.io.DataOutputStream#writeChars (java.lang.String) + */ + public void writeChars (String data) throws IOException + { + dataOutput.writeChars (data); + } + + + /** + @see java.io.DataOutputStream#writeUTF (java.lang.String) + */ + public void writeUTF (String data) throws IOException + { + dataOutput.writeUTF (data); + } + + + /** + This class allows a class to specify exactly which fields should + be written, and what values should be written for these fields. + + XXX: finish up comments + */ + public static abstract class PutField + { + public abstract void put (String name, boolean value) + throws IOException, IllegalArgumentException; + public abstract void put (String name, byte value) + throws IOException, IllegalArgumentException; + public abstract void put (String name, char value) + throws IOException, IllegalArgumentException; + public abstract void put (String name, double value) + throws IOException, IllegalArgumentException; + public abstract void put (String name, float value) + throws IOException, IllegalArgumentException; + public abstract void put (String name, int value) + throws IOException, IllegalArgumentException; + public abstract void put (String name, long value) + throws IOException, IllegalArgumentException; + public abstract void put (String name, short value) + throws IOException, IllegalArgumentException; + public abstract void put (String name, Object value) + throws IOException, IllegalArgumentException; + public abstract void write (ObjectOutput out) throws IOException; + } + + + public PutField putFields () throws IOException + { + markFieldsWritten (); + + currentPutField = new PutField () + { + private byte[] prim_field_data + = new byte[currentObjectStreamClass.primFieldSize]; + private Object[] objs + = new Object[currentObjectStreamClass.objectFieldCount]; + + public void put (String name, boolean value) + throws IOException, IllegalArgumentException + { + ObjectStreamField field + = currentObjectStreamClass.getField (name); + checkType (field, 'Z'); + prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0); + } + + public void put (String name, byte value) + throws IOException, IllegalArgumentException + { + ObjectStreamField field + = currentObjectStreamClass.getField (name); + checkType (field, 'B'); + prim_field_data[field.getOffset ()] = value; + } + + public void put (String name, char value) + throws IOException, IllegalArgumentException + { + ObjectStreamField field + = currentObjectStreamClass.getField (name); + checkType (field, 'B'); + int off = field.getOffset (); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put (String name, double value) + throws IOException, IllegalArgumentException + { + ObjectStreamField field + = currentObjectStreamClass.getField (name); + checkType (field, 'B'); + int off = field.getOffset (); + long l_value = Double.doubleToLongBits (value); + prim_field_data[off++] = (byte)(l_value >>> 52); + prim_field_data[off++] = (byte)(l_value >>> 48); + prim_field_data[off++] = (byte)(l_value >>> 40); + prim_field_data[off++] = (byte)(l_value >>> 32); + prim_field_data[off++] = (byte)(l_value >>> 24); + prim_field_data[off++] = (byte)(l_value >>> 16); + prim_field_data[off++] = (byte)(l_value >>> 8); + prim_field_data[off] = (byte)l_value; + } + + public void put (String name, float value) + throws IOException, IllegalArgumentException + { + ObjectStreamField field + = currentObjectStreamClass.getField (name); + checkType (field, 'B'); + int off = field.getOffset (); + int i_value = Float.floatToIntBits (value); + prim_field_data[off++] = (byte)(i_value >>> 24); + prim_field_data[off++] = (byte)(i_value >>> 16); + prim_field_data[off++] = (byte)(i_value >>> 8); + prim_field_data[off] = (byte)i_value; + } + + public void put (String name, int value) + throws IOException, IllegalArgumentException + { + ObjectStreamField field + = currentObjectStreamClass.getField (name); + checkType (field, 'B'); + int off = field.getOffset (); + prim_field_data[off++] = (byte)(value >>> 24); + prim_field_data[off++] = (byte)(value >>> 16); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put (String name, long value) + throws IOException, IllegalArgumentException + { + ObjectStreamField field + = currentObjectStreamClass.getField (name); + checkType (field, 'B'); + int off = field.getOffset (); + prim_field_data[off++] = (byte)(value >>> 52); + prim_field_data[off++] = (byte)(value >>> 48); + prim_field_data[off++] = (byte)(value >>> 40); + prim_field_data[off++] = (byte)(value >>> 32); + prim_field_data[off++] = (byte)(value >>> 24); + prim_field_data[off++] = (byte)(value >>> 16); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put (String name, short value) + throws IOException, IllegalArgumentException + { + ObjectStreamField field + = currentObjectStreamClass.getField (name); + checkType (field, 'B'); + int off = field.getOffset (); + prim_field_data[off++] = (byte)(value >>> 8); + prim_field_data[off] = (byte)value; + } + + public void put (String name, Object value) + throws IOException, IllegalArgumentException + { + ObjectStreamField field + = currentObjectStreamClass.getField (name); + if (! field.getType ().isAssignableFrom (value.getClass ())) + throw new IllegalArgumentException (); + objs[field.getOffset ()] = value; + } + + public void write (ObjectOutput out) throws IOException + { + out.write (prim_field_data); + for (int i = 0; i < objs.length; ++ i) + out.writeObject (objs[i]); + } + + private void checkType (ObjectStreamField field, char type) + throws IllegalArgumentException + { + if (TypeSignature.getEncodingOfClass (field.getType ()).charAt (0) != type) + throw new IllegalArgumentException (); + } + }; + // end PutFieldImpl + + return currentPutField; + } + + + public void writeFields () throws IOException + { + if (currentPutField == null) + throw new NotActiveException ("writeFields can only be called after putFields has been called"); + + currentPutField.write (this); + } + + + // write out the block-data buffer, picking the correct header + // depending on the size of the buffer + private void writeBlockDataHeader (int size) throws IOException + { + if (size < 256) + { + realOutput.writeByte (TC_BLOCKDATA); + realOutput.write (size); + } + else + { + realOutput.writeByte (TC_BLOCKDATALONG); + realOutput.writeInt (size); + } + } + + + // lookup the handle for OBJ, return null if OBJ doesn't have a + // handle yet + private Integer findHandle (Object obj) + { + return (Integer)OIDLookupTable.get (new ObjectIdentityWrapper (obj)); + } + + + // assigns the next availible handle to OBJ + private int assignNewHandle (Object obj) + { + OIDLookupTable.put (new ObjectIdentityWrapper (obj), + new Integer (nextOID)); + return nextOID++; + } + + + // resets mapping from objects to handles + private void clearHandles () + { + nextOID = baseWireHandle; + OIDLookupTable.clear (); + } + + + // write out array size followed by each element of the array + private void writeArraySizeAndElements (Object array, Class clazz) + throws IOException + { + int length = Array.getLength (array); + + if (clazz.isPrimitive ()) + { + if (clazz == Boolean.TYPE) + { + boolean[] cast_array = (boolean[])array; + realOutput.writeInt (length); + for (int i=0; i < length; i++) + realOutput.writeBoolean (cast_array[i]); + return; + } + if (clazz == Byte.TYPE) + { + byte[] cast_array = (byte[])array; + realOutput.writeInt (length); + for (int i=0; i < length; i++) + realOutput.writeByte (cast_array[i]); + return; + } + if (clazz == Character.TYPE) + { + char[] cast_array = (char[])array; + realOutput.writeInt (length); + for (int i=0; i < length; i++) + realOutput.writeChar (cast_array[i]); + return; + } + if (clazz == Double.TYPE) + { + double[] cast_array = (double[])array; + realOutput.writeInt (length); + for (int i=0; i < length; i++) + realOutput.writeDouble (cast_array[i]); + return; + } + if (clazz == Float.TYPE) + { + float[] cast_array = (float[])array; + realOutput.writeInt (length); + for (int i=0; i < length; i++) + realOutput.writeFloat (cast_array[i]); + return; + } + if (clazz == Integer.TYPE) + { + int[] cast_array = (int[])array; + realOutput.writeInt (length); + for (int i=0; i < length; i++) + realOutput.writeInt (cast_array[i]); + return; + } + if (clazz == Long.TYPE) + { + long[] cast_array = (long[])array; + realOutput.writeInt (length); + for (int i=0; i < length; i++) + realOutput.writeLong (cast_array[i]); + return; + } + if (clazz == Short.TYPE) + { + short[] cast_array = (short[])array; + realOutput.writeInt (length); + for (int i=0; i < length; i++) + realOutput.writeShort (cast_array[i]); + return; + } + } + else + { + Object[] cast_array = (Object[])array; + realOutput.writeInt (length); + for (int i=0; i < length; i++) + writeObject (cast_array[i]); + } + } + + + // writes out FIELDS of OBJECT. If CALL_WRITE_METHOD is true, use + // object's writeObject (ObjectOutputStream), otherwise use default + // serialization. FIELDS are already in canonical order. + private void writeFields (Object obj, + ObjectStreamField[] fields, + boolean call_write_method) throws IOException + { + if (call_write_method) + { + setBlockDataMode (true); + callWriteMethod (obj); + setBlockDataMode (false); + return; + } + + String field_name; + Class type; + for (int i=0; i < fields.length; i++) + { + field_name = fields[i].getName (); + type = fields[i].getType (); + + if (type == Boolean.TYPE) + realOutput.writeBoolean (getBooleanField (obj, field_name)); + else if (type == Byte.TYPE) + realOutput.writeByte (getByteField (obj, field_name)); + else if (type == Character.TYPE) + realOutput.writeChar (getCharField (obj, field_name)); + else if (type == Double.TYPE) + realOutput.writeDouble (getDoubleField (obj, field_name)); + else if (type == Float.TYPE) + realOutput.writeFloat (getFloatField (obj, field_name)); + else if (type == Integer.TYPE) + realOutput.writeInt (getIntField (obj, field_name)); + else if (type == Long.TYPE) + realOutput.writeLong (getLongField (obj, field_name)); + else if (type == Short.TYPE) + realOutput.writeShort (getShortField (obj, field_name)); + else + writeObject (getObjectField (obj, field_name, + TypeSignature.getEncodingOfClass (type))); + } + } + + + // Toggles writing primitive data to block-data buffer. + private void setBlockDataMode (boolean on) + { + writeDataAsBlocks = on; + + if (on) + dataOutput = blockDataOutput; + else + dataOutput = realOutput; + } + + + private void callWriteMethod (Object obj) throws IOException + { + try + { + Class classArgs[] = {Class.forName ("java.io.ObjectOutputStream")}; + Class klass = obj.getClass (); + Method m = getMethod (klass, "writeObject", classArgs); + if (m == null) + return; + Object args[] = {this}; + m.invoke (obj, args); + } + catch (Exception _) + { + throw new IOException (); + } + } + + private boolean getBooleanField (Object obj, String field_name) throws IOException + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + boolean b = f.getBoolean (obj); + return b; + } + catch (Exception _) + { + throw new IOException (); + } + } + + private byte getByteField (Object obj, String field_name) throws IOException + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + byte b = f.getByte (obj); + return b; + } + catch (Exception _) + { + throw new IOException (); + } + } + + private char getCharField (Object obj, String field_name) throws IOException + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + char b = f.getChar (obj); + return b; + } + catch (Exception _) + { + throw new IOException (); + } + } + + private double getDoubleField (Object obj, String field_name) throws IOException + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + double b = f.getDouble (obj); + return b; + } + catch (Exception _) + { + throw new IOException (); + } + } + + private float getFloatField (Object obj, String field_name) throws IOException + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + float b = f.getFloat (obj); + return b; + } + catch (Exception _) + { + throw new IOException (); + } + } + + private int getIntField (Object obj, String field_name) throws IOException + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + int b = f.getInt (obj); + return b; + } + catch (Exception _) + { + throw new IOException (); + } + } + + private long getLongField (Object obj, String field_name) throws IOException + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + long b = f.getLong (obj); + return b; + } + catch (Exception _) + { + throw new IOException (); + } + } + + private short getShortField (Object obj, String field_name) throws IOException + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + short b = f.getShort (obj); + return b; + } + catch (Exception _) + { + throw new IOException (); + } + } + + private Object getObjectField (Object obj, String field_name, + String type_code) throws IOException + { + try + { + Class klass = obj.getClass (); + Field f = getField (klass, field_name); + Object o = f.get (obj); + // FIXME: We should check the type_code here + return o; + } + catch (Exception _) + { + throw new IOException (); + } + } + + private static native Field getField (Class klass, String name) + throws java.lang.NoSuchFieldException; + + private static native Method getMethod (Class klass, String name, Class args[]) + throws java.lang.NoSuchMethodException; + + // this value comes from 1.2 spec, but is used in 1.1 as well + private final static int BUFFER_SIZE = 1024; + + private static int defaultProtocolVersion = PROTOCOL_VERSION_1; + + private DataOutputStream dataOutput; + private boolean writeDataAsBlocks; + private DataOutputStream realOutput; + private DataOutputStream blockDataOutput; + private byte[] blockData; + private int blockDataCount; + private Object currentObject; + private ObjectStreamClass currentObjectStreamClass; + private PutField currentPutField; + private boolean fieldsAlreadyWritten; + private boolean replacementEnabled; + private boolean isSerializing; + private int nextOID; + private Hashtable OIDLookupTable; + private int protocolVersion; + private boolean useSubclassMethod; +} diff --git a/libjava/java/io/ObjectStreamClass.java b/libjava/java/io/ObjectStreamClass.java new file mode 100644 index 0000000..f799b4f --- /dev/null +++ b/libjava/java/io/ObjectStreamClass.java @@ -0,0 +1,666 @@ +/* ObjectStreamClass.java -- Class used to write class information + about serialized objects. + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Hashtable; +import java.util.Vector; +import gnu.java.io.NullOutputStream; +import gnu.java.lang.reflect.TypeSignature; +import gnu.gcj.io.SimpleSHSStream; + + +public class ObjectStreamClass implements Serializable +{ + /** + Returns the <code>ObjectStreamClass</code> for <code>cl</code>. + If <code>cl</code> is null, or is not <code>Serializable</code>, + null is returned. <code>ObjectStreamClass</code>'s are memoized; + later calls to this method with the same class will return the + same <code>ObjectStreamClass</code> object and no recalculation + will be done. + + @see java.io.Serializable + */ + public static ObjectStreamClass lookup (Class cl) + { + if (cl == null) + return null; + + ObjectStreamClass osc = (ObjectStreamClass)classLookupTable.get (cl); + + if (osc != null) + return osc; + else if (! (Serializable.class).isAssignableFrom (cl)) + return null; + else + { + osc = new ObjectStreamClass (cl); + classLookupTable.put (cl, osc); + return osc; + } + } + + + /** + Returns the name of the class that this + <code>ObjectStreamClass</code> represents. + */ + public String getName () + { + return name; + } + + + /** + Returns the class that this <code>ObjectStreamClass</code> + represents. Null could be returned if this + <code>ObjectStreamClass</code> was read from an + <code>ObjectInputStream</code> and the class it represents cannot + be found or loaded. + + @see java.io.ObjectInputStream + */ + public Class forClass () + { + return clazz; + } + + + /** + Returns the serial version stream-unique identifier for the class + represented by this <code>ObjectStreamClass</code>. This SUID is + either defined by the class as <code>static final long + serialVersionUID</code> or is calculated as specified in + Javasoft's "Object Serialization Specification" XXX: add reference + */ + public long getSerialVersionUID () + { + return uid; + } + + + // Returns the serializable (non-static and non-transient) Fields + // of the class represented by this ObjectStreamClass. The Fields + // are sorted by name. + // XXX doc + public ObjectStreamField[] getFields () + { + ObjectStreamField[] copy = new ObjectStreamField[ fields.length ]; + System.arraycopy (fields, 0, copy, 0, fields.length); + return copy; + } + + + // XXX doc + // Can't do binary search since fields is sorted by name and + // primitiveness. + public ObjectStreamField getField (String name) + { + for (int i=0; i < fields.length; i++) + if (fields[i].getName ().equals (name)) + return fields[i]; + return null; + } + + + /** + Returns a textual representation of this + <code>ObjectStreamClass</code> object including the name of the + class it represents as well as that class's serial version + stream-unique identifier. + + @see getSerialVersionUID () + @see getName () + */ + public String toString () + { + return "java.io.ObjectStreamClass< " + name + ", " + uid + " >"; + } + + + // Returns true iff the class that this ObjectStreamClass represents + // has the following method: + // + // private void writeObject (ObjectOutputStream) + // + // This method is used by the class to override default + // serialization behaivior. + boolean hasWriteMethod () + { + return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0; + } + + + // Returns true iff the class that this ObjectStreamClass represents + // implements Serializable but does *not* implement Externalizable. + boolean isSerializable () + { + return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0; + } + + + // Returns true iff the class that this ObjectStreamClass represents + // implements Externalizable. + boolean isExternalizable () + { + return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0; + } + + + // Returns the <code>ObjectStreamClass</code> that represents the + // class that is the superclass of the class this + // <code>ObjectStreamClass</cdoe> represents. If the superclass is + // not Serializable, null is returned. + ObjectStreamClass getSuper () + { + return superClass; + } + + + // returns an array of ObjectStreamClasses that represent the super + // classes of CLAZZ and CLAZZ itself in order from most super to + // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ + // that is serializable. + static ObjectStreamClass[] getObjectStreamClasses (Class clazz) + { + ObjectStreamClass osc = ObjectStreamClass.lookup (clazz); + + ObjectStreamClass[] ret_val; + + 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; + } + } + + + // Returns an integer that consists of bit-flags that indicate + // properties of the class represented by this ObjectStreamClass. + // The bit-flags that could be present are those defined in + // ObjectStreamConstants that begin with `SC_' + int getFlags () + { + return flags; + } + + + ObjectStreamClass (String name, long uid, byte flags, + ObjectStreamField[] fields) + { + this.name = name; + this.uid = uid; + this.flags = flags; + this.fields = fields; + } + + + void setClass (Class clazz) + { + this.clazz = clazz; + } + + + void setSuperclass (ObjectStreamClass osc) + { + superClass = osc; + } + + + void calculateOffsets () + { + int i; + ObjectStreamField field; + primFieldSize = 0; + int fcount = fields.length; + for (i = 0; i < fcount; ++ i) + { + field = fields[i]; + + if (! field.isPrimitive ()) + break; + + field.setOffset (primFieldSize); + switch (field.getTypeCode ()) + { + case 'B': + case 'Z': + ++ primFieldSize; + break; + case 'C': + case 'S': + primFieldSize += 2; + break; + case 'I': + case 'F': + primFieldSize += 4; + break; + case 'D': + case 'J': + primFieldSize += 8; + break; + } + } + + for (objectFieldCount = 0; i < fcount; ++ i) + fields[i].setOffset (objectFieldCount++); + } + + + private ObjectStreamClass (Class cl) + { + uid = 0; + flags = 0; + + clazz = cl; + name = cl.getName (); + setFlags (cl); + setFields (cl); + setUID (cl); + superClass = lookup (cl.getSuperclass ()); + } + + + // Sets bits in flags according to features of CL. + private void setFlags (Class cl) + { + if ((java.io.Externalizable.class).isAssignableFrom (cl)) + flags |= ObjectStreamConstants.SC_EXTERNALIZABLE; + else if ((java.io.Serializable.class).isAssignableFrom (cl)) + // only set this bit if CL is NOT Externalizable + flags |= ObjectStreamConstants.SC_SERIALIZABLE; + + try + { + Method writeMethod = cl.getDeclaredMethod ("writeObject", + writeMethodArgTypes); + int modifiers = writeMethod.getModifiers (); + + if (writeMethod.getReturnType () == Void.TYPE + && Modifier.isPrivate (modifiers) + && !Modifier.isStatic (modifiers)) + flags |= ObjectStreamConstants.SC_WRITE_METHOD; + } + catch (NoSuchMethodException oh_well) + {} + } + + + // Sets fields to be a sorted array of the serializable fields of + // clazz. + private void setFields (Class cl) + { + if (! isSerializable () || isExternalizable ()) + { + fields = NO_FIELDS; + return; + } + + try + { + Field serialPersistantFields + = cl.getDeclaredField ("serialPersistantFields"); + int modifiers = serialPersistantFields.getModifiers (); + + if (Modifier.isStatic (modifiers) + && Modifier.isFinal (modifiers) + && Modifier.isPrivate (modifiers)) + { + fields = getSerialPersistantFields (cl); + Arrays.sort (fields); + calculateOffsets (); + return; + } + } + catch (NoSuchFieldException ignore) + {} + + int num_good_fields = 0; + Field[] all_fields = cl.getDeclaredFields (); + + int modifiers; + // set non-serializable fields to null in all_fields + for (int i=0; i < all_fields.length; i++) + { + modifiers = all_fields[i].getModifiers (); + if (Modifier.isTransient (modifiers) + || Modifier.isStatic (modifiers)) + all_fields[i] = null; + else + num_good_fields++; + } + + // make a copy of serializable (non-null) fields + fields = new ObjectStreamField[ num_good_fields ]; + for (int from=0, to=0; from < all_fields.length; from++) + if (all_fields[from] != null) + { + Field f = all_fields[from]; + fields[to] = new ObjectStreamField (f.getName (), f.getType ()); + to++; + } + + Arrays.sort (fields); + calculateOffsets (); + } + + // Sets uid be serial version UID defined by class, or if that + // isn't present, calculates value of serial version UID. + private void setUID (Class cl) + { + try + { + Field suid = cl.getDeclaredField ("serialVersionUID"); + int modifiers = suid.getModifiers (); + + if (Modifier.isStatic (modifiers) + && Modifier.isFinal (modifiers)) + { + uid = getDefinedSUID (cl); + return; + } + } + catch (NoSuchFieldException ignore) + {} + + // cl didn't define serialVersionUID, so we have to compute it + try + { + MessageDigest md = null; + DigestOutputStream digest_out = null; + DataOutputStream data_out = null; + SimpleSHSStream simple = null; + + try + { + md = MessageDigest.getInstance ("SHA"); + digest_out = new DigestOutputStream (nullOutputStream, md); + data_out = new DataOutputStream (digest_out); + } + catch (NoSuchAlgorithmException e) + { + simple = new SimpleSHSStream (nullOutputStream); + data_out = new DataOutputStream (simple); + } + + data_out.writeUTF (cl.getName ()); + + int modifiers = cl.getModifiers (); + // just look at interesting bits + modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL + | Modifier.INTERFACE | Modifier.PUBLIC); + data_out.writeInt (modifiers); + + Class[] interfaces = cl.getInterfaces (); + Arrays.sort (interfaces, interfaceComparator); + for (int i=0; i < interfaces.length; i++) + data_out.writeUTF (interfaces[i].getName ()); + + + Field field; + Field[] fields = cl.getDeclaredFields (); + Arrays.sort (fields, memberComparator); + for (int i=0; i < fields.length; i++) + { + field = fields[i]; + modifiers = field.getModifiers (); + if (Modifier.isPrivate (modifiers) + && (Modifier.isStatic (modifiers) + || Modifier.isTransient (modifiers))) + continue; + + data_out.writeUTF (field.getName ()); + data_out.writeInt (modifiers); + data_out.writeUTF (TypeSignature.getEncodingOfClass (field.getType ())); + } + + // write class initializer method if present + boolean has_init; + try + { + has_init = hasClassInitializer (cl); + } + catch (NoSuchMethodError e) + { + has_init = false; + } + + if (has_init) + { + data_out.writeUTF ("<clinit>"); + data_out.writeInt (Modifier.STATIC); + data_out.writeUTF ("()V"); + } + + Constructor constructor; + Constructor[] constructors = cl.getDeclaredConstructors (); + Arrays.sort (constructors, memberComparator); + for (int i=0; i < constructors.length; i++) + { + constructor = constructors[i]; + modifiers = constructor.getModifiers (); + if (Modifier.isPrivate (modifiers)) + continue; + + data_out.writeUTF ("<init>"); + data_out.writeInt (modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF ( + TypeSignature.getEncodingOfConstructor (constructor).replace ('/','.')); + } + + Method method; + Method[] methods = cl.getDeclaredMethods (); + Arrays.sort (methods, memberComparator); + for (int i=0; i < methods.length; i++) + { + method = methods[i]; + modifiers = method.getModifiers (); + if (Modifier.isPrivate (modifiers)) + continue; + + data_out.writeUTF (method.getName ()); + data_out.writeInt (modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF ( + TypeSignature.getEncodingOfMethod (method).replace ('/', '.')); + } + + data_out.close (); + byte[] sha = md != null ? md.digest () : simple.digest (); + long result = 0; + int len = sha.length < 8 ? sha.length : 8; + for (int i=0; i < len; i++) + result += (long)(sha[i] & 0xFF) << (8 * i); + + uid = result; + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException ("The SHA algorithm was not found to use in computing the Serial Version UID for class " + + cl.getName ()); + } + catch (IOException ioe) + { + throw new RuntimeException (ioe.getMessage ()); + } + } + + + // Returns the value of CLAZZ's final static long field named + // `serialVersionUID'. + private long getDefinedSUID (Class clazz) + { + long l = 0; + try + { + // Use getDeclaredField rather than getField, since serialVersionUID + // may not be public AND we only want the serialVersionUID of this + // class, not a superclass or interface. + Field f = clazz.getDeclaredField ("serialVersionUID"); + l = f.getLong (null); + } + catch (java.lang.NoSuchFieldException e) + { + } + + catch (java.lang.IllegalAccessException e) + { + } + + return l; + } + + // Returns the value of CLAZZ's private static final field named + // `serialPersistantFields'. + private ObjectStreamField[] getSerialPersistantFields (Class clazz) + { + ObjectStreamField[] o = null; + try + { + // Use getDeclaredField rather than getField for the same reason + // as above in getDefinedSUID. + Field f = clazz.getDeclaredField ("getSerialPersistantFields"); + o = (ObjectStreamField[])f.get (null); + } + catch (java.lang.NoSuchFieldException e) + { + } + catch (java.lang.IllegalAccessException e) + { + } + + return o; + } + + + // Returns true if CLAZZ has a static class initializer + // (a.k.a. <clinit>). + // + // A NoSuchMethodError is raised if CLAZZ has no such method. + private static boolean hasClassInitializer (Class clazz) + throws java.lang.NoSuchMethodError + { + Method m = null; + + try + { + Class classArgs[] = {}; + m = clazz.getMethod ("<clinit>", classArgs); + } + catch (java.lang.NoSuchMethodException e) + { + throw new java.lang.NoSuchMethodError (); + } + + return m != null; + } + + public static final ObjectStreamField[] NO_FIELDS = {}; + + private static Hashtable classLookupTable = new Hashtable (); + private static final NullOutputStream nullOutputStream = new NullOutputStream (); + private static final Comparator interfaceComparator = new InterfaceComparator (); + private static final Comparator memberComparator = new MemberComparator (); + private static final + Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class }; + + private ObjectStreamClass superClass; + private Class clazz; + private String name; + private long uid; + private byte flags; + + // this field is package protected so that ObjectInputStream and + // ObjectOutputStream can access it directly + ObjectStreamField[] fields; + + // these are accessed by ObjectIn/OutputStream + int primFieldSize = -1; // -1 if not yet calculated + int objectFieldCount; +} + + +// interfaces are compared only by name +class InterfaceComparator implements Comparator +{ + public int compare (Object o1, Object o2) + { + return ((Class)o1).getName ().compareTo (((Class)o2).getName ()); + } +} + + +// Members (Methods and Constructors) are compared first by name, +// conflicts are resolved by comparing type signatures +class MemberComparator implements Comparator +{ + public int compare (Object o1, Object o2) + { + Member m1 = (Member)o1; + Member m2 = (Member)o2; + + int comp = m1.getName ().compareTo (m2.getName ()); + + if (comp == 0) + return TypeSignature.getEncodingOfMember (m1). + compareTo (TypeSignature.getEncodingOfMember (m2)); + else + return comp; + } +} diff --git a/libjava/java/io/ObjectStreamConstants.java b/libjava/java/io/ObjectStreamConstants.java new file mode 100644 index 0000000..c9a2aea --- /dev/null +++ b/libjava/java/io/ObjectStreamConstants.java @@ -0,0 +1,74 @@ +/* ObjectStreamConstants.java -- Interface containing constant values + used in reading and writing serialized objects + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + This interface contains constants that are used in object + serialization. This interface is used by ObjectOutputStream, + ObjectInputStream, ObjectStreamClass, and possibly other classes. + The values for these constants are specified in Javasoft's "Object + Serialization Specification" TODO: add reference +*/ +public interface ObjectStreamConstants +{ + public final static int PROTOCOL_VERSION_1 = 1; + public final static int PROTOCOL_VERSION_2 = 2; + + final static short STREAM_MAGIC = (short)0xaced; + final static short STREAM_VERSION = 5; + + final static byte TC_NULL = (byte)112; + final static byte TC_REFERENCE = (byte)113; + final static byte TC_CLASSDESC = (byte)114; + final static byte TC_OBJECT = (byte)115; + final static byte TC_STRING = (byte)116; + final static byte TC_ARRAY = (byte)117; + final static byte TC_CLASS = (byte)118; + final static byte TC_BLOCKDATA = (byte)119; + final static byte TC_ENDBLOCKDATA = (byte)120; + final static byte TC_RESET = (byte)121; + final static byte TC_BLOCKDATALONG = (byte)122; + final static byte TC_EXCEPTION = (byte)123; + + final static byte TC_BASE = TC_NULL; + final static byte TC_MAX = TC_EXCEPTION; + + final static int baseWireHandle = 0x7e0000; + + final static byte SC_WRITE_METHOD = 0x01; + final static byte SC_SERIALIZABLE = 0x02; + final static byte SC_EXTERNALIZABLE = 0x04; + final static byte SC_BLOCK_DATA = 0x08; + + final static SerializablePermission SUBSTITUTION_PERMISSION + = new SerializablePermission("enableSubstitution"); + + final static SerializablePermission SUBCLASS_IMPLEMENTATION_PERMISSION + = new SerializablePermission("enableSubclassImplementation"); +} diff --git a/libjava/java/io/ObjectStreamField.java b/libjava/java/io/ObjectStreamField.java new file mode 100644 index 0000000..55181cc --- /dev/null +++ b/libjava/java/io/ObjectStreamField.java @@ -0,0 +1,99 @@ +/* ObjectStreamField.java -- Class used to store name and class of fields + Copyright (C) 1998, 1999 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +import gnu.java.lang.reflect.TypeSignature; + +// XXX doc +public class ObjectStreamField implements java.lang.Comparable +{ + public ObjectStreamField (String name, Class type) + { + this.name = name; + this.type = type; + } + + public String getName () + { + return name; + } + + public Class getType () + { + return type; + } + + public char getTypeCode () + { + return TypeSignature.getEncodingOfClass (type).charAt (0); + } + + public String getTypeString () + { + return TypeSignature.getEncodingOfClass (type); + } + + public int getOffset () + { + return offset; + } + + protected void setOffset (int off) + { + offset = off; + } + + public boolean isPrimitive () + { + return type.isPrimitive (); + } + + public int compareTo (Object o) + { + ObjectStreamField f = (ObjectStreamField)o; + boolean this_is_primitive = isPrimitive (); + boolean f_is_primitive = f.isPrimitive (); + + if (this_is_primitive && !f_is_primitive) + return -1; + + if (!this_is_primitive && f_is_primitive) + return 1; + + return getName ().compareTo (f.getName ()); + } + + public String toString () + { + return "ObjectStreamField< " + type + " " + name + " >"; + } + + private String name; + private Class type; + private int offset = -1; // XXX make sure this is correct +} diff --git a/libjava/java/io/Replaceable.java b/libjava/java/io/Replaceable.java new file mode 100644 index 0000000..1035ab5 --- /dev/null +++ b/libjava/java/io/Replaceable.java @@ -0,0 +1,54 @@ +/* Replaceable.java -- Replace an object with another object + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * This interface is used to indicate that an object may want to have + * another object serialized instead of itself. It contains one method + * that is to be called when an object is to be serialized. That method + * is reponsible for returning the real object that should be serialized + * instead of object being queried. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Replaceable extends Serializable +{ + +/** + * This method returns the object that should be serialized instead of + * this object + * + * @return The real object that should be serialized + */ +public abstract Object +writeReplace(); + +} // interface Replaceable + diff --git a/libjava/java/io/Resolvable.java b/libjava/java/io/Resolvable.java new file mode 100644 index 0000000..b7250de6 --- /dev/null +++ b/libjava/java/io/Resolvable.java @@ -0,0 +1,52 @@ +/* Resolvable.java -- Returns an object to replace the one being de-serialized + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * This interface is implemented when an object wishes to return another + * object to replace it during de-serialization. It has one method that + * returns the object that should be used to replace the original object. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public interface Resolvable extends Serializable +{ + +/** + * This method returns the object that should be used to replace the + * original object during de-serialization. + * + * @return The replacement object + */ +public abstract Object +readResolve(); + +} // interface Resolvable + diff --git a/libjava/java/io/SerializablePermission.java b/libjava/java/io/SerializablePermission.java new file mode 100644 index 0000000..78c7229 --- /dev/null +++ b/libjava/java/io/SerializablePermission.java @@ -0,0 +1,106 @@ +/* SerializablePermission.java -- Basic permissions related to serialization. + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +import java.security.BasicPermission; + +/** + * This class models permissions related to serialization. As a subclass + * of <code>BasicPermission</code>, this class has permissions that have + * a name only. There is no associated action list. + * <p> + * There are currently two allowable permission names for this class: + * <ul> + * <li><code>enableSubclassImplementation</code> - Allows a subclass to + * override the default serialization behavior of objects. + * <li><code>enableSubstitution</code> - Allows substitution of one object + * for another during serialization or deserialization. + * </ul> + * + * @see java.security.BasicPermission + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public final class SerializablePermission extends BasicPermission +{ + +/* + * Class Variables + */ + +public static final String[] legal_names = { "enableSubclassImplementation", + "enableSubstitution" }; +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * This method initializes a new instance of <code>SerializablePermission</code> + * that has the specified name. + * + * @param name The name of the permission. + * + * @exception IllegalArgumentException If the name is not valid for this class. + */ +public +SerializablePermission(String name) +{ + this(name, null); +} + +/*************************************************************************/ + +/** + * This method initializes a new instance of <code>SerializablePermission</code> + * that has the specified name and action list. Note that the action list + * is unused in this class. + * + * @param name The name of the permission. + * @param actions The action list (unused). + * + * @exception IllegalArgumentException If the name is not valid for this class. + */ +public +SerializablePermission(String name, String actions) +{ + super(name, actions); + + for (int i = 0; i < legal_names.length; i++) + if (legal_names[i].equals(name)) + return; + + throw new IllegalArgumentException("Bad permission name: " + name); +} + + +} // class SerializablePermission + diff --git a/libjava/java/io/WriteAbortedException.java b/libjava/java/io/WriteAbortedException.java new file mode 100644 index 0000000..1f22514 --- /dev/null +++ b/libjava/java/io/WriteAbortedException.java @@ -0,0 +1,89 @@ +/* WriteAbortedException.java -- An exception occured while writing a + serialization stream + Copyright (C) 1998 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. + +As a special exception, if you link this library with other files to +produce an executable, this library does not by itself cause the +resulting executable to be covered by the GNU General Public License. +This exception does not however invalidate any other reasons why the +executable file might be covered by the GNU General Public License. */ + + +package java.io; + +/** + * This exception is thrown when one of the other ObjectStreamException + * subclasses was thrown during a serialization write. + * + * @version 0.0 + * + * @author Aaron M. Renn (arenn@urbanophile.com) + */ +public class WriteAbortedException extends ObjectStreamException +{ + +/* + * Instance Variables + */ + +/** + * The detailed exception that caused this exception to be thrown + */ +public Exception detail; +private String message; + +/*************************************************************************/ + +/* + * Constructors + */ + +/** + * Create a new WriteAbortedException with an eof parameter indicating + * the detailed Exception that caused this exception to be thrown. + * + * @param detail The exception that caused this exception to be thrown + */ +public +WriteAbortedException(String msg, Exception detail) +{ + this.message = msg; + this.detail = detail; +} + +/*************************************************************************/ + +/* + * Instance Variables + */ + +/** + * This method returns a message indicating what went wrong, including + * the message text from the initial exception that caused this one to + * be thrown + */ +public String +getMessage() +{ + return(message + ": " + detail.getMessage()); +} + +} // class WriteAbortedException + diff --git a/libjava/java/io/natObjectInputStream.cc b/libjava/java/io/natObjectInputStream.cc new file mode 100644 index 0000000..b7a8dcb --- /dev/null +++ b/libjava/java/io/natObjectInputStream.cc @@ -0,0 +1,78 @@ +// natObjectInputStream.cc - Native part of ObjectInputStream class. + +/* Copyright (C) 1998, 1999 Free Software Foundation + + This ObjectInputStream is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the ObjectInputStream "LIBGCJ_LICENSE" for +details. */ + +#include <config.h> + +#include <gcj/cni.h> +#include <jvm.h> + +#include <java/io/ObjectInputStream$GetField.h> +#include <java/io/ObjectInputStream.h> +#include <java/io/IOException.h> +#include <java/lang/Class.h> +#include <java/lang/reflect/Modifier.h> +#include <java/lang/reflect/Method.h> + +jobject +java::io::ObjectInputStream::allocateObject (jclass klass) +{ + jobject obj = NULL; + using namespace java::lang::reflect; + + try + { + JvAssert (klass && ! klass->isArray ()); + if (klass->isInterface() || Modifier::isAbstract(klass->getModifiers())) + obj = NULL; + else + { + // FIXME: will this work for String? + obj = JvAllocObject (klass); + } + } + catch (jthrowable t) + { + return NULL; + } + + return obj; +} + + +#define ObjectClass _CL_Q34java4lang6Object +extern java::lang::Class ObjectClass; +#define ClassClass _CL_Q34java4lang5Class +extern java::lang::Class ClassClass; + +void +java::io::ObjectInputStream::callConstructor (jclass klass, jobject obj) +{ + jstring init_name = JvNewStringLatin1 ("<init>"); + JArray<jclass> *arg_types + = (JArray<jclass> *) JvNewObjectArray (0, &ClassClass, NULL); + JArray<jobject> *args + = (JArray<jobject> *) JvNewObjectArray (0, &ObjectClass, NULL); + java::lang::reflect::Method *m = klass->getPrivateMethod (init_name, arg_types); + m->invoke (obj, args); +} + +java::lang::reflect::Field * +java::io::ObjectInputStream::getField (jclass klass, jstring name) +{ + return klass->getPrivateField (name); +} + +java::lang::reflect::Method * +java::io::ObjectInputStream::getMethod (jclass klass, jstring name, + JArray<jclass> *arg_types) +{ + return klass->getPrivateMethod (name, arg_types); +} + diff --git a/libjava/java/io/natObjectOutputStream.cc b/libjava/java/io/natObjectOutputStream.cc new file mode 100644 index 0000000..45ab753 --- /dev/null +++ b/libjava/java/io/natObjectOutputStream.cc @@ -0,0 +1,33 @@ +// natObjectOutputStream.cc - Native part of ObjectOutputStream class. + +/* Copyright (C) 1998, 1999 Free Software Foundation + + This ObjectOutputStream is part of libgcj. + +This software is copyrighted work licensed under the terms of the +Libgcj License. Please consult the ObjectOutputStream "LIBGCJ_LICENSE" for +details. */ + +#include <config.h> + +#include <gcj/cni.h> +#include <jvm.h> +#include <java/io/ObjectOutputStream$PutField.h> +#include <java/io/ObjectOutputStream.h> +#include <java/io/IOException.h> +#include <java/lang/Class.h> + + +java::lang::reflect::Field * +java::io::ObjectOutputStream::getField (jclass klass, jstring name) +{ + return klass->getPrivateField (name); +} + +java::lang::reflect::Method * +java::io::ObjectOutputStream::getMethod (jclass klass, jstring name, + JArray<jclass> *arg_types) +{ + return klass->getPrivateMethod (name, arg_types); +} + |