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