Source for java.io.ObjectOutputStream

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