1:
38:
39:
40: package ;
41:
42: import ;
43: import ;
44: import ;
45: import ;
46:
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56: import ;
57: import ;
58: import ;
59: import ;
60: import ;
61: import ;
62:
63:
70: public class ObjectStreamClass implements Serializable
71: {
72: static final ObjectStreamField[] INVALID_FIELDS = new ObjectStreamField[0];
73:
74:
89: public static ObjectStreamClass lookup(Class<?> cl)
90: {
91: if (cl == null)
92: return null;
93: if (! (Serializable.class).isAssignableFrom(cl))
94: return null;
95:
96: return lookupForClassObject(cl);
97: }
98:
99:
104: static ObjectStreamClass lookupForClassObject(Class cl)
105: {
106: if (cl == null)
107: return null;
108:
109: ObjectStreamClass osc = classLookupTable.get(cl);
110:
111: if (osc != null)
112: return osc;
113: else
114: {
115: osc = new ObjectStreamClass(cl);
116: classLookupTable.put(cl, osc);
117: return osc;
118: }
119: }
120:
121:
127: public String getName()
128: {
129: return name;
130: }
131:
132:
141: public Class<?> forClass()
142: {
143: return clazz;
144: }
145:
146:
155: public long getSerialVersionUID()
156: {
157: return uid;
158: }
159:
160:
169: public ObjectStreamField[] getFields()
170: {
171: ObjectStreamField[] copy = new ObjectStreamField[ fields.length ];
172: System.arraycopy(fields, 0, copy, 0, fields.length);
173: return copy;
174: }
175:
176:
177:
178:
179: public ObjectStreamField getField (String name)
180: {
181: for (int i = 0; i < fields.length; i++)
182: if (fields[i].getName().equals(name))
183: return fields[i];
184: return null;
185: }
186:
187:
196: public String toString()
197: {
198: return "java.io.ObjectStreamClass< " + name + ", " + uid + " >";
199: }
200:
201:
202:
203:
204:
205:
206:
207:
208: boolean hasWriteMethod()
209: {
210: return (flags & ObjectStreamConstants.SC_WRITE_METHOD) != 0;
211: }
212:
213:
214:
215: boolean isSerializable()
216: {
217: return (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
218: }
219:
220:
221:
222:
223: boolean isExternalizable()
224: {
225: return (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
226: }
227:
228:
229:
230: boolean isEnum()
231: {
232: return (flags & ObjectStreamConstants.SC_ENUM) != 0;
233: }
234:
235:
236:
237:
238:
239: ObjectStreamClass getSuper()
240: {
241: return superClass;
242: }
243:
244:
257: ObjectStreamClass[] hierarchy()
258: {
259: ObjectStreamClass[] result = hierarchy;
260: if (result == null)
261: {
262: int d = 0;
263:
264: for(ObjectStreamClass osc = this; osc != null; osc = osc.getSuper())
265: d++;
266:
267: result = new ObjectStreamClass[d];
268:
269: for (ObjectStreamClass osc = this; osc != null; osc = osc.getSuper())
270: {
271: result[--d] = osc;
272: }
273:
274: hierarchy = result;
275: }
276: return result;
277: }
278:
279:
282: private ObjectStreamClass[] hierarchy = null;
283:
284:
285:
286:
287:
288: int getFlags()
289: {
290: return flags;
291: }
292:
293:
294: ObjectStreamClass(String name, long uid, byte flags,
295: ObjectStreamField[] fields)
296: {
297: this.name = name;
298: this.uid = uid;
299: this.flags = flags;
300: this.fields = fields;
301: }
302:
303:
314: void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException
315: {hierarchy = null;
316: this.clazz = cl;
317:
318: cacheMethods();
319:
320: long class_uid = getClassUID(cl);
321: if (uid == 0)
322: uid = class_uid;
323: else
324: {
325:
326:
327: if (!cl.isArray() && uid != class_uid)
328: {
329: String msg = cl +
330: ": Local class not compatible: stream serialVersionUID="
331: + uid + ", local serialVersionUID=" + class_uid;
332: throw new InvalidClassException (msg);
333: }
334: }
335:
336: isProxyClass = clazz != null && Proxy.isProxyClass(clazz);
337: this.superClass = superClass;
338: calculateOffsets();
339:
340: try
341: {
342: ObjectStreamField[] exportedFields = getSerialPersistentFields (clazz);
343:
344: if (exportedFields == null)
345: return;
346:
347: ObjectStreamField[] newFieldList = new ObjectStreamField[exportedFields.length + fields.length];
348: int i, j, k;
349:
350:
354:
355: Arrays.sort(exportedFields);
356:
357: i = 0; j = 0; k = 0;
358: while (i < fields.length && j < exportedFields.length)
359: {
360: int comp = fields[i].compareTo(exportedFields[j]);
361:
362: if (comp < 0)
363: {
364: newFieldList[k] = fields[i];
365: fields[i].setPersistent(false);
366: fields[i].setToSet(false);
367: i++;
368: }
369: else if (comp > 0)
370: {
371:
374: newFieldList[k] = exportedFields[j];
375: newFieldList[k].setPersistent(true);
376: newFieldList[k].setToSet(false);
377: try
378: {
379: newFieldList[k].lookupField(clazz);
380: newFieldList[k].checkFieldType();
381: }
382: catch (NoSuchFieldException _)
383: {
384: }
385: j++;
386: }
387: else
388: {
389: try
390: {
391: exportedFields[j].lookupField(clazz);
392: exportedFields[j].checkFieldType();
393: }
394: catch (NoSuchFieldException _)
395: {
396: }
397:
398: if (!fields[i].getType().equals(exportedFields[j].getType()))
399: throw new InvalidClassException
400: ("serialPersistentFields must be compatible with" +
401: " imported fields (about " + fields[i].getName() + ")");
402: newFieldList[k] = fields[i];
403: fields[i].setPersistent(true);
404: i++;
405: j++;
406: }
407: k++;
408: }
409:
410: if (i < fields.length)
411: for (;i<fields.length;i++,k++)
412: {
413: fields[i].setPersistent(false);
414: fields[i].setToSet(false);
415: newFieldList[k] = fields[i];
416: }
417: else
418: if (j < exportedFields.length)
419: for (;j<exportedFields.length;j++,k++)
420: {
421: exportedFields[j].setPersistent(true);
422: exportedFields[j].setToSet(false);
423: newFieldList[k] = exportedFields[j];
424: }
425:
426: fields = new ObjectStreamField[k];
427: System.arraycopy(newFieldList, 0, fields, 0, k);
428: }
429: catch (NoSuchFieldException ignore)
430: {
431: return;
432: }
433: catch (IllegalAccessException ignore)
434: {
435: return;
436: }
437: }
438:
439: void setSuperclass (ObjectStreamClass osc)
440: {
441: superClass = osc;
442: hierarchy = null;
443: }
444:
445: void calculateOffsets()
446: {
447: int i;
448: ObjectStreamField field;
449: primFieldSize = 0;
450: int fcount = fields.length;
451: for (i = 0; i < fcount; ++ i)
452: {
453: field = fields[i];
454:
455: if (! field.isPrimitive())
456: break;
457:
458: field.setOffset(primFieldSize);
459: switch (field.getTypeCode())
460: {
461: case 'B':
462: case 'Z':
463: ++ primFieldSize;
464: break;
465: case 'C':
466: case 'S':
467: primFieldSize += 2;
468: break;
469: case 'I':
470: case 'F':
471: primFieldSize += 4;
472: break;
473: case 'D':
474: case 'J':
475: primFieldSize += 8;
476: break;
477: }
478: }
479:
480: for (objectFieldCount = 0; i < fcount; ++ i)
481: fields[i].setOffset(objectFieldCount++);
482: }
483:
484: private Method findMethod(Method[] methods, String name, Class[] params,
485: Class returnType, boolean mustBePrivate)
486: {
487: outer:
488: for (int i = 0; i < methods.length; i++)
489: {
490: final Method m = methods[i];
491: int mods = m.getModifiers();
492: if (Modifier.isStatic(mods)
493: || (mustBePrivate && !Modifier.isPrivate(mods)))
494: {
495: continue;
496: }
497:
498: if (m.getName().equals(name)
499: && m.getReturnType() == returnType)
500: {
501: Class[] mp = m.getParameterTypes();
502: if (mp.length == params.length)
503: {
504: for (int j = 0; j < mp.length; j++)
505: {
506: if (mp[j] != params[j])
507: {
508: continue outer;
509: }
510: }
511: AccessController.doPrivileged(new SetAccessibleAction(m));
512: return m;
513: }
514: }
515: }
516: return null;
517: }
518:
519: private static boolean inSamePackage(Class c1, Class c2)
520: {
521: String name1 = c1.getName();
522: String name2 = c2.getName();
523:
524: int id1 = name1.lastIndexOf('.');
525: int id2 = name2.lastIndexOf('.');
526:
527:
528: if (id1 == -1 || id2 == -1)
529: return id1 == id2;
530:
531: String package1 = name1.substring(0, id1);
532: String package2 = name2.substring(0, id2);
533:
534: return package1.equals(package2);
535: }
536:
537: final static Class[] noArgs = new Class[0];
538:
539: private static Method findAccessibleMethod(String name, Class from)
540: {
541: for (Class c = from; c != null; c = c.getSuperclass())
542: {
543: try
544: {
545: Method res = c.getDeclaredMethod(name, noArgs);
546: int mods = res.getModifiers();
547:
548: if (c == from
549: || Modifier.isProtected(mods)
550: || Modifier.isPublic(mods)
551: || (! Modifier.isPrivate(mods) && inSamePackage(c, from)))
552: {
553: AccessController.doPrivileged(new SetAccessibleAction(res));
554: return res;
555: }
556: }
557: catch (NoSuchMethodException e)
558: {
559: }
560: }
561:
562: return null;
563: }
564:
565:
576: private static boolean loadedByBootOrApplicationClassLoader(Class cl)
577: {
578: ClassLoader l = cl.getClassLoader();
579: return
580: ( l == null )
581: || (l == ClassLoader.getSystemClassLoader() );
582: }
583:
584: static Hashtable methodCache = new Hashtable();
585:
586: static final Class[] readObjectSignature = { ObjectInputStream.class };
587: static final Class[] writeObjectSignature = { ObjectOutputStream.class };
588:
589: private void cacheMethods()
590: {
591: Class cl = forClass();
592: Method[] cached = (Method[]) methodCache.get(cl);
593: if (cached == null)
594: {
595: cached = new Method[4];
596: Method[] methods = cl.getDeclaredMethods();
597:
598: cached[0] = findMethod(methods, "readObject",
599: readObjectSignature,
600: Void.TYPE, true);
601: cached[1] = findMethod(methods, "writeObject",
602: writeObjectSignature,
603: Void.TYPE, true);
604:
605:
606:
607: cached[2] = findAccessibleMethod("readResolve", cl);
608: cached[3] = findAccessibleMethod("writeReplace", cl);
609:
610:
614: if (loadedByBootOrApplicationClassLoader(cl))
615: methodCache.put(cl,cached);
616: }
617: readObjectMethod = cached[0];
618: writeObjectMethod = cached[1];
619: readResolveMethod = cached[2];
620: writeReplaceMethod = cached[3];
621: }
622:
623: private ObjectStreamClass(Class cl)
624: {
625: uid = 0;
626: flags = 0;
627: isProxyClass = Proxy.isProxyClass(cl);
628:
629: clazz = cl;
630: cacheMethods();
631: name = cl.getName();
632: setFlags(cl);
633: setFields(cl);
634:
635: if ( (Serializable.class).isAssignableFrom(cl) && !isProxyClass)
636: uid = getClassUID(cl);
637: superClass = lookup(cl.getSuperclass());
638: }
639:
640:
641:
642: private void setFlags(Class cl)
643: {
644: if ((java.io.Externalizable.class).isAssignableFrom(cl))
645: flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
646: else if ((java.io.Serializable.class).isAssignableFrom(cl))
647:
648: flags |= ObjectStreamConstants.SC_SERIALIZABLE;
649:
650: if (writeObjectMethod != null)
651: flags |= ObjectStreamConstants.SC_WRITE_METHOD;
652:
653: if (cl.isEnum() || cl == Enum.class)
654: flags |= ObjectStreamConstants.SC_ENUM;
655: }
656:
657:
658:
659:
660:
661:
662:
663:
664: final void ensureFieldsSet(Class cl)
665: {
666: if (! fieldsSet)
667: setFields(cl);
668: }
669:
670:
671:
672:
673:
674: private void setFields(Class cl)
675: {
676:
677: fieldsSet = true;
678:
679:
680: SetAccessibleAction setAccessible = new SetAccessibleAction();
681:
682: if (!isSerializable() || isExternalizable() || isEnum())
683: {
684: fields = NO_FIELDS;
685: return;
686: }
687:
688: try
689: {
690: final Field f =
691: cl.getDeclaredField("serialPersistentFields");
692: setAccessible.setMember(f);
693: AccessController.doPrivileged(setAccessible);
694: int modifiers = f.getModifiers();
695:
696: if (Modifier.isStatic(modifiers)
697: && Modifier.isFinal(modifiers)
698: && Modifier.isPrivate(modifiers))
699: {
700: fields = getSerialPersistentFields(cl);
701: if (fields != null)
702: {
703: ObjectStreamField[] fieldsName = new ObjectStreamField[fields.length];
704: System.arraycopy(fields, 0, fieldsName, 0, fields.length);
705:
706: Arrays.sort (fieldsName, new Comparator() {
707: public int compare(Object o1, Object o2)
708: {
709: ObjectStreamField f1 = (ObjectStreamField)o1;
710: ObjectStreamField f2 = (ObjectStreamField)o2;
711:
712: return f1.getName().compareTo(f2.getName());
713: }
714: });
715:
716: for (int i=1; i < fields.length; i++)
717: {
718: if (fieldsName[i-1].getName().equals(fieldsName[i].getName()))
719: {
720: fields = INVALID_FIELDS;
721: return;
722: }
723: }
724:
725: Arrays.sort (fields);
726:
727: for (int i=0; i < fields.length; i++)
728: {
729: try
730: {
731: fields[i].lookupField(cl);
732: }
733: catch (NoSuchFieldException _)
734: {
735: fields[i].setToSet(false);
736: }
737: }
738:
739: calculateOffsets();
740: return;
741: }
742: }
743: }
744: catch (NoSuchFieldException ignore)
745: {
746: }
747: catch (IllegalAccessException ignore)
748: {
749: }
750:
751: int num_good_fields = 0;
752: Field[] all_fields = cl.getDeclaredFields();
753:
754: int modifiers;
755:
756: for (int i = 0; i < all_fields.length; i++)
757: {
758: modifiers = all_fields[i].getModifiers();
759: if (Modifier.isTransient(modifiers)
760: || Modifier.isStatic(modifiers))
761: all_fields[i] = null;
762: else
763: num_good_fields++;
764: }
765:
766:
767: fields = new ObjectStreamField[ num_good_fields ];
768: for (int from = 0, to = 0; from < all_fields.length; from++)
769: if (all_fields[from] != null)
770: {
771: final Field f = all_fields[from];
772: setAccessible.setMember(f);
773: AccessController.doPrivileged(setAccessible);
774: fields[to] = new ObjectStreamField(all_fields[from]);
775: to++;
776: }
777:
778: Arrays.sort(fields);
779:
780:
781: for (int i = 1; i < fields.length; i++)
782: {
783: if(fields[i - 1].getName().equals(fields[i].getName()))
784: throw new InternalError("Duplicate field " +
785: fields[i].getName() + " in class " + cl.getName());
786: }
787: calculateOffsets();
788: }
789:
790: static Hashtable uidCache = new Hashtable();
791:
792:
793:
794: private long getClassUID(Class cl)
795: {
796: long result = 0;
797: Long cache = (Long) uidCache.get(cl);
798: if (cache != null)
799: result = cache.longValue();
800: else
801: {
802:
803:
804: if (Enum.class.isAssignableFrom(cl) || Proxy.isProxyClass(cl))
805: {
806:
807:
808: return 0L;
809: }
810: try
811: {
812: result = getClassUIDFromField(cl);
813: }
814: catch (NoSuchFieldException ignore)
815: {
816: try
817: {
818: result = calculateClassUID(cl);
819: }
820: catch (NoSuchAlgorithmException e)
821: {
822: throw new RuntimeException
823: ("The SHA algorithm was not found to use in computing the Serial Version UID for class "
824: + cl.getName(), e);
825: }
826: catch (IOException ioe)
827: {
828: throw new RuntimeException(ioe);
829: }
830: }
831:
832: if (loadedByBootOrApplicationClassLoader(cl))
833: uidCache.put(cl,Long.valueOf(result));
834: }
835: return result;
836: }
837:
838:
847: long getClassUIDFromField(Class cl)
848: throws NoSuchFieldException
849: {
850: long result;
851:
852: try
853: {
854:
855:
856:
857: final Field suid = cl.getDeclaredField("serialVersionUID");
858: SetAccessibleAction setAccessible = new SetAccessibleAction(suid);
859: AccessController.doPrivileged(setAccessible);
860: int modifiers = suid.getModifiers();
861:
862: if (Modifier.isStatic(modifiers)
863: && Modifier.isFinal(modifiers)
864: && suid.getType() == Long.TYPE)
865: result = suid.getLong(null);
866: else
867: throw new NoSuchFieldException();
868: }
869: catch (IllegalAccessException ignore)
870: {
871: throw new NoSuchFieldException();
872: }
873:
874: return result;
875: }
876:
877:
890: long calculateClassUID(Class cl)
891: throws NoSuchAlgorithmException, IOException
892: {
893: long result;
894: MessageDigest md;
895: try
896: {
897: md = MessageDigest.getInstance("SHA");
898: }
899: catch (NoSuchAlgorithmException e)
900: {
901:
902: Gnu gnuProvider = new Gnu();
903: Security.addProvider(gnuProvider);
904: md = MessageDigest.getInstance("SHA");
905: }
906:
907: DigestOutputStream digest_out =
908: new DigestOutputStream(nullOutputStream, md);
909: DataOutputStream data_out = new DataOutputStream(digest_out);
910:
911: data_out.writeUTF(cl.getName());
912:
913: int modifiers = cl.getModifiers();
914:
915: modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL
916: | Modifier.INTERFACE | Modifier.PUBLIC);
917: data_out.writeInt(modifiers);
918:
919:
920:
921: if (! cl.isArray())
922: {
923: Class[] interfaces = cl.getInterfaces();
924: Arrays.sort(interfaces, interfaceComparator);
925: for (int i = 0; i < interfaces.length; i++)
926: data_out.writeUTF(interfaces[i].getName());
927: }
928:
929: Field field;
930: Field[] fields = cl.getDeclaredFields();
931: Arrays.sort(fields, memberComparator);
932: for (int i = 0; i < fields.length; i++)
933: {
934: field = fields[i];
935: modifiers = field.getModifiers();
936: if (Modifier.isPrivate(modifiers)
937: && (Modifier.isStatic(modifiers)
938: || Modifier.isTransient(modifiers)))
939: continue;
940:
941: data_out.writeUTF(field.getName());
942: data_out.writeInt(modifiers);
943: data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType()));
944: }
945:
946:
947: if (VMObjectStreamClass.hasClassInitializer(cl))
948: {
949: data_out.writeUTF("<clinit>");
950: data_out.writeInt(Modifier.STATIC);
951: data_out.writeUTF("()V");
952: }
953:
954: Constructor constructor;
955: Constructor[] constructors = cl.getDeclaredConstructors();
956: Arrays.sort (constructors, memberComparator);
957: for (int i = 0; i < constructors.length; i++)
958: {
959: constructor = constructors[i];
960: modifiers = constructor.getModifiers();
961: if (Modifier.isPrivate(modifiers))
962: continue;
963:
964: data_out.writeUTF("<init>");
965: data_out.writeInt(modifiers);
966:
967:
968:
969: data_out.writeUTF
970: (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.'));
971: }
972:
973: Method method;
974: Method[] methods = cl.getDeclaredMethods();
975: Arrays.sort(methods, memberComparator);
976: for (int i = 0; i < methods.length; i++)
977: {
978: method = methods[i];
979: modifiers = method.getModifiers();
980: if (Modifier.isPrivate(modifiers))
981: continue;
982:
983: data_out.writeUTF(method.getName());
984: data_out.writeInt(modifiers);
985:
986:
987:
988: data_out.writeUTF
989: (TypeSignature.getEncodingOfMethod(method).replace('/', '.'));
990: }
991:
992: data_out.close();
993: byte[] sha = md.digest();
994: result = 0;
995: int len = sha.length < 8 ? sha.length : 8;
996: for (int i = 0; i < len; i++)
997: result += (long) (sha[i] & 0xFF) << (8 * i);
998:
999: return result;
1000: }
1001:
1002:
1011: private ObjectStreamField[] getSerialPersistentFields(Class clazz)
1012: throws NoSuchFieldException, IllegalAccessException
1013: {
1014: ObjectStreamField[] fieldsArray = null;
1015: ObjectStreamField[] o;
1016:
1017:
1018:
1019: Field f = clazz.getDeclaredField("serialPersistentFields");
1020: f.setAccessible(true);
1021:
1022: int modifiers = f.getModifiers();
1023: if (!(Modifier.isStatic(modifiers) &&
1024: Modifier.isFinal(modifiers) &&
1025: Modifier.isPrivate(modifiers)))
1026: return null;
1027:
1028: o = (ObjectStreamField[]) f.get(null);
1029:
1030: if (o == null)
1031: return null;
1032:
1033: fieldsArray = new ObjectStreamField[ o.length ];
1034: System.arraycopy(o, 0, fieldsArray, 0, o.length);
1035:
1036: return fieldsArray;
1037: }
1038:
1039:
1046: Externalizable newInstance() throws InvalidClassException
1047: {
1048: synchronized(this)
1049: {
1050: if (constructor == null)
1051: {
1052: try
1053: {
1054: final Constructor c = clazz.getConstructor(new Class[0]);
1055:
1056: AccessController.doPrivileged(new PrivilegedAction()
1057: {
1058: public Object run()
1059: {
1060: c.setAccessible(true);
1061: return null;
1062: }
1063: });
1064:
1065: constructor = c;
1066: }
1067: catch(NoSuchMethodException x)
1068: {
1069: throw new InvalidClassException(clazz.getName(),
1070: "No public zero-argument constructor");
1071: }
1072: }
1073: }
1074:
1075: try
1076: {
1077: return (Externalizable)constructor.newInstance();
1078: }
1079: catch(Exception x)
1080: {
1081: throw (InvalidClassException)
1082: new InvalidClassException(clazz.getName(),
1083: "Unable to instantiate").initCause(x);
1084: }
1085: }
1086:
1087: public static final ObjectStreamField[] NO_FIELDS = {};
1088:
1089: private static Hashtable<Class,ObjectStreamClass> classLookupTable
1090: = new Hashtable<Class,ObjectStreamClass>();
1091: private static final NullOutputStream nullOutputStream = new NullOutputStream();
1092: private static final Comparator interfaceComparator = new InterfaceComparator();
1093: private static final Comparator memberComparator = new MemberComparator();
1094: private static final
1095: Class[] writeMethodArgTypes = { java.io.ObjectOutputStream.class };
1096:
1097: private ObjectStreamClass superClass;
1098: private Class<?> clazz;
1099: private String name;
1100: private long uid;
1101: private byte flags;
1102:
1103:
1104:
1105: ObjectStreamField[] fields;
1106:
1107:
1108: int primFieldSize = -1;
1109: int objectFieldCount;
1110:
1111: Method readObjectMethod;
1112: Method readResolveMethod;
1113: Method writeReplaceMethod;
1114: Method writeObjectMethod;
1115: boolean realClassIsSerializable;
1116: boolean realClassIsExternalizable;
1117: ObjectStreamField[] fieldMapping;
1118: Constructor firstNonSerializableParentConstructor;
1119: private Constructor constructor;
1120:
1121: boolean isProxyClass = false;
1122:
1123:
1124:
1125: private boolean fieldsSet = false;
1126:
1127:
1128:
1129:
1130: private static final long serialVersionUID = -6120832682080437368L;
1131:
1132:
1133:
1134: private static final class InterfaceComparator implements Comparator
1135: {
1136: public int compare(Object o1, Object o2)
1137: {
1138: return ((Class) o1).getName().compareTo(((Class) o2).getName());
1139: }
1140: }
1141:
1142:
1143:
1144:
1145: private static final class MemberComparator implements Comparator
1146: {
1147: public int compare(Object o1, Object o2)
1148: {
1149: Member m1 = (Member) o1;
1150: Member m2 = (Member) o2;
1151:
1152: int comp = m1.getName().compareTo(m2.getName());
1153:
1154: if (comp == 0)
1155: return TypeSignature.getEncodingOfMember(m1).
1156: compareTo(TypeSignature.getEncodingOfMember(m2));
1157: else
1158: return comp;
1159: }
1160: }
1161: }