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