1 /* 2 * Copyright (C) 2010 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.doclava; 18 19 import java.io.BufferedOutputStream; 20 import java.io.File; 21 import java.io.FileNotFoundException; 22 import java.io.FileOutputStream; 23 import java.io.PrintStream; 24 import java.util.ArrayList; 25 import java.util.Arrays; 26 import java.util.Collection; 27 import java.util.Collections; 28 import java.util.HashMap; 29 import java.util.HashSet; 30 import java.util.List; 31 import java.util.Set; 32 33 public class Stubs { 34 public static void writeStubsAndApi(String stubsDir, String apiFile, String keepListFile, 35 HashSet<String> stubPackages) { 36 // figure out which classes we need 37 final HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>(); 38 ClassInfo[] all = Converter.allClasses(); 39 PrintStream apiWriter = null; 40 PrintStream keepListWriter = null; 41 if (apiFile != null) { 42 try { 43 File xml = new File(apiFile); 44 xml.getParentFile().mkdirs(); 45 apiWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(xml))); 46 } catch (FileNotFoundException e) { 47 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(apiFile, 0, 0), 48 "Cannot open file for write."); 49 } 50 } 51 if (keepListFile != null) { 52 try { 53 File keepList = new File(keepListFile); 54 keepList.getParentFile().mkdirs(); 55 keepListWriter = new PrintStream(new BufferedOutputStream(new FileOutputStream(keepList))); 56 } catch (FileNotFoundException e) { 57 Errors.error(Errors.IO_ERROR, new SourcePositionInfo(keepListFile, 0, 0), 58 "Cannot open file for write."); 59 } 60 } 61 // If a class is public or protected, not hidden, and marked as included, 62 // then we can't strip it 63 for (ClassInfo cl : all) { 64 if (cl.checkLevel() && cl.isIncluded()) { 65 cantStripThis(cl, notStrippable, "0:0"); 66 } 67 } 68 69 // complain about anything that looks includeable but is not supposed to 70 // be written, e.g. hidden things 71 for (ClassInfo cl : notStrippable) { 72 if (!cl.isHidden()) { 73 for (MethodInfo m : cl.selfMethods()) { 74 if (m.isHidden()) { 75 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to hidden method " 76 + m.name()); 77 } else if (m.isDeprecated()) { 78 // don't bother reporting deprecated methods 79 // unless they are public 80 Errors.error(Errors.DEPRECATED, m.position(), "Method " + cl.qualifiedName() + "." 81 + m.name() + " is deprecated"); 82 } 83 84 ClassInfo returnClass = m.returnType().asClassInfo(); 85 if (returnClass != null && returnClass.isHidden()) { 86 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Method " + cl.qualifiedName() 87 + "." + m.name() + " returns unavailable type " + returnClass.name()); 88 } 89 90 for (ParameterInfo p : m.parameters()) { 91 TypeInfo t = p.type(); 92 if (!t.isPrimitive()) { 93 if (t.asClassInfo().isHidden()) { 94 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Parameter of hidden type " 95 + t.fullName() + " in " + cl.qualifiedName() + "." + m.name() + "()"); 96 } 97 } 98 } 99 } 100 101 // annotations are handled like methods 102 for (MethodInfo m : cl.annotationElements()) { 103 if (m.isHidden()) { 104 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Reference to hidden annotation " 105 + m.name()); 106 } 107 108 ClassInfo returnClass = m.returnType().asClassInfo(); 109 if (returnClass != null && returnClass.isHidden()) { 110 Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(), "Annotation '" + m.name() 111 + "' returns unavailable type " + returnClass.name()); 112 } 113 114 for (ParameterInfo p : m.parameters()) { 115 TypeInfo t = p.type(); 116 if (!t.isPrimitive()) { 117 if (t.asClassInfo().isHidden()) { 118 Errors.error(Errors.UNAVAILABLE_SYMBOL, p.position(), 119 "Reference to unavailable annotation class " + t.fullName()); 120 } 121 } 122 } 123 } 124 } else if (cl.isDeprecated()) { 125 // not hidden, but deprecated 126 Errors.error(Errors.DEPRECATED, cl.position(), "Class " + cl.qualifiedName() 127 + " is deprecated"); 128 } 129 } 130 131 HashMap<PackageInfo, List<ClassInfo>> packages = new HashMap<PackageInfo, List<ClassInfo>>(); 132 for (ClassInfo cl : notStrippable) { 133 if (!cl.isDocOnly()) { 134 if (stubPackages == null || stubPackages.contains(cl.containingPackage().name())) { 135 // write out the stubs 136 if (stubsDir != null) { 137 writeClassFile(stubsDir, notStrippable, cl); 138 } 139 // build class list for api file or keep list file 140 if (apiWriter != null || keepListWriter != null) { 141 if (packages.containsKey(cl.containingPackage())) { 142 packages.get(cl.containingPackage()).add(cl); 143 } else { 144 ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>(); 145 classes.add(cl); 146 packages.put(cl.containingPackage(), classes); 147 } 148 } 149 } 150 } 151 } 152 153 // write out the Api 154 if (apiWriter != null) { 155 writeApi(apiWriter, packages, notStrippable); 156 apiWriter.close(); 157 } 158 159 // write out the keep list 160 if (keepListWriter != null) { 161 writeKeepList(keepListWriter, packages, notStrippable); 162 keepListWriter.close(); 163 } 164 } 165 166 public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why) { 167 168 if (!notStrippable.add(cl)) { 169 // slight optimization: if it already contains cl, it already contains 170 // all of cl's parents 171 return; 172 } 173 cl.setReasonIncluded(why); 174 175 // cant strip annotations 176 /* 177 * if (cl.annotations() != null){ for (AnnotationInstanceInfo ai : cl.annotations()){ if 178 * (ai.type() != null){ cantStripThis(ai.type(), notStrippable, "1:" + cl.qualifiedName()); } } 179 * } 180 */ 181 // cant strip any public fields or their generics 182 if (cl.allSelfFields() != null) { 183 for (FieldInfo fInfo : cl.allSelfFields()) { 184 if (fInfo.type() != null) { 185 if (fInfo.type().asClassInfo() != null) { 186 cantStripThis(fInfo.type().asClassInfo(), notStrippable, "2:" + cl.qualifiedName()); 187 } 188 if (fInfo.type().typeArguments() != null) { 189 for (TypeInfo tTypeInfo : fInfo.type().typeArguments()) { 190 if (tTypeInfo.asClassInfo() != null) { 191 cantStripThis(tTypeInfo.asClassInfo(), notStrippable, "3:" + cl.qualifiedName()); 192 } 193 } 194 } 195 } 196 } 197 } 198 // cant strip any of the type's generics 199 if (cl.asTypeInfo() != null) { 200 if (cl.asTypeInfo().typeArguments() != null) { 201 for (TypeInfo tInfo : cl.asTypeInfo().typeArguments()) { 202 if (tInfo.asClassInfo() != null) { 203 cantStripThis(tInfo.asClassInfo(), notStrippable, "4:" + cl.qualifiedName()); 204 } 205 } 206 } 207 } 208 // cant strip any of the annotation elements 209 // cantStripThis(cl.annotationElements(), notStrippable); 210 // take care of methods 211 cantStripThis(cl.allSelfMethods(), notStrippable); 212 cantStripThis(cl.allConstructors(), notStrippable); 213 // blow the outer class open if this is an inner class 214 if (cl.containingClass() != null) { 215 cantStripThis(cl.containingClass(), notStrippable, "5:" + cl.qualifiedName()); 216 } 217 // blow open super class and interfaces 218 ClassInfo supr = cl.realSuperclass(); 219 if (supr != null) { 220 if (supr.isHidden()) { 221 // cl is a public class declared as extending a hidden superclass. 222 // this is not a desired practice but it's happened, so we deal 223 // with it by finding the first super class which passes checklevel for purposes of 224 // generating the doc & stub information, and proceeding normally. 225 cl.init(cl.asTypeInfo(), cl.realInterfaces(), cl.realInterfaceTypes(), cl.innerClasses(), 226 cl.allConstructors(), cl.allSelfMethods(), cl.annotationElements(), cl.allSelfFields(), 227 cl.enumConstants(), cl.containingPackage(), cl.containingClass(), 228 supr.superclass(), supr.superclassType(), cl.annotations()); 229 Errors.error(Errors.HIDDEN_SUPERCLASS, cl.position(), "Public class " + cl.qualifiedName() 230 + " stripped of unavailable superclass " + supr.qualifiedName()); 231 } else { 232 cantStripThis(supr, notStrippable, "6:" + cl.realSuperclass().name() + cl.qualifiedName()); 233 } 234 } 235 } 236 237 private static void cantStripThis(ArrayList<MethodInfo> mInfos, HashSet<ClassInfo> notStrippable) { 238 // for each method, blow open the parameters, throws and return types. also blow open their 239 // generics 240 if (mInfos != null) { 241 for (MethodInfo mInfo : mInfos) { 242 if (mInfo.getTypeParameters() != null) { 243 for (TypeInfo tInfo : mInfo.getTypeParameters()) { 244 if (tInfo.asClassInfo() != null) { 245 cantStripThis(tInfo.asClassInfo(), notStrippable, "8:" 246 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name()); 247 } 248 } 249 } 250 if (mInfo.parameters() != null) { 251 for (ParameterInfo pInfo : mInfo.parameters()) { 252 if (pInfo.type() != null && pInfo.type().asClassInfo() != null) { 253 cantStripThis(pInfo.type().asClassInfo(), notStrippable, "9:" 254 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name()); 255 if (pInfo.type().typeArguments() != null) { 256 for (TypeInfo tInfoType : pInfo.type().typeArguments()) { 257 if (tInfoType.asClassInfo() != null) { 258 ClassInfo tcl = tInfoType.asClassInfo(); 259 if (tcl.isHidden()) { 260 Errors 261 .error(Errors.UNAVAILABLE_SYMBOL, mInfo.position(), 262 "Parameter of hidden type " + tInfoType.fullName() + " in " 263 + mInfo.containingClass().qualifiedName() + '.' + mInfo.name() 264 + "()"); 265 } else { 266 cantStripThis(tcl, notStrippable, "10:" 267 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name()); 268 } 269 } 270 } 271 } 272 } 273 } 274 } 275 for (ClassInfo thrown : mInfo.thrownExceptions()) { 276 cantStripThis(thrown, notStrippable, "11:" + mInfo.realContainingClass().qualifiedName() 277 + ":" + mInfo.name()); 278 } 279 if (mInfo.returnType() != null && mInfo.returnType().asClassInfo() != null) { 280 cantStripThis(mInfo.returnType().asClassInfo(), notStrippable, "12:" 281 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name()); 282 if (mInfo.returnType().typeArguments() != null) { 283 for (TypeInfo tyInfo : mInfo.returnType().typeArguments()) { 284 if (tyInfo.asClassInfo() != null) { 285 cantStripThis(tyInfo.asClassInfo(), notStrippable, "13:" 286 + mInfo.realContainingClass().qualifiedName() + ":" + mInfo.name()); 287 } 288 } 289 } 290 } 291 } 292 } 293 } 294 295 static String javaFileName(ClassInfo cl) { 296 String dir = ""; 297 PackageInfo pkg = cl.containingPackage(); 298 if (pkg != null) { 299 dir = pkg.name(); 300 dir = dir.replace('.', '/') + '/'; 301 } 302 return dir + cl.name() + ".java"; 303 } 304 305 static void writeClassFile(String stubsDir, HashSet<ClassInfo> notStrippable, ClassInfo cl) { 306 // inner classes are written by their containing class 307 if (cl.containingClass() != null) { 308 return; 309 } 310 311 // Work around the bogus "Array" class we invent for 312 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505) 313 if (cl.containingPackage() != null 314 && cl.containingPackage().name().equals(PackageInfo.DEFAULT_PACKAGE)) { 315 return; 316 } 317 318 String filename = stubsDir + '/' + javaFileName(cl); 319 File file = new File(filename); 320 ClearPage.ensureDirectory(file); 321 322 PrintStream stream = null; 323 try { 324 stream = new PrintStream(new BufferedOutputStream(new FileOutputStream(file))); 325 writeClassFile(stream, notStrippable, cl); 326 } catch (FileNotFoundException e) { 327 System.err.println("error writing file: " + filename); 328 } finally { 329 if (stream != null) { 330 stream.close(); 331 } 332 } 333 } 334 335 static void writeClassFile(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) { 336 PackageInfo pkg = cl.containingPackage(); 337 if (pkg != null) { 338 stream.println("package " + pkg.name() + ";"); 339 } 340 writeClass(stream, notStrippable, cl); 341 } 342 343 static void writeClass(PrintStream stream, HashSet<ClassInfo> notStrippable, ClassInfo cl) { 344 writeAnnotations(stream, cl.annotations(), cl.isDeprecated()); 345 346 stream.print(cl.scope() + " "); 347 if (cl.isAbstract() && !cl.isAnnotation() && !cl.isInterface()) { 348 stream.print("abstract "); 349 } 350 if (cl.isStatic()) { 351 stream.print("static "); 352 } 353 if (cl.isFinal() && !cl.isEnum()) { 354 stream.print("final "); 355 } 356 if (false) { 357 stream.print("strictfp "); 358 } 359 360 HashSet<String> classDeclTypeVars = new HashSet(); 361 String leafName = cl.asTypeInfo().fullName(classDeclTypeVars); 362 int bracket = leafName.indexOf('<'); 363 if (bracket < 0) bracket = leafName.length() - 1; 364 int period = leafName.lastIndexOf('.', bracket); 365 if (period < 0) period = -1; 366 leafName = leafName.substring(period + 1); 367 368 String kind = cl.kind(); 369 stream.println(kind + " " + leafName); 370 371 TypeInfo base = cl.superclassType(); 372 373 if (!"enum".equals(kind)) { 374 if (base != null && !"java.lang.Object".equals(base.qualifiedTypeName())) { 375 stream.println(" extends " + base.fullName(classDeclTypeVars)); 376 } 377 } 378 379 List<TypeInfo> usedInterfaces = new ArrayList<TypeInfo>(); 380 for (TypeInfo iface : cl.realInterfaceTypes()) { 381 if (notStrippable.contains(iface.asClassInfo()) && !iface.asClassInfo().isDocOnly()) { 382 usedInterfaces.add(iface); 383 } 384 } 385 if (usedInterfaces.size() > 0 && !cl.isAnnotation()) { 386 // can java annotations extend other ones? 387 if (cl.isInterface() || cl.isAnnotation()) { 388 stream.print(" extends "); 389 } else { 390 stream.print(" implements "); 391 } 392 String comma = ""; 393 for (TypeInfo iface : usedInterfaces) { 394 stream.print(comma + iface.fullName(classDeclTypeVars)); 395 comma = ", "; 396 } 397 stream.println(); 398 } 399 400 stream.println("{"); 401 402 ArrayList<FieldInfo> enumConstants = cl.enumConstants(); 403 int N = enumConstants.size(); 404 int i = 0; 405 for (FieldInfo field : enumConstants) { 406 if (!field.constantLiteralValue().equals("null")) { 407 stream.println(field.name() + "(" + field.constantLiteralValue() 408 + (i == N - 1 ? ");" : "),")); 409 } else { 410 stream.println(field.name() + "(" + (i == N - 1 ? ");" : "),")); 411 } 412 i++; 413 } 414 415 for (ClassInfo inner : cl.getRealInnerClasses()) { 416 if (notStrippable.contains(inner) && !inner.isDocOnly()) { 417 writeClass(stream, notStrippable, inner); 418 } 419 } 420 421 422 for (MethodInfo method : cl.constructors()) { 423 if (!method.isDocOnly()) { 424 writeMethod(stream, method, true); 425 } 426 } 427 428 boolean fieldNeedsInitialization = false; 429 boolean staticFieldNeedsInitialization = false; 430 for (FieldInfo field : cl.allSelfFields()) { 431 if (!field.isDocOnly()) { 432 if (!field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) { 433 fieldNeedsInitialization = true; 434 } 435 if (field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) { 436 staticFieldNeedsInitialization = true; 437 } 438 } 439 } 440 441 // The compiler includes a default public constructor that calls the super classes 442 // default constructor in the case where there are no written constructors. 443 // So, if we hide all the constructors, java may put in a constructor 444 // that calls a nonexistent super class constructor. So, if there are no constructors, 445 // and the super class doesn't have a default constructor, write in a private constructor 446 // that works. TODO -- we generate this as protected, but we really should generate 447 // it as private unless it also exists in the real code. 448 if ((cl.constructors().isEmpty() && (!cl.getNonWrittenConstructors().isEmpty() || fieldNeedsInitialization)) 449 && !cl.isAnnotation() && !cl.isInterface() && !cl.isEnum()) { 450 // Errors.error(Errors.HIDDEN_CONSTRUCTOR, 451 // cl.position(), "No constructors " + 452 // "found and superclass has no parameterless constructor. A constructor " + 453 // "that calls an appropriate superclass constructor " + 454 // "was automatically written to stubs.\n"); 455 stream.println(cl.leafName() + "() { " + superCtorCall(cl, null) + "throw new" 456 + " RuntimeException(\"Stub!\"); }"); 457 } 458 459 for (MethodInfo method : cl.allSelfMethods()) { 460 if (cl.isEnum()) { 461 if (("values".equals(method.name()) && "()".equals(method.signature())) 462 || ("valueOf".equals(method.name()) && "(java.lang.String)".equals(method.signature()))) { 463 // skip these two methods on enums, because they're synthetic, 464 // although for some reason javadoc doesn't mark them as synthetic, 465 // maybe because they still want them documented 466 continue; 467 } 468 } 469 if (!method.isDocOnly()) { 470 writeMethod(stream, method, false); 471 } 472 } 473 // Write all methods that are hidden, but override abstract methods or interface methods. 474 // These can't be hidden. 475 for (MethodInfo method : cl.getHiddenMethods()) { 476 MethodInfo overriddenMethod = 477 method.findRealOverriddenMethod(method.name(), method.signature(), notStrippable); 478 ClassInfo classContainingMethod = 479 method.findRealOverriddenClass(method.name(), method.signature()); 480 if (overriddenMethod != null && !overriddenMethod.isHidden() && !overriddenMethod.isDocOnly() 481 && (overriddenMethod.isAbstract() || overriddenMethod.containingClass().isInterface())) { 482 method.setReason("1:" + classContainingMethod.qualifiedName()); 483 cl.addMethod(method); 484 writeMethod(stream, method, false); 485 } 486 } 487 488 for (MethodInfo element : cl.annotationElements()) { 489 if (!element.isDocOnly()) { 490 writeAnnotationElement(stream, element); 491 } 492 } 493 494 for (FieldInfo field : cl.allSelfFields()) { 495 if (!field.isDocOnly()) { 496 writeField(stream, field); 497 } 498 } 499 500 if (staticFieldNeedsInitialization) { 501 stream.print("static { "); 502 for (FieldInfo field : cl.allSelfFields()) { 503 if (!field.isDocOnly() && field.isStatic() && field.isFinal() && !fieldIsInitialized(field) 504 && field.constantValue() == null) { 505 stream.print(field.name() + " = " + field.type().defaultValue() + "; "); 506 } 507 } 508 stream.println("}"); 509 } 510 511 stream.println("}"); 512 } 513 514 515 static void writeMethod(PrintStream stream, MethodInfo method, boolean isConstructor) { 516 String comma; 517 518 writeAnnotations(stream, method.annotations(), method.isDeprecated()); 519 520 stream.print(method.scope() + " "); 521 if (method.isStatic()) { 522 stream.print("static "); 523 } 524 if (method.isFinal()) { 525 stream.print("final "); 526 } 527 if (method.isAbstract()) { 528 stream.print("abstract "); 529 } 530 if (method.isSynchronized()) { 531 stream.print("synchronized "); 532 } 533 if (method.isNative()) { 534 stream.print("native "); 535 } 536 if (false /* method.isStictFP() */) { 537 stream.print("strictfp "); 538 } 539 540 stream.print(method.typeArgumentsName(new HashSet()) + " "); 541 542 if (!isConstructor) { 543 stream.print(method.returnType().fullName(method.typeVariables()) + " "); 544 } 545 String n = method.name(); 546 int pos = n.lastIndexOf('.'); 547 if (pos >= 0) { 548 n = n.substring(pos + 1); 549 } 550 stream.print(n + "("); 551 comma = ""; 552 int count = 1; 553 int size = method.parameters().size(); 554 for (ParameterInfo param : method.parameters()) { 555 stream.print(comma + fullParameterTypeName(method, param.type(), count == size) + " " 556 + param.name()); 557 comma = ", "; 558 count++; 559 } 560 stream.print(")"); 561 562 comma = ""; 563 if (method.thrownExceptions().size() > 0) { 564 stream.print(" throws "); 565 for (ClassInfo thrown : method.thrownExceptions()) { 566 stream.print(comma + thrown.qualifiedName()); 567 comma = ", "; 568 } 569 } 570 if (method.isAbstract() || method.isNative() || method.containingClass().isInterface()) { 571 stream.println(";"); 572 } else { 573 stream.print(" { "); 574 if (isConstructor) { 575 stream.print(superCtorCall(method.containingClass(), method.thrownExceptions())); 576 } 577 stream.println("throw new RuntimeException(\"Stub!\"); }"); 578 } 579 } 580 581 static void writeField(PrintStream stream, FieldInfo field) { 582 writeAnnotations(stream, field.annotations(), field.isDeprecated()); 583 584 stream.print(field.scope() + " "); 585 if (field.isStatic()) { 586 stream.print("static "); 587 } 588 if (field.isFinal()) { 589 stream.print("final "); 590 } 591 if (field.isTransient()) { 592 stream.print("transient "); 593 } 594 if (field.isVolatile()) { 595 stream.print("volatile "); 596 } 597 598 stream.print(field.type().fullName()); 599 stream.print(" "); 600 stream.print(field.name()); 601 602 if (fieldIsInitialized(field)) { 603 stream.print(" = " + field.constantLiteralValue()); 604 } 605 606 stream.println(";"); 607 } 608 609 static boolean fieldIsInitialized(FieldInfo field) { 610 return (field.isFinal() && field.constantValue() != null) 611 || !field.type().dimension().equals("") || field.containingClass().isInterface(); 612 } 613 614 // Returns 'true' if the method is an @Override of a visible parent 615 // method implementation, and thus does not affect the API. 616 static boolean methodIsOverride(HashSet<ClassInfo> notStrippable, MethodInfo mi) { 617 // Abstract/static/final methods are always listed in the API description 618 if (mi.isAbstract() || mi.isStatic() || mi.isFinal()) { 619 return false; 620 } 621 622 // Find any relevant ancestor declaration and inspect it 623 MethodInfo om = mi.findSuperclassImplementation(notStrippable); 624 if (om != null) { 625 // Visibility mismatch is an API change, so check for it 626 if (mi.mIsPrivate == om.mIsPrivate && mi.mIsPublic == om.mIsPublic 627 && mi.mIsProtected == om.mIsProtected) { 628 // Look only for overrides of an ancestor class implementation, 629 // not of e.g. an abstract or interface method declaration 630 if (!om.isAbstract()) { 631 // If the parent is hidden, we can't rely on it to provide 632 // the API 633 if (!om.isHidden()) { 634 // If the only "override" turns out to be in our own class 635 // (which sometimes happens in concrete subclasses of 636 // abstract base classes), it's not really an override 637 if (!mi.mContainingClass.equals(om.mContainingClass)) { 638 return true; 639 } 640 } 641 } 642 } 643 } 644 return false; 645 } 646 647 static boolean canCallMethod(ClassInfo from, MethodInfo m) { 648 if (m.isPublic() || m.isProtected()) { 649 return true; 650 } 651 if (m.isPackagePrivate()) { 652 String fromPkg = from.containingPackage().name(); 653 String pkg = m.containingClass().containingPackage().name(); 654 if (fromPkg.equals(pkg)) { 655 return true; 656 } 657 } 658 return false; 659 } 660 661 // call a constructor, any constructor on this class's superclass. 662 static String superCtorCall(ClassInfo cl, ArrayList<ClassInfo> thrownExceptions) { 663 ClassInfo base = cl.realSuperclass(); 664 if (base == null) { 665 return ""; 666 } 667 HashSet<String> exceptionNames = new HashSet<String>(); 668 if (thrownExceptions != null) { 669 for (ClassInfo thrown : thrownExceptions) { 670 exceptionNames.add(thrown.name()); 671 } 672 } 673 ArrayList<MethodInfo> ctors = base.constructors(); 674 MethodInfo ctor = null; 675 // bad exception indicates that the exceptions thrown by the super constructor 676 // are incompatible with the constructor we're using for the sub class. 677 Boolean badException = false; 678 for (MethodInfo m : ctors) { 679 if (canCallMethod(cl, m)) { 680 if (m.thrownExceptions() != null) { 681 for (ClassInfo thrown : m.thrownExceptions()) { 682 if (!exceptionNames.contains(thrown.name())) { 683 badException = true; 684 } 685 } 686 } 687 if (badException) { 688 badException = false; 689 continue; 690 } 691 // if it has no args, we're done 692 if (m.parameters().isEmpty()) { 693 return ""; 694 } 695 ctor = m; 696 } 697 } 698 if (ctor != null) { 699 String result = ""; 700 result += "super("; 701 ArrayList<ParameterInfo> params = ctor.parameters(); 702 for (ParameterInfo param : params) { 703 TypeInfo t = param.type(); 704 if (t.isPrimitive() && t.dimension().equals("")) { 705 String n = t.simpleTypeName(); 706 if (("byte".equals(n) || "short".equals(n) || "int".equals(n) || "long".equals(n) 707 || "float".equals(n) || "double".equals(n)) 708 && t.dimension().equals("")) { 709 result += "0"; 710 } else if ("char".equals(n)) { 711 result += "'\\0'"; 712 } else if ("boolean".equals(n)) { 713 result += "false"; 714 } else { 715 result += "<<unknown-" + n + ">>"; 716 } 717 } else { 718 // put null in each super class method. Cast null to the correct type 719 // to avoid collisions with other constructors. If the type is generic 720 // don't cast it 721 result += 722 (!t.isTypeVariable() ? "(" + t.qualifiedTypeName() + t.dimension() + ")" : "") 723 + "null"; 724 } 725 if (param != params.get(params.size()-1)) { 726 result += ","; 727 } 728 } 729 result += "); "; 730 return result; 731 } else { 732 return ""; 733 } 734 } 735 736 /** 737 * Write out the given list of annotations. If the {@code isDeprecated} 738 * flag is true also write out a {@code @Deprecated} annotation if it did not 739 * already appear in the list of annotations. (This covers APIs that mention 740 * {@code @deprecated} in their documentation but fail to add 741 * {@code @Deprecated} as an annotation. 742 * <p> 743 * {@code @Override} annotations are deliberately skipped. 744 */ 745 static void writeAnnotations(PrintStream stream, List<AnnotationInstanceInfo> annotations, 746 boolean isDeprecated) { 747 assert annotations != null; 748 for (AnnotationInstanceInfo ann : annotations) { 749 // Skip @Override annotations: the stubs do not need it and in some cases it leads 750 // to compilation errors with the way the stubs are generated 751 if (ann.type() != null && ann.type().qualifiedName().equals("java.lang.Override")) { 752 continue; 753 } 754 if (!ann.type().isHidden()) { 755 stream.println(ann.toString()); 756 if (isDeprecated && ann.type() != null 757 && ann.type().qualifiedName().equals("java.lang.Deprecated")) { 758 isDeprecated = false; // Prevent duplicate annotations 759 } 760 } 761 } 762 if (isDeprecated) { 763 stream.println("@Deprecated"); 764 } 765 } 766 767 static void writeAnnotationElement(PrintStream stream, MethodInfo ann) { 768 stream.print(ann.returnType().fullName()); 769 stream.print(" "); 770 stream.print(ann.name()); 771 stream.print("()"); 772 AnnotationValueInfo def = ann.defaultAnnotationElementValue(); 773 if (def != null) { 774 stream.print(" default "); 775 stream.print(def.valueString()); 776 } 777 stream.println(";"); 778 } 779 780 static void writeXML(PrintStream xmlWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses, 781 HashSet<ClassInfo> notStrippable) { 782 // extract the set of packages, sort them by name, and write them out in that order 783 Set<PackageInfo> allClassKeys = allClasses.keySet(); 784 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]); 785 Arrays.sort(allPackages, PackageInfo.comparator); 786 787 xmlWriter.println("<api>"); 788 for (PackageInfo pack : allPackages) { 789 writePackageXML(xmlWriter, pack, allClasses.get(pack), notStrippable); 790 } 791 xmlWriter.println("</api>"); 792 } 793 794 public static void writeXml(PrintStream xmlWriter, Collection<PackageInfo> pkgs) { 795 final PackageInfo[] packages = pkgs.toArray(new PackageInfo[pkgs.size()]); 796 Arrays.sort(packages, PackageInfo.comparator); 797 798 HashSet<ClassInfo> notStrippable = new HashSet(); 799 for (PackageInfo pkg: packages) { 800 for (ClassInfo cl: pkg.allClasses().values()) { 801 notStrippable.add(cl); 802 } 803 } 804 xmlWriter.println("<api>"); 805 for (PackageInfo pkg: packages) { 806 writePackageXML(xmlWriter, pkg, pkg.allClasses().values(), notStrippable); 807 } 808 xmlWriter.println("</api>"); 809 } 810 811 static void writePackageXML(PrintStream xmlWriter, PackageInfo pack, 812 Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) { 813 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]); 814 Arrays.sort(classes, ClassInfo.comparator); 815 // Work around the bogus "Array" class we invent for 816 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505) 817 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) { 818 return; 819 } 820 xmlWriter.println("<package name=\"" + pack.name() + "\"\n" 821 // + " source=\"" + pack.position() + "\"\n" 822 + ">"); 823 for (ClassInfo cl : classes) { 824 writeClassXML(xmlWriter, cl, notStrippable); 825 } 826 xmlWriter.println("</package>"); 827 828 829 } 830 831 static void writeClassXML(PrintStream xmlWriter, ClassInfo cl, HashSet<ClassInfo> notStrippable) { 832 String scope = cl.scope(); 833 String deprecatedString = ""; 834 String declString = (cl.isInterface()) ? "interface" : "class"; 835 if (cl.isDeprecated()) { 836 deprecatedString = "deprecated"; 837 } else { 838 deprecatedString = "not deprecated"; 839 } 840 xmlWriter.println("<" + declString + " name=\"" + cl.name() + "\""); 841 if (!cl.isInterface() && !cl.qualifiedName().equals("java.lang.Object")) { 842 xmlWriter.println(" extends=\"" 843 + ((cl.realSuperclass() == null) ? "java.lang.Object" : cl.realSuperclass() 844 .qualifiedName()) + "\""); 845 } 846 xmlWriter.println(" abstract=\"" + cl.isAbstract() + "\"\n" + " static=\"" + cl.isStatic() 847 + "\"\n" + " final=\"" + cl.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString 848 + "\"\n" + " visibility=\"" + scope + "\"\n" 849 // + " source=\"" + cl.position() + "\"\n" 850 + ">"); 851 852 ArrayList<ClassInfo> interfaces = cl.realInterfaces(); 853 Collections.sort(interfaces, ClassInfo.comparator); 854 for (ClassInfo iface : interfaces) { 855 if (notStrippable.contains(iface)) { 856 xmlWriter.println("<implements name=\"" + iface.qualifiedName() + "\">"); 857 xmlWriter.println("</implements>"); 858 } 859 } 860 861 ArrayList<MethodInfo> constructors = cl.constructors(); 862 Collections.sort(constructors, MethodInfo.comparator); 863 for (MethodInfo mi : constructors) { 864 writeConstructorXML(xmlWriter, mi); 865 } 866 867 ArrayList<MethodInfo> methods = cl.allSelfMethods(); 868 Collections.sort(methods, MethodInfo.comparator); 869 for (MethodInfo mi : methods) { 870 if (!methodIsOverride(notStrippable, mi)) { 871 writeMethodXML(xmlWriter, mi); 872 } 873 } 874 875 ArrayList<FieldInfo> fields = cl.allSelfFields(); 876 Collections.sort(fields, FieldInfo.comparator); 877 for (FieldInfo fi : fields) { 878 writeFieldXML(xmlWriter, fi); 879 } 880 xmlWriter.println("</" + declString + ">"); 881 882 } 883 884 static void writeMethodXML(PrintStream xmlWriter, MethodInfo mi) { 885 String scope = mi.scope(); 886 887 String deprecatedString = ""; 888 if (mi.isDeprecated()) { 889 deprecatedString = "deprecated"; 890 } else { 891 deprecatedString = "not deprecated"; 892 } 893 xmlWriter.println("<method name=\"" 894 + mi.name() 895 + "\"\n" 896 + ((mi.returnType() != null) ? " return=\"" 897 + makeXMLcompliant(fullParameterTypeName(mi, mi.returnType(), false)) + "\"\n" : "") 898 + " abstract=\"" + mi.isAbstract() + "\"\n" + " native=\"" + mi.isNative() + "\"\n" 899 + " synchronized=\"" + mi.isSynchronized() + "\"\n" + " static=\"" + mi.isStatic() + "\"\n" 900 + " final=\"" + mi.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString + "\"\n" 901 + " visibility=\"" + scope + "\"\n" 902 // + " source=\"" + mi.position() + "\"\n" 903 + ">"); 904 905 // write parameters in declaration order 906 int numParameters = mi.parameters().size(); 907 int count = 0; 908 for (ParameterInfo pi : mi.parameters()) { 909 count++; 910 writeParameterXML(xmlWriter, mi, pi, count == numParameters); 911 } 912 913 // but write exceptions in canonicalized order 914 ArrayList<ClassInfo> exceptions = mi.thrownExceptions(); 915 Collections.sort(exceptions, ClassInfo.comparator); 916 for (ClassInfo pi : exceptions) { 917 xmlWriter.println("<exception name=\"" + pi.name() + "\" type=\"" + pi.qualifiedName() 918 + "\">"); 919 xmlWriter.println("</exception>"); 920 } 921 xmlWriter.println("</method>"); 922 } 923 924 static void writeConstructorXML(PrintStream xmlWriter, MethodInfo mi) { 925 String scope = mi.scope(); 926 String deprecatedString = ""; 927 if (mi.isDeprecated()) { 928 deprecatedString = "deprecated"; 929 } else { 930 deprecatedString = "not deprecated"; 931 } 932 xmlWriter.println("<constructor name=\"" + mi.name() + "\"\n" + " type=\"" 933 + mi.containingClass().qualifiedName() + "\"\n" + " static=\"" + mi.isStatic() + "\"\n" 934 + " final=\"" + mi.isFinal() + "\"\n" + " deprecated=\"" + deprecatedString + "\"\n" 935 + " visibility=\"" + scope + "\"\n" 936 // + " source=\"" + mi.position() + "\"\n" 937 + ">"); 938 939 int numParameters = mi.parameters().size(); 940 int count = 0; 941 for (ParameterInfo pi : mi.parameters()) { 942 count++; 943 writeParameterXML(xmlWriter, mi, pi, count == numParameters); 944 } 945 946 ArrayList<ClassInfo> exceptions = mi.thrownExceptions(); 947 Collections.sort(exceptions, ClassInfo.comparator); 948 for (ClassInfo pi : exceptions) { 949 xmlWriter.println("<exception name=\"" + pi.name() + "\" type=\"" + pi.qualifiedName() 950 + "\">"); 951 xmlWriter.println("</exception>"); 952 } 953 xmlWriter.println("</constructor>"); 954 } 955 956 static void writeParameterXML(PrintStream xmlWriter, MethodInfo method, ParameterInfo pi, 957 boolean isLast) { 958 xmlWriter.println("<parameter name=\"" + pi.name() + "\" type=\"" 959 + makeXMLcompliant(fullParameterTypeName(method, pi.type(), isLast)) + "\">"); 960 xmlWriter.println("</parameter>"); 961 } 962 963 static void writeFieldXML(PrintStream xmlWriter, FieldInfo fi) { 964 String scope = fi.scope(); 965 String deprecatedString = ""; 966 if (fi.isDeprecated()) { 967 deprecatedString = "deprecated"; 968 } else { 969 deprecatedString = "not deprecated"; 970 } 971 // need to make sure value is valid XML 972 String value = makeXMLcompliant(fi.constantLiteralValue()); 973 974 String fullTypeName = makeXMLcompliant(fi.type().qualifiedTypeName()) + fi.type().dimension(); 975 976 xmlWriter.println("<field name=\"" + fi.name() + "\"\n" + " type=\"" + fullTypeName + "\"\n" 977 + " transient=\"" + fi.isTransient() + "\"\n" + " volatile=\"" + fi.isVolatile() + "\"\n" 978 + (fieldIsInitialized(fi) ? " value=\"" + value + "\"\n" : "") + " static=\"" 979 + fi.isStatic() + "\"\n" + " final=\"" + fi.isFinal() + "\"\n" + " deprecated=\"" 980 + deprecatedString + "\"\n" + " visibility=\"" + scope + "\"\n" 981 // + " source=\"" + fi.position() + "\"\n" 982 + ">"); 983 xmlWriter.println("</field>"); 984 } 985 986 static String makeXMLcompliant(String s) { 987 String returnString = ""; 988 returnString = s.replaceAll("&", "&"); 989 returnString = returnString.replaceAll("<", "<"); 990 returnString = returnString.replaceAll(">", ">"); 991 returnString = returnString.replaceAll("\"", """); 992 returnString = returnString.replaceAll("'", "&pos;"); 993 return returnString; 994 } 995 996 public static void writeApi(PrintStream apiWriter, Collection<PackageInfo> pkgs) { 997 final PackageInfo[] packages = pkgs.toArray(new PackageInfo[pkgs.size()]); 998 Arrays.sort(packages, PackageInfo.comparator); 999 1000 HashSet<ClassInfo> notStrippable = new HashSet(); 1001 for (PackageInfo pkg: packages) { 1002 for (ClassInfo cl: pkg.allClasses().values()) { 1003 notStrippable.add(cl); 1004 } 1005 } 1006 for (PackageInfo pkg: packages) { 1007 writePackageApi(apiWriter, pkg, pkg.allClasses().values(), notStrippable); 1008 } 1009 } 1010 1011 static void writeApi(PrintStream apiWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses, 1012 HashSet<ClassInfo> notStrippable) { 1013 // extract the set of packages, sort them by name, and write them out in that order 1014 Set<PackageInfo> allClassKeys = allClasses.keySet(); 1015 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]); 1016 Arrays.sort(allPackages, PackageInfo.comparator); 1017 1018 for (PackageInfo pack : allPackages) { 1019 writePackageApi(apiWriter, pack, allClasses.get(pack), notStrippable); 1020 } 1021 } 1022 1023 static void writePackageApi(PrintStream apiWriter, PackageInfo pack, 1024 Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) { 1025 // Work around the bogus "Array" class we invent for 1026 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505) 1027 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) { 1028 return; 1029 } 1030 1031 apiWriter.print("package "); 1032 apiWriter.print(pack.qualifiedName()); 1033 apiWriter.print(" {\n\n"); 1034 1035 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]); 1036 Arrays.sort(classes, ClassInfo.comparator); 1037 for (ClassInfo cl : classes) { 1038 writeClassApi(apiWriter, cl, notStrippable); 1039 } 1040 1041 apiWriter.print("}\n\n"); 1042 } 1043 1044 static void writeClassApi(PrintStream apiWriter, ClassInfo cl, HashSet<ClassInfo> notStrippable) { 1045 boolean first; 1046 1047 apiWriter.print(" "); 1048 apiWriter.print(cl.scope()); 1049 if (cl.isStatic()) { 1050 apiWriter.print(" static"); 1051 } 1052 if (cl.isFinal()) { 1053 apiWriter.print(" final"); 1054 } 1055 if (cl.isAbstract()) { 1056 apiWriter.print(" abstract"); 1057 } 1058 if (cl.isDeprecated()) { 1059 apiWriter.print(" deprecated"); 1060 } 1061 apiWriter.print(" "); 1062 apiWriter.print(cl.isInterface() ? "interface" : "class"); 1063 apiWriter.print(" "); 1064 apiWriter.print(cl.name()); 1065 1066 if (!cl.isInterface() 1067 && !"java.lang.Object".equals(cl.qualifiedName()) 1068 && cl.realSuperclass() != null 1069 && !"java.lang.Object".equals(cl.realSuperclass().qualifiedName())) { 1070 apiWriter.print(" extends "); 1071 apiWriter.print(cl.realSuperclass().qualifiedName()); 1072 } 1073 1074 ArrayList<ClassInfo> interfaces = cl.realInterfaces(); 1075 Collections.sort(interfaces, ClassInfo.comparator); 1076 first = true; 1077 for (ClassInfo iface : interfaces) { 1078 if (notStrippable.contains(iface)) { 1079 if (first) { 1080 apiWriter.print(" implements"); 1081 first = false; 1082 } 1083 apiWriter.print(" "); 1084 apiWriter.print(iface.qualifiedName()); 1085 } 1086 } 1087 1088 apiWriter.print(" {\n"); 1089 1090 ArrayList<MethodInfo> constructors = cl.constructors(); 1091 Collections.sort(constructors, MethodInfo.comparator); 1092 for (MethodInfo mi : constructors) { 1093 writeConstructorApi(apiWriter, mi); 1094 } 1095 1096 ArrayList<MethodInfo> methods = cl.allSelfMethods(); 1097 Collections.sort(methods, MethodInfo.comparator); 1098 for (MethodInfo mi : methods) { 1099 if (!methodIsOverride(notStrippable, mi)) { 1100 writeMethodApi(apiWriter, mi); 1101 } 1102 } 1103 1104 ArrayList<FieldInfo> enums = cl.enumConstants(); 1105 Collections.sort(enums, FieldInfo.comparator); 1106 for (FieldInfo fi : enums) { 1107 writeFieldApi(apiWriter, fi, "enum_constant"); 1108 } 1109 1110 ArrayList<FieldInfo> fields = cl.allSelfFields(); 1111 Collections.sort(fields, FieldInfo.comparator); 1112 for (FieldInfo fi : fields) { 1113 writeFieldApi(apiWriter, fi, "field"); 1114 } 1115 1116 apiWriter.print(" }\n\n"); 1117 } 1118 1119 static void writeConstructorApi(PrintStream apiWriter, MethodInfo mi) { 1120 apiWriter.print(" ctor "); 1121 apiWriter.print(mi.scope()); 1122 if (mi.isDeprecated()) { 1123 apiWriter.print(" deprecated"); 1124 } 1125 apiWriter.print(" "); 1126 apiWriter.print(mi.name()); 1127 1128 writeParametersApi(apiWriter, mi, mi.parameters()); 1129 writeThrowsApi(apiWriter, mi.thrownExceptions()); 1130 apiWriter.print(";\n"); 1131 } 1132 1133 static void writeMethodApi(PrintStream apiWriter, MethodInfo mi) { 1134 apiWriter.print(" method "); 1135 apiWriter.print(mi.scope()); 1136 if (mi.isStatic()) { 1137 apiWriter.print(" static"); 1138 } 1139 if (mi.isFinal()) { 1140 apiWriter.print(" final"); 1141 } 1142 if (mi.isAbstract()) { 1143 apiWriter.print(" abstract"); 1144 } 1145 if (mi.isDeprecated()) { 1146 apiWriter.print(" deprecated"); 1147 } 1148 if (mi.isSynchronized()) { 1149 apiWriter.print(" synchronized"); 1150 } 1151 apiWriter.print(" "); 1152 if (mi.returnType() == null) { 1153 apiWriter.print("void"); 1154 } else { 1155 apiWriter.print(fullParameterTypeName(mi, mi.returnType(), false)); 1156 } 1157 apiWriter.print(" "); 1158 apiWriter.print(mi.name()); 1159 1160 writeParametersApi(apiWriter, mi, mi.parameters()); 1161 writeThrowsApi(apiWriter, mi.thrownExceptions()); 1162 1163 apiWriter.print(";\n"); 1164 } 1165 1166 static void writeParametersApi(PrintStream apiWriter, MethodInfo method, ArrayList<ParameterInfo> params) { 1167 apiWriter.print("("); 1168 1169 for (ParameterInfo pi : params) { 1170 if (pi != params.get(0)) { 1171 apiWriter.print(", "); 1172 } 1173 apiWriter.print(fullParameterTypeName(method, pi.type(), pi == params.get(params.size()-1))); 1174 // turn on to write the names too 1175 if (false) { 1176 apiWriter.print(" "); 1177 apiWriter.print(pi.name()); 1178 } 1179 } 1180 1181 apiWriter.print(")"); 1182 } 1183 1184 static void writeThrowsApi(PrintStream apiWriter, ArrayList<ClassInfo> exceptions) { 1185 // write in a canonical order 1186 exceptions = (ArrayList<ClassInfo>) exceptions.clone(); 1187 Collections.sort(exceptions, ClassInfo.comparator); 1188 //final int N = exceptions.length; 1189 boolean first = true; 1190 for (ClassInfo ex : exceptions) { 1191 // Turn this off, b/c we need to regenrate the old xml files. 1192 if (true || !"java.lang.RuntimeException".equals(ex.qualifiedName()) 1193 && !ex.isDerivedFrom("java.lang.RuntimeException")) { 1194 if (first) { 1195 apiWriter.print(" throws "); 1196 first = false; 1197 } else { 1198 apiWriter.print(", "); 1199 } 1200 apiWriter.print(ex.qualifiedName()); 1201 } 1202 } 1203 } 1204 1205 static void writeFieldApi(PrintStream apiWriter, FieldInfo fi, String label) { 1206 apiWriter.print(" "); 1207 apiWriter.print(label); 1208 apiWriter.print(" "); 1209 apiWriter.print(fi.scope()); 1210 if (fi.isStatic()) { 1211 apiWriter.print(" static"); 1212 } 1213 if (fi.isFinal()) { 1214 apiWriter.print(" final"); 1215 } 1216 if (fi.isDeprecated()) { 1217 apiWriter.print(" deprecated"); 1218 } 1219 if (fi.isTransient()) { 1220 apiWriter.print(" transient"); 1221 } 1222 if (fi.isVolatile()) { 1223 apiWriter.print(" volatile"); 1224 } 1225 1226 apiWriter.print(" "); 1227 apiWriter.print(fi.type().qualifiedTypeName() + fi.type().dimension()); 1228 1229 apiWriter.print(" "); 1230 apiWriter.print(fi.name()); 1231 1232 Object val = null; 1233 if (fi.isConstant() && fieldIsInitialized(fi)) { 1234 apiWriter.print(" = "); 1235 apiWriter.print(fi.constantLiteralValue()); 1236 val = fi.constantValue(); 1237 } 1238 1239 apiWriter.print(";"); 1240 1241 if (val != null) { 1242 if (val instanceof Integer && "char".equals(fi.type().qualifiedTypeName())) { 1243 apiWriter.format(" // 0x%04x '%s'", val, 1244 FieldInfo.javaEscapeString("" + ((char)((Integer)val).intValue()))); 1245 } else if (val instanceof Byte || val instanceof Short || val instanceof Integer) { 1246 apiWriter.format(" // 0x%x", val); 1247 } else if (val instanceof Long) { 1248 apiWriter.format(" // 0x%xL", val); 1249 } 1250 } 1251 1252 apiWriter.print("\n"); 1253 } 1254 1255 static void writeKeepList(PrintStream keepListWriter, 1256 HashMap<PackageInfo, List<ClassInfo>> allClasses, HashSet<ClassInfo> notStrippable) { 1257 // extract the set of packages, sort them by name, and write them out in that order 1258 Set<PackageInfo> allClassKeys = allClasses.keySet(); 1259 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]); 1260 Arrays.sort(allPackages, PackageInfo.comparator); 1261 1262 for (PackageInfo pack : allPackages) { 1263 writePackageKeepList(keepListWriter, pack, allClasses.get(pack), notStrippable); 1264 } 1265 } 1266 1267 static void writePackageKeepList(PrintStream keepListWriter, PackageInfo pack, 1268 Collection<ClassInfo> classList, HashSet<ClassInfo> notStrippable) { 1269 // Work around the bogus "Array" class we invent for 1270 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505) 1271 if (pack.name().equals(PackageInfo.DEFAULT_PACKAGE)) { 1272 return; 1273 } 1274 1275 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]); 1276 Arrays.sort(classes, ClassInfo.comparator); 1277 for (ClassInfo cl : classes) { 1278 writeClassKeepList(keepListWriter, cl, notStrippable); 1279 } 1280 } 1281 1282 static void writeClassKeepList(PrintStream keepListWriter, ClassInfo cl, 1283 HashSet<ClassInfo> notStrippable) { 1284 keepListWriter.print("-keep class "); 1285 keepListWriter.print(to$Class(cl.qualifiedName())); 1286 1287 keepListWriter.print(" {\n"); 1288 1289 ArrayList<MethodInfo> constructors = cl.constructors(); 1290 Collections.sort(constructors, MethodInfo.comparator); 1291 for (MethodInfo mi : constructors) { 1292 writeConstructorKeepList(keepListWriter, mi); 1293 } 1294 1295 keepListWriter.print("\n"); 1296 1297 ArrayList<MethodInfo> methods = cl.allSelfMethods(); 1298 Collections.sort(methods, MethodInfo.comparator); 1299 for (MethodInfo mi : methods) { 1300 if (!methodIsOverride(notStrippable, mi)) { 1301 writeMethodKeepList(keepListWriter, mi); 1302 } 1303 } 1304 1305 keepListWriter.print("\n"); 1306 1307 ArrayList<FieldInfo> enums = cl.enumConstants(); 1308 Collections.sort(enums, FieldInfo.comparator); 1309 for (FieldInfo fi : enums) { 1310 writeFieldKeepList(keepListWriter, fi); 1311 } 1312 1313 keepListWriter.print("\n"); 1314 1315 ArrayList<FieldInfo> fields = cl.allSelfFields(); 1316 Collections.sort(fields, FieldInfo.comparator); 1317 for (FieldInfo fi : fields) { 1318 writeFieldKeepList(keepListWriter, fi); 1319 } 1320 1321 keepListWriter.print("}\n\n"); 1322 } 1323 1324 static void writeConstructorKeepList(PrintStream keepListWriter, MethodInfo mi) { 1325 keepListWriter.print(" "); 1326 String name = mi.name(); 1327 name = name.replace(".", "$"); 1328 keepListWriter.print(name); 1329 1330 writeParametersKeepList(keepListWriter, mi, mi.parameters()); 1331 keepListWriter.print(";\n"); 1332 } 1333 1334 static void writeMethodKeepList(PrintStream keepListWriter, MethodInfo mi) { 1335 keepListWriter.print(" "); 1336 keepListWriter.print(mi.scope()); 1337 if (mi.isStatic()) { 1338 keepListWriter.print(" static"); 1339 } 1340 if (mi.isAbstract()) { 1341 keepListWriter.print(" abstract"); 1342 } 1343 if (mi.isSynchronized()) { 1344 keepListWriter.print(" synchronized"); 1345 } 1346 keepListWriter.print(" "); 1347 if (mi.returnType() == null) { 1348 keepListWriter.print("void"); 1349 } else { 1350 keepListWriter.print(getCleanTypeName(mi.returnType())); 1351 } 1352 keepListWriter.print(" "); 1353 keepListWriter.print(mi.name()); 1354 1355 writeParametersKeepList(keepListWriter, mi, mi.parameters()); 1356 1357 keepListWriter.print(";\n"); 1358 } 1359 1360 static void writeParametersKeepList(PrintStream keepListWriter, MethodInfo method, 1361 ArrayList<ParameterInfo> params) { 1362 keepListWriter.print("("); 1363 1364 for (ParameterInfo pi : params) { 1365 if (pi != params.get(0)) { 1366 keepListWriter.print(", "); 1367 } 1368 keepListWriter.print(getCleanTypeName(pi.type())); 1369 } 1370 1371 keepListWriter.print(")"); 1372 } 1373 1374 static void writeFieldKeepList(PrintStream keepListWriter, FieldInfo fi) { 1375 keepListWriter.print(" "); 1376 keepListWriter.print(fi.scope()); 1377 if (fi.isStatic()) { 1378 keepListWriter.print(" static"); 1379 } 1380 if (fi.isTransient()) { 1381 keepListWriter.print(" transient"); 1382 } 1383 if (fi.isVolatile()) { 1384 keepListWriter.print(" volatile"); 1385 } 1386 1387 keepListWriter.print(" "); 1388 keepListWriter.print(getCleanTypeName(fi.type()) + fi.type().dimension()); 1389 1390 keepListWriter.print(" "); 1391 keepListWriter.print(fi.name()); 1392 1393 keepListWriter.print(";\n"); 1394 } 1395 1396 static String fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast) { 1397 String fullTypeName = type.fullName(method.typeVariables()); 1398 if (isLast && method.isVarArgs()) { 1399 // TODO: note that this does not attempt to handle hypothetical 1400 // vararg methods whose last parameter is a list of arrays, e.g. 1401 // "Object[]...". 1402 fullTypeName = type.fullNameNoDimension(method.typeVariables()) + "..."; 1403 } 1404 return fullTypeName; 1405 } 1406 1407 static String to$Class(String name) { 1408 int pos = 0; 1409 while ((pos = name.indexOf('.', pos)) > 0) { 1410 String n = name.substring(0, pos); 1411 if (Converter.obtainClass(n) != null) { 1412 return n + (name.substring(pos).replace('.', '$')); 1413 } 1414 pos = pos + 1; 1415 } 1416 return name; 1417 } 1418 1419 static String getCleanTypeName(TypeInfo t) { 1420 return t.isPrimitive() ? t.simpleTypeName() + t.dimension() : 1421 to$Class(t.asClassInfo().qualifiedName() + t.dimension()); 1422 } 1423 } 1424