Source for java.io.ObjectOutputStream

   1: /* ObjectOutputStream.java -- Class used to write serialized objects
   2:    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
   3:    Free Software Foundation, Inc.
   4: 
   5: This file is part of GNU Classpath.
   6: 
   7: GNU Classpath is free software; you can redistribute it and/or modify
   8: it under the terms of the GNU General Public License as published by
   9: the Free Software Foundation; either version 2, or (at your option)
  10: any later version.
  11: 
  12: GNU Classpath is distributed in the hope that it will be useful, but
  13: WITHOUT ANY WARRANTY; without even the implied warranty of
  14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15: General Public License for more details.
  16: 
  17: You should have received a copy of the GNU General Public License
  18: along with GNU Classpath; see the file COPYING.  If not, write to the
  19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20: 02110-1301 USA.
  21: 
  22: Linking this library statically or dynamically with other modules is
  23: making a combined work based on this library.  Thus, the terms and
  24: conditions of the GNU General Public License cover the whole
  25: combination.
  26: 
  27: As a special exception, the copyright holders of this library give you
  28: permission to link this library with independent modules to produce an
  29: executable, regardless of the license terms of these independent
  30: modules, and to copy and distribute the resulting executable under
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: 
  40: package java.io;
  41: 
  42: import gnu.java.io.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:         assignNewHandle(osc);
 425:     
 426:         boolean oldmode = setBlockDataMode(true);
 427:         annotateProxyClass(osc.forClass());
 428:         setBlockDataMode(oldmode);
 429:         realOutput.writeByte(TC_ENDBLOCKDATA);
 430:       }
 431:     else
 432:       {
 433:         realOutput.writeByte(TC_CLASSDESC);
 434:         realOutput.writeUTF(osc.getName());
 435:         realOutput.writeLong(osc.getSerialVersionUID());
 436:         assignNewHandle(osc);
 437: 
 438:         int flags = osc.getFlags();
 439: 
 440:         if (protocolVersion == PROTOCOL_VERSION_2
 441:         && osc.isExternalizable())
 442:         flags |= SC_BLOCK_DATA;
 443: 
 444:         realOutput.writeByte(flags);
 445: 
 446:         ObjectStreamField[] fields = osc.fields;
 447: 
 448:     if (fields == ObjectStreamClass.INVALID_FIELDS)
 449:       throw new InvalidClassException
 450:           (osc.getName(), "serialPersistentFields is invalid");
 451: 
 452:         realOutput.writeShort(fields.length);
 453: 
 454:         ObjectStreamField field;
 455:         for (int i = 0; i < fields.length; i++)
 456:           {
 457:         field = fields[i];
 458:         realOutput.writeByte(field.getTypeCode ());
 459:         realOutput.writeUTF(field.getName ());
 460: 
 461:         if (! field.isPrimitive())
 462:           writeObject(field.getTypeString());
 463:           }
 464: 
 465:         boolean oldmode = setBlockDataMode(true);
 466:         annotateClass(osc.forClass());
 467:         setBlockDataMode(oldmode);
 468:         realOutput.writeByte(TC_ENDBLOCKDATA);
 469:       }
 470: 
 471:     if (osc.isSerializable() || osc.isExternalizable())
 472:       writeObject(osc.getSuper());
 473:     else
 474:       writeObject(null);
 475:   }
 476:   
 477:   /**
 478:    * Writes the current objects non-transient, non-static fields from
 479:    * the current class to the underlying output stream.
 480:    *
 481:    * This method is intended to be called from within a object's
 482:    * <code>private void writeObject (ObjectOutputStream)</code>
 483:    * method.
 484:    *
 485:    * @exception NotActiveException This method was called from a
 486:    * context other than from the current object's and current class's
 487:    * <code>private void writeObject (ObjectOutputStream)</code>
 488:    * method.
 489:    *
 490:    * @exception IOException Exception from underlying
 491:    * <code>OutputStream</code>.
 492:    */
 493:   public void defaultWriteObject()
 494:     throws IOException, NotActiveException
 495:   {
 496:     markFieldsWritten();
 497:     writeFields(currentObject, currentObjectStreamClass);
 498:   }
 499: 
 500: 
 501:   private void markFieldsWritten() throws IOException
 502:   {
 503:     if (currentObject == null || currentObjectStreamClass == null)
 504:       throw new NotActiveException
 505:     ("defaultWriteObject called by non-active class and/or object");
 506: 
 507:     if (fieldsAlreadyWritten)
 508:       throw new IOException
 509:     ("Only one of writeFields and defaultWriteObject may be called, and it may only be called once");
 510: 
 511:     fieldsAlreadyWritten = true;
 512:   }
 513: 
 514:   /**
 515:    * Resets stream to state equivalent to the state just after it was
 516:    * constructed.
 517:    *
 518:    * Causes all objects previously written to the stream to be
 519:    * forgotten.  A notification of this reset is also written to the
 520:    * underlying stream.
 521:    *
 522:    * @exception IOException Exception from underlying
 523:    * <code>OutputStream</code> or reset called while serialization is
 524:    * in progress.
 525:    */
 526:   public void reset() throws IOException
 527:   {
 528:     reset(false);
 529:   }
 530: 
 531: 
 532:   private void reset(boolean internal) throws IOException
 533:   {
 534:     if (!internal)
 535:       {
 536:     if (isSerializing)
 537:       throw new IOException("Reset called while serialization in progress");
 538: 
 539:     realOutput.writeByte(TC_RESET);
 540:       }
 541:     
 542:     clearHandles();
 543:   }
 544: 
 545: 
 546:   /**
 547:    * Informs this <code>ObjectOutputStream</code> to write data
 548:    * according to the specified protocol.  There are currently two
 549:    * different protocols, specified by <code>PROTOCOL_VERSION_1</code>
 550:    * and <code>PROTOCOL_VERSION_2</code>.  This implementation writes
 551:    * data using <code>PROTOCOL_VERSION_2</code> by default, as is done
 552:    * since the JDK 1.2.
 553:    * <p>
 554:    * For an explanation of the differences between the two protocols
 555:    * see the Java Object Serialization Specification.
 556:    * </p>
 557:    * 
 558:    * @param version the version to use.
 559:    * 
 560:    * @throws IllegalArgumentException if <code>version</code> is not a valid 
 561:    * protocol.
 562:    * @throws IllegalStateException if called after the first the first object
 563:    * was serialized.
 564:    * @throws IOException if an I/O error occurs.
 565:    * 
 566:    * @see ObjectStreamConstants#PROTOCOL_VERSION_1
 567:    * @see ObjectStreamConstants#PROTOCOL_VERSION_2
 568:    * 
 569:    * @since 1.2
 570:    */
 571:   public void useProtocolVersion(int version) throws IOException
 572:   {
 573:     if (version != PROTOCOL_VERSION_1 && version != PROTOCOL_VERSION_2)
 574:       throw new IllegalArgumentException("Invalid protocol version requested.");
 575:     
 576:     if (nextOID != baseWireHandle)
 577:       throw new IllegalStateException("Protocol version cannot be changed " 
 578:                                       + "after serialization started.");
 579:     
 580:     protocolVersion = version;
 581:   }
 582: 
 583:   /**
 584:    * An empty hook that allows subclasses to write extra information
 585:    * about classes to the stream.  This method is called the first
 586:    * time each class is seen, and after all of the standard
 587:    * information about the class has been written.
 588:    *
 589:    * @exception IOException Exception from underlying
 590:    * <code>OutputStream</code>.
 591:    *
 592:    * @see ObjectInputStream#resolveClass(java.io.ObjectStreamClass)
 593:    */
 594:   protected void annotateClass(Class cl) throws IOException
 595:   {
 596:   }
 597: 
 598:   protected void annotateProxyClass(Class cl) throws IOException
 599:   {
 600:   }
 601: 
 602:   /**
 603:    * Allows subclasses to replace objects that are written to the
 604:    * stream with other objects to be written in their place.  This
 605:    * method is called the first time each object is encountered
 606:    * (modulo reseting of the stream).
 607:    *
 608:    * This method must be enabled before it will be called in the
 609:    * serialization process.
 610:    *
 611:    * @exception IOException Exception from underlying
 612:    * <code>OutputStream</code>.
 613:    *
 614:    * @see #enableReplaceObject(boolean)
 615:    */
 616:   protected Object replaceObject(Object obj) throws IOException
 617:   {
 618:     return obj;
 619:   }
 620: 
 621: 
 622:   /**
 623:    * If <code>enable</code> is <code>true</code> and this object is
 624:    * trusted, then <code>replaceObject (Object)</code> will be called
 625:    * in subsequent calls to <code>writeObject (Object)</code>.
 626:    * Otherwise, <code>replaceObject (Object)</code> will not be called.
 627:    *
 628:    * @exception SecurityException This class is not trusted.
 629:    */
 630:   protected boolean enableReplaceObject(boolean enable)
 631:     throws SecurityException
 632:   {
 633:     if (enable)
 634:       {
 635:     SecurityManager sm = System.getSecurityManager();
 636:     if (sm != null)
 637:       sm.checkPermission(new SerializablePermission("enableSubstitution"));
 638:       }
 639: 
 640:     boolean old_val = replacementEnabled;
 641:     replacementEnabled = enable;
 642:     return old_val;
 643:   }
 644: 
 645: 
 646:   /**
 647:    * Writes stream magic and stream version information to the
 648:    * underlying stream.
 649:    *
 650:    * @exception IOException Exception from underlying
 651:    * <code>OutputStream</code>.
 652:    */
 653:   protected void writeStreamHeader() throws IOException
 654:   {
 655:     realOutput.writeShort(STREAM_MAGIC);
 656:     realOutput.writeShort(STREAM_VERSION);
 657:   }
 658: 
 659:   /**
 660:    * Protected constructor that allows subclasses to override
 661:    * serialization.  This constructor should be called by subclasses
 662:    * that wish to override <code>writeObject (Object)</code>.  This
 663:    * method does a security check <i>NOTE: currently not
 664:    * implemented</i>, then sets a flag that informs
 665:    * <code>writeObject (Object)</code> to call the subclasses
 666:    * <code>writeObjectOverride (Object)</code> method.
 667:    *
 668:    * @see #writeObjectOverride(Object)
 669:    */
 670:   protected ObjectOutputStream() throws IOException, SecurityException
 671:   {
 672:     SecurityManager sec_man = System.getSecurityManager ();
 673:     if (sec_man != null)
 674:       sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
 675:     useSubclassMethod = true;
 676:   }
 677: 
 678: 
 679:   /**
 680:    * This method allows subclasses to override the default
 681:    * serialization mechanism provided by
 682:    * <code>ObjectOutputStream</code>.  To make this method be used for
 683:    * writing objects, subclasses must invoke the 0-argument
 684:    * constructor on this class from there constructor.
 685:    *
 686:    * @see #ObjectOutputStream()
 687:    *
 688:    * @exception NotActiveException Subclass has arranged for this
 689:    * method to be called, but did not implement this method.
 690:    */
 691:   protected void writeObjectOverride(Object obj) throws NotActiveException,
 692:     IOException
 693:   {
 694:     throw new NotActiveException
 695:       ("Subclass of ObjectOutputStream must implement writeObjectOverride");
 696:   }
 697: 
 698: 
 699:   /**
 700:    * @see DataOutputStream#write(int)
 701:    */
 702:   public void write (int data) throws IOException
 703:   {
 704:     if (writeDataAsBlocks)
 705:       {
 706:     if (blockDataCount == BUFFER_SIZE)
 707:       drain();
 708: 
 709:     blockData[ blockDataCount++ ] = (byte)data;
 710:       }
 711:     else
 712:       realOutput.write(data);
 713:   }
 714: 
 715: 
 716:   /**
 717:    * @see DataOutputStream#write(byte[])
 718:    */
 719:   public void write(byte[] b) throws IOException
 720:   {
 721:     write(b, 0, b.length);
 722:   }
 723: 
 724: 
 725:   /**
 726:    * @see DataOutputStream#write(byte[],int,int)
 727:    */
 728:   public void write(byte[] b, int off, int len) throws IOException
 729:   {
 730:     if (writeDataAsBlocks)
 731:       {
 732:     if (len < 0)
 733:       throw new IndexOutOfBoundsException();
 734: 
 735:     if (blockDataCount + len < BUFFER_SIZE)
 736:       {
 737:         System.arraycopy(b, off, blockData, blockDataCount, len);
 738:         blockDataCount += len;
 739:       }
 740:     else
 741:       {
 742:         drain();
 743:         writeBlockDataHeader(len);
 744:         realOutput.write(b, off, len);
 745:       }
 746:       }
 747:     else
 748:       realOutput.write(b, off, len);
 749:   }
 750: 
 751: 
 752:   /**
 753:    * @see DataOutputStream#flush()
 754:    */
 755:   public void flush () throws IOException
 756:   {
 757:     drain();
 758:     realOutput.flush();
 759:   }
 760: 
 761: 
 762:   /**
 763:    * Causes the block-data buffer to be written to the underlying
 764:    * stream, but does not flush underlying stream.
 765:    *
 766:    * @exception IOException Exception from underlying
 767:    * <code>OutputStream</code>.
 768:    */
 769:   protected void drain() throws IOException
 770:   {
 771:     if (blockDataCount == 0)
 772:       return;
 773: 
 774:     if (writeDataAsBlocks)
 775:       writeBlockDataHeader(blockDataCount);
 776:     realOutput.write(blockData, 0, blockDataCount);
 777:     blockDataCount = 0;
 778:   }
 779: 
 780: 
 781:   /**
 782:    * @see java.io.DataOutputStream#close ()
 783:    */
 784:   public void close() throws IOException
 785:   {
 786:     flush();
 787:     realOutput.close();
 788:   }
 789: 
 790: 
 791:   /**
 792:    * @see java.io.DataOutputStream#writeBoolean (boolean)
 793:    */
 794:   public void writeBoolean(boolean data) throws IOException
 795:   {
 796:     blockDataOutput.writeBoolean(data);
 797:   }
 798: 
 799: 
 800:   /**
 801:    * @see java.io.DataOutputStream#writeByte (int)
 802:    */
 803:   public void writeByte(int data) throws IOException
 804:   {
 805:     blockDataOutput.writeByte(data);
 806:   }
 807: 
 808: 
 809:   /**
 810:    * @see java.io.DataOutputStream#writeShort (int)
 811:    */
 812:   public void writeShort (int data) throws IOException
 813:   {
 814:     blockDataOutput.writeShort(data);
 815:   }
 816: 
 817: 
 818:   /**
 819:    * @see java.io.DataOutputStream#writeChar (int)
 820:    */
 821:   public void writeChar(int data) throws IOException
 822:   {
 823:     blockDataOutput.writeChar(data);
 824:   }
 825: 
 826: 
 827:   /**
 828:    * @see java.io.DataOutputStream#writeInt (int)
 829:    */
 830:   public void writeInt(int data) throws IOException
 831:   {
 832:     blockDataOutput.writeInt(data);
 833:   }
 834: 
 835: 
 836:   /**
 837:    * @see java.io.DataOutputStream#writeLong (long)
 838:    */
 839:   public void writeLong(long data) throws IOException
 840:   {
 841:     blockDataOutput.writeLong(data);
 842:   }
 843: 
 844: 
 845:   /**
 846:    * @see java.io.DataOutputStream#writeFloat (float)
 847:    */
 848:   public void writeFloat(float data) throws IOException
 849:   {
 850:     blockDataOutput.writeFloat(data);
 851:   }
 852: 
 853: 
 854:   /**
 855:    * @see java.io.DataOutputStream#writeDouble (double)
 856:    */
 857:   public void writeDouble(double data) throws IOException
 858:   {
 859:     blockDataOutput.writeDouble(data);
 860:   }
 861: 
 862: 
 863:   /**
 864:    * @see java.io.DataOutputStream#writeBytes (java.lang.String)
 865:    */
 866:   public void writeBytes(String data) throws IOException
 867:   {
 868:     blockDataOutput.writeBytes(data);
 869:   }
 870: 
 871: 
 872:   /**
 873:    * @see java.io.DataOutputStream#writeChars (java.lang.String)
 874:    */
 875:   public void writeChars(String data) throws IOException
 876:   {
 877:     dataOutput.writeChars(data);
 878:   }
 879: 
 880: 
 881:   /**
 882:    * @see java.io.DataOutputStream#writeUTF (java.lang.String)
 883:    */
 884:   public void writeUTF(String data) throws IOException
 885:   {
 886:     dataOutput.writeUTF(data);
 887:   }
 888: 
 889: 
 890:   /**
 891:    * This class allows a class to specify exactly which fields should
 892:    * be written, and what values should be written for these fields.
 893:    *
 894:    * XXX: finish up comments
 895:    */
 896:   public abstract static class PutField
 897:   {
 898:     public abstract void put (String name, boolean value);
 899:     public abstract void put (String name, byte value);
 900:     public abstract void put (String name, char value);
 901:     public abstract void put (String name, double value);
 902:     public abstract void put (String name, float value);
 903:     public abstract void put (String name, int value);
 904:     public abstract void put (String name, long value);
 905:     public abstract void put (String name, short value);
 906:     public abstract void put (String name, Object value);
 907: 
 908:     /**
 909:      * @deprecated
 910:      */
 911:     public abstract void write (ObjectOutput out) throws IOException;
 912:   }
 913: 
 914:   public PutField putFields() throws IOException
 915:   {
 916:     if (currentPutField != null)
 917:       return currentPutField;
 918: 
 919:     currentPutField = new PutField()
 920:       {
 921:     private byte[] prim_field_data
 922:       = new byte[currentObjectStreamClass.primFieldSize];
 923:     private Object[] objs
 924:       = new Object[currentObjectStreamClass.objectFieldCount];
 925: 
 926:     private ObjectStreamField getField (String name)
 927:     {
 928:       ObjectStreamField field
 929:         = currentObjectStreamClass.getField(name);
 930:       
 931:       if (field == null)
 932:         throw new IllegalArgumentException("no such serializable field " + name);
 933:       
 934:       return field;
 935:     }
 936:     
 937:     public void put(String name, boolean value)
 938:     {
 939:       ObjectStreamField field = getField(name);
 940: 
 941:       checkType(field, 'Z');
 942:       prim_field_data[field.getOffset ()] = (byte)(value ? 1 : 0);
 943:     }
 944: 
 945:     public void put(String name, byte value)
 946:     {
 947:       ObjectStreamField field = getField(name);
 948: 
 949:       checkType(field, 'B');
 950:       prim_field_data[field.getOffset()] = value;
 951:     }
 952: 
 953:     public void put(String name, char value)
 954:     {
 955:       ObjectStreamField field = getField(name);
 956: 
 957:       checkType(field, 'C');
 958:       int off = field.getOffset();
 959:       prim_field_data[off++] = (byte)(value >>> 8);
 960:       prim_field_data[off] = (byte)value;
 961:     }
 962: 
 963:     public void put(String name, double value)
 964:     {
 965:       ObjectStreamField field = getField (name);
 966: 
 967:       checkType(field, 'D');
 968:       int off = field.getOffset();
 969:       long l_value = Double.doubleToLongBits (value);
 970:       prim_field_data[off++] = (byte)(l_value >>> 52);
 971:       prim_field_data[off++] = (byte)(l_value >>> 48);
 972:       prim_field_data[off++] = (byte)(l_value >>> 40);
 973:       prim_field_data[off++] = (byte)(l_value >>> 32);
 974:       prim_field_data[off++] = (byte)(l_value >>> 24);
 975:       prim_field_data[off++] = (byte)(l_value >>> 16);
 976:       prim_field_data[off++] = (byte)(l_value >>> 8);
 977:       prim_field_data[off] = (byte)l_value;
 978:     }
 979: 
 980:     public void put(String name, float value)
 981:     {
 982:       ObjectStreamField field = getField(name);
 983: 
 984:       checkType(field, 'F');
 985:       int off = field.getOffset();
 986:       int i_value = Float.floatToIntBits(value);
 987:       prim_field_data[off++] = (byte)(i_value >>> 24);
 988:       prim_field_data[off++] = (byte)(i_value >>> 16);
 989:       prim_field_data[off++] = (byte)(i_value >>> 8);
 990:       prim_field_data[off] = (byte)i_value;
 991:     }
 992: 
 993:     public void put(String name, int value)
 994:     {
 995:       ObjectStreamField field = getField(name);
 996:       checkType(field, 'I');
 997:       int off = field.getOffset();
 998:       prim_field_data[off++] = (byte)(value >>> 24);
 999:       prim_field_data[off++] = (byte)(value >>> 16);
1000:       prim_field_data[off++] = (byte)(value >>> 8);
1001:       prim_field_data[off] = (byte)value;
1002:     }
1003: 
1004:     public void put(String name, long value)
1005:     {
1006:       ObjectStreamField field = getField(name);
1007:       checkType(field, 'J');
1008:       int off = field.getOffset();
1009:       prim_field_data[off++] = (byte)(value >>> 52);
1010:       prim_field_data[off++] = (byte)(value >>> 48);
1011:       prim_field_data[off++] = (byte)(value >>> 40);
1012:       prim_field_data[off++] = (byte)(value >>> 32);
1013:       prim_field_data[off++] = (byte)(value >>> 24);
1014:       prim_field_data[off++] = (byte)(value >>> 16);
1015:       prim_field_data[off++] = (byte)(value >>> 8);
1016:       prim_field_data[off] = (byte)value;
1017:     }
1018: 
1019:     public void put(String name, short value)
1020:     {
1021:       ObjectStreamField field = getField(name);
1022:       checkType(field, 'S');
1023:       int off = field.getOffset();
1024:       prim_field_data[off++] = (byte)(value >>> 8);
1025:       prim_field_data[off] = (byte)value;
1026:     }
1027: 
1028:     public void put(String name, Object value)
1029:     {
1030:       ObjectStreamField field = getField(name);
1031: 
1032:       if (value != null &&
1033:           ! field.getType().isAssignableFrom(value.getClass ()))        
1034:         throw new IllegalArgumentException("Class " + value.getClass() +
1035:                            " cannot be cast to " + field.getType());
1036:       objs[field.getOffset()] = value;
1037:     }
1038: 
1039:     public void write(ObjectOutput out) throws IOException
1040:     {
1041:       // Apparently Block data is not used with PutField as per
1042:       // empirical evidence against JDK 1.2.  Also see Mauve test
1043:       // java.io.ObjectInputOutput.Test.GetPutField.
1044:       boolean oldmode = setBlockDataMode(false);
1045:       out.write(prim_field_data);
1046:       for (int i = 0; i < objs.length; ++ i)
1047:         out.writeObject(objs[i]);
1048:       setBlockDataMode(oldmode);
1049:     }
1050: 
1051:     private void checkType(ObjectStreamField field, char type)
1052:       throws IllegalArgumentException
1053:     {
1054:       if (TypeSignature.getEncodingOfClass(field.getType()).charAt(0)
1055:           != type)
1056:         throw new IllegalArgumentException();
1057:     }
1058:       };
1059:     // end PutFieldImpl
1060: 
1061:     return currentPutField;
1062:   }
1063: 
1064: 
1065:   public void writeFields() throws IOException
1066:   {
1067:     if (currentPutField == null)
1068:       throw new NotActiveException("writeFields can only be called after putFields has been called");
1069: 
1070:     markFieldsWritten();
1071:     currentPutField.write(this);
1072:   }
1073: 
1074: 
1075:   // write out the block-data buffer, picking the correct header
1076:   // depending on the size of the buffer
1077:   private void writeBlockDataHeader(int size) throws IOException
1078:   {
1079:     if (size < 256)
1080:       {
1081:     realOutput.writeByte(TC_BLOCKDATA);
1082:     realOutput.write(size);
1083:       }
1084:     else
1085:       {
1086:     realOutput.writeByte(TC_BLOCKDATALONG);
1087:     realOutput.writeInt(size);
1088:       }
1089:   }
1090: 
1091: 
1092:   // lookup the handle for OBJ, return null if OBJ doesn't have a
1093:   // handle yet
1094:   private Integer findHandle(Object obj)
1095:   {
1096:     return (Integer)OIDLookupTable.get(new ObjectIdentityWrapper(obj));
1097:   }
1098: 
1099: 
1100:   // assigns the next availible handle to OBJ
1101:   private int assignNewHandle(Object obj)
1102:   {
1103:     OIDLookupTable.put(new ObjectIdentityWrapper(obj),
1104:                new Integer(nextOID));
1105:     return nextOID++;
1106:   }
1107: 
1108: 
1109:   // resets mapping from objects to handles
1110:   private void clearHandles()
1111:   {
1112:     nextOID = baseWireHandle;
1113:     OIDLookupTable.clear();
1114:   }
1115: 
1116: 
1117:   // write out array size followed by each element of the array
1118:   private void writeArraySizeAndElements(Object array, Class clazz)
1119:     throws IOException
1120:   {
1121:     int length = Array.getLength(array);
1122: 
1123:     if (clazz.isPrimitive())
1124:       {
1125:     if (clazz == Boolean.TYPE)
1126:       {
1127:         boolean[] cast_array = (boolean[])array;
1128:         realOutput.writeInt (length);
1129:         for (int i = 0; i < length; i++)
1130:           realOutput.writeBoolean(cast_array[i]);
1131:         return;
1132:       }
1133:     if (clazz == Byte.TYPE)
1134:       {
1135:         byte[] cast_array = (byte[])array;
1136:         realOutput.writeInt(length);
1137:         realOutput.write(cast_array, 0, length);
1138:         return;
1139:       }
1140:     if (clazz == Character.TYPE)
1141:       {
1142:         char[] cast_array = (char[])array;
1143:         realOutput.writeInt(length);
1144:         for (int i = 0; i < length; i++)
1145:           realOutput.writeChar(cast_array[i]);
1146:         return;
1147:       }
1148:     if (clazz == Double.TYPE)
1149:       {
1150:         double[] cast_array = (double[])array;
1151:         realOutput.writeInt(length);
1152:         for (int i = 0; i < length; i++)
1153:           realOutput.writeDouble(cast_array[i]);
1154:         return;
1155:       }
1156:     if (clazz == Float.TYPE)
1157:       {
1158:         float[] cast_array = (float[])array;
1159:         realOutput.writeInt(length);
1160:         for (int i = 0; i < length; i++)
1161:           realOutput.writeFloat(cast_array[i]);
1162:         return;
1163:       }
1164:     if (clazz == Integer.TYPE)
1165:       {
1166:         int[] cast_array = (int[])array;
1167:         realOutput.writeInt(length);
1168:         for (int i = 0; i < length; i++)
1169:           realOutput.writeInt(cast_array[i]);
1170:         return;
1171:       }
1172:     if (clazz == Long.TYPE)
1173:       {
1174:         long[] cast_array = (long[])array;
1175:         realOutput.writeInt (length);
1176:         for (int i = 0; i < length; i++)
1177:           realOutput.writeLong(cast_array[i]);
1178:         return;
1179:       }
1180:     if (clazz == Short.TYPE)
1181:       {
1182:         short[] cast_array = (short[])array;
1183:         realOutput.writeInt (length);
1184:         for (int i = 0; i < length; i++)
1185:           realOutput.writeShort(cast_array[i]);
1186:         return;
1187:       }
1188:       }
1189:     else
1190:       {
1191:     Object[] cast_array = (Object[])array;
1192:     realOutput.writeInt(length);
1193:     for (int i = 0; i < length; i++)
1194:       writeObject(cast_array[i]);
1195:       }
1196:   }
1197: 
1198: 
1199:   // writes out FIELDS of OBJECT for the specified ObjectStreamClass.
1200:   // FIELDS are already in canonical order.
1201:   private void writeFields(Object obj, ObjectStreamClass osc)
1202:     throws IOException
1203:   {
1204:     ObjectStreamField[] fields = osc.fields;
1205:     boolean oldmode = setBlockDataMode(false);
1206:     String field_name;
1207:     Class type;
1208: 
1209:     for (int i = 0; i < fields.length; i++)
1210:       {
1211:     field_name = fields[i].getName();
1212:     type = fields[i].getType();
1213: 
1214:     if (dump)
1215:       dumpElementln ("WRITE FIELD: " + field_name + " type=" + type);
1216: 
1217:     if (type == Boolean.TYPE)
1218:       realOutput.writeBoolean(getBooleanField(obj, osc.forClass(), field_name));
1219:     else if (type == Byte.TYPE)
1220:       realOutput.writeByte(getByteField(obj, osc.forClass(), field_name));
1221:     else if (type == Character.TYPE)
1222:       realOutput.writeChar(getCharField(obj, osc.forClass(), field_name));
1223:     else if (type == Double.TYPE)
1224:       realOutput.writeDouble(getDoubleField(obj, osc.forClass(), field_name));
1225:     else if (type == Float.TYPE)
1226:       realOutput.writeFloat(getFloatField(obj, osc.forClass(), field_name));
1227:     else if (type == Integer.TYPE)
1228:       realOutput.writeInt(getIntField(obj, osc.forClass(), field_name));
1229:     else if (type == Long.TYPE)
1230:       realOutput.writeLong(getLongField(obj, osc.forClass(), field_name));
1231:     else if (type == Short.TYPE)
1232:       realOutput.writeShort(getShortField(obj, osc.forClass(), field_name));
1233:     else
1234:       writeObject(getObjectField(obj, osc.forClass(), field_name,
1235:                      fields[i].getTypeString ()));
1236:       }
1237:     setBlockDataMode(oldmode);
1238:   }
1239: 
1240: 
1241:   // Toggles writing primitive data to block-data buffer.
1242:   // Package-private to avoid a trampoline constructor.
1243:   boolean setBlockDataMode(boolean on) throws IOException
1244:   {
1245:     if (on == writeDataAsBlocks)
1246:       return on;
1247: 
1248:     drain();
1249:     boolean oldmode = writeDataAsBlocks;
1250:     writeDataAsBlocks = on;
1251: 
1252:     if (on)
1253:       dataOutput = blockDataOutput;
1254:     else
1255:       dataOutput = realOutput;
1256: 
1257:     return oldmode;
1258:   }
1259: 
1260: 
1261:   private void callWriteMethod(Object obj, ObjectStreamClass osc)
1262:     throws IOException
1263:   {
1264:     currentPutField = null;
1265:     try
1266:       {
1267:         Object args[] = {this};
1268:         osc.writeObjectMethod.invoke(obj, args);
1269:       }
1270:     catch (InvocationTargetException x)
1271:       {
1272:         /* Rethrow if possible. */
1273:     Throwable exception = x.getTargetException();
1274:     if (exception instanceof RuntimeException)
1275:       throw (RuntimeException) exception;
1276:     if (exception instanceof IOException)
1277:       throw (IOException) exception;
1278: 
1279:     IOException ioe
1280:       = new IOException("Exception thrown from writeObject() on " +
1281:                 osc.forClass().getName() + ": " +
1282:                             exception.getClass().getName());
1283:     ioe.initCause(exception);
1284:     throw ioe;
1285:       }
1286:     catch (Exception x)
1287:       {
1288:     IOException ioe
1289:       = new IOException("Failure invoking writeObject() on " +
1290:                 osc.forClass().getName() + ": " +
1291:                 x.getClass().getName());
1292:     ioe.initCause(x);
1293:     throw ioe;
1294:       }
1295:   }
1296: 
1297:   private boolean getBooleanField(Object obj, Class klass, String field_name)
1298:     throws IOException
1299:   {
1300:     try
1301:       {
1302:     Field f = getField(klass, field_name);
1303:     boolean b = f.getBoolean(obj);
1304:     return b;
1305:       }
1306:     catch (IllegalArgumentException _)
1307:       {
1308:     throw new InvalidClassException
1309:       ("invalid requested type for field " + field_name + " in class " + klass.getName());
1310:       }
1311:     catch (IOException e)
1312:       {
1313:     throw e;
1314:       }
1315:     catch (Exception _)
1316:       {
1317:     throw new IOException("Unexpected exception " + _);
1318:       }
1319:   }
1320: 
1321:   private byte getByteField (Object obj, Class klass, String field_name)
1322:     throws IOException
1323:   {
1324:     try
1325:       {
1326:     Field f = getField (klass, field_name);
1327:     byte b = f.getByte (obj);
1328:     return b;
1329:       }
1330:     catch (IllegalArgumentException _)
1331:       {
1332:     throw new InvalidClassException
1333:       ("invalid requested type for field " + field_name + " in class " + klass.getName());
1334:       }
1335:     catch (IOException e)
1336:       {
1337:     throw e;
1338:       }
1339:     catch (Exception _)
1340:       {
1341:     throw new IOException("Unexpected exception " + _);
1342:       }    
1343:   }
1344: 
1345:   private char getCharField (Object obj, Class klass, String field_name)
1346:     throws IOException
1347:   {
1348:     try
1349:       {
1350:     Field f = getField (klass, field_name);
1351:     char b = f.getChar (obj);
1352:     return b;
1353:       }
1354:     catch (IllegalArgumentException _)
1355:       {
1356:     throw new InvalidClassException
1357:       ("invalid requested type for field " + field_name + " in class " + klass.getName());
1358:       }
1359:     catch (IOException e)
1360:       {
1361:     throw e;
1362:       }
1363:     catch (Exception _)
1364:       {
1365:     throw new IOException("Unexpected exception " + _);
1366:       }    
1367:   }
1368: 
1369:   private double getDoubleField (Object obj, Class klass, String field_name)
1370:     throws IOException
1371:   {
1372:     try
1373:       {
1374:     Field f = getField (klass, field_name);
1375:     double b = f.getDouble (obj);
1376:     return b;
1377:       }
1378:     catch (IllegalArgumentException _)
1379:       {
1380:     throw new InvalidClassException
1381:       ("invalid requested type for field " + field_name + " in class " + klass.getName());
1382:       }
1383:     catch (IOException e)
1384:       {
1385:     throw e;
1386:       }
1387:     catch (Exception _)
1388:       {
1389:     throw new IOException("Unexpected exception " + _);
1390:       }    
1391:   }
1392: 
1393:   private float getFloatField (Object obj, Class klass, String field_name)
1394:     throws IOException
1395:   {
1396:     try
1397:       {
1398:     Field f = getField (klass, field_name);
1399:     float b = f.getFloat (obj);
1400:     return b;
1401:       }
1402:     catch (IllegalArgumentException _)
1403:       {
1404:     throw new InvalidClassException
1405:       ("invalid requested type for field " + field_name + " in class " + klass.getName());
1406:       }
1407:     catch (IOException e)
1408:       {
1409:     throw e;
1410:       }
1411:     catch (Exception _)
1412:       {
1413:     throw new IOException("Unexpected exception " + _);
1414:       }
1415:   }
1416: 
1417:   private int getIntField (Object obj, Class klass, String field_name)
1418:     throws IOException
1419:   {
1420:     try
1421:       {
1422:     Field f = getField (klass, field_name);
1423:     int b = f.getInt (obj);
1424:     return b;
1425:       }
1426:     catch (IllegalArgumentException _)
1427:       {
1428:     throw new InvalidClassException
1429:       ("invalid requested type for field " + field_name + " in class " + klass.getName());
1430:       }
1431:     catch (IOException e)
1432:       {
1433:     throw e;
1434:       }
1435:     catch (Exception _)
1436:       {
1437:     throw new IOException("Unexpected exception " + _);
1438:       }
1439:   }
1440: 
1441:   private long getLongField (Object obj, Class klass, String field_name)
1442:     throws IOException
1443:   {
1444:     try
1445:       {
1446:     Field f = getField (klass, field_name);
1447:     long b = f.getLong (obj);
1448:     return b;
1449:       }
1450:     catch (IllegalArgumentException _)
1451:       {
1452:     throw new InvalidClassException
1453:       ("invalid requested type for field " + field_name + " in class " + klass.getName());
1454:       }
1455:     catch (IOException e)
1456:       {
1457:     throw e;
1458:       }
1459:     catch (Exception _)
1460:       {
1461:     throw new IOException("Unexpected exception " + _);
1462:       }    
1463:   }
1464: 
1465:   private short getShortField (Object obj, Class klass, String field_name)
1466:     throws IOException
1467:   {
1468:     try
1469:       {
1470:     Field f = getField (klass, field_name);
1471:     short b = f.getShort (obj);
1472:     return b;
1473:       }
1474:     catch (IllegalArgumentException _)
1475:       {
1476:     throw new InvalidClassException
1477:       ("invalid requested type for field " + field_name + " in class " + klass.getName());
1478:       }
1479:     catch (IOException e)
1480:       {
1481:        throw e;
1482:       }
1483:     catch (Exception _)
1484:       {
1485:     throw new IOException("Unexpected exception " + _);
1486:       }
1487:   }
1488: 
1489:   private Object getObjectField (Object obj, Class klass, String field_name,
1490:                  String type_code) throws IOException
1491:   {
1492:     try
1493:       {
1494:     Field f = getField (klass, field_name);
1495:     ObjectStreamField of = new ObjectStreamField(f.getName(), f.getType());
1496: 
1497:     /* if of is primitive something went wrong
1498:      * in the check for primitive classes in writeFields.
1499:      */
1500:     if (of.isPrimitive())
1501:       throw new InvalidClassException
1502:         ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field is primitive");
1503: 
1504:     if (!of.getTypeString().equals(type_code))
1505:         throw new InvalidClassException
1506:         ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field " + of + " has type string " + of.getTypeString() + " instead of " + type_code);
1507: 
1508:     Object o = f.get (obj);
1509:     // FIXME: We should check the type_code here
1510:     return o;
1511:       }
1512:     catch (IOException e)
1513:       {
1514:     throw e;
1515:       }
1516:     catch (Exception e)
1517:       {
1518:     throw new IOException ();
1519:       }    
1520:   }
1521: 
1522:   private Field getField (Class klass, String name)
1523:     throws java.io.InvalidClassException
1524:   {
1525:     try
1526:       {
1527:     final Field f = klass.getDeclaredField(name);
1528:     setAccessible.setMember(f);
1529:     AccessController.doPrivileged(setAccessible);
1530:     return f;
1531:       }
1532:     catch (java.lang.NoSuchFieldException e)
1533:       {
1534:     throw new InvalidClassException
1535:       ("no field called " + name + " in class " + klass.getName());
1536:       }
1537:   }
1538: 
1539:   private void dumpElementln (String msg)
1540:   {
1541:     for (int i = 0; i < depth; i++)
1542:       System.out.print (" ");
1543:     System.out.print (Thread.currentThread() + ": ");
1544:     System.out.println(msg);
1545:   }
1546: 
1547:   // this value comes from 1.2 spec, but is used in 1.1 as well
1548:   private static final int BUFFER_SIZE = 1024;
1549: 
1550:   private static int defaultProtocolVersion = PROTOCOL_VERSION_2;
1551: 
1552:   private DataOutputStream dataOutput;
1553:   private boolean writeDataAsBlocks;
1554:   private DataOutputStream realOutput;
1555:   private DataOutputStream blockDataOutput;
1556:   private byte[] blockData;
1557:   private int blockDataCount;
1558:   private Object currentObject;
1559:   // Package-private to avoid a trampoline.
1560:   ObjectStreamClass currentObjectStreamClass;
1561:   private PutField currentPutField;
1562:   private boolean fieldsAlreadyWritten;
1563:   private boolean replacementEnabled;
1564:   private boolean isSerializing;
1565:   private int nextOID;
1566:   private Hashtable OIDLookupTable;
1567:   private int protocolVersion;
1568:   private boolean useSubclassMethod;
1569:   private SetAccessibleAction setAccessible = new SetAccessibleAction();
1570: 
1571:   // The nesting depth for debugging output
1572:   private int depth = 0;
1573: 
1574:   // Set if we're generating debugging dumps
1575:   private boolean dump = false;
1576: 
1577:   private static final boolean DEBUG = false;
1578: }