blob: e1ec76a303f10758a4da0bbd717578fb09e60489 [file] [log] [blame]
The Android Open Source Project88b60792009-03-03 19:28:42 -08001/*
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
17import java.util.HashMap;
18import java.util.HashSet;
19import java.util.List;
20import java.util.ArrayList;
21import java.util.Arrays;
22import java.util.Set;
23import java.util.Comparator;
24import java.io.File;
25import java.io.FileNotFoundException;
26import java.io.PrintStream;
27
28public class Stubs {
29 private static HashSet<ClassInfo> notStrippable;
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
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
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
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
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
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
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
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
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
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.
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
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.
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
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
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
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
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
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
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
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
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
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
968 static String makeXMLcompliant(String s) {
969 String returnString = "";
970 returnString = s.replaceAll("&", "&amp;");
971 returnString = returnString.replaceAll("<", "&lt;");
972 returnString = returnString.replaceAll(">", "&gt;");
973 returnString = returnString.replaceAll("\"", "&quot;");
974 returnString = returnString.replaceAll("'", "&pos;");
975 return returnString;
976 }
977
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}