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