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