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
11package com.ibm.icu.dev.tool.docs;
12
13import java.io.BufferedReader;
14import java.io.File;
15import java.io.FileInputStream;
16import java.io.InputStreamReader;
17import java.io.PrintWriter;
18import java.lang.reflect.Constructor;
19import java.lang.reflect.Field;
20import java.lang.reflect.Method;
21import java.lang.reflect.Modifier;
22import java.util.ArrayList;
23import java.util.Iterator;
24import java.util.Map;
25import java.util.Set;
26import java.util.TreeMap;
27import 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 */
37public 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
83    public static void main(String[] args) {
84        System.exit(doMain(args));
85    }
86
87    // return non-zero if there were some missing APIs
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
96    public ICUJDKCompare setOutputWriter(PrintWriter pw) {
97        this.pw = pw;
98        return this;
99    }
100
101    public ICUJDKCompare setSrcPrefix(String srcPrefix) {
102        this.srcPrefix = srcPrefix;
103        return this;
104    }
105
106    public ICUJDKCompare setTrgPrefix(String trgPrefix) {
107        this.trgPrefix = trgPrefix;
108        return this;
109    }
110
111    public ICUJDKCompare setClassPairs(Class[] classPairs) {
112        this.classPairs = classPairs;
113        return this;
114    }
115
116    public ICUJDKCompare setNamePairs(String[] namePairs) {
117        this.namePairs = namePairs;
118        return this;
119    }
120
121    public ICUJDKCompare setIgnore(String[] ignore) {
122        this.ignore = ignore;
123        return this;
124    }
125
126    public ICUJDKCompare setSwap(boolean swap) {
127        this.swap = swap;
128        return this;
129    }
130
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
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
272    private void println(String s) {
273        if (pw != null) pw.println(s);
274    }
275
276    private void flush() {
277        if (pw != null) pw.flush();
278    }
279
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
334        MorC(Method m) {
335            mref = m;
336        }
337
338        MorC(Constructor c) {
339            cref = c;
340        }
341
342        int getModifiers() {
343            return mref == null ? cref.getModifiers() : mref.getModifiers();
344        }
345
346        Class getReturnType() {
347            return mref == null ? void.class : mref.getReturnType();
348        }
349
350        Class[] getParameterTypes() {
351            return mref == null ? cref.getParameterTypes() : mref.getParameterTypes();
352        }
353
354        String getName() {
355            return mref == null ? "<init>" : mref.getName();
356        }
357
358        String getSignature() {
359            return mref == null ? cref.toString() : mref.toString();
360        }
361    }
362
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
425        MethodRecord(MorC m) {
426            overrides = new MorC[] { m };
427        }
428
429        MethodRecord(MorC[] ms) {
430            overrides = ms;
431        }
432
433        MethodRecord copy() {
434            return new MethodRecord((MorC[])overrides.clone());
435        }
436
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
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
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
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
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
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
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
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
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
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
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
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
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
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
626    private void dumpMethodRecord(MethodRecord mr) {
627        pw.println(mr.toString());
628    }
629
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
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
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
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
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
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
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
781    static Set diffFieldSets(Set s1, Set s2) {
782        Set result = (Set)((TreeSet)s1).clone();
783        result.removeAll(s2);
784        return result;
785    }
786
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
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
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