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