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