• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html#License
3 /*
4  *******************************************************************************
5  * Copyright (C) 2005-2012, International Business Machines Corporation and    *
6  * others. All Rights Reserved.                                                *
7  *******************************************************************************
8  *
9  */
10 
11 package com.ibm.icu.dev.tool.docs;
12 
13 import java.io.BufferedReader;
14 import java.io.File;
15 import java.io.FileInputStream;
16 import java.io.InputStreamReader;
17 import java.io.PrintWriter;
18 import java.lang.reflect.Constructor;
19 import java.lang.reflect.Field;
20 import java.lang.reflect.Method;
21 import java.lang.reflect.Modifier;
22 import java.util.ArrayList;
23 import java.util.Iterator;
24 import java.util.Map;
25 import java.util.Set;
26 import java.util.TreeMap;
27 import java.util.TreeSet;
28 
29 /**
30  * Compare ICU4J and JDK APIS.
31  *
32  * TODO: compare protected APIs.  Reflection on Class allows you
33  * to either get all inherited methods with public access, or get methods
34  * on the particular class with any access, but no way to get all
35  * inherited methods with any access.  Go figure.
36  */
37 public class ICUJDKCompare {
38     static final boolean DEBUG = false;
39 
40     // set up defaults
41     private static final String kSrcPrefix = "java.";
42     private static final String kTrgPrefix = "com.ibm.icu.";
43     private static final String[] kPairInfo = {
44         "lang.Character/UCharacter",
45         "lang.Character$UnicodeBlock/UCharacter$UnicodeBlock",
46         "text.BreakIterator",
47         "text.Collator",
48         "text.DateFormat",
49         "text.DateFormatSymbols",
50         "text.DecimalFormat",
51         "text.DecimalFormatSymbols",
52         "text.Format/UFormat",
53         "text.MessageFormat",
54         "text.NumberFormat",
55         "text.SimpleDateFormat",
56         "util.Calendar",
57         "util.Currency",
58         "util.GregorianCalendar",
59         "util.SimpleTimeZone",
60         "util.TimeZone",
61         "util.Locale/ULocale",
62         "util.ResourceBundle/UResourceBundle",
63     };
64 
65     private static final String[] kIgnore = new String[] {
66         "lang.Character <init> charValue compareTo MAX_VALUE MIN_VALUE TYPE",
67         "lang.Character$UnicodeBlock SURROGATES_AREA",
68         "util.Calendar FIELD_COUNT",
69         "util.GregorianCalendar FIELD_COUNT",
70         "util.SimpleTimeZone STANDARD_TIME UTC_TIME WALL_TIME",
71     };
72 
73     private PrintWriter pw;
74     private String srcPrefix;
75     private String trgPrefix;
76     private Class[] classPairs;
77     private String[] namePairs;
78     private String[] ignore;
79     private boolean swap;
80     //private boolean signature;
81 
82     // call System.exit with non-zero if there were some missing APIs
main(String[] args)83     public static void main(String[] args) {
84         System.exit(doMain(args));
85     }
86 
87     // return non-zero if there were some missing APIs
doMain(String[] args)88     public static int doMain(String[] args) {
89         ICUJDKCompare p = new ICUJDKCompare();
90         p.setOutputWriter(new PrintWriter(System.out));
91         p.setup(args);
92         return p.process();
93     }
94 
95     // setters
setOutputWriter(PrintWriter pw)96     public ICUJDKCompare setOutputWriter(PrintWriter pw) {
97         this.pw = pw;
98         return this;
99     }
100 
setSrcPrefix(String srcPrefix)101     public ICUJDKCompare setSrcPrefix(String srcPrefix) {
102         this.srcPrefix = srcPrefix;
103         return this;
104     }
105 
setTrgPrefix(String trgPrefix)106     public ICUJDKCompare setTrgPrefix(String trgPrefix) {
107         this.trgPrefix = trgPrefix;
108         return this;
109     }
110 
setClassPairs(Class[] classPairs)111     public ICUJDKCompare setClassPairs(Class[] classPairs) {
112         this.classPairs = classPairs;
113         return this;
114     }
115 
setNamePairs(String[] namePairs)116     public ICUJDKCompare setNamePairs(String[] namePairs) {
117         this.namePairs = namePairs;
118         return this;
119     }
120 
setIgnore(String[] ignore)121     public ICUJDKCompare setIgnore(String[] ignore) {
122         this.ignore = ignore;
123         return this;
124     }
125 
setSwap(boolean swap)126     public ICUJDKCompare setSwap(boolean swap) {
127         this.swap = swap;
128         return this;
129     }
130 
setup(String[] args)131     public ICUJDKCompare setup(String[] args) {
132         String namelist = null;
133         String ignorelist = null;
134         for (int i = 0; i < args.length; ++i) {
135             String arg = args[i];
136             if (arg.equals("-swap")) {
137                 swap = true;
138             } else if (arg.equals("-srcPrefix:")) {
139                 srcPrefix = args[++i];
140                 if (!srcPrefix.endsWith(".")) {
141                     srcPrefix += '.';
142                 }
143             } else if (arg.equals("-trgPrefix:")) {
144                 trgPrefix = args[++i];
145                 if (!trgPrefix.endsWith(".")) {
146                     trgPrefix += '.';
147                 }
148             } else if (arg.equals("-names:")) {
149                 namelist = args[++i];
150             } else if (arg.equals("-ignore:")) {
151                 ignorelist = args[++i];
152             } else {
153                 System.err.println("unrecognized argument: " + arg);
154                 throw new IllegalStateException();
155             }
156         }
157 
158         if (ignorelist != null) {
159             if (ignorelist.charAt(0) == '@') { // a file containing ignoreinfo
160                 BufferedReader br = null;
161                 try {
162                     ArrayList nl = new ArrayList();
163                     File f = new File(namelist.substring(1));
164                     FileInputStream fis = new FileInputStream(f);
165                     InputStreamReader isr = new InputStreamReader(fis);
166                     br = new BufferedReader(isr);
167                     String line = null;
168                     while (null != (line = br.readLine())) {
169                         nl.add(line);
170                     }
171                     ignore = (String[])nl.toArray(new String[nl.size()]);
172                 }
173                 catch (Exception e) {
174                     System.err.println(e);
175                     throw new IllegalStateException();
176                 }
177                 finally {
178                     if (br != null) {
179                         try {
180                             br.close();
181                         } catch (Exception e) {
182                             // ignore
183                         }
184                     }
185                 }
186             } else { // a list of ignoreinfo separated by semicolons
187                 ignore = ignorelist.split("\\s*;\\s*");
188             }
189         }
190 
191         if (namelist != null) {
192             String[] names = null;
193             if (namelist.charAt(0) == '@') { // a file
194                 BufferedReader br = null;
195                 try {
196                     ArrayList nl = new ArrayList();
197                     File f = new File(namelist.substring(1));
198                     FileInputStream fis = new FileInputStream(f);
199                     InputStreamReader isr = new InputStreamReader(fis);
200                     br = new BufferedReader(isr);
201                     String line = null;
202                     while (null != (line = br.readLine())) {
203                         nl.add(line);
204                     }
205                     names = (String[])nl.toArray(new String[nl.size()]);
206                 }
207                 catch (Exception e) {
208                     System.err.println(e);
209                     throw new IllegalStateException();
210                 } finally {
211                     if (br != null) {
212                         try {
213                             br.close();
214                         } catch (Exception e) {
215                             // ignore
216                         }
217                     }
218                 }
219 
220             } else { // a list of names separated by semicolons
221                 names = namelist.split("\\s*;\\s*");
222             }
223             processPairInfo(names);
224         }
225 
226         pw.flush();
227 
228         return this;
229     }
230 
processPairInfo(String[] names)231     private void processPairInfo(String[] names) {
232         ArrayList cl = new ArrayList();
233         ArrayList nl = new ArrayList();
234         for (int i = 0; i < names.length; ++i) {
235             String name = names[i];
236             String srcName = srcPrefix;
237             String trgName = trgPrefix;
238 
239             int n = name.indexOf('/');
240             if (n == -1) {
241                 srcName += name;
242                 trgName += name;
243             } else {
244                 String srcSuffix = name.substring(0, n).trim();
245                 String trgSuffix = name.substring(n+1).trim();
246                 int jx = srcSuffix.length()+1;
247                 int ix = trgSuffix.length()+1;
248                 while (ix != -1) {
249                     jx = srcSuffix.lastIndexOf('.', jx-1);
250                     ix = trgSuffix.lastIndexOf('.', ix-1);
251                 }
252                 srcName += srcSuffix;
253                 trgName += srcSuffix.substring(0, jx+1) + trgSuffix;
254             }
255 
256             try {
257                 Class jc = Class.forName(srcName);
258                 Class ic = Class.forName(trgName);
259                 cl.add(ic);
260                 cl.add(jc);
261                 nl.add(ic.getName());
262                 nl.add(jc.getName());
263             }
264             catch (Exception e) {
265                 if (DEBUG) System.err.println("can't load class: " + e.getMessage());
266             }
267         }
268         classPairs = (Class[])cl.toArray(new Class[cl.size()]);
269         namePairs = (String[])nl.toArray(new String[nl.size()]);
270     }
271 
println(String s)272     private void println(String s) {
273         if (pw != null) pw.println(s);
274     }
275 
flush()276     private void flush() {
277         if (pw != null) pw.flush();
278     }
279 
process()280     public int process() {
281         // set defaults
282         if (srcPrefix == null) {
283             srcPrefix = kSrcPrefix;
284         }
285 
286         if (trgPrefix == null) {
287             trgPrefix = kTrgPrefix;
288         }
289 
290         if (classPairs == null) {
291             processPairInfo(kPairInfo);
292         }
293 
294         if (ignore == null) {
295             ignore = kIgnore;
296         }
297 
298         println("ICU and Java API Comparison");
299         String ICU_VERSION = "unknown";
300         try {
301             Class cls = Class.forName("com.ibm.icu.util.VersionInfo");
302             Field fld = cls.getField("ICU_VERSION");
303             ICU_VERSION = fld.get(null).toString();
304         }
305         catch (Exception e) {
306             if (DEBUG) System.err.println("can't get VersionInfo: " + e.getMessage());
307         }
308         println("ICU Version " + ICU_VERSION);
309         println("JDK Version " + System.getProperty("java.version"));
310 
311         int errorCount = 0;
312         for (int i = 0; i < classPairs.length; i += 2) {
313             try {
314                 if (swap) {
315                     errorCount += compare(classPairs[i+1], classPairs[i]);
316                 } else {
317                     errorCount += compare(classPairs[i], classPairs[i+1]);
318                 }
319             }
320             catch (Exception e) {
321                 System.err.println("exception: " + e);
322                 System.err.println("between " + namePairs[i] + " and " + namePairs[i+1]);
323                 e.printStackTrace();
324                 errorCount += 1;
325             }
326         }
327         return errorCount;
328     }
329 
330     static class MorC {
331         private Method mref;
332         private Constructor cref;
333 
MorC(Method m)334         MorC(Method m) {
335             mref = m;
336         }
337 
MorC(Constructor c)338         MorC(Constructor c) {
339             cref = c;
340         }
341 
getModifiers()342         int getModifiers() {
343             return mref == null ? cref.getModifiers() : mref.getModifiers();
344         }
345 
getReturnType()346         Class getReturnType() {
347             return mref == null ? void.class : mref.getReturnType();
348         }
349 
getParameterTypes()350         Class[] getParameterTypes() {
351             return mref == null ? cref.getParameterTypes() : mref.getParameterTypes();
352         }
353 
getName()354         String getName() {
355             return mref == null ? "<init>" : mref.getName();
356         }
357 
getSignature()358         String getSignature() {
359             return mref == null ? cref.toString() : mref.toString();
360         }
361     }
362 
compare(Class class1, Class class2)363     private int compare(Class class1, Class class2) throws Exception {
364         String n1 = class1.getName();
365         String n2 = class2.getName();
366 
367         println("\ncompare " + n1 + " <> " + n2);
368 
369         MorC[] conss1 = getMorCArray(class1.getConstructors());
370         MorC[] conss2 = getMorCArray(class2.getConstructors());
371 
372         Map cmap1 = getMethodMap(conss1);
373         Map cmap2 = getMethodMap(conss2);
374 
375         MorC[] meths1 = getMorCArray(class1.getMethods());
376         MorC[] meths2 = getMorCArray(class2.getMethods());
377 
378         Map map1 = getMethodMap(meths1);
379         Map map2 = getMethodMap(meths2);
380 
381         Field[] fields1 = class1.getFields();
382         Field[] fields2 = class2.getFields();
383 
384         Set set1 = getFieldSet(fields1);
385         Set set2 = getFieldSet(fields2);
386 
387         if (n1.indexOf("DecimalFormatSymbols") != -1) {
388           pw.format("fields in %s: %s%n", n1, set1);
389           pw.format("fields in %s: %s%n", n2, set2);
390         }
391 
392         Map diffConss = diffMethodMaps(cmap2, cmap1);
393         Map diffMeths = diffMethodMaps(map2, map1);
394         Set diffFields = diffFieldSets(set2, set1);
395 
396         diffConss = removeIgnored(n2, diffConss);
397         diffMeths = removeIgnored(n2, diffMeths);
398         diffFields = removeIgnored(n2, diffFields);
399 
400         int result = diffConss.size() + diffMeths.size() + diffFields.size();
401         if (result > 0 && pw != null) {
402             pw.println("Public API in " + n2 + " but not in " + n1);
403             if (diffConss.size() > 0) {
404                 pw.println("CONSTRUCTORS");
405                 dumpMethodMap(diffConss, pw);
406             }
407             if (diffMeths.size() > 0) {
408                 pw.println("METHODS");
409                 dumpMethodMap(diffMeths, pw);
410             }
411             if (diffFields.size() > 0) {
412                 pw.println("FIELDS");
413                 dumpFieldSet(diffFields, pw);
414             }
415         }
416 
417         flush();
418 
419         return result;
420     }
421 
422     final class MethodRecord {
423         MorC[] overrides;
424 
MethodRecord(MorC m)425         MethodRecord(MorC m) {
426             overrides = new MorC[] { m };
427         }
428 
MethodRecord(MorC[] ms)429         MethodRecord(MorC[] ms) {
430             overrides = ms;
431         }
432 
copy()433         MethodRecord copy() {
434             return new MethodRecord((MorC[])overrides.clone());
435         }
436 
count()437         int count() {
438             for (int i = 0; i < overrides.length; ++i) {
439                 if (overrides[i] == null) {
440                     return i;
441                 }
442             }
443             return overrides.length;
444         }
445 
add(MorC m)446         void add(MorC m) {
447             MorC[] temp = new MorC[overrides.length + 1];
448             for (int i = 0; i < overrides.length; ++i) {
449                 temp[i] = overrides[i];
450             }
451             temp[overrides.length] = m;
452             overrides = temp;
453         }
454 
remove(int index)455         void remove(int index) {
456             int i = index;
457             while (overrides[i] != null && i < overrides.length-1) {
458                 overrides[i] = overrides[i+1];
459                 ++i;
460             }
461             overrides[i] = null;
462         }
463 
464         // if a call to a method can be handled by a call to t, remove the
465         // method from our list, and return true
removeOverridden(MorC t)466         boolean removeOverridden(MorC t) {
467             boolean result = false;
468             int i = 0;
469             while (i < overrides.length) {
470                 MorC m = overrides[i];
471                 if (m == null) {
472                     break;
473                 }
474                 if (handles(t, m)) {
475                     remove(i);
476                     result = true;
477                 } else {
478                     ++i;
479                 }
480             }
481             return result;
482         }
483 
484         // remove all methods handled by any method of mr
removeOverridden(MethodRecord mr)485         boolean removeOverridden(MethodRecord mr) {
486             boolean result = false;
487             for (int i = 0; i < mr.overrides.length; ++i) {
488                 MorC t = mr.overrides[i];
489                 if (t == null) {
490                     // this shouldn't happen, as the target record should not have been modified
491                     throw new IllegalStateException();
492                 }
493                 if (removeOverridden(t)) {
494                     result = true;
495                 }
496             }
497             return result;
498         }
499 
debugmsg(MorC t, MorC m, String msg)500         void debugmsg(MorC t, MorC m, String msg) {
501             StringBuffer buf = new StringBuffer();
502             buf.append(t.getName());
503             buf.append(" ");
504             buf.append(msg);
505             buf.append("\n   ");
506             toString(t, buf);
507             buf.append("\n   ");
508             toString(m, buf);
509             System.out.println(buf.toString());
510         }
511 
handles(MorC t, MorC m)512         boolean handles(MorC t, MorC m) {
513             // relevant modifiers must match
514             if ((t.getModifiers() & MOD_MASK) != (m.getModifiers() & MOD_MASK)) {
515                 if (DEBUG) debugmsg(t, m, "modifier mismatch");
516                 return false;
517             }
518 
519             Class tr = pairClassEquivalent(t.getReturnType());
520             Class mr = pairClassEquivalent(m.getReturnType());
521             if (!assignableFrom(mr, tr)) { // t return type must be same or narrower than m
522                 if (DEBUG) debugmsg(t, m, "return value mismatch");
523                 return false;
524             }
525             Class[] tts = t.getParameterTypes();
526             Class[] mts = m.getParameterTypes();
527             if (tts.length != mts.length) {
528                 if (DEBUG) debugmsg(t, m, "param count mismatch");
529                 return false;
530             }
531 
532             for (int i = 0; i < tts.length; ++i) {
533                 Class tc = pairClassEquivalent(tts[i]);
534                 Class mc = pairClassEquivalent(mts[i]);
535                 if (!assignableFrom(tc, mc)) { // m param must be same or narrower than t
536                     if (DEBUG) debugmsg(t, m, "parameter " + i + " mismatch, " +
537                                    tts[i].getName() + " not assignable from " + mts[i].getName());
538                     return false;
539                 }
540             }
541             return true;
542         }
543 
toString(MorC m, StringBuffer buf)544         public void toString(MorC m, StringBuffer buf) {
545             int mod = m.getModifiers();
546             if (mod != 0) {
547                 buf.append(Modifier.toString(mod) + " ");
548             }
549             buf.append(nameOf(m.getReturnType()));
550             buf.append(" ");
551             buf.append(m.getName());
552             buf.append("(");
553             Class[] ptypes = m.getParameterTypes();
554             for (int j = 0; j < ptypes.length; ++j) {
555                 if (j > 0) {
556                     buf.append(", ");
557                 }
558                 buf.append(nameOf(ptypes[j]));
559             }
560             buf.append(')');
561         }
562 
toString()563         public String toString() {
564             StringBuffer buf = new StringBuffer();
565             buf.append(overrides[0].getName());
566             for (int i = 0; i < overrides.length; ++i) {
567                 MorC m = overrides[i];
568                 if (m == null) {
569                     break;
570                 }
571                 buf.append("\n   ");
572                 toString(m, buf);
573             }
574             return buf.toString();
575         }
576     }
577 
nameOf(Class c)578     public static String nameOf(Class c) {
579         if (c.isArray()) {
580             return nameOf(c.getComponentType()) + "[]";
581         }
582         String name = c.getName();
583         return name.substring(name.lastIndexOf('.') + 1);
584     }
585 
getMorCArray(Constructor[] cons)586     static MorC[] getMorCArray(Constructor[] cons) {
587         MorC[] result = new MorC[cons.length];
588         for (int i = 0 ; i < cons.length; ++i) {
589             result[i] = new MorC(cons[i]);
590         }
591         return result;
592     }
593 
getMorCArray(Method[] meths)594     static MorC[] getMorCArray(Method[] meths) {
595         MorC[] result = new MorC[meths.length];
596         for (int i = 0 ; i < meths.length; ++i) {
597             result[i] = new MorC(meths[i]);
598         }
599         return result;
600     }
601 
getMethodMap(MorC[] meths)602     private Map getMethodMap(MorC[] meths) {
603         Map result = new TreeMap();
604         for (int i = 0; i < meths.length; ++i) {
605             MorC m = meths[i];
606             String key = m.getName();
607             MethodRecord mr = (MethodRecord)result.get(key);
608             if (mr == null) {
609                 mr = new MethodRecord(m);
610                 result.put(key, mr);
611             } else {
612                 mr.add(m);
613             }
614         }
615         return result;
616     }
617 
dumpMethodMap(Map m, PrintWriter pw)618     private void dumpMethodMap(Map m, PrintWriter pw) {
619         Iterator iter = m.entrySet().iterator();
620         while (iter.hasNext()) {
621             dumpMethodRecord((MethodRecord)((Map.Entry)iter.next()).getValue());
622         }
623         pw.flush();
624     }
625 
dumpMethodRecord(MethodRecord mr)626     private void dumpMethodRecord(MethodRecord mr) {
627         pw.println(mr.toString());
628     }
629 
diffMethodMaps(Map m1, Map m2)630     static Map diffMethodMaps(Map m1, Map m2) {
631         // get all the methods in m1 that aren't mentioned in m2 at all
632         Map result = (Map)((TreeMap)m1).clone();
633         result.keySet().removeAll(m2.keySet());
634         return result;
635     }
636 
removeIgnored(String name, Map m1)637     private Map removeIgnored(String name, Map m1) {
638         if (ignore == null) {
639             return m1;
640         }
641         if (name.startsWith(srcPrefix)) {
642             name = name.substring(srcPrefix.length());
643         }
644         name += " "; // to avoid accidental prefix of nested class name
645 
646         // prune ignore list to relevant items
647         ArrayList il = null;
648         for (int i = 0; i < ignore.length; ++i) {
649             String s = ignore[i];
650             if (s.startsWith(name)) {
651                 if (il == null) {
652                     il = new ArrayList();
653                 }
654                 il.add(s);
655             }
656         }
657         if (il == null) {
658             return m1;
659         }
660 
661         Map result = new TreeMap(((TreeMap)m1).comparator());
662         result.putAll(m1);
663         Iterator iter = result.entrySet().iterator();
664         loop: while (iter.hasNext()) {
665             Map.Entry e = (Map.Entry)iter.next();
666             String key = (String)e.getKey();
667             for (int i = 0; i < il.size(); ++i) {
668                 String ig = (String)il.get(i);
669                 if (ig.indexOf(" " + key) != 0) {
670                     iter.remove();
671                     continue loop;
672                 }
673             }
674         }
675         return result;
676     }
677 
removeIgnored(String name, Set s1)678     private Set removeIgnored(String name, Set s1) {
679         if (ignore == null) {
680             return s1;
681         }
682         if (name.startsWith(srcPrefix)) {
683             name = name.substring(srcPrefix.length());
684         }
685         name += " "; // to avoid accidental prefix of nested class name
686 
687         // prune ignore list to relevant items
688         ArrayList il = null;
689         for (int i = 0; i < ignore.length; ++i) {
690             String s = ignore[i];
691             if (s.startsWith(name)) {
692                 if (il == null) {
693                     il = new ArrayList();
694                 }
695                 il.add(s);
696             }
697         }
698         if (il == null) {
699             return s1;
700         }
701 
702         Set result = (Set)((TreeSet)s1).clone();
703         Iterator iter = result.iterator();
704         loop: while (iter.hasNext()) {
705             String key = (String)iter.next();
706             String fieldname = key.substring(0, key.indexOf(' '));
707             for (int i = 0; i < il.size(); ++i) {
708                 String ig = (String)il.get(i);
709                 if (ig.indexOf(" " + fieldname) != 0) {
710                     iter.remove();
711                     continue loop;
712                 }
713             }
714         }
715         return result;
716     }
717 
718     static final boolean[][] assignmentMap = {
719         // bool   char   byte  short    int   long  float double   void
720         {  true, false, false, false, false, false, false, false, false }, // boolean
721         { false,  true,  true,  true, false, false, false, false, false }, // char
722         { false, false,  true, false, false, false, false, false, false }, // byte
723         { false, false,  true,  true, false, false, false, false, false }, // short
724         { false,  true,  true,  true,  true, false, false, false, false }, // int
725         { false,  true,  true,  true,  true,  true, false, false, false }, // long
726         { false,  true,  true,  true,  true, false,  true, false, false }, // float
727         { false,  true,  true,  true,  true, false,  true,  true, false }, // double
728         { false, false, false, false, false, false, false, false,  true }, // void
729     };
730 
731     static final Class[] prims = {
732         boolean.class, char.class, byte.class, short.class,
733         int.class, long.class, float.class, double.class, void.class
734     };
735 
primIndex(Class cls)736     static int primIndex(Class cls) {
737         for (int i = 0; i < prims.length; ++i) {
738             if (cls == prims[i]) {
739                 return i;
740             }
741         }
742         throw new IllegalStateException("could not find primitive class: " + cls);
743     }
744 
assignableFrom(Class lhs, Class rhs)745     static boolean assignableFrom(Class lhs, Class rhs) {
746         if (lhs == rhs) {
747             return true;
748         }
749         if (lhs.isPrimitive()) {
750             if (!rhs.isPrimitive()) {
751                 return false;
752             }
753             int lhsx = primIndex(lhs);
754             int rhsx = primIndex(rhs);
755             return assignmentMap[lhsx][rhsx];
756         }
757         return lhs.isAssignableFrom(rhs);
758     }
759 
toString(Field f)760     private String toString(Field f) {
761         StringBuffer buf = new StringBuffer(f.getName());
762         int mod = f.getModifiers() & MOD_MASK;
763         if (mod != 0) {
764             buf.append(" " + Modifier.toString(mod));
765         }
766         buf.append(" ");
767         String n = pairEquivalent(f.getType().getName());
768         n = n.substring(n.lastIndexOf('.') + 1);
769         buf.append(n);
770         return buf.toString();
771     }
772 
getFieldSet(Field[] fs)773     private Set getFieldSet(Field[] fs) {
774         Set set = new TreeSet();
775         for (int i = 0; i < fs.length; ++i) {
776             set.add(toString(fs[i]));
777         }
778         return set;
779     }
780 
diffFieldSets(Set s1, Set s2)781     static Set diffFieldSets(Set s1, Set s2) {
782         Set result = (Set)((TreeSet)s1).clone();
783         result.removeAll(s2);
784         return result;
785     }
786 
dumpFieldSet(Set s, PrintWriter pw)787     private void dumpFieldSet(Set s, PrintWriter pw) {
788         Iterator iter = s.iterator();
789         while (iter.hasNext()) {
790             pw.println(iter.next());
791         }
792         pw.flush();
793     }
794 
795     // given a target string, if it matches the first of one of our pairs, return the second
796     // or vice-versa if swap is true
pairEquivalent(String target)797     private String pairEquivalent(String target) {
798         for (int i = 0; i < namePairs.length; i += 2) {
799             if (swap) {
800                 if (target.equals(namePairs[i+1])) {
801                     return namePairs[i];
802                 }
803             } else {
804                 if (target.equals(namePairs[i])) {
805                     return namePairs[i+1];
806                 }
807             }
808         }
809         return target;
810     }
811 
pairClassEquivalent(Class target)812     private Class pairClassEquivalent(Class target) {
813         for (int i = 0; i < classPairs.length; i += 2) {
814             if (target.equals(classPairs[i])) {
815                 return classPairs[i+1];
816             }
817         }
818         return target;
819     }
820 
821     static final int MOD_MASK = ~(Modifier.FINAL|Modifier.SYNCHRONIZED|
822                                   Modifier.VOLATILE|Modifier.TRANSIENT|Modifier.NATIVE);
823 }
824