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