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