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 } 822