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