Source for java.io.ObjectInputStream

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