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