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