1 | /* ObjectOutputStream.java -- Class used to write serialized objects
|
---|
2 | Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
|
---|
3 |
|
---|
4 | This file is part of GNU Classpath.
|
---|
5 |
|
---|
6 | GNU Classpath is free software; you can redistribute it and/or modify
|
---|
7 | it under the terms of the GNU General Public License as published by
|
---|
8 | the Free Software Foundation; either version 2, or (at your option)
|
---|
9 | any later version.
|
---|
10 |
|
---|
11 | GNU Classpath is distributed in the hope that it will be useful, but
|
---|
12 | WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
---|
14 | General Public License for more details.
|
---|
15 |
|
---|
16 | You should have received a copy of the GNU General Public License
|
---|
17 | along with GNU Classpath; see the file COPYING. If not, write to the
|
---|
18 | Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
---|
19 | 02111-1307 USA.
|
---|
20 |
|
---|
21 | Linking this library statically or dynamically with other modules is
|
---|
22 | making a combined work based on this library. Thus, the terms and
|
---|
23 | conditions of the GNU General Public License cover the whole
|
---|
24 | combination.
|
---|
25 |
|
---|
26 | As a special exception, the copyright holders of this library give you
|
---|
27 | permission to link this library with independent modules to produce an
|
---|
28 | executable, regardless of the license terms of these independent
|
---|
29 | modules, and to copy and distribute the resulting executable under
|
---|
30 | terms of your choice, provided that you also meet, for each linked
|
---|
31 | independent module, the terms and conditions of the license of that
|
---|
32 | module. An independent module is a module which is not derived from
|
---|
33 | or based on this library. If you modify this library, you may extend
|
---|
34 | this exception to your version of the library, but you are not
|
---|
35 | obligated to do so. If you do not wish to do so, delete this
|
---|
36 | exception statement from your version. */
|
---|
37 |
|
---|
38 |
|
---|
39 | package java.io;
|
---|
40 |
|
---|
41 | import java.lang.reflect.Array;
|
---|
42 | import java.lang.reflect.Field;
|
---|
43 | import java.lang.reflect.Method;
|
---|
44 | import java.lang.reflect.InvocationTargetException;
|
---|
45 | import java.util.Hashtable;
|
---|
46 |
|
---|
47 | import gnu.java.io.ObjectIdentityWrapper;
|
---|
48 | import gnu.java.lang.reflect.TypeSignature;
|
---|
49 | import 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<type></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 | */
|
---|
118 | public 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 | }
|
---|