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