Source for java.io.ObjectStreamClass

   1: /* ObjectStreamClass.java -- Class used to write class information
   2:    about serialized objects.
   3:    Copyright (C) 1998, 1999, 2000, 2001, 2003  Free Software Foundation, Inc.
   4: 
   5: This file is part of GNU Classpath.
   6: 
   7: GNU Classpath is free software; you can redistribute it and/or modify
   8: it under the terms of the GNU General Public License as published by
   9: the Free Software Foundation; either version 2, or (at your option)
  10: any later version.
  11:  
  12: GNU Classpath is distributed in the hope that it will be useful, but
  13: WITHOUT ANY WARRANTY; without even the implied warranty of
  14: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15: General Public License for more details.
  16: 
  17: You should have received a copy of the GNU General Public License
  18: along with GNU Classpath; see the file COPYING.  If not, write to the
  19: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  20: 02110-1301 USA.
  21: 
  22: Linking this library statically or dynamically with other modules is
  23: making a combined work based on this library.  Thus, the terms and
  24: conditions of the GNU General Public License cover the whole
  25: combination.
  26: 
  27: As a special exception, the copyright holders of this library give you
  28: permission to link this library with independent modules to produce an
  29: executable, regardless of the license terms of these independent
  30: modules, and to copy and distribute the resulting executable under
  31: terms of your choice, provided that you also meet, for each linked
  32: independent module, the terms and conditions of the license of that
  33: module.  An independent module is a module which is not derived from
  34: or based on this library.  If you modify this library, you may extend
  35: this exception to your version of the library, but you are not
  36: obligated to do so.  If you do not wish to do so, delete this
  37: exception statement from your version. */
  38: 
  39: 
  40: package java.io;
  41: 
  42: import gnu.java.io.NullOutputStream;
  43: import gnu.java.lang.reflect.TypeSignature;
  44: import gnu.java.security.action.SetAccessibleAction;
  45: import gnu.java.security.provider.Gnu;
  46: 
  47: import java.lang.reflect.Constructor;
  48: import java.lang.reflect.Field;
  49: import java.lang.reflect.Member;
  50: import java.lang.reflect.Method;
  51: import java.lang.reflect.Modifier;
  52: import java.lang.reflect.Proxy;
  53: import java.security.AccessController;
  54: import java.security.DigestOutputStream;
  55: import java.security.MessageDigest;
  56: import java.security.NoSuchAlgorithmException;
  57: import java.security.PrivilegedAction;
  58: import java.security.Security;
  59: import java.util.Arrays;
  60: import java.util.Comparator;
  61: import java.util.Hashtable;
  62: import java.util.Vector;
  63: 
  64: public class ObjectStreamClass implements Serializable
  65: {
  66:   static final ObjectStreamField[] INVALID_FIELDS = new ObjectStreamField[0];
  67: 
  68:   /**
  69:    * Returns the <code>ObjectStreamClass</code> for <code>cl</code>.
  70:    * If <code>cl</code> is null, or is not <code>Serializable</code>,
  71:    * null is returned.  <code>ObjectStreamClass</code>'s are memorized;
  72:    * later calls to this method with the same class will return the
  73:    * same <code>ObjectStreamClass</code> object and no recalculation
  74:    * will be done.
  75:    *
  76:    * Warning: If this class contains an invalid serialPersistentField arrays
  77:    * lookup will not throw anything. However {@link #getFields()} will return
  78:    * an empty array and {@link java.io.ObjectOutputStream#writeObject} will throw an 
  79:    * {@link java.io.InvalidClassException}.
  80:    *
  81:    * @see java.io.Serializable
  82:    */
  83:   public static ObjectStreamClass lookup(Class cl)
  84:   {
  85:     if (cl == null)
  86:       return null;
  87:     if (! (Serializable.class).isAssignableFrom(cl))
  88:       return null;
  89: 
  90:     return lookupForClassObject(cl);
  91:   }
  92: 
  93:   /**
  94:    * This lookup for internal use by ObjectOutputStream.  Suppose
  95:    * we have a java.lang.Class object C for class A, though A is not
  96:    * serializable, but it's okay to serialize C.
  97:    */
  98:   static ObjectStreamClass lookupForClassObject(Class cl)
  99:   {
 100:     if (cl == null)
 101:       return null;
 102: 
 103:     ObjectStreamClass osc = (ObjectStreamClass) classLookupTable.get(cl);
 104: 
 105:     if (osc != null)
 106:       return osc;
 107:     else
 108:       {
 109:     osc = new ObjectStreamClass(cl);
 110:     classLookupTable.put(cl, osc);
 111:     return osc;
 112:       }
 113:   }
 114: 
 115:   /**
 116:    * Returns the name of the class that this
 117:    * <code>ObjectStreamClass</code> represents.
 118:    *
 119:    * @return the name of the class.
 120:    */
 121:   public String getName()
 122:   {
 123:     return name;
 124:   }
 125: 
 126:   /**
 127:    * Returns the class that this <code>ObjectStreamClass</code>
 128:    * represents.  Null could be returned if this
 129:    * <code>ObjectStreamClass</code> was read from an
 130:    * <code>ObjectInputStream</code> and the class it represents cannot
 131:    * be found or loaded.
 132:    *
 133:    * @see java.io.ObjectInputStream
 134:    */
 135:   public Class forClass()
 136:   {
 137:     return clazz;
 138:   }
 139: 
 140:   /**
 141:    * Returns the serial version stream-unique identifier for the class
 142:    * represented by this <code>ObjectStreamClass</code>.  This SUID is
 143:    * either defined by the class as <code>static final long
 144:    * serialVersionUID</code> or is calculated as specified in
 145:    * Javasoft's "Object Serialization Specification" XXX: add reference
 146:    *
 147:    * @return the serial version UID.
 148:    */
 149:   public long getSerialVersionUID()
 150:   {
 151:     return uid;
 152:   }
 153: 
 154:   /**
 155:    * Returns the serializable (non-static and non-transient) Fields
 156:    * of the class represented by this ObjectStreamClass.  The Fields
 157:    * are sorted by name.
 158:    * If fields were obtained using serialPersistentFields and this array
 159:    * is faulty then the returned array of this method will be empty.
 160:    *
 161:    * @return the fields.
 162:    */
 163:   public ObjectStreamField[] getFields()
 164:   {
 165:     ObjectStreamField[] copy = new ObjectStreamField[ fields.length ];
 166:     System.arraycopy(fields, 0, copy, 0, fields.length);
 167:     return copy;
 168:   }
 169: 
 170:   // XXX doc
 171:   // Can't do binary search since fields is sorted by name and
 172:   // primitiveness.
 173:   public ObjectStreamField getField (String name)
 174:   {
 175:     for (int i = 0; i < fields.length; i++)
 176:       if (fields[i].getName().equals(name))
 177:     return fields[i];
 178:     return null;
 179:   }
 180: 
 181:   /**
 182:    * Returns a textual representation of this
 183:    * <code>ObjectStreamClass</code> object including the name of the
 184:    * class it represents as well as that class's serial version
 185:    * stream-unique identifier.
 186:    *
 187:    * @see #getSerialVersionUID()
 188:    * @see #getName()
 189:    */
 190:   public String toString()
 191:   {
 192:     return "java.io.ObjectStreamClass< " + name + ", " + uid + " >";
 193:   }
 194: 
 195:   // Returns true iff the class that this ObjectStreamClass represents
 196:   // has the following method:
 197:   //
 198:   // private void writeObject (ObjectOutputStream)
 199:   //
 200:   // This method is used by the class to override default
 201:   // serialization behavior.
 202:   boolean hasWriteMethod()
 203:   {
 204:     return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0;
 205:   }
 206: 
 207:   // Returns true iff the class that this ObjectStreamClass represents
 208:   // implements Serializable but does *not* implement Externalizable.
 209:   boolean isSerializable()
 210:   {
 211:     return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
 212:   }
 213: 
 214: 
 215:   // Returns true iff the class that this ObjectStreamClass represents
 216:   // implements Externalizable.
 217:   boolean isExternalizable()
 218:   {
 219:     return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
 220:   }
 221: 
 222:   // Returns true iff the class that this ObjectStreamClass represents
 223:   // implements Externalizable.
 224:   boolean isEnum()
 225:   {
 226:     return (flags & ObjectStreamConstants.SC_ENUM) != 0;
 227:   }
 228: 
 229:   // Returns the <code>ObjectStreamClass</code> that represents the
 230:   // class that is the superclass of the class this
 231:   // <code>ObjectStreamClass</code> represents.  If the superclass is
 232:   // not Serializable, null is returned.
 233:   ObjectStreamClass getSuper()
 234:   {
 235:     return superClass;
 236:   }
 237: 
 238: 
 239:   // returns an array of ObjectStreamClasses that represent the super
 240:   // classes of CLAZZ and CLAZZ itself in order from most super to
 241:   // CLAZZ.  ObjectStreamClass[0] is the highest superclass of CLAZZ
 242:   // that is serializable.
 243:   static ObjectStreamClass[] getObjectStreamClasses(Class clazz)
 244:   {
 245:     ObjectStreamClass osc = ObjectStreamClass.lookup(clazz);
 246: 
 247:     if (osc == null)
 248:       return new ObjectStreamClass[0];
 249:     else
 250:       {
 251:     Vector oscs = new Vector();
 252: 
 253:     while (osc != null)
 254:       {
 255:         oscs.addElement (osc);
 256:         osc = osc.getSuper();
 257:       }
 258: 
 259:     int count = oscs.size();
 260:     ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ];
 261: 
 262:     for (int i = count - 1; i >= 0; i--)
 263:       sorted_oscs[ count - i - 1 ] = (ObjectStreamClass) oscs.elementAt(i);
 264: 
 265:     return sorted_oscs;
 266:       }
 267:   }
 268: 
 269: 
 270:   // Returns an integer that consists of bit-flags that indicate
 271:   // properties of the class represented by this ObjectStreamClass.
 272:   // The bit-flags that could be present are those defined in
 273:   // ObjectStreamConstants that begin with `SC_'
 274:   int getFlags()
 275:   {
 276:     return flags;
 277:   }
 278: 
 279: 
 280:   ObjectStreamClass(String name, long uid, byte flags,
 281:             ObjectStreamField[] fields)
 282:   {
 283:     this.name = name;
 284:     this.uid = uid;
 285:     this.flags = flags;
 286:     this.fields = fields;
 287:   }
 288: 
 289:   /**
 290:    * This method builds the internal description corresponding to a Java Class.
 291:    * As the constructor only assign a name to the current ObjectStreamClass instance,
 292:    * that method sets the serial UID, chose the fields which will be serialized,
 293:    * and compute the position of the fields in the serialized stream.
 294:    *
 295:    * @param cl The Java class which is used as a reference for building the descriptor.
 296:    * @param superClass The descriptor of the super class for this class descriptor.
 297:    * @throws InvalidClassException if an incompatibility between computed UID and
 298:    * already set UID is found.
 299:    */
 300:   void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException
 301:   {
 302:     this.clazz = cl;
 303: 
 304:     cacheMethods();
 305: 
 306:     long class_uid = getClassUID(cl);
 307:     if (uid == 0)
 308:       uid = class_uid;
 309:     else
 310:       {
 311:     // Check that the actual UID of the resolved class matches the UID from 
 312:     // the stream.    
 313:     if (uid != class_uid)
 314:       {
 315:         String msg = cl + 
 316:           ": Local class not compatible: stream serialVersionUID="
 317:           + uid + ", local serialVersionUID=" + class_uid;
 318:         throw new InvalidClassException (msg);
 319:       }
 320:       }
 321: 
 322:     isProxyClass = clazz != null && Proxy.isProxyClass(clazz);
 323:     this.superClass = superClass;
 324:     calculateOffsets();
 325:     
 326:     try
 327:       {
 328:     ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz);  
 329: 
 330:     if (exportedFields == null)
 331:       return;
 332: 
 333:     ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length];
 334:     int i, j, k;
 335: 
 336:     /* We now check the import fields against the exported fields.
 337:      * There should not be contradiction (e.g. int x and String x)
 338:      * but extra virtual fields can be added to the class.
 339:      */
 340: 
 341:     Arrays.sort(exportedFields);
 342: 
 343:     i = 0; j = 0; k = 0;
 344:     while (i < fields.length && j < exportedFields.length)
 345:       {
 346:         int comp = fields[i].compareTo(exportedFields[j]);
 347: 
 348:         if (comp < 0)
 349:           {
 350:         newFieldList[k] = fields[i];
 351:         fields[i].setPersistent(false);
 352:         fields[i].setToSet(false);
 353:         i++;
 354:           }
 355:         else if (comp > 0)
 356:           {
 357:         /* field not found in imported fields. We add it
 358:          * in the list of supported fields.
 359:          */
 360:         newFieldList[k] = exportedFields[j];
 361:         newFieldList[k].setPersistent(true);
 362:         newFieldList[k].setToSet(false);
 363:         try
 364:           {
 365:             newFieldList[k].lookupField(clazz);
 366:             newFieldList[k].checkFieldType();
 367:           }
 368:         catch (NoSuchFieldException _)
 369:           {
 370:           }
 371:         j++;
 372:           }
 373:         else
 374:           {
 375:         try
 376:           {
 377:             exportedFields[j].lookupField(clazz);
 378:             exportedFields[j].checkFieldType();
 379:           }
 380:         catch (NoSuchFieldException _)
 381:           {
 382:           }
 383: 
 384:         if (!fields[i].getType().equals(exportedFields[j].getType()))
 385:           throw new InvalidClassException
 386:             ("serialPersistentFields must be compatible with" +
 387:              " imported fields (about " + fields[i].getName() + ")");
 388:         newFieldList[k] = fields[i];
 389:         fields[i].setPersistent(true);
 390:         i++;
 391:         j++;
 392:           }
 393:         k++;
 394:       }
 395: 
 396:     if (i < fields.length)
 397:       for (;i<fields.length;i++,k++)
 398:         {
 399:           fields[i].setPersistent(false);
 400:           fields[i].setToSet(false);
 401:           newFieldList[k] = fields[i];
 402:         }
 403:     else
 404:       if (j < exportedFields.length)
 405:         for (;j<exportedFields.length;j++,k++)
 406:           {
 407:         exportedFields[j].setPersistent(true);
 408:         exportedFields[j].setToSet(false);
 409:         newFieldList[k] = exportedFields[j];
 410:           }
 411:     
 412:     fields = new ObjectStreamField[k];
 413:     System.arraycopy(newFieldList, 0, fields, 0, k);
 414:       }
 415:     catch (NoSuchFieldException ignore)
 416:       {
 417:     return;
 418:       }
 419:     catch (IllegalAccessException ignore)
 420:       {
 421:     return;
 422:       }
 423:   }
 424: 
 425:   void setSuperclass (ObjectStreamClass osc)
 426:   {
 427:     superClass = osc;
 428:   }
 429: 
 430:   void calculateOffsets()
 431:   {
 432:     int i;
 433:     ObjectStreamField field;
 434:     primFieldSize = 0;
 435:     int fcount = fields.length;
 436:     for (i = 0; i < fcount; ++ i)
 437:       {
 438:     field = fields[i];
 439: 
 440:     if (! field.isPrimitive())
 441:       break;
 442: 
 443:     field.setOffset(primFieldSize);
 444:     switch (field.getTypeCode())
 445:       {
 446:       case 'B':
 447:       case 'Z':
 448:         ++ primFieldSize;
 449:         break;
 450:       case 'C':
 451:       case 'S':
 452:         primFieldSize += 2;
 453:         break;
 454:       case 'I':
 455:       case 'F':
 456:         primFieldSize += 4;
 457:         break;
 458:       case 'D':
 459:       case 'J':
 460:         primFieldSize += 8;
 461:         break;
 462:       }
 463:       }
 464: 
 465:     for (objectFieldCount = 0; i < fcount; ++ i)
 466:       fields[i].setOffset(objectFieldCount++);
 467:   }
 468: 
 469:   private Method findMethod(Method[] methods, String name, Class[] params,
 470:                 Class returnType, boolean mustBePrivate)
 471:   {
 472: outer:
 473:     for (int i = 0; i < methods.length; i++)
 474:     {
 475:     final Method m = methods[i];
 476:         int mods = m.getModifiers();
 477:         if (Modifier.isStatic(mods)
 478:             || (mustBePrivate && !Modifier.isPrivate(mods)))
 479:         {
 480:             continue;
 481:         }
 482: 
 483:     if (m.getName().equals(name)
 484:        && m.getReturnType() == returnType)
 485:     {
 486:         Class[] mp = m.getParameterTypes();
 487:         if (mp.length == params.length)
 488:         {
 489:         for (int j = 0; j < mp.length; j++)
 490:         {
 491:             if (mp[j] != params[j])
 492:             {
 493:             continue outer;
 494:             }
 495:         }
 496:         AccessController.doPrivileged(new SetAccessibleAction(m));
 497:         return m;
 498:         }
 499:     }
 500:     }
 501:     return null;
 502:   }
 503: 
 504:   private static boolean inSamePackage(Class c1, Class c2)
 505:   {
 506:     String name1 = c1.getName();
 507:     String name2 = c2.getName();
 508: 
 509:     int id1 = name1.lastIndexOf('.');
 510:     int id2 = name2.lastIndexOf('.');
 511: 
 512:     // Handle the default package
 513:     if (id1 == -1 || id2 == -1)
 514:       return id1 == id2;
 515: 
 516:     String package1 = name1.substring(0, id1);
 517:     String package2 = name2.substring(0, id2);
 518: 
 519:     return package1.equals(package2);
 520:   }
 521: 
 522:   final static Class[] noArgs = new Class[0];
 523: 
 524:   private static Method findAccessibleMethod(String name, Class from)
 525:   {
 526:     for (Class c = from; c != null; c = c.getSuperclass())
 527:       {
 528:     try
 529:       {
 530:         Method res = c.getDeclaredMethod(name, noArgs);
 531:         int mods = res.getModifiers();
 532:         
 533:         if (c == from  
 534:         || Modifier.isProtected(mods)
 535:         || Modifier.isPublic(mods)
 536:         || (! Modifier.isPrivate(mods) && inSamePackage(c, from)))
 537:           {
 538:         AccessController.doPrivileged(new SetAccessibleAction(res));
 539:         return res;
 540:           }
 541:       }
 542:     catch (NoSuchMethodException e)
 543:       {
 544:       }
 545:       }
 546: 
 547:     return null;
 548:   }
 549: 
 550:   private void cacheMethods()
 551:   {
 552:     Method[] methods = forClass().getDeclaredMethods();
 553: 
 554:     readObjectMethod = findMethod(methods, "readObject",
 555:                   new Class[] { ObjectInputStream.class },
 556:                   Void.TYPE, true);
 557:     writeObjectMethod = findMethod(methods, "writeObject",
 558:                                    new Class[] { ObjectOutputStream.class },
 559:                                    Void.TYPE, true);
 560: 
 561:     // readResolve and writeReplace can be in parent classes, as long as they
 562:     // are accessible from this class.
 563:     readResolveMethod = findAccessibleMethod("readResolve", forClass());
 564:     writeReplaceMethod = findAccessibleMethod("writeReplace", forClass());
 565:   }
 566: 
 567:   private ObjectStreamClass(Class cl)
 568:   {
 569:     uid = 0;
 570:     flags = 0;
 571:     isProxyClass = Proxy.isProxyClass(cl);
 572: 
 573:     clazz = cl;
 574:     cacheMethods();
 575:     name = cl.getName();
 576:     setFlags(cl);
 577:     setFields(cl);
 578:     // to those class nonserializable, its uid field is 0
 579:     if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass)
 580:       uid = getClassUID(cl);
 581:     superClass = lookup(cl.getSuperclass());
 582:   }
 583: 
 584: 
 585:   // Sets bits in flags according to features of CL.
 586:   private void setFlags(Class cl)
 587:   {
 588:     if ((java.io.Externalizable.class).isAssignableFrom(cl))
 589:       flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
 590:     else if ((java.io.Serializable.class).isAssignableFrom(cl))
 591:       // only set this bit if CL is NOT Externalizable
 592:       flags |= ObjectStreamConstants.SC_SERIALIZABLE;
 593: 
 594:     if (writeObjectMethod != null)
 595:       flags |= ObjectStreamConstants.SC_WRITE_METHOD;
 596: 
 597:     if (cl.isEnum() || cl == Enum.class)
 598:       flags |= ObjectStreamConstants.SC_ENUM;
 599:   }
 600: 
 601: 
 602:   // Sets fields to be a sorted array of the serializable fields of
 603:   // clazz.
 604:   private void setFields(Class cl)
 605:   {
 606:     SetAccessibleAction setAccessible = new SetAccessibleAction();
 607: 
 608:     if (!isSerializable() || isExternalizable() || isEnum())
 609:       {
 610:     fields = NO_FIELDS;
 611:     return;
 612:       }
 613: 
 614:     try
 615:       {
 616:     final Field f =
 617:       cl.getDeclaredField("serialPersistentFields");
 618:     setAccessible.setMember(f);
 619:     AccessController.doPrivileged(setAccessible);
 620:     int modifiers = f.getModifiers();
 621: 
 622:     if (Modifier.isStatic(modifiers)
 623:         && Modifier.isFinal(modifiers)
 624:         && Modifier.isPrivate(modifiers))
 625:       {
 626:         fields = getSerialPersistentFields(cl);
 627:         if (fields != null)
 628:           {
 629:         ObjectStreamField[] fieldsName = new ObjectStreamField[fields.length];
 630:         System.arraycopy(fields, 0, fieldsName, 0, fields.length);
 631: 
 632:         Arrays.sort (fieldsName, new Comparator() {
 633:             public int compare(Object o1, Object o2)
 634:             {
 635:               ObjectStreamField f1 = (ObjectStreamField)o1;
 636:               ObjectStreamField f2 = (ObjectStreamField)o2;
 637:                 
 638:               return f1.getName().compareTo(f2.getName());
 639:             }
 640:             });
 641:         
 642:         for (int i=1; i < fields.length; i++)
 643:           {
 644:             if (fieldsName[i-1].getName().equals(fieldsName[i].getName()))
 645:             {
 646:                 fields = INVALID_FIELDS;
 647:                 return;
 648:             }
 649:           }
 650: 
 651:         Arrays.sort (fields);
 652:         // Retrieve field reference.
 653:         for (int i=0; i < fields.length; i++)
 654:           {
 655:             try
 656:               {
 657:             fields[i].lookupField(cl);
 658:               }
 659:             catch (NoSuchFieldException _)
 660:               {
 661:             fields[i].setToSet(false);
 662:               }
 663:           }
 664:         
 665:         calculateOffsets();
 666:         return;
 667:           }
 668:       }
 669:       }
 670:     catch (NoSuchFieldException ignore)
 671:       {
 672:       }
 673:     catch (IllegalAccessException ignore)
 674:       {
 675:       }
 676: 
 677:     int num_good_fields = 0;
 678:     Field[] all_fields = cl.getDeclaredFields();
 679: 
 680:     int modifiers;
 681:     // set non-serializable fields to null in all_fields
 682:     for (int i = 0; i < all_fields.length; i++)
 683:       {
 684:     modifiers = all_fields[i].getModifiers();
 685:     if (Modifier.isTransient(modifiers)
 686:         || Modifier.isStatic(modifiers))
 687:       all_fields[i] = null;
 688:     else
 689:       num_good_fields++;
 690:       }
 691: 
 692:     // make a copy of serializable (non-null) fields
 693:     fields = new ObjectStreamField[ num_good_fields ];
 694:     for (int from = 0, to = 0; from < all_fields.length; from++)
 695:       if (all_fields[from] != null)
 696:     {
 697:       final Field f = all_fields[from];
 698:       setAccessible.setMember(f);
 699:       AccessController.doPrivileged(setAccessible);
 700:       fields[to] = new ObjectStreamField(all_fields[from]);
 701:       to++;
 702:     }
 703: 
 704:     Arrays.sort(fields);
 705:     // Make sure we don't have any duplicate field names
 706:     // (Sun JDK 1.4.1. throws an Internal Error as well)
 707:     for (int i = 1; i < fields.length; i++)
 708:       {
 709:     if(fields[i - 1].getName().equals(fields[i].getName()))
 710:         throw new InternalError("Duplicate field " + 
 711:             fields[i].getName() + " in class " + cl.getName());
 712:       }
 713:     calculateOffsets();
 714:   }
 715: 
 716:   // Returns the serial version UID defined by class, or if that
 717:   // isn't present, calculates value of serial version UID.
 718:   private long getClassUID(Class cl)
 719:   {
 720:     try
 721:       {
 722:     // Use getDeclaredField rather than getField, since serialVersionUID
 723:     // may not be public AND we only want the serialVersionUID of this
 724:     // class, not a superclass or interface.
 725:     final Field suid = cl.getDeclaredField("serialVersionUID");
 726:     SetAccessibleAction setAccessible = new SetAccessibleAction(suid);
 727:     AccessController.doPrivileged(setAccessible);
 728:     int modifiers = suid.getModifiers();
 729: 
 730:     if (Modifier.isStatic(modifiers)
 731:         && Modifier.isFinal(modifiers)
 732:         && suid.getType() == Long.TYPE)
 733:       return suid.getLong(null);
 734:       }
 735:     catch (NoSuchFieldException ignore)
 736:       {
 737:       }
 738:     catch (IllegalAccessException ignore)
 739:       {
 740:       }
 741: 
 742:     // cl didn't define serialVersionUID, so we have to compute it
 743:     try
 744:       {
 745:     MessageDigest md;
 746:     try 
 747:       {
 748:         md = MessageDigest.getInstance("SHA");
 749:       }
 750:     catch (NoSuchAlgorithmException e)
 751:       {
 752:         // If a provider already provides SHA, use it; otherwise, use this.
 753:         Gnu gnuProvider = new Gnu();
 754:         Security.addProvider(gnuProvider);
 755:         md = MessageDigest.getInstance("SHA");
 756:       }
 757: 
 758:     DigestOutputStream digest_out =
 759:       new DigestOutputStream(nullOutputStream, md);
 760:     DataOutputStream data_out = new DataOutputStream(digest_out);
 761: 
 762:     data_out.writeUTF(cl.getName());
 763: 
 764:     int modifiers = cl.getModifiers();
 765:     // just look at interesting bits
 766:     modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL
 767:                  | Modifier.INTERFACE | Modifier.PUBLIC);
 768:     data_out.writeInt(modifiers);
 769: 
 770:     // Pretend that an array has no interfaces, because when array
 771:     // serialization was defined (JDK 1.1), arrays didn't have it.
 772:     if (! cl.isArray())
 773:       {
 774:         Class[] interfaces = cl.getInterfaces();
 775:         Arrays.sort(interfaces, interfaceComparator);
 776:         for (int i = 0; i < interfaces.length; i++)
 777:           data_out.writeUTF(interfaces[i].getName());
 778:       }
 779: 
 780:     Field field;
 781:     Field[] fields = cl.getDeclaredFields();
 782:     Arrays.sort(fields, memberComparator);
 783:     for (int i = 0; i < fields.length; i++)
 784:       {
 785:         field = fields[i];
 786:         modifiers = field.getModifiers();
 787:         if (Modifier.isPrivate(modifiers)
 788:         && (Modifier.isStatic(modifiers)
 789:             || Modifier.isTransient(modifiers)))
 790:           continue;
 791: 
 792:         data_out.writeUTF(field.getName());
 793:         data_out.writeInt(modifiers);
 794:         data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType()));
 795:       }
 796: 
 797:     // write class initializer method if present
 798:     if (VMObjectStreamClass.hasClassInitializer(cl))
 799:       {
 800:         data_out.writeUTF("<clinit>");
 801:         data_out.writeInt(Modifier.STATIC);
 802:         data_out.writeUTF("()V");
 803:       }
 804: 
 805:     Constructor constructor;
 806:     Constructor[] constructors = cl.getDeclaredConstructors();
 807:     Arrays.sort (constructors, memberComparator);
 808:     for (int i = 0; i < constructors.length; i++)
 809:       {
 810:         constructor = constructors[i];
 811:         modifiers = constructor.getModifiers();
 812:         if (Modifier.isPrivate(modifiers))
 813:           continue;
 814: 
 815:         data_out.writeUTF("<init>");
 816:         data_out.writeInt(modifiers);
 817: 
 818:         // the replacement of '/' with '.' was needed to make computed
 819:         // SUID's agree with those computed by JDK
 820:         data_out.writeUTF 
 821:           (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.'));
 822:       }
 823: 
 824:     Method method;
 825:     Method[] methods = cl.getDeclaredMethods();
 826:     Arrays.sort(methods, memberComparator);
 827:     for (int i = 0; i < methods.length; i++)
 828:       {
 829:         method = methods[i];
 830:         modifiers = method.getModifiers();
 831:         if (Modifier.isPrivate(modifiers))
 832:           continue;
 833: 
 834:         data_out.writeUTF(method.getName());
 835:         data_out.writeInt(modifiers);
 836: 
 837:         // the replacement of '/' with '.' was needed to make computed
 838:         // SUID's agree with those computed by JDK
 839:         data_out.writeUTF
 840:           (TypeSignature.getEncodingOfMethod(method).replace('/', '.'));
 841:       }
 842: 
 843:     data_out.close();
 844:     byte[] sha = md.digest();
 845:     long result = 0;
 846:     int len = sha.length < 8 ? sha.length : 8;
 847:     for (int i = 0; i < len; i++)
 848:       result += (long) (sha[i] & 0xFF) << (8 * i);
 849: 
 850:     return result;
 851:       }
 852:     catch (NoSuchAlgorithmException e)
 853:       {
 854:     throw new RuntimeException
 855:       ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
 856:        + cl.getName(), e);
 857:       }
 858:     catch (IOException ioe)
 859:       {
 860:     throw new RuntimeException(ioe);
 861:       }
 862:   }
 863: 
 864:   /**
 865:    * Returns the value of CLAZZ's private static final field named
 866:    * `serialPersistentFields'. It performs some sanity checks before
 867:    * returning the real array. Besides, the returned array is a clean
 868:    * copy of the original. So it can be modified.
 869:    *
 870:    * @param clazz Class to retrieve 'serialPersistentFields' from.
 871:    * @return The content of 'serialPersistentFields'.
 872:    */
 873:   private ObjectStreamField[] getSerialPersistentFields(Class clazz) 
 874:     throws NoSuchFieldException, IllegalAccessException
 875:   {
 876:     ObjectStreamField[] fieldsArray = null;
 877:     ObjectStreamField[] o;
 878: 
 879:     // Use getDeclaredField rather than getField for the same reason
 880:     // as above in getDefinedSUID.
 881:     Field f = clazz.getDeclaredField("serialPersistentFields");
 882:     f.setAccessible(true);
 883: 
 884:     int modifiers = f.getModifiers();
 885:     if (!(Modifier.isStatic(modifiers) &&
 886:       Modifier.isFinal(modifiers) &&
 887:       Modifier.isPrivate(modifiers)))
 888:       return null;
 889:     
 890:     o = (ObjectStreamField[]) f.get(null);
 891:     
 892:     if (o == null)
 893:       return null;
 894: 
 895:     fieldsArray = new ObjectStreamField[ o.length ];
 896:     System.arraycopy(o, 0, fieldsArray, 0, o.length);
 897: 
 898:     return fieldsArray;
 899:   }
 900: 
 901:   /**
 902:    * Returns a new instance of the Class this ObjectStreamClass corresponds
 903:    * to.
 904:    * Note that this should only be used for Externalizable classes.
 905:    *
 906:    * @return A new instance.
 907:    */
 908:   Externalizable newInstance() throws InvalidClassException
 909:   {
 910:     synchronized(this)
 911:     {
 912:     if (constructor == null)
 913:     {
 914:         try
 915:         {
 916:         final Constructor c = clazz.getConstructor(new Class[0]);
 917: 
 918:         AccessController.doPrivileged(new PrivilegedAction()
 919:         {
 920:             public Object run()
 921:             {
 922:             c.setAccessible(true);
 923:             return null;
 924:             }
 925:         });
 926: 
 927:         constructor = c;
 928:         }
 929:         catch(NoSuchMethodException x)
 930:         {
 931:         throw new InvalidClassException(clazz.getName(),
 932:             "No public zero-argument constructor");
 933:         }
 934:     }
 935:     }
 936: 
 937:     try
 938:     {
 939:     return (Externalizable)constructor.newInstance(null);
 940:     }
 941:     catch(Exception x)
 942:     {
 943:     throw (InvalidClassException)
 944:         new InvalidClassException(clazz.getName(),
 945:              "Unable to instantiate").initCause(x);
 946:     }
 947:   }
 948: 
 949:   public static final ObjectStreamField[] NO_FIELDS = {};
 950: 
 951:   private static Hashtable classLookupTable = new Hashtable();
 952:   private static final NullOutputStream nullOutputStream = new NullOutputStream();
 953:   private static final Comparator interfaceComparator = new InterfaceComparator();
 954:   private static final Comparator memberComparator = new MemberComparator();
 955:   private static final
 956:     Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class };
 957: 
 958:   private ObjectStreamClass superClass;
 959:   private Class clazz;
 960:   private String name;
 961:   private long uid;
 962:   private byte flags;
 963: 
 964:   // this field is package protected so that ObjectInputStream and
 965:   // ObjectOutputStream can access it directly
 966:   ObjectStreamField[] fields;
 967: 
 968:   // these are accessed by ObjectIn/OutputStream
 969:   int primFieldSize = -1;  // -1 if not yet calculated
 970:   int objectFieldCount;
 971: 
 972:   Method readObjectMethod;
 973:   Method readResolveMethod;
 974:   Method writeReplaceMethod;
 975:   Method writeObjectMethod;
 976:   boolean realClassIsSerializable;
 977:   boolean realClassIsExternalizable;
 978:   ObjectStreamField[] fieldMapping;
 979:   Constructor firstNonSerializableParentConstructor;
 980:   private Constructor constructor;  // default constructor for Externalizable
 981: 
 982:   boolean isProxyClass = false;
 983: 
 984:   // This is probably not necessary because this class is special cased already
 985:   // but it will avoid showing up as a discrepancy when comparing SUIDs.
 986:   private static final long serialVersionUID = -6120832682080437368L;
 987: 
 988: 
 989:   // interfaces are compared only by name
 990:   private static final class InterfaceComparator implements Comparator
 991:   {
 992:     public int compare(Object o1, Object o2)
 993:     {
 994:       return ((Class) o1).getName().compareTo(((Class) o2).getName());
 995:     }
 996:   }
 997: 
 998: 
 999:   // Members (Methods and Constructors) are compared first by name,
1000:   // conflicts are resolved by comparing type signatures
1001:   private static final class MemberComparator implements Comparator
1002:   {
1003:     public int compare(Object o1, Object o2)
1004:     {
1005:       Member m1 = (Member) o1;
1006:       Member m2 = (Member) o2;
1007: 
1008:       int comp = m1.getName().compareTo(m2.getName());
1009: 
1010:       if (comp == 0)
1011:         return TypeSignature.getEncodingOfMember(m1).
1012:       compareTo(TypeSignature.getEncodingOfMember(m2));
1013:       else
1014:         return comp;
1015:     }
1016:   }
1017: }