source: trunk/gcc/libjava/java/io/ObjectOutputStream.java

Last change on this file was 1392, checked in by bird, 21 years ago

This commit was generated by cvs2svn to compensate for changes in r1391,
which included commits to RCS files with non-trunk default branches.

  • Property cvs2svn:cvs-rev set to 1.1.1.2
  • Property svn:eol-style set to native
  • Property svn:executable set to *
File size: 37.8 KB
Line 
1/* ObjectOutputStream.java -- Class used to write serialized objects
2 Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
3
4This file is part of GNU Classpath.
5
6GNU Classpath is free software; you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation; either version 2, or (at your option)
9any later version.
10
11GNU Classpath is distributed in the hope that it will be useful, but
12WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with GNU Classpath; see the file COPYING. If not, write to the
18Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
1902111-1307 USA.
20
21Linking this library statically or dynamically with other modules is
22making a combined work based on this library. Thus, the terms and
23conditions of the GNU General Public License cover the whole
24combination.
25
26As a special exception, the copyright holders of this library give you
27permission to link this library with independent modules to produce an
28executable, regardless of the license terms of these independent
29modules, and to copy and distribute the resulting executable under
30terms of your choice, provided that you also meet, for each linked
31independent module, the terms and conditions of the license of that
32module. An independent module is a module which is not derived from
33or based on this library. If you modify this library, you may extend
34this exception to your version of the library, but you are not
35obligated to do so. If you do not wish to do so, delete this
36exception statement from your version. */
37
38
39package java.io;
40
41import java.lang.reflect.Array;
42import java.lang.reflect.Field;
43import java.lang.reflect.Method;
44import java.lang.reflect.InvocationTargetException;
45import java.util.Hashtable;
46
47import gnu.java.io.ObjectIdentityWrapper;
48import gnu.java.lang.reflect.TypeSignature;
49import gnu.classpath.Configuration;
50
51/**
52 An <code>ObjectOutputStream</code> can be used to write objects
53 as well as primitive data in a platform-independent manner to an
54 <code>OutputStream</code>.
55
56 The data produced by an <code>ObjectOutputStream</code> can be read
57 and reconstituted by an <code>ObjectInputStream</code>.
58
59 <code>writeObject (Object)</code> is used to write Objects, the
60 <code>write&lt;type&gt;</code> methods are used to write primitive
61 data (as in <code>DataOutputStream</code>). Strings can be written
62 as objects or as primitive data.
63
64 Not all objects can be written out using an
65 <code>ObjectOutputStream</code>. Only those objects that are an
66 instance of <code>java.io.Serializable</code> can be written.
67
68 Using default serialization, information about the class of an
69 object is written, all of the non-transient, non-static fields of
70 the object are written, if any of these fields are objects, they are
71 written out in the same manner.
72
73 An object is only written out the first time it is encountered. If
74 the object is encountered later, a reference to it is written to
75 the underlying stream. Thus writing circular object graphs
76 does not present a problem, nor are relationships between objects
77 in a graph lost.
78
79 Example usage:
80 <pre>
81 Hashtable map = new Hashtable ();
82 map.put ("one", new Integer (1));
83 map.put ("two", new Integer (2));
84
85 ObjectOutputStream oos =
86 new ObjectOutputStream (new FileOutputStream ("numbers"));
87 oos.writeObject (map);
88 oos.close ();
89
90 ObjectInputStream ois =
91 new ObjectInputStream (new FileInputStream ("numbers"));
92 Hashtable newmap = (Hashtable)ois.readObject ();
93
94 System.out.println (newmap);
95 </pre>
96
97 The default serialization can be overriden in two ways.
98
99 By defining a method <code>private void
100 writeObject (ObjectOutputStream)</code>, a class can dictate exactly
101 how information about itself is written.
102 <code>defaultWriteObject ()</code> may be called from this method to
103 carry out default serialization. This method is not
104 responsible for dealing with fields of super-classes or subclasses.
105
106 By implementing <code>java.io.Externalizable</code>. This gives
107 the class complete control over the way it is written to the
108 stream. If this approach is used the burden of writing superclass
109 and subclass data is transfered to the class implementing
110 <code>java.io.Externalizable</code>.
111
112 @see java.io.DataOutputStream
113 @see java.io.Externalizable
114 @see java.io.ObjectInputStream
115 @see java.io.Serializable
116 @see XXX: java serialization spec
117*/
118public class ObjectOutputStream extends OutputStream
119 implements ObjectOutput, ObjectStreamConstants
120{
121 /**
122 Creates a new <code>ObjectOutputStream</code> that will do all of
123 its writing onto <code>out</code>. This method also initializes
124 the stream by writing the header information (stream magic number
125 and stream version).
126
127 @exception IOException Writing stream header to underlying
128 stream cannot be completed.
129
130 @see writeStreamHeader ()
131 */
132 public ObjectOutputStream (OutputStream out) throws IOException
133 {
134 realOutput = new DataOutputStream (out);
135 blockData = new byte[ BUFFER_SIZE ];
136 blockDataCount = 0;
137 blockDataOutput = new DataOutputStream (this);
138 setBlockDataMode (true);
139 replacementEnabled = false;
140 isSerializing = false;
141 nextOID = baseWireHandle;
142 OIDLookupTable = new Hashtable ();
143 protocolVersion = defaultProtocolVersion;
144 useSubclassMethod = false;
145 writeStreamHeader ();
146 }
147
148
149 /**
150 Writes a representation of <code>obj</code> to the underlying
151 output stream by writing out information about its class, then
152 writing out each of the objects non-transient, non-static
153 fields. If any of these fields are other objects,
154 they are written out in the same manner.
155
156 This method can be overriden by a class by implementing
157 <code>private void writeObject (ObjectOutputStream)</code>.
158
159 If an exception is thrown from this method, the stream is left in
160 an undefined state.
161
162 @exception NotSerializableException An attempt was made to
163 serialize an <code>Object</code> that is not serializable.
164
165 @exception IOException Exception from underlying
166 <code>OutputStream</code>.
167 */
168 public final void writeObject (Object obj) throws IOException
169 {
170 if (useSubclassMethod)
171 {
172 writeObjectOverride (obj);
173 return;
174 }
175
176 boolean was_serializing = isSerializing;
177 boolean old_mode = setBlockDataMode (false);
178 try
179 {
180 isSerializing = true;
181 boolean replaceDone = false;
182 Object replacedObject = null;
183
184 while (true)
185 {
186 if (obj == null)
187 {
188 realOutput.writeByte (TC_NULL);
189 break;
190 }
191
192 Integer handle = findHandle (obj);
193 if (handle != null)
194 {
195 realOutput.writeByte (TC_REFERENCE);
196 realOutput.writeInt (handle.intValue ());
197 break;
198 }
199
200 if (obj instanceof Class)
201 {
202 Class cl = (Class)obj;
203 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject (cl);
204 assignNewHandle (obj);
205 realOutput.writeByte (TC_CLASS);
206 if (!osc.isProxyClass)
207 {
208 writeObject (osc);
209 }
210 else
211 {
212 realOutput.writeByte (TC_PROXYCLASSDESC);
213 Class[] intfs = cl.getInterfaces();
214 realOutput.writeInt(intfs.length);
215 for (int i = 0; i < intfs.length; i++)
216 realOutput.writeUTF(intfs[i].getName());
217
218 boolean oldmode = setBlockDataMode (true);
219 annotateProxyClass(cl);
220 setBlockDataMode (oldmode);
221 realOutput.writeByte(TC_ENDBLOCKDATA);
222
223 writeObject (osc.getSuper());
224 }
225 break;
226 }
227
228 if (obj instanceof ObjectStreamClass)
229 {
230 ObjectStreamClass osc = (ObjectStreamClass)obj;
231 realOutput.writeByte (TC_CLASSDESC);
232 realOutput.writeUTF (osc.getName ());
233 realOutput.writeLong (osc.getSerialVersionUID ());
234 assignNewHandle (obj);
235
236 int flags = osc.getFlags ();
237
238 if (protocolVersion == PROTOCOL_VERSION_2
239 && osc.isExternalizable ())
240 flags |= SC_BLOCK_DATA;
241
242 realOutput.writeByte (flags);
243
244 ObjectStreamField[] fields = osc.fields;
245 realOutput.writeShort (fields.length);
246
247 ObjectStreamField field;
248 for (int i=0; i < fields.length; i++)
249 {
250 field = fields[i];
251 realOutput.writeByte (field.getTypeCode ());
252 realOutput.writeUTF (field.getName ());
253
254 if (! field.isPrimitive ())
255 writeObject (field.getTypeString ());
256 }
257
258 boolean oldmode = setBlockDataMode (true);
259 annotateClass (osc.forClass ());
260 setBlockDataMode (oldmode);
261 realOutput.writeByte (TC_ENDBLOCKDATA);
262
263 if (osc.isSerializable ())
264 writeObject (osc.getSuper ());
265 else
266 writeObject (null);
267 break;
268 }
269
270 if ((replacementEnabled || obj instanceof Serializable)
271 && ! replaceDone)
272 {
273 replacedObject = obj;
274
275 if (obj instanceof Serializable)
276 {
277 Method m = null;
278 try
279 {
280 Class classArgs[] = {};
281 m = obj.getClass ().getDeclaredMethod ("writeReplace",
282 classArgs);
283 // m can't be null by definition since an exception would
284 // have been thrown so a check for null is not needed.
285 obj = m.invoke (obj, new Object[] {});
286 }
287 catch (NoSuchMethodException ignore)
288 {
289 }
290 catch (IllegalAccessException ignore)
291 {
292 }
293 catch (InvocationTargetException ignore)
294 {
295 }
296 }
297
298 if (replacementEnabled)
299 obj = replaceObject (obj);
300
301 replaceDone = true;
302 continue;
303 }
304
305 if (obj instanceof String)
306 {
307 realOutput.writeByte (TC_STRING);
308 assignNewHandle (obj);
309 realOutput.writeUTF ((String)obj);
310 break;
311 }
312
313 Class clazz = obj.getClass ();
314 ObjectStreamClass osc = ObjectStreamClass.lookupForClassObject (clazz);
315 if (osc == null)
316 throw new NotSerializableException (clazz.getName ());
317
318 if (clazz.isArray ())
319 {
320 realOutput.writeByte (TC_ARRAY);
321 writeObject (osc);
322 assignNewHandle (obj);
323 writeArraySizeAndElements (obj, clazz.getComponentType ());
324 break;
325 }
326
327 realOutput.writeByte (TC_OBJECT);
328 writeObject (osc);
329
330 if (replaceDone)
331 assignNewHandle (replacedObject);
332 else
333 assignNewHandle (obj);
334
335 if (obj instanceof Externalizable)
336 {
337 if (protocolVersion == PROTOCOL_VERSION_2)
338 setBlockDataMode (true);
339
340 ((Externalizable)obj).writeExternal (this);
341
342 if (protocolVersion == PROTOCOL_VERSION_2)
343 {
344 setBlockDataMode (false);
345 realOutput.writeByte (TC_ENDBLOCKDATA);
346 }
347
348 break;
349 }
350
351 if (obj instanceof Serializable)
352 {
353 currentObject = obj;
354 ObjectStreamClass[] hierarchy =
355 ObjectStreamClass.getObjectStreamClasses (clazz);
356
357 for (int i=0; i < hierarchy.length; i++)
358 {
359 currentObjectStreamClass = hierarchy[i];
360
361 fieldsAlreadyWritten = false;
362 if (currentObjectStreamClass.hasWriteMethod ())
363 {
364 setBlockDataMode (true);
365 callWriteMethod (obj, currentObjectStreamClass);
366 setBlockDataMode (false);
367 realOutput.writeByte (TC_ENDBLOCKDATA);
368 }
369 else
370 writeFields (obj, currentObjectStreamClass);
371 }
372
373 currentObject = null;
374 currentObjectStreamClass = null;
375 currentPutField = null;
376 break;
377 }
378
379 throw new NotSerializableException (clazz.getName ());
380 } // end pseudo-loop
381 }
382 catch (ObjectStreamException ose)
383 {
384 // Rethrow these are fatal.
385 throw ose;
386 }
387 catch (IOException e)
388 {
389 realOutput.writeByte (TC_EXCEPTION);
390 reset (true);
391
392 setBlockDataMode (false);
393 try
394 {
395 writeObject (e);
396 }
397 catch (IOException ioe)
398 {
399 throw new StreamCorruptedException ("Exception " + ioe + " thrown while exception was being written to stream.");
400 }
401
402 reset (true);
403 }
404 finally
405 {
406 isSerializing = was_serializing;
407 setBlockDataMode (old_mode);
408 }
409 }
410
411 /**
412 Writes the current objects non-transient, non-static fields from
413 the current class to the underlying output stream.
414
415 This method is intended to be called from within a object's
416 <code>private void writeObject (ObjectOutputStream)</code>
417 method.
418
419 @exception NotActiveException This method was called from a
420 context other than from the current object's and current class's
421 <code>private void writeObject (ObjectOutputStream)</code>
422 method.
423
424 @exception IOException Exception from underlying
425 <code>OutputStream</code>.
426 */
427 public void defaultWriteObject ()
428 throws IOException, NotActiveException
429 {
430 markFieldsWritten ();
431 writeFields (currentObject, currentObjectStreamClass);
432 }
433
434
435 private void markFieldsWritten () throws IOException
436 {
437 if (currentObject == null || currentObjectStreamClass == null)
438 throw new NotActiveException ("defaultWriteObject called by non-active class and/or object");
439
440 if (fieldsAlreadyWritten)
441 throw new IOException ("Only one of putFields and defaultWriteObject may be called, and it may only be called once");
442
443 fieldsAlreadyWritten = true;
444 }
445
446
447 /**
448 Resets stream to state equivalent to the state just after it was
449 constructed.
450
451 Causes all objects previously written to the stream to be
452 forgotten. A notification of this reset is also written to the
453 underlying stream.
454
455 @exception IOException Exception from underlying
456 <code>OutputStream</code> or reset called while serialization is
457 in progress.
458 */
459 public void reset () throws IOException
460 {
461 reset (false);
462 }
463
464
465 private void reset (boolean internal) throws IOException
466 {
467 if (!internal)
468 {
469 if (isSerializing)
470 throw new IOException ("Reset called while serialization in progress");
471
472 realOutput.writeByte (TC_RESET);
473 }
474
475 clearHandles ();
476 }
477
478
479 /**
480 Informs this <code>ObjectOutputStream</code> to write data
481 according to the specified protocol. There are currently two
482 different protocols, specified by <code>PROTOCOL_VERSION_1</code>
483 and <code>PROTOCOL_VERSION_2</code>. This implementation writes
484 data using <code>PROTOCOL_VERSION_2</code> by default, as is done
485 by the JDK 1.2.
486
487 A non-portable method, <code>setDefaultProtocolVersion (int
488 version)</code> is provided to change the default protocol
489 version.
490
491 For an explination of the differences beween the two protocols
492 see XXX: the Java ObjectSerialization Specification.
493
494 @exception IOException if <code>version</code> is not a valid
495 protocol
496
497 @see setDefaultProtocolVersion (int)
498 */
499 public void useProtocolVersion (int version) throws IOException
500 {
501 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
502 throw new IOException ("Invalid protocol version requested.");
503
504 protocolVersion = version;
505 }
506
507
508 /**
509 <em>GNU $classpath specific</em>
510
511 Changes the default stream protocol used by all
512 <code>ObjectOutputStream</code>s. There are currently two
513 different protocols, specified by <code>PROTOCOL_VERSION_1</code>
514 and <code>PROTOCOL_VERSION_2</code>. The default default is
515 <code>PROTOCOL_VERSION_1</code>.
516
517 @exception IOException if <code>version</code> is not a valid
518 protocol
519
520 @see useProtocolVersion (int)
521 */
522 public static void setDefaultProtocolVersion (int version)
523 throws IOException
524 {
525 if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
526 throw new IOException ("Invalid protocol version requested.");
527
528 defaultProtocolVersion = version;
529 }
530
531
532 /**
533 An empty hook that allows subclasses to write extra information
534 about classes to the stream. This method is called the first
535 time each class is seen, and after all of the standard
536 information about the class has been written.
537
538 @exception IOException Exception from underlying
539 <code>OutputStream</code>.
540
541 @see java.io.ObjectInputStream#resolveClass (java.io.ObjectStreamClass)
542 */
543 protected void annotateClass (Class cl) throws IOException
544 {}
545
546 protected void annotateProxyClass(Class cl) throws IOException
547 {}
548
549 /**
550 Allows subclasses to replace objects that are written to the
551 stream with other objects to be written in their place. This
552 method is called the first time each object is encountered
553 (modulo reseting of the stream).
554
555 This method must be enabled before it will be called in the
556 serialization process.
557
558 @exception IOException Exception from underlying
559 <code>OutputStream</code>.
560
561 @see enableReplaceObject (boolean)
562 */
563 protected Object replaceObject (Object obj) throws IOException
564 {
565 return obj;
566 }
567
568
569 /**
570 If <code>enable</code> is <code>true</code> and this object is
571 trusted, then <code>replaceObject (Object)</code> will be called
572 in subsequent calls to <code>writeObject (Object)</code>.
573 Otherwise, <code>replaceObject (Object)</code> will not be called.
574
575 @exception SecurityException This class is not trusted.
576 */
577 protected boolean enableReplaceObject (boolean enable)
578 throws SecurityException
579 {
580 if (enable)
581 {
582 SecurityManager sm = System.getSecurityManager ();
583 if (sm != null)
584 sm.checkPermission (new SerializablePermission ("enableSubstitution"));
585 }
586
587 boolean old_val = replacementEnabled;
588 replacementEnabled = enable;
589 return old_val;
590 }
591
592
593 /**
594 Writes stream magic and stream version information to the
595 underlying stream.
596
597 @exception IOException Exception from underlying
598 <code>OutputStream</code>.
599 */
600 protected void writeStreamHeader () throws IOException
601 {
602 realOutput.writeShort (STREAM_MAGIC);
603 realOutput.writeShort (STREAM_VERSION);
604 }
605
606
607
608 /**
609 Protected constructor that allows subclasses to override
610 serialization. This constructor should be called by subclasses
611 that wish to override <code>writeObject (Object)</code>. This
612 method does a security check <i>NOTE: currently not
613 implemented</i>, then sets a flag that informs
614 <code>writeObject (Object)</code> to call the subclasses
615 <code>writeObjectOverride (Object)</code> method.
616
617 @see writeObjectOverride (Object)
618 */
619 protected ObjectOutputStream () throws IOException, SecurityException
620 {
621 SecurityManager sec_man = System.getSecurityManager ();
622 if (sec_man != null)
623 sec_man.checkPermission (SUBCLASS_IMPLEMENTATION_PERMISSION);
624 useSubclassMethod = true;
625 }
626
627
628 /**
629 This method allows subclasses to override the default
630 serialization mechanism provided by
631 <code>ObjectOutputStream</code>. To make this method be used for
632 writing objects, subclasses must invoke the 0-argument
633 constructor on this class from there constructor.
634
635 @see ObjectOutputStream ()
636
637 @exception NotActiveException Subclass has arranged for this
638 method to be called, but did not implement this method.
639 */
640 protected void writeObjectOverride (Object obj) throws NotActiveException,
641 IOException
642 {
643 throw new NotActiveException ("Subclass of ObjectOutputStream must implement writeObjectOverride");
644 }
645
646
647 /**
648 @see java.io.DataOutputStream#write (int)
649 */
650 public void write (int data) throws IOException
651 {
652 if (writeDataAsBlocks)
653 {
654 if (blockDataCount == BUFFER_SIZE)
655 drain ();
656
657 blockData[ blockDataCount++ ] = (byte)data;
658 }
659 else
660 realOutput.write (data);
661 }
662
663
664 /**
665 @see java.io.DataOutputStream#write (byte[])
666 */
667 public void write (byte[] b) throws IOException
668 {
669 write (b, 0, b.length);
670 }
671
672
673 /**
674 @see java.io.DataOutputStream#write (byte[],int,int)
675 */
676 public void write (byte[] b, int off, int len) throws IOException
677 {
678 if (writeDataAsBlocks)
679 {
680 if (len < 0)
681 throw new IndexOutOfBoundsException ();
682
683 if (blockDataCount + len < BUFFER_SIZE)
684 {
685 System.arraycopy (b, off, blockData, blockDataCount, len);
686 blockDataCount += len;
687 }
688 else
689 {
690 drain ();
691 writeBlockDataHeader (len);
692 realOutput.write (b, off, len);
693 }
694 }
695 else
696 realOutput.write (b, off, len);
697 }
698
699
700 /**
701 @see java.io.DataOutputStream#flush ()
702 */
703 public void flush () throws IOException
704 {
705 drain ();
706 realOutput.flush ();
707 }
708
709
710 /**
711 Causes the block-data buffer to be written to the underlying
712 stream, but does not flush underlying stream.
713
714 @exception IOException Exception from underlying
715 <code>OutputStream</code>.
716 */
717 protected void drain () throws IOException
718 {
719 if (blockDataCount == 0)
720 return;
721
722 if (writeDataAsBlocks)
723 writeBlockDataHeader (blockDataCount);
724 realOutput.write (blockData, 0, blockDataCount);
725 blockDataCount = 0;
726 }
727
728
729 /**
730 @see java.io.DataOutputStream#close ()
731 */
732 public void close () throws IOException
733 {
734 flush ();
735 realOutput.close ();
736 }
737
738
739 /**
740 @see java.io.DataOutputStream#writeBoolean (boolean)
741 */
742 public void writeBoolean (boolean data) throws IOException
743 {
744 blockDataOutput.writeBoolean (data);
745 }
746
747
748 /**
749 @see java.io.DataOutputStream#writeByte (int)
750 */
751 public void writeByte (int data) throws IOException
752 {
753 blockDataOutput.writeByte (data);
754 }
755
756
757 /**
758 @see java.io.DataOutputStream#writeShort (int)
759 */
760 public void writeShort (int data) throws IOException
761 {
762 blockDataOutput.writeShort (data);
763 }
764
765
766 /**
767 @see java.io.DataOutputStream#writeChar (int)
768 */
769 public void writeChar (int data) throws IOException
770 {
771 blockDataOutput.writeChar (data);
772 }
773
774
775 /**
776 @see java.io.DataOutputStream#writeInt (int)
777 */
778 public void writeInt (int data) throws IOException
779 {
780 blockDataOutput.writeInt (data);
781 }
782
783
784 /**
785 @see java.io.DataOutputStream#writeLong (long)
786 */
787 public void writeLong (long data) throws IOException
788 {
789 blockDataOutput.writeLong (data);
790 }
791
792
793 /**
794 @see java.io.DataOutputStream#writeFloat (float)
795 */
796 public void writeFloat (float data) throws IOException
797 {
798 blockDataOutput.writeFloat (data);
799 }
800
801
802 /**
803 @see java.io.DataOutputStream#writeDouble (double)
804 */
805 public void writeDouble (double data) throws IOException
806 {
807 blockDataOutput.writeDouble (data);
808 }
809
810
811 /**
812 @see java.io.DataOutputStream#writeBytes (java.lang.String)
813 */
814 public void writeBytes (String data) throws IOException
815 {
816 blockDataOutput.writeBytes (data);
817 }
818
819
820 /**
821 @see java.io.DataOutputStream#writeChars (java.lang.String)
822 */
823 public void writeChars (String data) throws IOException
824 {
825 dataOutput.writeChars (data);
826 }
827
828
829 /**
830 @see java.io.DataOutputStream#writeUTF (java.lang.String)
831 */
832 public void writeUTF (String data) throws IOException
833 {
834 dataOutput.writeUTF (data);
835 }
836
837
838 /**
839 This class allows a class to specify exactly which fields should
840 be written, and what values should be written for these fields.
841
842 XXX: finish up comments
843 */
844 public static abstract class PutField
845 {
846 public abstract void put (String name, boolean value)
847 throws IOException, IllegalArgumentException;
848 public abstract void put (String name, byte value)
849 throws IOException, IllegalArgumentException;
850 public abstract void put (String name, char value)
851 throws IOException, IllegalArgumentException;
852 public abstract void put (String name, double value)
853 throws IOException, IllegalArgumentException;
854 public abstract void put (String name, float value)
855 throws IOException, IllegalArgumentException;
856 public abstract void put (String name, int value)
857 throws IOException, IllegalArgumentException;
858 public abstract void put (String name, long value)
859 throws IOException, IllegalArgumentException;
860 public abstract void put (String name, short value)
861 throws IOException, IllegalArgumentException;
862 public abstract void put (String name, Object value)
863 throws IOException, IllegalArgumentException;
864 public abstract void write (ObjectOutput out) throws IOException;
865 }
866
867
868 public PutField putFields () throws IOException
869 {
870 markFieldsWritten ();
871
872 currentPutField = new PutField ()
873 {
874 private byte[] prim_field_data
875 = new byte[currentObjectStreamClass.primFieldSize];
876 private Object[] objs
877 = new Object[currentObjectStreamClass.objectFieldCount];
878
879 public void put (String name, boolean value)
880 throws IOException, IllegalArgumentException
881 {
882 ObjectStreamField field
883 = currentObjectStreamClass.getField (name);
884 checkType (field, 'Z');
885 prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
886 }
887
888 public void put (String name, byte value)
889 throws IOException, IllegalArgumentException
890 {
891 ObjectStreamField field
892 = currentObjectStreamClass.getField (name);
893 checkType (field, 'B');
894 prim_field_data[field.getOffset ()] = value;
895 }
896
897 public void put (String name, char value)
898 throws IOException, IllegalArgumentException
899 {
900 ObjectStreamField field
901 = currentObjectStreamClass.getField (name);
902 checkType (field, 'C');
903 int off = field.getOffset ();
904 prim_field_data[off++] = (byte)(value >>> 8);
905 prim_field_data[off] = (byte)value;
906 }
907
908 public void put (String name, double value)
909 throws IOException, IllegalArgumentException
910 {
911 ObjectStreamField field
912 = currentObjectStreamClass.getField (name);
913 checkType (field, 'D');
914 int off = field.getOffset ();
915 long l_value = Double.doubleToLongBits (value);
916 prim_field_data[off++] = (byte)(l_value >>> 52);
917 prim_field_data[off++] = (byte)(l_value >>> 48);
918 prim_field_data[off++] = (byte)(l_value >>> 40);
919 prim_field_data[off++] = (byte)(l_value >>> 32);
920 prim_field_data[off++] = (byte)(l_value >>> 24);
921 prim_field_data[off++] = (byte)(l_value >>> 16);
922 prim_field_data[off++] = (byte)(l_value >>> 8);
923 prim_field_data[off] = (byte)l_value;
924 }
925
926 public void put (String name, float value)
927 throws IOException, IllegalArgumentException
928 {
929 ObjectStreamField field
930 = currentObjectStreamClass.getField (name);
931 checkType (field, 'F');
932 int off = field.getOffset ();
933 int i_value = Float.floatToIntBits (value);
934 prim_field_data[off++] = (byte)(i_value >>> 24);
935 prim_field_data[off++] = (byte)(i_value >>> 16);
936 prim_field_data[off++] = (byte)(i_value >>> 8);
937 prim_field_data[off] = (byte)i_value;
938 }
939
940 public void put (String name, int value)
941 throws IOException, IllegalArgumentException
942 {
943 ObjectStreamField field
944 = currentObjectStreamClass.getField (name);
945 checkType (field, 'I');
946 int off = field.getOffset ();
947 prim_field_data[off++] = (byte)(value >>> 24);
948 prim_field_data[off++] = (byte)(value >>> 16);
949 prim_field_data[off++] = (byte)(value >>> 8);
950 prim_field_data[off] = (byte)value;
951 }
952
953 public void put (String name, long value)
954 throws IOException, IllegalArgumentException
955 {
956 ObjectStreamField field
957 = currentObjectStreamClass.getField (name);
958 checkType (field, 'J');
959 int off = field.getOffset ();
960 prim_field_data[off++] = (byte)(value >>> 52);
961 prim_field_data[off++] = (byte)(value >>> 48);
962 prim_field_data[off++] = (byte)(value >>> 40);
963 prim_field_data[off++] = (byte)(value >>> 32);
964 prim_field_data[off++] = (byte)(value >>> 24);
965 prim_field_data[off++] = (byte)(value >>> 16);
966 prim_field_data[off++] = (byte)(value >>> 8);
967 prim_field_data[off] = (byte)value;
968 }
969
970 public void put (String name, short value)
971 throws IOException, IllegalArgumentException
972 {
973 ObjectStreamField field
974 = currentObjectStreamClass.getField (name);
975 checkType (field, 'S');
976 int off = field.getOffset ();
977 prim_field_data[off++] = (byte)(value >>> 8);
978 prim_field_data[off] = (byte)value;
979 }
980
981 public void put (String name, Object value)
982 throws IOException, IllegalArgumentException
983 {
984 ObjectStreamField field
985 = currentObjectStreamClass.getField (name);
986 if (field == null)
987 throw new IllegalArgumentException ();
988 if (value != null &&
989 ! field.getType ().isAssignableFrom (value.getClass ()))
990 throw new IllegalArgumentException ();
991 objs[field.getOffset ()] = value;
992 }
993
994 public void write (ObjectOutput out) throws IOException
995 {
996 // Apparently Block data is not used with PutField as per
997 // empirical evidence against JDK 1.2. Also see Mauve test
998 // java.io.ObjectInputOutput.Test.GetPutField.
999 boolean oldmode = setBlockDataMode (false);
1000 out.write (prim_field_data);
1001 for (int i = 0; i < objs.length; ++ i)
1002 out.writeObject (objs[i]);
1003 setBlockDataMode (oldmode);
1004 }
1005
1006 private void checkType (ObjectStreamField field, char type)
1007 throws IllegalArgumentException
1008 {
1009 if (TypeSignature.getEncodingOfClass (field.getType ()).charAt (0) != type)
1010 throw new IllegalArgumentException ();
1011 }
1012 };
1013 // end PutFieldImpl
1014
1015 return currentPutField;
1016 }
1017
1018
1019 public void writeFields () throws IOException
1020 {
1021 if (currentPutField == null)
1022 throw new NotActiveException ("writeFields can only be called after putFields has been called");
1023
1024 currentPutField.write (this);
1025 }
1026
1027
1028 // write out the block-data buffer, picking the correct header
1029 // depending on the size of the buffer
1030 private void writeBlockDataHeader (int size) throws IOException
1031 {
1032 if (size < 256)
1033 {
1034 realOutput.writeByte (TC_BLOCKDATA);
1035 realOutput.write (size);
1036 }
1037 else
1038 {
1039 realOutput.writeByte (TC_BLOCKDATALONG);
1040 realOutput.writeInt (size);
1041 }
1042 }
1043
1044
1045 // lookup the handle for OBJ, return null if OBJ doesn't have a
1046 // handle yet
1047 private Integer findHandle (Object obj)
1048 {
1049 return (Integer)OIDLookupTable.get (new ObjectIdentityWrapper (obj));
1050 }
1051
1052
1053 // assigns the next availible handle to OBJ
1054 private int assignNewHandle (Object obj)
1055 {
1056 OIDLookupTable.put (new ObjectIdentityWrapper (obj),
1057 new Integer (nextOID));
1058 return nextOID++;
1059 }
1060
1061
1062 // resets mapping from objects to handles
1063 private void clearHandles ()
1064 {
1065 nextOID = baseWireHandle;
1066 OIDLookupTable.clear ();
1067 }
1068
1069
1070 // write out array size followed by each element of the array
1071 private void writeArraySizeAndElements (Object array, Class clazz)
1072 throws IOException
1073 {
1074 int length = Array.getLength (array);
1075
1076 if (clazz.isPrimitive ())
1077 {
1078 if (clazz == Boolean.TYPE)
1079 {
1080 boolean[] cast_array = (boolean[])array;
1081 realOutput.writeInt (length);
1082 for (int i=0; i < length; i++)
1083 realOutput.writeBoolean (cast_array[i]);
1084 return;
1085 }
1086 if (clazz == Byte.TYPE)
1087 {
1088 byte[] cast_array = (byte[])array;
1089 realOutput.writeInt (length);
1090 realOutput.write(cast_array, 0, length);
1091 return;
1092 }
1093 if (clazz == Character.TYPE)
1094 {
1095 char[] cast_array = (char[])array;
1096 realOutput.writeInt (length);
1097 for (int i=0; i < length; i++)
1098 realOutput.writeChar (cast_array[i]);
1099 return;
1100 }
1101 if (clazz == Double.TYPE)
1102 {
1103 double[] cast_array = (double[])array;
1104 realOutput.writeInt (length);
1105 for (int i=0; i < length; i++)
1106 realOutput.writeDouble (cast_array[i]);
1107 return;
1108 }
1109 if (clazz == Float.TYPE)
1110 {
1111 float[] cast_array = (float[])array;
1112 realOutput.writeInt (length);
1113 for (int i=0; i < length; i++)
1114 realOutput.writeFloat (cast_array[i]);
1115 return;
1116 }
1117 if (clazz == Integer.TYPE)
1118 {
1119 int[] cast_array = (int[])array;
1120 realOutput.writeInt (length);
1121 for (int i=0; i < length; i++)
1122 realOutput.writeInt (cast_array[i]);
1123 return;
1124 }
1125 if (clazz == Long.TYPE)
1126 {
1127 long[] cast_array = (long[])array;
1128 realOutput.writeInt (length);
1129 for (int i=0; i < length; i++)
1130 realOutput.writeLong (cast_array[i]);
1131 return;
1132 }
1133 if (clazz == Short.TYPE)
1134 {
1135 short[] cast_array = (short[])array;
1136 realOutput.writeInt (length);
1137 for (int i=0; i < length; i++)
1138 realOutput.writeShort (cast_array[i]);
1139 return;
1140 }
1141 }
1142 else
1143 {
1144 Object[] cast_array = (Object[])array;
1145 realOutput.writeInt (length);
1146 for (int i=0; i < length; i++)
1147 writeObject (cast_array[i]);
1148 }
1149 }
1150
1151
1152 // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1153 // FIELDS are already in canonical order.
1154 private void writeFields (Object obj, ObjectStreamClass osc)
1155 throws IOException
1156 {
1157 ObjectStreamField[] fields = osc.fields;
1158 boolean oldmode = setBlockDataMode (false);
1159 String field_name;
1160 Class type;
1161 for (int i=0; i < fields.length; i++)
1162 {
1163 field_name = fields[i].getName ();
1164 type = fields[i].getType ();
1165
1166 if (type == Boolean.TYPE)
1167 realOutput.writeBoolean (getBooleanField (obj, osc.forClass(), field_name));
1168 else if (type == Byte.TYPE)
1169 realOutput.writeByte (getByteField (obj, osc.forClass(), field_name));
1170 else if (type == Character.TYPE)
1171 realOutput.writeChar (getCharField (obj, osc.forClass(), field_name));
1172 else if (type == Double.TYPE)
1173 realOutput.writeDouble (getDoubleField (obj, osc.forClass(), field_name));
1174 else if (type == Float.TYPE)
1175 realOutput.writeFloat (getFloatField (obj, osc.forClass(), field_name));
1176 else if (type == Integer.TYPE)
1177 realOutput.writeInt (getIntField (obj, osc.forClass(), field_name));
1178 else if (type == Long.TYPE)
1179 realOutput.writeLong (getLongField (obj, osc.forClass(), field_name));
1180 else if (type == Short.TYPE)
1181 realOutput.writeShort (getShortField (obj, osc.forClass(), field_name));
1182 else
1183 writeObject (getObjectField (obj, osc.forClass(), field_name,
1184 fields[i].getTypeString ()));
1185 }
1186 setBlockDataMode (oldmode);
1187 }
1188
1189
1190 // Toggles writing primitive data to block-data buffer.
1191 private boolean setBlockDataMode (boolean on) throws IOException
1192 {
1193 if (on == writeDataAsBlocks)
1194 return on;
1195
1196 drain();
1197 boolean oldmode = writeDataAsBlocks;
1198 writeDataAsBlocks = on;
1199
1200 if (on)
1201 dataOutput = blockDataOutput;
1202 else
1203 dataOutput = realOutput;
1204
1205 return oldmode;
1206 }
1207
1208
1209 private void callWriteMethod (Object obj, ObjectStreamClass osc) throws IOException
1210 {
1211 Class klass = osc.forClass();
1212 try
1213 {
1214 Class classArgs[] = {ObjectOutputStream.class};
1215 Method m = getMethod (klass, "writeObject", classArgs);
1216 if (m == null)
1217 return;
1218 Object args[] = {this};
1219 m.invoke (obj, args);
1220 }
1221 catch (InvocationTargetException x)
1222 {
1223 /* Rethrow if possible. */
1224 Throwable exception = x.getTargetException();
1225 if (exception instanceof RuntimeException)
1226 throw (RuntimeException) exception;
1227 if (exception instanceof IOException)
1228 throw (IOException) exception;
1229
1230 throw new IOException ("Exception thrown from writeObject() on " +
1231 klass + ": " + exception.getClass().getName());
1232 }
1233 catch (Exception x)
1234 {
1235 throw new IOException ("Failure invoking writeObject() on " +
1236 klass + ": " + x.getClass().getName());
1237 }
1238 }
1239
1240 private boolean getBooleanField (Object obj, Class klass, String field_name)
1241 throws IOException
1242 {
1243 try
1244 {
1245 Field f = getField (klass, field_name);
1246 boolean b = f.getBoolean (obj);
1247 return b;
1248 }
1249 catch (Exception _)
1250 {
1251 throw new IOException ();
1252 }
1253 }
1254
1255 private byte getByteField (Object obj, Class klass, String field_name) throws IOException
1256 {
1257 try
1258 {
1259 Field f = getField (klass, field_name);
1260 byte b = f.getByte (obj);
1261 return b;
1262 }
1263 catch (Exception _)
1264 {
1265 throw new IOException ();
1266 }
1267 }
1268
1269 private char getCharField (Object obj, Class klass, String field_name) throws IOException
1270 {
1271 try
1272 {
1273 Field f = getField (klass, field_name);
1274 char b = f.getChar (obj);
1275 return b;
1276 }
1277 catch (Exception _)
1278 {
1279 throw new IOException ();
1280 }
1281 }
1282
1283 private double getDoubleField (Object obj, Class klass, String field_name)
1284 throws IOException
1285 {
1286 try
1287 {
1288 Field f = getField (klass, field_name);
1289 double b = f.getDouble (obj);
1290 return b;
1291 }
1292 catch (Exception _)
1293 {
1294 throw new IOException ();
1295 }
1296 }
1297
1298 private float getFloatField (Object obj, Class klass, String field_name)
1299 throws IOException
1300 {
1301 try
1302 {
1303 Field f = getField (klass, field_name);
1304 float b = f.getFloat (obj);
1305 return b;
1306 }
1307 catch (Exception _)
1308 {
1309 throw new IOException ();
1310 }
1311 }
1312
1313 private int getIntField (Object obj, Class klass, String field_name) throws IOException
1314 {
1315 try
1316 {
1317 Field f = getField (klass, field_name);
1318 int b = f.getInt (obj);
1319 return b;
1320 }
1321 catch (Exception _)
1322 {
1323 throw new IOException ();
1324 }
1325 }
1326
1327 private long getLongField (Object obj, Class klass, String field_name) throws IOException
1328 {
1329 try
1330 {
1331 Field f = getField (klass, field_name);
1332 long b = f.getLong (obj);
1333 return b;
1334 }
1335 catch (Exception _)
1336 {
1337 throw new IOException ();
1338 }
1339 }
1340
1341 private short getShortField (Object obj, Class klass, String field_name)
1342 throws IOException
1343 {
1344 try
1345 {
1346 Field f = getField (klass, field_name);
1347 short b = f.getShort (obj);
1348 return b;
1349 }
1350 catch (Exception _)
1351 {
1352 throw new IOException ();
1353 }
1354 }
1355
1356 private Object getObjectField (Object obj, Class klass, String field_name,
1357 String type_code) throws IOException
1358 {
1359 try
1360 {
1361 Field f = getField (klass, field_name);
1362 Object o = f.get (obj);
1363 // FIXME: We should check the type_code here
1364 return o;
1365 }
1366 catch (Exception _)
1367 {
1368 throw new IOException ();
1369 }
1370 }
1371
1372 private static Field getField (Class klass, String name)
1373 throws java.lang.NoSuchFieldException
1374 {
1375 return klass.getDeclaredField(name);
1376 }
1377
1378 private static Method getMethod (Class klass, String name, Class[] args)
1379 throws java.lang.NoSuchMethodException
1380 {
1381 return klass.getDeclaredMethod(name, args);
1382 }
1383
1384 // this value comes from 1.2 spec, but is used in 1.1 as well
1385 private final static int BUFFER_SIZE = 1024;
1386
1387 private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
1388
1389 private DataOutputStream dataOutput;
1390 private boolean writeDataAsBlocks;
1391 private DataOutputStream realOutput;
1392 private DataOutputStream blockDataOutput;
1393 private byte[] blockData;
1394 private int blockDataCount;
1395 private Object currentObject;
1396 private ObjectStreamClass currentObjectStreamClass;
1397 private PutField currentPutField;
1398 private boolean fieldsAlreadyWritten;
1399 private boolean replacementEnabled;
1400 private boolean isSerializing;
1401 private int nextOID;
1402 private Hashtable OIDLookupTable;
1403 private int protocolVersion;
1404 private boolean useSubclassMethod;
1405
1406 static
1407 {
1408 if (Configuration.INIT_LOAD_LIBRARY)
1409 {
1410 System.loadLibrary ("javaio");
1411 }
1412 }
1413}
Note: See TracBrowser for help on using the repository browser.