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