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