Source for java.io.ObjectInputStream

   1: /* ObjectInputStream.java -- Class used to read serialized objects
   2:    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008
   3:    Free Software Foundation, Inc.
   4: 
   5: This file is part of GNU Classpath.
   6: 
   7: GNU Classpath is free software; you can redistribute it and/or modify
   8: it under the terms of the GNU General Public License as published by
   9: the Free Software Foundation; either version 2, or (at your option)
  10: any later version.
  11: 
  12: GNU Classpath is distributed in the hope that it will be useful, but
  13: WITHOUT ANY WARRANTY; without even the implied warranty of
  14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15: General Public License for more details.
  16: 
  17: You should have received a copy of the GNU General Public License
  18: along with GNU Classpath; see the file COPYING.  If not, write to the
  19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20: 02110-1301 USA.
  21: 
  22: Linking this library statically or dynamically with other modules is
  23: making a combined work based on this library.  Thus, the terms and
  24: conditions of the GNU General Public License cover the whole
  25: combination.
  26: 
  27: As a special exception, the copyright holders of this library give you
  28: permission to link this library with independent modules to produce an
  29: executable, regardless of the license terms of these independent
  30: modules, and to copy and distribute the resulting executable under
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: 
  40: package java.io;
  41: 
  42: import gnu.classpath.Pair;
  43: import gnu.classpath.VMStackWalker;
  44: 
  45: import java.lang.reflect.Array;
  46: import java.lang.reflect.Constructor;
  47: import java.lang.reflect.Field;
  48: import java.lang.reflect.InvocationTargetException;
  49: import java.lang.reflect.Method;
  50: import java.lang.reflect.Modifier;
  51: import java.lang.reflect.Proxy;
  52: import java.security.AccessController;
  53: import java.security.PrivilegedAction;
  54: import java.util.HashMap;
  55: import java.util.Hashtable;
  56: import java.util.Iterator;
  57: import java.util.Map;
  58: import java.util.TreeSet;
  59: 
  60: /**
  61:  * @author Tom Tromey (tromey@redhat.com)
  62:  * @author Jeroen Frijters (jeroen@frijters.net)
  63:  * @author Guilhem Lavaux (guilhem@kaffe.org)
  64:  * @author Michael Koch (konqueror@gmx.de)
  65:  * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
  66:  */
  67: public class ObjectInputStream extends InputStream
  68:   implements ObjectInput, ObjectStreamConstants
  69: {
  70:   /**
  71:    * Creates a new <code>ObjectInputStream</code> that will do all of
  72:    * its reading from <code>in</code>.  This method also checks
  73:    * the stream by reading the header information (stream magic number
  74:    * and stream version).
  75:    *
  76:    * @exception IOException Reading stream header from underlying
  77:    * stream cannot be completed.
  78:    *
  79:    * @exception StreamCorruptedException An invalid stream magic
  80:    * number or stream version was read from the stream.
  81:    *
  82:    * @see #readStreamHeader()
  83:    */
  84:   public ObjectInputStream(InputStream in)
  85:     throws IOException, StreamCorruptedException
  86:   {
  87:     if (DEBUG)
  88:       {
  89:     String val = System.getProperty("gcj.dumpobjects");
  90:     if (dump == false && val != null && !val.equals(""))
  91:       {
  92:         dump = true;
  93:         System.out.println ("Serialization debugging enabled");
  94:       }
  95:     else if (dump == true && (val == null || val.equals("")))
  96:       {
  97:         dump = false;
  98:         System.out.println ("Serialization debugging disabled");
  99:       }
 100:       }
 101: 
 102:     this.resolveEnabled = false;
 103:     this.blockDataPosition = 0;
 104:     this.blockDataBytes = 0;
 105:     this.blockData = new byte[BUFFER_SIZE];
 106:     this.blockDataInput = new DataInputStream(this);
 107:     this.realInputStream = new DataInputStream(in);
 108:     this.nextOID = baseWireHandle;
 109:     handles = new HashMap<Integer,Pair<Boolean,Object>>();
 110:     this.classLookupTable = new Hashtable<Class,ObjectStreamClass>();
 111:     setBlockDataMode(true);
 112:     readStreamHeader();
 113:   }
 114: 
 115: 
 116:   /**
 117:    * Returns the next deserialized object read from the underlying stream.
 118:    *
 119:    * This method can be overriden by a class by implementing
 120:    * <code>private void readObject (ObjectInputStream)</code>.
 121:    *
 122:    * If an exception is thrown from this method, the stream is left in
 123:    * an undefined state. This method can also throw Errors and 
 124:    * RuntimeExceptions if caused by existing readResolve() user code.
 125:    * 
 126:    * @return The object read from the underlying stream.
 127:    *
 128:    * @exception ClassNotFoundException The class that an object being
 129:    * read in belongs to cannot be found.
 130:    *
 131:    * @exception IOException Exception from underlying
 132:    * <code>InputStream</code>.
 133:    */
 134:   public final Object readObject()
 135:     throws ClassNotFoundException, IOException
 136:   {
 137:     return readObject(true);
 138:   }
 139: 
 140:   /**
 141:    * <p>
 142:    * Returns the next deserialized object read from the
 143:    * underlying stream in an unshared manner.  Any object
 144:    * returned by this method will not be returned by
 145:    * subsequent calls to either this method or {@link #readObject()}.
 146:    * </p>
 147:    * <p>
 148:    * This behaviour is achieved by:
 149:    * </p>
 150:    * <ul>
 151:    * <li>Marking the handles created by successful calls to this
 152:    * method, so that future calls to {@link #readObject()} or
 153:    * {@link #readUnshared()} will throw an {@link ObjectStreamException}
 154:    * rather than returning the same object reference.</li>
 155:    * <li>Throwing an {@link ObjectStreamException} if the next
 156:    * element in the stream is a reference to an earlier object.</li>
 157:    * </ul>
 158:    *
 159:    * @return a reference to the deserialized object.
 160:    * @throws ClassNotFoundException if the class of the object being
 161:    *                                deserialized can not be found.
 162:    * @throws StreamCorruptedException if information in the stream
 163:    *                                  is inconsistent.
 164:    * @throws ObjectStreamException if the next object has already been
 165:    *                               returned by an earlier call to this
 166:    *                               method or {@link #readObject()}.
 167:    * @throws OptionalDataException if primitive data occurs next in the stream.
 168:    * @throws IOException if an I/O error occurs from the stream.
 169:    * @since 1.4
 170:    * @see #readObject()
 171:    */
 172:   public Object readUnshared()
 173:     throws IOException, ClassNotFoundException
 174:   {
 175:     return readObject(false);
 176:   }
 177: 
 178:   /**
 179:    * Returns the next deserialized object read from the underlying stream.
 180:    *
 181:    * This method can be overriden by a class by implementing
 182:    * <code>private void readObject (ObjectInputStream)</code>.
 183:    *
 184:    * If an exception is thrown from this method, the stream is left in
 185:    * an undefined state. This method can also throw Errors and 
 186:    * RuntimeExceptions if caused by existing readResolve() user code.
 187:    * 
 188:    * @param shared true if handles created by this call should be shared
 189:    *               with later calls.
 190:    * @return The object read from the underlying stream.
 191:    *
 192:    * @exception ClassNotFoundException The class that an object being
 193:    * read in belongs to cannot be found.
 194:    *
 195:    * @exception IOException Exception from underlying
 196:    * <code>InputStream</code>.
 197:    */
 198:   private final Object readObject(boolean shared)
 199:     throws ClassNotFoundException, IOException
 200:   {
 201:     if (this.useSubclassMethod)
 202:       return readObjectOverride();
 203: 
 204:     Object ret_val;
 205:     boolean old_mode = setBlockDataMode(false);
 206:     byte marker = this.realInputStream.readByte();
 207: 
 208:     if (DEBUG)
 209:       depth += 2;
 210: 
 211:     if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " ");
 212: 
 213:     try
 214:       {
 215:      ret_val = parseContent(marker, shared);
 216:       }
 217:     finally
 218:       {
 219:      setBlockDataMode(old_mode);
 220:      if (DEBUG)
 221:       depth -= 2;
 222:       }
 223:     
 224:     return ret_val;
 225:   }
 226: 
 227:    /**
 228:     * Handles a content block within the stream, which begins with a marker
 229:     * byte indicating its type.
 230:     *
 231:     * @param marker the byte marker.
 232:     * @param shared true if handles created by this call should be shared
 233:     *               with later calls.
 234:     * @return an object which represents the parsed content.
 235:     * @throws ClassNotFoundException if the class of an object being
 236:     *                                read in cannot be found.
 237:     * @throws IOException if invalid data occurs or one is thrown by the
 238:     *                     underlying <code>InputStream</code>.
 239:     */
 240:    private Object parseContent(byte marker, boolean shared)
 241:      throws ClassNotFoundException, IOException
 242:    {
 243:      Object ret_val;
 244:      boolean is_consumed = false;
 245: 
 246:      switch (marker)
 247:        {
 248:        case TC_ENDBLOCKDATA:
 249:      {
 250:        ret_val = null;
 251:        is_consumed = true;
 252:        break;
 253:      }
 254:      
 255:        case TC_BLOCKDATA:
 256:        case TC_BLOCKDATALONG:
 257:      {
 258:        if (marker == TC_BLOCKDATALONG)
 259:          { if(dump) dumpElementln("BLOCKDATALONG"); }
 260:        else
 261:          { if(dump) dumpElementln("BLOCKDATA"); }
 262:        readNextBlock(marker);
 263:      }
 264:      
 265:        case TC_NULL:
 266:      {
 267:        if(dump) dumpElementln("NULL");
 268:        ret_val = null;
 269:        break;
 270:      }
 271:      
 272:        case TC_REFERENCE:
 273:      {
 274:        if(dump) dumpElement("REFERENCE ");
 275:        int oid = realInputStream.readInt();
 276:        if(dump) dumpElementln(Integer.toHexString(oid));
 277:        ret_val = lookupHandle(oid);
 278:       if (!shared)
 279:         throw new
 280:           InvalidObjectException("References can not be read unshared.");
 281:        break;
 282:      }
 283:      
 284:        case TC_CLASS:
 285:      {
 286:        if(dump) dumpElementln("CLASS");
 287:        ObjectStreamClass osc = (ObjectStreamClass)readObject();
 288:        Class clazz = osc.forClass();
 289:        assignNewHandle(clazz,shared);
 290:        ret_val = clazz;
 291:        break;
 292:      }
 293:      
 294:        case TC_PROXYCLASSDESC:
 295:      {
 296:        if(dump) dumpElementln("PROXYCLASS");
 297:        int n_intf = this.realInputStream.readInt();
 298:        String[] intfs = new String[n_intf];
 299:        for (int i = 0; i < n_intf; i++)
 300:          {
 301:            intfs[i] = this.realInputStream.readUTF();
 302:          }
 303:        
 304:        boolean oldmode = setBlockDataMode(true);
 305:        Class cl = resolveProxyClass(intfs);
 306:        setBlockDataMode(oldmode);
 307:        
 308:        ObjectStreamClass osc = lookupClass(cl);
 309:           if (osc.firstNonSerializableParentConstructor == null)
 310:             {
 311:               osc.realClassIsSerializable = true;
 312:               osc.fields = osc.fieldMapping = new ObjectStreamField[0];
 313:               try
 314:                 {
 315:                   osc.firstNonSerializableParentConstructor =
 316:                     Object.class.getConstructor(new Class[0]);
 317:                 }
 318:               catch (NoSuchMethodException x)
 319:                 {
 320:                   throw (InternalError)
 321:                     new InternalError("Object ctor missing").initCause(x);
 322:                 }
 323:             }
 324:        assignNewHandle(osc,shared);
 325:        
 326:        if (!is_consumed)
 327:          {
 328:            byte b = this.realInputStream.readByte();
 329:            if (b != TC_ENDBLOCKDATA)
 330:          throw new IOException("Data annotated to class was not consumed." + b);
 331:          }
 332:        else
 333:          is_consumed = false;
 334:        ObjectStreamClass superosc = (ObjectStreamClass)readObject();
 335:        osc.setSuperclass(superosc);
 336:        ret_val = osc;
 337:        break;
 338:      }
 339:      
 340:        case TC_CLASSDESC:
 341:      {
 342:        ObjectStreamClass osc = readClassDescriptor();
 343:        
 344:        if (!is_consumed)
 345:          {
 346:            byte b = this.realInputStream.readByte();
 347:            if (b != TC_ENDBLOCKDATA)
 348:          throw new IOException("Data annotated to class was not consumed." + b);
 349:          }
 350:        else
 351:          is_consumed = false;
 352:        
 353:        osc.setSuperclass ((ObjectStreamClass)readObject());
 354:        ret_val = osc;
 355:        break;
 356:      }
 357:      
 358:        case TC_STRING:
 359:      {
 360:        if(dump) dumpElement("STRING=");
 361:        String s = this.realInputStream.readUTF();
 362:        if(dump) dumpElementln(s);
 363:        ret_val = processResolution(null, s, assignNewHandle(s,shared),
 364:                       shared);
 365:        break;
 366:      }
 367:  
 368:        case TC_LONGSTRING:
 369:      {
 370:        if(dump) dumpElement("STRING=");
 371:        String s = this.realInputStream.readUTFLong();
 372:        if(dump) dumpElementln(s);
 373:        ret_val = processResolution(null, s, assignNewHandle(s,shared),
 374:                       shared);
 375:        break;
 376:      }
 377: 
 378:        case TC_ARRAY:
 379:      {
 380:        if(dump) dumpElementln("ARRAY");
 381:        ObjectStreamClass osc = (ObjectStreamClass)readObject();
 382:        Class componentType = osc.forClass().getComponentType();
 383:        if(dump) dumpElement("ARRAY LENGTH=");
 384:        int length = this.realInputStream.readInt();
 385:        if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType);
 386:        Object array = Array.newInstance(componentType, length);
 387:        int handle = assignNewHandle(array,shared);
 388:        readArrayElements(array, componentType);
 389:        if(dump)
 390:          for (int i = 0, len = Array.getLength(array); i < len; i++)
 391:            dumpElementln("  ELEMENT[" + i + "]=", Array.get(array, i));
 392:        ret_val = processResolution(null, array, handle, shared);
 393:        break;
 394:      }
 395:      
 396:        case TC_OBJECT:
 397:      {
 398:        if(dump) dumpElementln("OBJECT");
 399:        ObjectStreamClass osc = (ObjectStreamClass)readObject();
 400:        Class clazz = osc.forClass();
 401:        
 402:        if (!osc.realClassIsSerializable)
 403:          throw new NotSerializableException
 404:            (clazz + " is not Serializable, and thus cannot be deserialized.");
 405:        
 406:        if (osc.realClassIsExternalizable)
 407:         {
 408:            Externalizable obj = osc.newInstance();
 409:           
 410:            int handle = assignNewHandle(obj,shared);
 411:           
 412:            boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0);
 413:           
 414:            boolean oldmode = this.readDataFromBlock;
 415:            if (read_from_blocks)
 416:          setBlockDataMode(true);
 417:           
 418:            obj.readExternal(this);
 419:            
 420:            if (read_from_blocks)
 421:                 {
 422:            setBlockDataMode(oldmode);
 423:            if (!oldmode)
 424:              if (this.realInputStream.readByte() != TC_ENDBLOCKDATA)
 425:                throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method.");
 426:         }
 427: 
 428:            ret_val = processResolution(osc, obj, handle,shared);
 429:               break;
 430:           
 431:          } // end if (osc.realClassIsExternalizable)
 432:        
 433:        Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor);
 434:        
 435:        int handle = assignNewHandle(obj,shared);
 436:        Object prevObject = this.currentObject;
 437:        ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
 438:       TreeSet<ValidatorAndPriority> prevObjectValidators =
 439:         this.currentObjectValidators;
 440:        
 441:        this.currentObject = obj;
 442:       this.currentObjectValidators = null;
 443:        ObjectStreamClass[] hierarchy = hierarchy(clazz);
 444:        
 445:        for (int i = 0; i < hierarchy.length; i++)      
 446:           {
 447:            this.currentObjectStreamClass = hierarchy[i];
 448:            if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ());
 449:             
 450:            // XXX: should initialize fields in classes in the hierarchy
 451:            // that aren't in the stream
 452:            // should skip over classes in the stream that aren't in the
 453:            // real classes hierarchy
 454:             
 455:            Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod;
 456:            if (readObjectMethod != null)
 457:          {
 458:            fieldsAlreadyRead = false;
 459:            boolean oldmode = setBlockDataMode(true);
 460:            callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj);
 461:            setBlockDataMode(oldmode);
 462:          }
 463:            else
 464:          {
 465:            readFields(obj, currentObjectStreamClass);
 466:          }
 467:             
 468:            if (this.currentObjectStreamClass.hasWriteMethod())
 469:           {
 470:            if(dump) dumpElement("ENDBLOCKDATA? ");
 471:            try
 472:               {
 473:                /* Read blocks until an end marker */
 474:                byte writeMarker = this.realInputStream.readByte();
 475:                while (writeMarker != TC_ENDBLOCKDATA)
 476:             {    
 477:                parseContent(writeMarker, shared);
 478:                writeMarker = this.realInputStream.readByte();
 479:             }
 480:                if(dump) dumpElementln("yes");
 481:              }
 482:            catch (EOFException e)
 483:              {
 484:                throw (IOException) new IOException
 485:              ("No end of block data seen for class with readObject (ObjectInputStream) method.").initCause(e);
 486:             }
 487:         }
 488:         }
 489:        
 490:        this.currentObject = prevObject;
 491:        this.currentObjectStreamClass = prevObjectStreamClass;
 492:        ret_val = processResolution(osc, obj, handle, shared);
 493:       if (currentObjectValidators != null)
 494:         invokeValidators();
 495:       this.currentObjectValidators = prevObjectValidators;
 496: 
 497:        break;
 498:      }
 499:     
 500:        case TC_RESET:
 501:      if(dump) dumpElementln("RESET");
 502:      clearHandles();
 503:      ret_val = readObject();
 504:      break;
 505:     
 506:        case TC_EXCEPTION:
 507:      {
 508:        if(dump) dumpElement("EXCEPTION=");
 509:        Exception e = (Exception)readObject();
 510:        if(dump) dumpElementln(e.toString());
 511:        clearHandles();
 512:        throw new WriteAbortedException("Exception thrown during writing of stream", e);
 513:      }
 514: 
 515:        case TC_ENUM:
 516:      {
 517:        /* TC_ENUM classDesc newHandle enumConstantName */
 518:        if (dump)
 519:          dumpElementln("ENUM=");
 520:        ObjectStreamClass osc = (ObjectStreamClass) readObject();
 521:        String constantName = (String) readObject();
 522:        if (dump)
 523:          dumpElementln("CONSTANT NAME = " + constantName);
 524:        Class clazz = osc.forClass();
 525:        Enum instance = Enum.valueOf(clazz, constantName);
 526:        assignNewHandle(instance,shared);
 527:        ret_val = instance;
 528:        break;
 529:      }
 530: 
 531:        default:
 532:      throw new IOException("Unknown marker on stream: " + marker);
 533:       }
 534:     return ret_val;
 535:   }
 536: 
 537:   /**
 538:    * This method makes a partial check of types for the fields
 539:    * contained given in arguments. It checks primitive types of
 540:    * fields1 against non primitive types of fields2. This method 
 541:    * assumes the two lists has already been sorted according to 
 542:    * the Java specification.
 543:    *
 544:    * @param name Name of the class owning the given fields.
 545:    * @param fields1 First list to check.
 546:    * @param fields2 Second list to check.
 547:    * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present
 548:    * in the non primitive part in fields2.
 549:    */
 550:   private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2)
 551:     throws InvalidClassException
 552:   {
 553:     int nonPrimitive = 0;
 554:     
 555:     for (nonPrimitive = 0; 
 556:      nonPrimitive < fields1.length
 557:        && fields1[nonPrimitive].isPrimitive(); nonPrimitive++)
 558:       {
 559:       }
 560: 
 561:     if (nonPrimitive == fields1.length)
 562:       return;
 563:     
 564:     int i = 0;
 565:     ObjectStreamField f1;
 566:     ObjectStreamField f2;
 567:     
 568:     while (i < fields2.length
 569:        && nonPrimitive < fields1.length)
 570:       {
 571:     f1 = fields1[nonPrimitive];
 572:     f2 = fields2[i];
 573:     
 574:     if (!f2.isPrimitive())
 575:       break;
 576: 
 577:     int compVal = f1.getName().compareTo (f2.getName());
 578: 
 579:     if (compVal < 0)
 580:       {
 581:         nonPrimitive++;
 582:       }
 583:     else if (compVal > 0)
 584:       {
 585:         i++;
 586:       }
 587:     else
 588:       {
 589:         throw new InvalidClassException
 590:           ("invalid field type for " + f2.getName() +
 591:            " in class " + name);
 592:       }
 593:       }
 594:   }
 595: 
 596:   /**
 597:    * This method reads a class descriptor from the real input stream
 598:    * and use these data to create a new instance of ObjectStreamClass.
 599:    * Fields are sorted and ordered for the real read which occurs for
 600:    * each instance of the described class. Be aware that if you call that
 601:    * method you must ensure that the stream is synchronized, in the other
 602:    * case it may be completely desynchronized.
 603:    *
 604:    * @return A new instance of ObjectStreamClass containing the freshly
 605:    * created descriptor.
 606:    * @throws ClassNotFoundException if the required class to build the
 607:    * descriptor has not been found in the system.
 608:    * @throws IOException An input/output error occured.
 609:    * @throws InvalidClassException If there was a compatibility problem
 610:    * between the class present in the system and the serialized class.
 611:    */
 612:   protected ObjectStreamClass readClassDescriptor()
 613:     throws ClassNotFoundException, IOException
 614:   {
 615:     if(dump) dumpElement("CLASSDESC NAME=");
 616:     String name = this.realInputStream.readUTF();
 617:     if(dump) dumpElement(name + "; UID=");
 618:     long uid = this.realInputStream.readLong ();
 619:     if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS=");
 620:     byte flags = this.realInputStream.readByte ();
 621:     if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT=");
 622:     short field_count = this.realInputStream.readShort();
 623:     if(dump) dumpElementln(Short.toString(field_count));
 624:     ObjectStreamField[] fields = new ObjectStreamField[field_count];
 625:     ObjectStreamClass osc = new ObjectStreamClass(name, uid,
 626:                           flags, fields);
 627:     assignNewHandle(osc,true);
 628: 
 629:     for (int i = 0; i < field_count; i++)
 630:       {
 631:     if(dump) dumpElement("  TYPE CODE=");
 632:     char type_code = (char)this.realInputStream.readByte();
 633:     if(dump) dumpElement(type_code + "; FIELD NAME=");
 634:     String field_name = this.realInputStream.readUTF();
 635:     if(dump) dumpElementln(field_name);
 636:     String class_name;
 637:           
 638:     // If the type code is an array or an object we must
 639:     // decode a String here. In the other case we convert
 640:     // the type code and pass it to ObjectStreamField.
 641:     // Type codes are decoded by gnu.java.lang.reflect.TypeSignature.
 642:     if (type_code == 'L' || type_code == '[')
 643:       class_name = (String)readObject();
 644:     else
 645:       class_name = String.valueOf(type_code);
 646:           
 647:     fields[i] =
 648:       new ObjectStreamField(field_name, class_name);
 649:       }
 650:           
 651:     /* Now that fields have been read we may resolve the class
 652:      * (and read annotation if needed). */
 653:     Class clazz = resolveClass(osc);
 654:     ClassLoader loader = clazz.getClassLoader();
 655:     for (int i = 0; i < field_count; i++)
 656:       {
 657:         fields[i].resolveType(loader);
 658:       }
 659:     boolean oldmode = setBlockDataMode(true);
 660:     osc.setClass(clazz, lookupClass(clazz.getSuperclass()));
 661:     classLookupTable.put(clazz, osc);
 662:     setBlockDataMode(oldmode);
 663: 
 664:     // find the first non-serializable class in clazz's inheritance hierarchy
 665:     Class first_nonserial = clazz.getSuperclass();
 666:     // Maybe it is a primitive class, those don't have a super class,
 667:     // or Object itself.  Otherwise we can keep getting the superclass
 668:     // till we hit the Object class, or some other non-serializable class.
 669: 
 670:     if (first_nonserial == null)
 671:       first_nonserial = clazz;
 672:     else
 673:       while (Serializable.class.isAssignableFrom(first_nonserial))
 674:         first_nonserial = first_nonserial.getSuperclass();
 675: 
 676:     final Class local_constructor_class = first_nonserial;
 677: 
 678:     osc.firstNonSerializableParentConstructor =
 679:         (Constructor)AccessController.doPrivileged(new PrivilegedAction()
 680:           {
 681:             public Object run()
 682:             {
 683:               try
 684:                 {
 685:                   Constructor c = local_constructor_class.
 686:                                     getDeclaredConstructor(new Class[0]);
 687:                   if (Modifier.isPrivate(c.getModifiers()))
 688:                     return null;
 689:                   return c;
 690:                 }
 691:               catch (NoSuchMethodException e)
 692:                 {
 693:                   // error will be reported later, in newObject()
 694:                   return null;
 695:                 }
 696:             }
 697:           });
 698: 
 699:     osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz);
 700:     osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz);
 701: 
 702:     ObjectStreamField[] stream_fields = osc.fields;
 703:     ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields;
 704:     ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)];
 705: 
 706:     int stream_idx = 0;
 707:     int real_idx = 0;
 708:     int map_idx = 0;
 709: 
 710:     /*
 711:      * Check that there is no type inconsistencies between the lists.
 712:      * A special checking must be done for the two groups: primitive types and
 713:      * not primitive types. 
 714:      */
 715:     checkTypeConsistency(name, real_fields, stream_fields);
 716:     checkTypeConsistency(name, stream_fields, real_fields);
 717: 
 718:     
 719:     while (stream_idx < stream_fields.length
 720:        || real_idx < real_fields.length)
 721:       {
 722:     ObjectStreamField stream_field = null;
 723:     ObjectStreamField real_field = null;
 724: 
 725:     if (stream_idx == stream_fields.length)
 726:       {
 727:         real_field = real_fields[real_idx++];
 728:       }
 729:     else if (real_idx == real_fields.length)
 730:       {
 731:         stream_field = stream_fields[stream_idx++];
 732:       }
 733:     else
 734:       {
 735:         int comp_val =
 736:           real_fields[real_idx].compareTo (stream_fields[stream_idx]);
 737: 
 738:         if (comp_val < 0)
 739:           {
 740:         real_field = real_fields[real_idx++];
 741:           }
 742:         else if (comp_val > 0)
 743:           {
 744:         stream_field = stream_fields[stream_idx++];
 745:           }
 746:         else
 747:           {
 748:         stream_field = stream_fields[stream_idx++];
 749:         real_field = real_fields[real_idx++];
 750:         if (stream_field.getType() != real_field.getType())
 751:           throw new InvalidClassException
 752:             ("invalid field type for " + real_field.getName() +
 753:              " in class " + name);
 754:           }
 755:       }
 756: 
 757:     /* If some of stream_fields does not correspond to any of real_fields,
 758:      * or the opposite, then fieldmapping will go short.
 759:      */
 760:     if (map_idx == fieldmapping.length)
 761:       {
 762:         ObjectStreamField[] newfieldmapping =
 763:           new ObjectStreamField[fieldmapping.length + 2];
 764:         System.arraycopy(fieldmapping, 0,
 765:                  newfieldmapping, 0, fieldmapping.length);
 766:         fieldmapping = newfieldmapping;
 767:       }
 768:     fieldmapping[map_idx++] = stream_field;
 769:     fieldmapping[map_idx++] = real_field;
 770:       }
 771:     osc.fieldMapping = fieldmapping;
 772: 
 773:     return osc;
 774:   }
 775: 
 776:   /**
 777:    * Reads the current objects non-transient, non-static fields from
 778:    * the current class from the underlying output stream.
 779:    *
 780:    * This method is intended to be called from within a object's
 781:    * <code>private void readObject (ObjectInputStream)</code>
 782:    * method.
 783:    *
 784:    * @exception ClassNotFoundException The class that an object being
 785:    * read in belongs to cannot be found.
 786:    *
 787:    * @exception NotActiveException This method was called from a
 788:    * context other than from the current object's and current class's
 789:    * <code>private void readObject (ObjectInputStream)</code>
 790:    * method.
 791:    *
 792:    * @exception IOException Exception from underlying
 793:    * <code>OutputStream</code>.
 794:    */
 795:   public void defaultReadObject()
 796:     throws ClassNotFoundException, IOException, NotActiveException
 797:   {
 798:     if (this.currentObject == null || this.currentObjectStreamClass == null)
 799:       throw new NotActiveException("defaultReadObject called by non-active"
 800:                    + " class and/or object");
 801: 
 802:     if (fieldsAlreadyRead)
 803:       throw new NotActiveException("defaultReadObject called but fields "
 804:                    + "already read from stream (by "
 805:                    + "defaultReadObject or readFields)");
 806: 
 807:     boolean oldmode = setBlockDataMode(false);
 808:     readFields(this.currentObject, this.currentObjectStreamClass);
 809:     setBlockDataMode(oldmode);
 810: 
 811:     fieldsAlreadyRead = true;
 812:   }
 813: 
 814: 
 815:   /**
 816:    * Registers a <code>ObjectInputValidation</code> to be carried out
 817:    * on the object graph currently being deserialized before it is
 818:    * returned to the original caller of <code>readObject ()</code>.
 819:    * The order of validation for multiple
 820:    * <code>ObjectInputValidation</code>s can be controled using
 821:    * <code>priority</code>.  Validators with higher priorities are
 822:    * called first.
 823:    *
 824:    * @see java.io.ObjectInputValidation
 825:    *
 826:    * @exception InvalidObjectException <code>validator</code> is
 827:    * <code>null</code>
 828:    *
 829:    * @exception NotActiveException an attempt was made to add a
 830:    * validator outside of the <code>readObject</code> method of the
 831:    * object currently being deserialized
 832:    */
 833:   public void registerValidation(ObjectInputValidation validator,
 834:                  int priority)
 835:     throws InvalidObjectException, NotActiveException
 836:   {
 837:     if (this.currentObject == null || this.currentObjectStreamClass == null)
 838:       throw new NotActiveException("registerValidation called by non-active "
 839:                    + "class and/or object");
 840: 
 841:     if (validator == null)
 842:       throw new InvalidObjectException("attempt to add a null "
 843:                        + "ObjectInputValidation object");
 844: 
 845:     if (currentObjectValidators == null)
 846:       currentObjectValidators = new TreeSet<ValidatorAndPriority>();
 847:     
 848:     currentObjectValidators.add(new ValidatorAndPriority(validator, priority));
 849:   }
 850: 
 851: 
 852:   /**
 853:    * Called when a class is being deserialized.  This is a hook to
 854:    * allow subclasses to read in information written by the
 855:    * <code>annotateClass (Class)</code> method of an
 856:    * <code>ObjectOutputStream</code>.
 857:    *
 858:    * This implementation looks up the active call stack for a
 859:    * <code>ClassLoader</code>; if a <code>ClassLoader</code> is found,
 860:    * it is used to load the class associated with <code>osc</code>,
 861:    * otherwise, the default system <code>ClassLoader</code> is used.
 862:    *
 863:    * @exception IOException Exception from underlying
 864:    * <code>OutputStream</code>.
 865:    *
 866:    * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class)
 867:    */
 868:   protected Class<?> resolveClass(ObjectStreamClass osc)
 869:     throws ClassNotFoundException, IOException
 870:   {
 871:     String name = osc.getName();
 872:     try
 873:       {
 874:         return Class.forName(name, true, currentLoader());
 875:       }
 876:     catch(ClassNotFoundException x)
 877:       {
 878:         if (name.equals("void"))
 879:           return Void.TYPE;
 880:         else if (name.equals("boolean"))
 881:           return Boolean.TYPE;
 882:         else if (name.equals("byte"))
 883:           return Byte.TYPE;
 884:         else if (name.equals("char"))
 885:           return Character.TYPE;
 886:         else if (name.equals("short"))
 887:           return Short.TYPE;
 888:         else if (name.equals("int"))
 889:           return Integer.TYPE;
 890:         else if (name.equals("long"))
 891:           return Long.TYPE;
 892:         else if (name.equals("float"))
 893:           return Float.TYPE;
 894:         else if (name.equals("double"))
 895:           return Double.TYPE;
 896:         else
 897:           throw x;
 898:       }
 899:   }
 900: 
 901:   /**
 902:    * Returns the most recent user defined ClassLoader on the execution stack
 903:    * or null if none is found.
 904:    */
 905:   private ClassLoader currentLoader()
 906:   {
 907:     return VMStackWalker.firstNonNullClassLoader();
 908:   }
 909: 
 910:   /**
 911:    * Lookup a class stored in the local hashtable. If it is not
 912:    * use the global lookup function in ObjectStreamClass to build
 913:    * the ObjectStreamClass. This method is requested according to
 914:    * the behaviour detected in the JDK by Kaffe's team.
 915:    *
 916:    * @param clazz Class to lookup in the hash table or for which
 917:    * we must build a descriptor.
 918:    * @return A valid instance of ObjectStreamClass corresponding
 919:    * to the specified class.
 920:    */
 921:   private ObjectStreamClass lookupClass(Class clazz)
 922:   {
 923:     if (clazz == null)
 924:       return null;
 925: 
 926:     ObjectStreamClass oclazz;
 927:     oclazz = classLookupTable.get(clazz);
 928:     if (oclazz == null)
 929:       return ObjectStreamClass.lookup(clazz);
 930:     else
 931:       return oclazz;
 932:   }
 933: 
 934:   /**
 935:    * Reconstruct class hierarchy the same way {@link
 936:    * java.io.ObjectStreamClass#hierarchy} does but using lookupClass
 937:    * instead of ObjectStreamClass.lookup.
 938:    *
 939:    * @param clazz This is the class for which we want the hierarchy.
 940:    *
 941:    * @return An array of valid {@link java.io.ObjectStreamClass} instances which
 942:    * represent the class hierarchy for clazz.
 943:    */
 944:   private ObjectStreamClass[] hierarchy(Class clazz)
 945:   { 
 946:     ObjectStreamClass osc = lookupClass(clazz);
 947: 
 948:     return osc == null ? new ObjectStreamClass[0] : osc.hierarchy(); 
 949:   }
 950: 
 951:   /**
 952:    * Allows subclasses to resolve objects that are read from the
 953:    * stream with other objects to be returned in their place.  This
 954:    * method is called the first time each object is encountered.
 955:    *
 956:    * This method must be enabled before it will be called in the
 957:    * serialization process.
 958:    *
 959:    * @exception IOException Exception from underlying
 960:    * <code>OutputStream</code>.
 961:    *
 962:    * @see #enableResolveObject(boolean)
 963:    */
 964:   protected Object resolveObject(Object obj) throws IOException
 965:   {
 966:     return obj;
 967:   }
 968: 
 969: 
 970:   protected Class<?> resolveProxyClass(String[] intfs)
 971:     throws IOException, ClassNotFoundException
 972:   {
 973:     ClassLoader cl = currentLoader();
 974:     
 975:     Class<?>[] clss = new Class<?>[intfs.length];
 976:     if(cl == null)
 977:       {
 978:     for (int i = 0; i < intfs.length; i++)
 979:       clss[i] = Class.forName(intfs[i]);
 980:     cl = ClassLoader.getSystemClassLoader();
 981:       }
 982:     else
 983:       for (int i = 0; i < intfs.length; i++)
 984:     clss[i] = Class.forName(intfs[i], false, cl);
 985:     try 
 986:       {
 987:     return Proxy.getProxyClass(cl, clss);
 988:       } 
 989:     catch (IllegalArgumentException e) 
 990:       {
 991:     throw new ClassNotFoundException(null, e);
 992:       }
 993:   }
 994:   
 995:   /**
 996:    * If <code>enable</code> is <code>true</code> and this object is
 997:    * trusted, then <code>resolveObject (Object)</code> will be called
 998:    * in subsequent calls to <code>readObject (Object)</code>.
 999:    * Otherwise, <code>resolveObject (Object)</code> will not be called.
1000:    *
1001:    * @exception SecurityException This class is not trusted.
1002:    */
1003:   protected boolean enableResolveObject (boolean enable)
1004:     throws SecurityException
1005:   {
1006:     if (enable)
1007:       {
1008:     SecurityManager sm = System.getSecurityManager();
1009:     if (sm != null)
1010:       sm.checkPermission(new SerializablePermission("enableSubstitution"));
1011:       }
1012: 
1013:     boolean old_val = this.resolveEnabled;
1014:     this.resolveEnabled = enable;
1015:     return old_val;
1016:   }
1017: 
1018:   /**
1019:    * Reads stream magic and stream version information from the
1020:    * underlying stream.
1021:    *
1022:    * @exception IOException Exception from underlying stream.
1023:    *
1024:    * @exception StreamCorruptedException An invalid stream magic
1025:    * number or stream version was read from the stream.
1026:    */
1027:   protected void readStreamHeader()
1028:     throws IOException, StreamCorruptedException
1029:   {
1030:     if(dump) dumpElement("STREAM MAGIC ");
1031:     if (this.realInputStream.readShort() != STREAM_MAGIC)
1032:       throw new StreamCorruptedException("Invalid stream magic number");
1033: 
1034:     if(dump) dumpElementln("STREAM VERSION ");
1035:     if (this.realInputStream.readShort() != STREAM_VERSION)
1036:       throw new StreamCorruptedException("Invalid stream version number");
1037:   }
1038: 
1039:   public int read() throws IOException
1040:   {
1041:     if (this.readDataFromBlock)
1042:       {
1043:     if (this.blockDataPosition >= this.blockDataBytes)
1044:       readNextBlock();
1045:     return (this.blockData[this.blockDataPosition++] & 0xff);
1046:       }
1047:     else
1048:       return this.realInputStream.read();
1049:   }
1050: 
1051:   public int read(byte[] data, int offset, int length) throws IOException
1052:   {
1053:     if (this.readDataFromBlock)
1054:       {
1055:         int remain = this.blockDataBytes - this.blockDataPosition;
1056:         if (remain == 0)
1057:           {
1058:             readNextBlock();
1059:             remain = this.blockDataBytes - this.blockDataPosition;
1060:           }
1061:         length = Math.min(length, remain);
1062:     System.arraycopy(this.blockData, this.blockDataPosition,
1063:              data, offset, length);
1064:     this.blockDataPosition += length;
1065: 
1066:     return length;
1067:       }
1068:     else
1069:       return this.realInputStream.read(data, offset, length);
1070:   }
1071: 
1072:   public int available() throws IOException
1073:   {
1074:     if (this.readDataFromBlock)
1075:       {
1076:     if (this.blockDataPosition >= this.blockDataBytes)
1077:       readNextBlock ();
1078: 
1079:     return this.blockDataBytes - this.blockDataPosition;
1080:       }
1081:     else
1082:       return this.realInputStream.available();
1083:   }
1084: 
1085:   public void close() throws IOException
1086:   {
1087:     this.realInputStream.close();
1088:   }
1089: 
1090:   public boolean readBoolean() throws IOException
1091:   {
1092:     boolean switchmode = true;
1093:     boolean oldmode = this.readDataFromBlock;
1094:     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1095:       switchmode = false;
1096:     if (switchmode)
1097:       oldmode = setBlockDataMode (true);
1098:     boolean value = this.dataInputStream.readBoolean ();
1099:     if (switchmode)
1100:       setBlockDataMode (oldmode);
1101:     return value;
1102:   }
1103: 
1104:   public byte readByte() throws IOException
1105:   {
1106:     boolean switchmode = true;
1107:     boolean oldmode = this.readDataFromBlock;
1108:     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1109:       switchmode = false;
1110:     if (switchmode)
1111:       oldmode = setBlockDataMode(true);
1112:     byte value = this.dataInputStream.readByte();
1113:     if (switchmode)
1114:       setBlockDataMode(oldmode);
1115:     return value;
1116:   }
1117: 
1118:   public int readUnsignedByte() throws IOException
1119:   {
1120:     boolean switchmode = true;
1121:     boolean oldmode = this.readDataFromBlock;
1122:     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1123:       switchmode = false;
1124:     if (switchmode)
1125:       oldmode = setBlockDataMode(true);
1126:     int value = this.dataInputStream.readUnsignedByte();
1127:     if (switchmode)
1128:       setBlockDataMode(oldmode);
1129:     return value;
1130:   }
1131: 
1132:   public short readShort() throws IOException
1133:   {
1134:     boolean switchmode = true;
1135:     boolean oldmode = this.readDataFromBlock;
1136:     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1137:       switchmode = false;
1138:     if (switchmode)
1139:       oldmode = setBlockDataMode(true);
1140:     short value = this.dataInputStream.readShort();
1141:     if (switchmode)
1142:       setBlockDataMode(oldmode);
1143:     return value;
1144:   }
1145: 
1146:   public int readUnsignedShort() throws IOException
1147:   {
1148:     boolean switchmode = true;
1149:     boolean oldmode = this.readDataFromBlock;
1150:     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1151:       switchmode = false;
1152:     if (switchmode)
1153:       oldmode = setBlockDataMode(true);
1154:     int value = this.dataInputStream.readUnsignedShort();
1155:     if (switchmode)
1156:       setBlockDataMode(oldmode);
1157:     return value;
1158:   }
1159: 
1160:   public char readChar() throws IOException
1161:   {
1162:     boolean switchmode = true;
1163:     boolean oldmode = this.readDataFromBlock;
1164:     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1165:       switchmode = false;
1166:     if (switchmode)
1167:       oldmode = setBlockDataMode(true);
1168:     char value = this.dataInputStream.readChar();
1169:     if (switchmode)
1170:       setBlockDataMode(oldmode);
1171:     return value;
1172:   }
1173: 
1174:   public int readInt() throws IOException
1175:   {
1176:     boolean switchmode = true;
1177:     boolean oldmode = this.readDataFromBlock;
1178:     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4)
1179:       switchmode = false;
1180:     if (switchmode)
1181:       oldmode = setBlockDataMode(true);
1182:     int value = this.dataInputStream.readInt();
1183:     if (switchmode)
1184:       setBlockDataMode(oldmode);
1185:     return value;
1186:   }
1187: 
1188:   public long readLong() throws IOException
1189:   {
1190:     boolean switchmode = true;
1191:     boolean oldmode = this.readDataFromBlock;
1192:     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8)
1193:       switchmode = false;
1194:     if (switchmode)
1195:       oldmode = setBlockDataMode(true);
1196:     long value = this.dataInputStream.readLong();
1197:     if (switchmode)
1198:       setBlockDataMode(oldmode);
1199:     return value;
1200:   }
1201: 
1202:   public float readFloat() throws IOException
1203:   {
1204:     boolean switchmode = true;
1205:     boolean oldmode = this.readDataFromBlock;
1206:     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4)
1207:       switchmode = false;
1208:     if (switchmode)
1209:       oldmode = setBlockDataMode(true);
1210:     float value = this.dataInputStream.readFloat();
1211:     if (switchmode)
1212:       setBlockDataMode(oldmode);
1213:     return value;
1214:   }
1215: 
1216:   public double readDouble() throws IOException
1217:   {
1218:     boolean switchmode = true;
1219:     boolean oldmode = this.readDataFromBlock;
1220:     if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8)
1221:       switchmode = false;
1222:     if (switchmode)
1223:       oldmode = setBlockDataMode(true);
1224:     double value = this.dataInputStream.readDouble();
1225:     if (switchmode)
1226:       setBlockDataMode(oldmode);
1227:     return value;
1228:   }
1229: 
1230:   public void readFully(byte data[]) throws IOException
1231:   {
1232:     this.dataInputStream.readFully(data);
1233:   }
1234: 
1235:   public void readFully(byte data[], int offset, int size)
1236:     throws IOException
1237:   {
1238:     this.dataInputStream.readFully(data, offset, size);
1239:   }
1240: 
1241:   public int skipBytes(int len) throws IOException
1242:   {
1243:     return this.dataInputStream.skipBytes(len);
1244:   }
1245: 
1246:   /**
1247:    * @deprecated
1248:    * @see java.io.DataInputStream#readLine ()
1249:    */
1250:   public String readLine() throws IOException
1251:   {
1252:     return this.dataInputStream.readLine();
1253:   }
1254: 
1255:   public String readUTF() throws IOException
1256:   {
1257:     return this.dataInputStream.readUTF();
1258:   }
1259: 
1260:   /**
1261:    * This class allows a class to specify exactly which fields should
1262:    * be read, and what values should be read for these fields.
1263:    *
1264:    * XXX: finish up comments
1265:    */
1266:   public abstract static class GetField
1267:   {
1268:     public abstract ObjectStreamClass getObjectStreamClass();
1269: 
1270:     public abstract boolean defaulted(String name)
1271:       throws IOException, IllegalArgumentException;
1272: 
1273:     public abstract boolean get(String name, boolean defvalue)
1274:       throws IOException, IllegalArgumentException;
1275: 
1276:     public abstract char get(String name, char defvalue)
1277:       throws IOException, IllegalArgumentException;
1278: 
1279:     public abstract byte get(String name, byte defvalue)
1280:       throws IOException, IllegalArgumentException;
1281: 
1282:     public abstract short get(String name, short defvalue)
1283:       throws IOException, IllegalArgumentException;
1284: 
1285:     public abstract int get(String name, int defvalue)
1286:       throws IOException, IllegalArgumentException;
1287: 
1288:     public abstract long get(String name, long defvalue)
1289:       throws IOException, IllegalArgumentException;
1290: 
1291:     public abstract float get(String name, float defvalue)
1292:       throws IOException, IllegalArgumentException;
1293: 
1294:     public abstract double get(String name, double defvalue)
1295:       throws IOException, IllegalArgumentException;
1296: 
1297:     public abstract Object get(String name, Object defvalue)
1298:       throws IOException, IllegalArgumentException;
1299:   }
1300: 
1301:   /**
1302:    * This method should be called by a method called 'readObject' in the
1303:    * deserializing class (if present). It cannot (and should not)be called
1304:    * outside of it. Its goal is to read all fields in the real input stream
1305:    * and keep them accessible through the {@link GetField} class. Calling
1306:    * this method will not alter the deserializing object.
1307:    *
1308:    * @return A valid freshly created 'GetField' instance to get access to
1309:    * the deserialized stream.
1310:    * @throws IOException An input/output exception occured. 
1311:    * @throws ClassNotFoundException 
1312:    * @throws NotActiveException
1313:    */
1314:   public GetField readFields()
1315:     throws IOException, ClassNotFoundException, NotActiveException
1316:   {
1317:     if (this.currentObject == null || this.currentObjectStreamClass == null)
1318:       throw new NotActiveException("readFields called by non-active class and/or object");
1319: 
1320:     if (prereadFields != null)
1321:       return prereadFields;
1322: 
1323:     if (fieldsAlreadyRead)
1324:       throw new NotActiveException("readFields called but fields already read from"
1325:                    + " stream (by defaultReadObject or readFields)");
1326: 
1327:     final ObjectStreamClass clazz = this.currentObjectStreamClass;
1328:     final byte[] prim_field_data = new byte[clazz.primFieldSize];
1329:     final Object[] objs = new Object[clazz.objectFieldCount];
1330: 
1331:     // Apparently Block data is not used with GetField as per
1332:     // empirical evidence against JDK 1.2.  Also see Mauve test
1333:     // java.io.ObjectInputOutput.Test.GetPutField.
1334:     boolean oldmode = setBlockDataMode(false);
1335:     readFully(prim_field_data);
1336:     for (int i = 0; i < objs.length; ++ i)
1337:       objs[i] = readObject();
1338:     setBlockDataMode(oldmode);
1339: 
1340:     prereadFields = new GetField()
1341:       {
1342:     public ObjectStreamClass getObjectStreamClass()
1343:     {
1344:       return clazz;
1345:     }
1346: 
1347:     public boolean defaulted(String name)
1348:       throws IOException, IllegalArgumentException
1349:     {
1350:       ObjectStreamField f = clazz.getField(name);
1351:       
1352:       /* First if we have a serialized field use the descriptor */
1353:       if (f != null)
1354:         {
1355:           /* It is in serialPersistentFields but setClass tells us
1356:            * it should not be set. This value is defaulted.
1357:            */
1358:           if (f.isPersistent() && !f.isToSet())
1359:         return true;
1360:           
1361:           return false;
1362:         }
1363: 
1364:       /* This is not a serialized field. There should be
1365:        * a default value only if the field really exists.
1366:        */
1367:       try
1368:         {
1369:           return (clazz.forClass().getDeclaredField (name) != null);
1370:         }
1371:       catch (NoSuchFieldException e)
1372:         {
1373:           throw new IllegalArgumentException(e);
1374:         }
1375:     }
1376: 
1377:     public boolean get(String name, boolean defvalue)
1378:       throws IOException, IllegalArgumentException
1379:     {
1380:       ObjectStreamField field = getField(name, Boolean.TYPE);
1381: 
1382:       if (field == null)
1383:         return defvalue;
1384: 
1385:       return prim_field_data[field.getOffset()] == 0 ? false : true;
1386:     }
1387: 
1388:     public char get(String name, char defvalue)
1389:       throws IOException, IllegalArgumentException
1390:     {
1391:       ObjectStreamField field = getField(name, Character.TYPE);
1392: 
1393:       if (field == null)
1394:         return defvalue;
1395: 
1396:       int off = field.getOffset();
1397: 
1398:       return (char)(((prim_field_data[off++] & 0xFF) << 8)
1399:             | (prim_field_data[off] & 0xFF));
1400:     }
1401: 
1402:     public byte get(String name, byte defvalue)
1403:       throws IOException, IllegalArgumentException
1404:     {
1405:       ObjectStreamField field = getField(name, Byte.TYPE);
1406: 
1407:       if (field == null)
1408:         return defvalue;
1409: 
1410:       return prim_field_data[field.getOffset()];
1411:     }
1412: 
1413:     public short get(String name, short defvalue)
1414:       throws IOException, IllegalArgumentException
1415:     {
1416:       ObjectStreamField field = getField(name, Short.TYPE);
1417: 
1418:       if (field == null)
1419:         return defvalue;
1420: 
1421:       int off = field.getOffset();
1422: 
1423:       return (short)(((prim_field_data[off++] & 0xFF) << 8)
1424:              | (prim_field_data[off] & 0xFF));
1425:     }
1426: 
1427:     public int get(String name, int defvalue)
1428:       throws IOException, IllegalArgumentException
1429:     {
1430:       ObjectStreamField field = getField(name, Integer.TYPE);
1431: 
1432:       if (field == null)
1433:         return defvalue;
1434: 
1435:       int off = field.getOffset();
1436: 
1437:       return ((prim_field_data[off++] & 0xFF) << 24)
1438:         | ((prim_field_data[off++] & 0xFF) << 16)
1439:         | ((prim_field_data[off++] & 0xFF) << 8)
1440:         | (prim_field_data[off] & 0xFF);
1441:     }
1442: 
1443:     public long get(String name, long defvalue)
1444:       throws IOException, IllegalArgumentException
1445:     {
1446:       ObjectStreamField field = getField(name, Long.TYPE);
1447: 
1448:       if (field == null)
1449:         return defvalue;
1450: 
1451:       int off = field.getOffset();
1452: 
1453:       return (long)(((prim_field_data[off++] & 0xFFL) << 56)
1454:             | ((prim_field_data[off++] & 0xFFL) << 48)
1455:             | ((prim_field_data[off++] & 0xFFL) << 40)
1456:             | ((prim_field_data[off++] & 0xFFL) << 32)
1457:             | ((prim_field_data[off++] & 0xFF) << 24)
1458:             | ((prim_field_data[off++] & 0xFF) << 16)
1459:             | ((prim_field_data[off++] & 0xFF) << 8)
1460:             | (prim_field_data[off] & 0xFF));
1461:     }
1462: 
1463:     public float get(String name, float defvalue)
1464:       throws IOException, IllegalArgumentException
1465:     {
1466:       ObjectStreamField field = getField(name, Float.TYPE);
1467: 
1468:       if (field == null)
1469:         return defvalue;
1470: 
1471:       int off = field.getOffset();
1472: 
1473:       return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24)
1474:                       | ((prim_field_data[off++] & 0xFF) << 16)
1475:                       | ((prim_field_data[off++] & 0xFF) << 8)
1476:                       | (prim_field_data[off] & 0xFF));
1477:     }
1478: 
1479:     public double get(String name, double defvalue)
1480:       throws IOException, IllegalArgumentException
1481:     {
1482:       ObjectStreamField field = getField(name, Double.TYPE);
1483: 
1484:       if (field == null)
1485:         return defvalue;
1486: 
1487:       int off = field.getOffset();
1488: 
1489:       return Double.longBitsToDouble
1490:         ( (long) (((prim_field_data[off++] & 0xFFL) << 56)
1491:               | ((prim_field_data[off++] & 0xFFL) << 48)
1492:               | ((prim_field_data[off++] & 0xFFL) << 40)
1493:               | ((prim_field_data[off++] & 0xFFL) << 32)
1494:               | ((prim_field_data[off++] & 0xFF) << 24)
1495:               | ((prim_field_data[off++] & 0xFF) << 16)
1496:               | ((prim_field_data[off++] & 0xFF) << 8)
1497:               | (prim_field_data[off] & 0xFF)));
1498:     }
1499: 
1500:     public Object get(String name, Object defvalue)
1501:       throws IOException, IllegalArgumentException
1502:     {
1503:       ObjectStreamField field =
1504:         getField(name, defvalue == null ? null : defvalue.getClass ());
1505: 
1506:       if (field == null)
1507:         return defvalue;
1508: 
1509:       return objs[field.getOffset()];
1510:     }
1511: 
1512:     private ObjectStreamField getField(String name, Class type)
1513:       throws IllegalArgumentException
1514:     {
1515:       ObjectStreamField field = clazz.getField(name);
1516:       boolean illegal = false;
1517: 
1518:           // XXX This code is horrible and needs to be rewritten!
1519:       try
1520:         {
1521:           try
1522:         {
1523:           Class field_type = field.getType();
1524:           
1525:           if (type == field_type ||
1526:               (type == null && !field_type.isPrimitive()))
1527:             {
1528:               /* See defaulted */
1529:               return field;
1530:             }
1531:      
1532:           illegal = true;
1533:           throw new IllegalArgumentException
1534:             ("Field requested is of type "
1535:              + field_type.getName()
1536:              + ", but requested type was "
1537:              + (type == null ?  "Object" : type.getName()));
1538:         }
1539:           catch (NullPointerException _)
1540:         {
1541:           /* Here we catch NullPointerException, because it may
1542:              only come from the call 'field.getType()'. If field
1543:              is null, we have to return null and classpath ethic
1544:              say we must try to avoid 'if (xxx == null)'.
1545:           */
1546:         }
1547:           catch (IllegalArgumentException e)
1548:         {
1549:           throw e;
1550:         }
1551:           
1552:           return null;
1553:         }
1554:       finally
1555:         {
1556:           /* If this is an unassigned field we should return
1557:            * the default value.
1558:            */
1559:           if (!illegal && field != null && !field.isToSet() && field.isPersistent())
1560:         return null;
1561: 
1562:           /* We do not want to modify transient fields. They should
1563:            * be left to 0.
1564:            */
1565:           try
1566:         {
1567:           Field f = clazz.forClass().getDeclaredField(name);
1568:           if (Modifier.isTransient(f.getModifiers()))
1569:             throw new IllegalArgumentException
1570:               ("no such field (non transient) " + name);
1571:           if (field == null && f.getType() != type)
1572:             throw new IllegalArgumentException
1573:               ("Invalid requested type for field " + name);
1574:         }
1575:           catch (NoSuchFieldException e)
1576:         {
1577:           if (field == null)
1578:             throw new IllegalArgumentException(e);
1579:         }
1580:            
1581:         }
1582:     }
1583:       };
1584: 
1585:     fieldsAlreadyRead = true;
1586:     return prereadFields;
1587:   }
1588: 
1589:   /**
1590:    * Protected constructor that allows subclasses to override
1591:    * deserialization.  This constructor should be called by subclasses
1592:    * that wish to override <code>readObject (Object)</code>.  This
1593:    * method does a security check <i>NOTE: currently not
1594:    * implemented</i>, then sets a flag that informs
1595:    * <code>readObject (Object)</code> to call the subclasses
1596:    * <code>readObjectOverride (Object)</code> method.
1597:    *
1598:    * @see #readObjectOverride()
1599:    */
1600:   protected ObjectInputStream()
1601:     throws IOException, SecurityException
1602:   {
1603:     SecurityManager sec_man = System.getSecurityManager();
1604:     if (sec_man != null)
1605:       sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
1606:     this.useSubclassMethod = true;
1607:   }
1608: 
1609:   /**
1610:    * This method allows subclasses to override the default
1611:    * de serialization mechanism provided by
1612:    * <code>ObjectInputStream</code>.  To make this method be used for
1613:    * writing objects, subclasses must invoke the 0-argument
1614:    * constructor on this class from their constructor.
1615:    *
1616:    * @see #ObjectInputStream()
1617:    */
1618:   protected Object readObjectOverride()
1619:     throws ClassNotFoundException, IOException, OptionalDataException
1620:   {
1621:     throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride");
1622:   }
1623: 
1624:   /**
1625:    * Assigns the next available handle to <code>obj</code>.
1626:    *
1627:    * @param obj The object for which we want a new handle.
1628:    * @param shared True if the handle should be shared
1629:    *               with later calls.
1630:    * @return A valid handle for the specified object.
1631:    */
1632:   private int assignNewHandle(Object obj, boolean shared)
1633:   {
1634:     int handle = this.nextOID;
1635:     this.nextOID = handle + 1;
1636:     rememberHandle(obj,shared,handle);
1637:     return handle;
1638:   }
1639: 
1640:   /**
1641:    * Remember the object associated with the given handle.
1642:    *
1643:    * @param obj an object
1644:    * @param shared true if the reference should be shared
1645:    *               with later calls.
1646:    * @param handle a handle, must be >= baseWireHandle
1647:    *
1648:    * @see #lookupHandle
1649:    */
1650:   private void rememberHandle(Object obj, boolean shared,
1651:                   int handle)
1652:   {
1653:     handles.put(handle, new Pair<Boolean,Object>(shared, obj));
1654:   }
1655:   
1656:   /**
1657:    * Look up the object associated with a given handle.
1658:    *
1659:    * @param handle a handle, must be >= baseWireHandle
1660:    * @return the object remembered for handle or null if none.
1661:    * @throws StreamCorruptedException if the handle is invalid.
1662:    * @throws InvalidObjectException if the reference is not shared.
1663:    * @see #rememberHandle
1664:    */
1665:   private Object lookupHandle(int handle)
1666:     throws ObjectStreamException
1667:   {
1668:     Pair<Boolean,Object> result = handles.get(handle);
1669:     if (result == null)
1670:       throw new StreamCorruptedException("The handle, " + 
1671:                      Integer.toHexString(handle) +
1672:                      ", is invalid.");
1673:     if (!result.getLeft())
1674:       throw new InvalidObjectException("The handle, " + 
1675:                        Integer.toHexString(handle) +
1676:                        ", is not shared.");
1677:     return result.getRight();
1678:   }
1679: 
1680:   private Object processResolution(ObjectStreamClass osc, Object obj, int handle,
1681:                    boolean shared)
1682:     throws IOException
1683:   {
1684:     if (osc != null && obj instanceof Serializable)
1685:       {
1686:     try
1687:       {
1688:         Method m = osc.readResolveMethod; 
1689:         if(m != null)
1690:         {
1691:         obj = m.invoke(obj, new Object[] {});
1692:         }
1693:       }
1694:     catch (IllegalAccessException ignore)
1695:       {
1696:       }
1697:     catch (InvocationTargetException exception)
1698:       {
1699:         Throwable cause = exception.getCause();
1700:         if (cause instanceof ObjectStreamException)
1701:           throw (ObjectStreamException) cause;
1702:         else if (cause instanceof RuntimeException)
1703:           throw (RuntimeException) cause;
1704:         else if (cause instanceof Error)
1705:           throw (Error) cause;
1706:       }
1707:       }
1708: 
1709:     if (this.resolveEnabled)
1710:       obj = resolveObject(obj);
1711: 
1712:     rememberHandle(obj, shared, handle);
1713:     if (!shared)
1714:       {
1715:     if (obj instanceof byte[])
1716:       return ((byte[]) obj).clone();
1717:     if (obj instanceof short[])
1718:       return ((short[]) obj).clone();
1719:     if (obj instanceof int[])
1720:       return ((int[]) obj).clone();
1721:     if (obj instanceof long[])
1722:       return ((long[]) obj).clone();
1723:     if (obj instanceof char[])
1724:       return ((char[]) obj).clone();
1725:     if (obj instanceof boolean[])
1726:       return ((boolean[]) obj).clone();
1727:     if (obj instanceof float[])
1728:       return ((float[]) obj).clone();
1729:     if (obj instanceof double[])
1730:       return ((double[]) obj).clone();
1731:     if (obj instanceof Object[])
1732:       return ((Object[]) obj).clone();
1733:       }
1734:     return obj;
1735:   }
1736: 
1737:   private void clearHandles()
1738:   {
1739:     handles.clear();
1740:     this.nextOID = baseWireHandle;
1741:   }
1742: 
1743:   private void readNextBlock() throws IOException
1744:   {
1745:     byte marker = this.realInputStream.readByte();
1746:     while (marker == TC_RESET)
1747:       {
1748:         if(dump) dumpElementln("RESET");
1749:         clearHandles();
1750:         marker = this.realInputStream.readByte();
1751:       }
1752:     readNextBlock(marker);
1753:   }
1754: 
1755:   private void readNextBlock(byte marker) throws IOException
1756:   {
1757:     if (marker == TC_BLOCKDATA)
1758:       {
1759:     if(dump) dumpElement("BLOCK DATA SIZE=");
1760:     this.blockDataBytes = this.realInputStream.readUnsignedByte();
1761:     if(dump) dumpElementln (Integer.toString(this.blockDataBytes));
1762:       }
1763:     else if (marker == TC_BLOCKDATALONG)
1764:       {
1765:     if(dump) dumpElement("BLOCK DATA LONG SIZE=");
1766:     this.blockDataBytes = this.realInputStream.readInt();
1767:     if(dump) dumpElementln (Integer.toString(this.blockDataBytes));
1768:       }
1769:     else
1770:       {
1771:     throw new EOFException("Attempt to read primitive data, but no data block is active.");
1772:       }
1773: 
1774:     if (this.blockData.length < this.blockDataBytes)
1775:       this.blockData = new byte[this.blockDataBytes];
1776: 
1777:     this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes);
1778:     this.blockDataPosition = 0;
1779:   }
1780: 
1781:   private void readArrayElements (Object array, Class clazz)
1782:     throws ClassNotFoundException, IOException
1783:   {
1784:     if (clazz.isPrimitive())
1785:       {
1786:     if (clazz == Boolean.TYPE)
1787:       {
1788:         boolean[] cast_array = (boolean[])array;
1789:         for (int i=0; i < cast_array.length; i++)
1790:           cast_array[i] = this.realInputStream.readBoolean();
1791:         return;
1792:       }
1793:     if (clazz == Byte.TYPE)
1794:       {
1795:         byte[] cast_array = (byte[])array;
1796:         for (int i=0; i < cast_array.length; i++)
1797:           cast_array[i] = this.realInputStream.readByte();
1798:         return;
1799:       }
1800:     if (clazz == Character.TYPE)
1801:       {
1802:         char[] cast_array = (char[])array;
1803:         for (int i=0; i < cast_array.length; i++)
1804:           cast_array[i] = this.realInputStream.readChar();
1805:         return;
1806:       }
1807:     if (clazz == Double.TYPE)
1808:       {
1809:         double[] cast_array = (double[])array;
1810:         for (int i=0; i < cast_array.length; i++)
1811:           cast_array[i] = this.realInputStream.readDouble();
1812:         return;
1813:       }
1814:     if (clazz == Float.TYPE)
1815:       {
1816:         float[] cast_array = (float[])array;
1817:         for (int i=0; i < cast_array.length; i++)
1818:           cast_array[i] = this.realInputStream.readFloat();
1819:         return;
1820:       }
1821:     if (clazz == Integer.TYPE)
1822:       {
1823:         int[] cast_array = (int[])array;
1824:         for (int i=0; i < cast_array.length; i++)
1825:           cast_array[i] = this.realInputStream.readInt();
1826:         return;
1827:       }
1828:     if (clazz == Long.TYPE)
1829:       {
1830:         long[] cast_array = (long[])array;
1831:         for (int i=0; i < cast_array.length; i++)
1832:           cast_array[i] = this.realInputStream.readLong();
1833:         return;
1834:       }
1835:     if (clazz == Short.TYPE)
1836:       {
1837:         short[] cast_array = (short[])array;
1838:         for (int i=0; i < cast_array.length; i++)
1839:           cast_array[i] = this.realInputStream.readShort();
1840:         return;
1841:       }
1842:       }
1843:     else
1844:       {
1845:     Object[] cast_array = (Object[])array;
1846:     for (int i=0; i < cast_array.length; i++)
1847:        cast_array[i] = readObject();
1848:       }
1849:   }
1850: 
1851:   private void readFields (Object obj, ObjectStreamClass stream_osc)
1852:     throws ClassNotFoundException, IOException
1853:   {
1854:     ObjectStreamField[] fields = stream_osc.fieldMapping;
1855: 
1856:     for (int i = 0; i < fields.length; i += 2)
1857:       {
1858:     ObjectStreamField stream_field = fields[i];
1859:     ObjectStreamField real_field = fields[i + 1];
1860:     boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet());
1861:     boolean set_value = (real_field != null && real_field.isToSet());
1862:     String field_name;
1863:     char type;
1864: 
1865:     if (stream_field != null)
1866:       {
1867:         field_name = stream_field.getName();
1868:         type = stream_field.getTypeCode();
1869:       }
1870:     else
1871:       {
1872:         field_name = real_field.getName();
1873:         type = real_field.getTypeCode();
1874:       }
1875:     
1876:     switch(type)
1877:       {
1878:       case 'Z':
1879:         {
1880:           boolean value =
1881:         read_value ? this.realInputStream.readBoolean() : false;
1882:           if (dump && read_value && set_value)
1883:         dumpElementln("  " + field_name + ": " + value);
1884:           if (set_value)
1885:         real_field.setBooleanField(obj, value);
1886:           break;
1887:         }
1888:       case 'B':
1889:         {
1890:           byte value =
1891:         read_value ? this.realInputStream.readByte() : 0;
1892:           if (dump && read_value && set_value)
1893:         dumpElementln("  " + field_name + ": " + value);
1894:           if (set_value)
1895:         real_field.setByteField(obj, value);
1896:           break;
1897:         }
1898:       case 'C':
1899:         {
1900:           char value =
1901:         read_value ? this.realInputStream.readChar(): 0;
1902:           if (dump && read_value && set_value)
1903:         dumpElementln("  " + field_name + ": " + value);
1904:           if (set_value)
1905:         real_field.setCharField(obj, value);
1906:           break;
1907:         }
1908:       case 'D':
1909:         {
1910:           double value =
1911:         read_value ? this.realInputStream.readDouble() : 0;
1912:           if (dump && read_value && set_value)
1913:         dumpElementln("  " + field_name + ": " + value);
1914:           if (set_value)
1915:         real_field.setDoubleField(obj, value);
1916:           break;
1917:         }
1918:       case 'F':
1919:         {
1920:           float value =
1921:         read_value ? this.realInputStream.readFloat() : 0;
1922:           if (dump && read_value && set_value)
1923:         dumpElementln("  " + field_name + ": " + value);
1924:           if (set_value)
1925:         real_field.setFloatField(obj, value);
1926:           break;
1927:         }
1928:       case 'I':
1929:         {
1930:           int value =
1931:         read_value ? this.realInputStream.readInt() : 0;
1932:           if (dump && read_value && set_value)
1933:         dumpElementln("  " + field_name + ": " + value);
1934:           if (set_value)
1935:         real_field.setIntField(obj, value);
1936:           break;
1937:         }
1938:       case 'J':
1939:         {
1940:           long value =
1941:         read_value ? this.realInputStream.readLong() : 0;
1942:           if (dump && read_value && set_value)
1943:         dumpElementln("  " + field_name + ": " + value);
1944:           if (set_value)
1945:         real_field.setLongField(obj, value);
1946:           break;
1947:         }
1948:       case 'S':
1949:         {
1950:           short value =
1951:         read_value ? this.realInputStream.readShort() : 0;
1952:           if (dump && read_value && set_value)
1953:         dumpElementln("  " + field_name + ": " + value);
1954:           if (set_value)
1955:         real_field.setShortField(obj, value);
1956:           break;
1957:         }
1958:       case 'L':
1959:       case '[':
1960:         {
1961:           Object value =
1962:         read_value ? readObject() : null;
1963:           if (set_value)
1964:         real_field.setObjectField(obj, value);
1965:           break;
1966:         }
1967:       default:
1968:         throw new InternalError("Invalid type code: " + type);
1969:       }
1970:       }
1971:   }
1972:   
1973:   // Toggles writing primitive data to block-data buffer.
1974:   private boolean setBlockDataMode (boolean on)
1975:   {
1976:     boolean oldmode = this.readDataFromBlock;
1977:     this.readDataFromBlock = on;
1978: 
1979:     if (on)
1980:       this.dataInputStream = this.blockDataInput;
1981:     else
1982:       this.dataInputStream = this.realInputStream;
1983:     return oldmode;
1984:   }
1985: 
1986:   // returns a new instance of REAL_CLASS that has been constructed
1987:   // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS)
1988:   private Object newObject (Class real_class, Constructor constructor)
1989:     throws ClassNotFoundException, IOException
1990:   {
1991:     if (constructor == null)
1992:         throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName()); 
1993:     try
1994:       {
1995:     return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor);
1996:       }
1997:     catch (InstantiationException e)
1998:       {
1999:         throw (ClassNotFoundException) new ClassNotFoundException
2000:           ("Instance of " + real_class + " could not be created").initCause(e);
2001:       }
2002:   }
2003: 
2004:   // runs all registered ObjectInputValidations in prioritized order
2005:   // on OBJ
2006:   private void invokeValidators() throws InvalidObjectException
2007:   {
2008:     try
2009:       {
2010:     Iterator<ValidatorAndPriority> it = currentObjectValidators.iterator();
2011:     while(it.hasNext())
2012:       {
2013:         ValidatorAndPriority vap = it.next();
2014:         ObjectInputValidation validator = vap.validator;
2015:         validator.validateObject();
2016:       }
2017:       }
2018:     finally
2019:       {
2020:     currentObjectValidators = null;
2021:       }
2022:   }
2023: 
2024:   private void callReadMethod (Method readObject, Class klass, Object obj)
2025:     throws ClassNotFoundException, IOException
2026:   {
2027:     try
2028:       {
2029:     readObject.invoke(obj, new Object[] { this });
2030:       }
2031:     catch (InvocationTargetException x)
2032:       {
2033:         /* Rethrow if possible. */
2034:     Throwable exception = x.getTargetException();
2035:     if (exception instanceof RuntimeException)
2036:       throw (RuntimeException) exception;
2037:     if (exception instanceof IOException)
2038:       throw (IOException) exception;
2039:         if (exception instanceof ClassNotFoundException)
2040:           throw (ClassNotFoundException) exception;
2041: 
2042:     throw (IOException) new IOException(
2043:       "Exception thrown from readObject() on " + klass).initCause(x);
2044:       }
2045:     catch (Exception x)
2046:       {
2047:     throw (IOException) new IOException(
2048:       "Failure invoking readObject() on " + klass).initCause(x);
2049:       }
2050: 
2051:     // Invalidate fields which has been read through readFields.
2052:     prereadFields = null;
2053:   }
2054:     
2055:   private static final int BUFFER_SIZE = 1024;
2056: 
2057:   private DataInputStream realInputStream;
2058:   private DataInputStream dataInputStream;
2059:   private DataInputStream blockDataInput;
2060:   private int blockDataPosition;
2061:   private int blockDataBytes;
2062:   private byte[] blockData;
2063:   private boolean useSubclassMethod;
2064:   private int nextOID;
2065:   private boolean resolveEnabled;
2066:   private Map<Integer,Pair<Boolean,Object>> handles;
2067:   private Object currentObject;
2068:   private ObjectStreamClass currentObjectStreamClass;
2069:   private TreeSet<ValidatorAndPriority> currentObjectValidators;
2070:   private boolean readDataFromBlock;
2071:   private boolean fieldsAlreadyRead;
2072:   private Hashtable<Class,ObjectStreamClass> classLookupTable;
2073:   private GetField prereadFields;
2074: 
2075:   private static boolean dump;
2076: 
2077:   // The nesting depth for debugging output
2078:   private int depth = 0;
2079: 
2080:   private static final boolean DEBUG = false;
2081: 
2082:   private void dumpElement (String msg)
2083:   {
2084:     System.out.print(msg);
2085:   }
2086:   
2087:   private void dumpElementln (String msg)
2088:   {
2089:     System.out.println(msg);
2090:     for (int i = 0; i < depth; i++)
2091:       System.out.print (" ");
2092:     System.out.print (Thread.currentThread() + ": ");
2093:   }
2094: 
2095:   private void dumpElementln (String msg, Object obj)
2096:   {
2097:     try
2098:       {
2099:     System.out.print(msg);
2100:     if (java.lang.reflect.Proxy.isProxyClass(obj.getClass()))
2101:       System.out.println(obj.getClass());
2102:     else
2103:     System.out.println(obj);
2104:       }
2105:     catch (Exception _)
2106:       {
2107:       }
2108:     for (int i = 0; i < depth; i++)
2109:       System.out.print (" ");
2110:     System.out.print (Thread.currentThread() + ": ");
2111:   }
2112: 
2113:   // used to keep a prioritized list of object validators
2114:   private static final class ValidatorAndPriority implements Comparable
2115:   {
2116:     int priority;
2117:     ObjectInputValidation validator;
2118: 
2119:     ValidatorAndPriority (ObjectInputValidation validator, int priority)
2120:     {
2121:       this.priority = priority;
2122:       this.validator = validator;
2123:     }
2124: 
2125:     public int compareTo (Object o)
2126:     {
2127:       ValidatorAndPriority vap = (ValidatorAndPriority)o;
2128:       return this.priority - vap.priority;
2129:     }
2130:   }
2131: }