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