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