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