Source for java.io.ObjectInputStream

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