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