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