blob: b988ef52a9cac449e2967d722b384904217d5ab7 [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
Brian Carlstrom7dc35a32010-08-04 23:17:47 -0700318 // Work around the bogus "Array" class we invent for
319 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
320 if (cl.containingPackage() != null && cl.containingPackage().name().equals("")) {
321 return;
322 }
323
The Android Open Source Project88b60792009-03-03 19:28:42 -0800324 String filename = stubsDir + '/' + javaFileName(cl);
325 File file = new File(filename);
326 ClearPage.ensureDirectory(file);
327
328 PrintStream stream = null;
329 try {
330 stream = new PrintStream(file);
331 writeClassFile(stream, cl);
332 }
333 catch (FileNotFoundException e) {
334 System.err.println("error writing file: " + filename);
335 }
336 finally {
337 if (stream != null) {
338 stream.close();
339 }
340 }
341 }
342
343 static void writeClassFile(PrintStream stream, ClassInfo cl) {
344 PackageInfo pkg = cl.containingPackage();
345 if (pkg != null) {
346 stream.println("package " + pkg.name() + ";");
347 }
348 writeClass(stream, cl);
349 }
350
351 static void writeClass(PrintStream stream, ClassInfo cl) {
352 writeAnnotations(stream, cl.annotations());
353
354 stream.print(DroidDoc.scope(cl) + " ");
355 if (cl.isAbstract() && !cl.isAnnotation() && !cl.isInterface()) {
356 stream.print("abstract ");
357 }
358 if (cl.isStatic()){
359 stream.print("static ");
360 }
361 if (cl.isFinal() && !cl.isEnum()) {
362 stream.print("final ");
363 }
364 if (false) {
365 stream.print("strictfp ");
366 }
367
368 HashSet<String> classDeclTypeVars = new HashSet();
369 String leafName = cl.asTypeInfo().fullName(classDeclTypeVars);
370 int bracket = leafName.indexOf('<');
371 if (bracket < 0) bracket = leafName.length() - 1;
372 int period = leafName.lastIndexOf('.', bracket);
373 if (period < 0) period = -1;
374 leafName = leafName.substring(period+1);
375
376 String kind = cl.kind();
377 stream.println(kind + " " + leafName);
378
379 TypeInfo base = cl.superclassType();
380
381 if (!"enum".equals(kind)) {
382 if (base != null && !"java.lang.Object".equals(base.qualifiedTypeName())) {
383 stream.println(" extends " + base.fullName(classDeclTypeVars));
384 }
385 }
386
387 TypeInfo[] interfaces = cl.realInterfaceTypes();
388 List<TypeInfo> usedInterfaces = new ArrayList<TypeInfo>();
389 for (TypeInfo iface : interfaces) {
390 if (notStrippable.contains(iface.asClassInfo())
391 && !iface.asClassInfo().isDocOnly()) {
392 usedInterfaces.add(iface);
393 }
394 }
395 if (usedInterfaces.size() > 0 && !cl.isAnnotation()) {
396 // can java annotations extend other ones?
397 if (cl.isInterface() || cl.isAnnotation()) {
398 stream.print(" extends ");
399 } else {
400 stream.print(" implements ");
401 }
402 String comma = "";
403 for (TypeInfo iface: usedInterfaces) {
404 stream.print(comma + iface.fullName(classDeclTypeVars));
405 comma = ", ";
406 }
407 stream.println();
408 }
409
410 stream.println("{");
411
412 FieldInfo[] enumConstants = cl.enumConstants();
413 int N = enumConstants.length;
414 for (int i=0; i<N; i++) {
415 FieldInfo field = enumConstants[i];
416 if (!field.constantLiteralValue().equals("null")){
417 stream.println(field.name() + "(" + field.constantLiteralValue()
418 + (i==N-1 ? ");" : "),"));
419 }else{
420 stream.println(field.name() + "(" + (i==N-1 ? ");" : "),"));
421 }
422 }
423
424 for (ClassInfo inner: cl.getRealInnerClasses()) {
425 if (notStrippable.contains(inner)
426 && !inner.isDocOnly()){
427 writeClass(stream, inner);
428 }
429 }
430
431
432 for (MethodInfo method: cl.constructors()) {
433 if (!method.isDocOnly()) {
434 writeMethod(stream, method, true);
435 }
436 }
437
438 boolean fieldNeedsInitialization = false;
439 boolean staticFieldNeedsInitialization = false;
440 for (FieldInfo field: cl.allSelfFields()) {
441 if (!field.isDocOnly()) {
442 if (!field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
443 fieldNeedsInitialization = true;
444 }
445 if (field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
446 staticFieldNeedsInitialization = true;
447 }
448 }
449 }
450
451 // The compiler includes a default public constructor that calls the super classes
452 // default constructor in the case where there are no written constructors.
453 // So, if we hide all the constructors, java may put in a constructor
454 // that calls a nonexistent super class constructor. So, if there are no constructors,
455 // and the super class doesn't have a default constructor, write in a private constructor
456 // that works. TODO -- we generate this as protected, but we really should generate
457 // it as private unless it also exists in the real code.
458 if ((cl.constructors().length == 0 && (cl.getNonWrittenConstructors().length != 0
459 || fieldNeedsInitialization))
460 && !cl.isAnnotation()
461 && !cl.isInterface()
462 && !cl.isEnum() ) {
463 //Errors.error(Errors.HIDDEN_CONSTRUCTOR,
464 // cl.position(), "No constructors " +
465 // "found and superclass has no parameterless constructor. A constructor " +
466 // "that calls an appropriate superclass constructor " +
467 // "was automatically written to stubs.\n");
468 stream.println(cl.leafName()
469 + "() { " + superCtorCall(cl,null)
470 + "throw new" + " RuntimeException(\"Stub!\"); }");
471 }
472
473 for (MethodInfo method: cl.allSelfMethods()) {
474 if (cl.isEnum()) {
475 if (("values".equals(method.name())
476 && "()".equals(method.signature()))
477 || ("valueOf".equals(method.name())
478 && "(java.lang.String)".equals(method.signature()))) {
479 // skip these two methods on enums, because they're synthetic,
480 // although for some reason javadoc doesn't mark them as synthetic,
481 // maybe because they still want them documented
482 continue;
483 }
484 }
485 if (!method.isDocOnly()) {
486 writeMethod(stream, method, false);
487 }
488 }
489 //Write all methods that are hidden, but override abstract methods or interface methods.
490 //These can't be hidden.
491 for (MethodInfo method : cl.getHiddenMethods()){
492 MethodInfo overriddenMethod = method.findRealOverriddenMethod(method.name(), method.signature(), notStrippable);
493 ClassInfo classContainingMethod = method.findRealOverriddenClass(method.name(),
494 method.signature());
495 if (overriddenMethod != null && !overriddenMethod.isHidden()
496 && !overriddenMethod.isDocOnly() &&
497 (overriddenMethod.isAbstract() ||
498 overriddenMethod.containingClass().isInterface())) {
499 method.setReason("1:" + classContainingMethod.qualifiedName());
500 cl.addMethod(method);
501 writeMethod(stream, method, false);
502 }
503 }
504
505 for (MethodInfo element: cl.annotationElements()) {
506 if (!element.isDocOnly()) {
507 writeAnnotationElement(stream, element);
508 }
509 }
510
511 for (FieldInfo field: cl.allSelfFields()) {
512 if (!field.isDocOnly()) {
513 writeField(stream, field);
514 }
515 }
516
517 if (staticFieldNeedsInitialization) {
518 stream.print("static { ");
519 for (FieldInfo field: cl.allSelfFields()) {
520 if (!field.isDocOnly() && field.isStatic() && field.isFinal()
521 && !fieldIsInitialized(field) && field.constantValue() == null) {
522 stream.print(field.name() + " = " + field.type().defaultValue()
523 + "; ");
524 }
525 }
526 stream.println("}");
527 }
528
529 stream.println("}");
530 }
531
532
533 static void writeMethod(PrintStream stream, MethodInfo method, boolean isConstructor) {
534 String comma;
535
536 stream.print(DroidDoc.scope(method) + " ");
537 if (method.isStatic()) {
538 stream.print("static ");
539 }
540 if (method.isFinal()) {
541 stream.print("final ");
542 }
543 if (method.isAbstract()) {
544 stream.print("abstract ");
545 }
546 if (method.isSynchronized()) {
547 stream.print("synchronized ");
548 }
549 if (method.isNative()) {
550 stream.print("native ");
551 }
552 if (false /*method.isStictFP()*/) {
553 stream.print("strictfp ");
554 }
555
556 stream.print(method.typeArgumentsName(new HashSet()) + " ");
557
558 if (!isConstructor) {
559 stream.print(method.returnType().fullName(method.typeVariables()) + " ");
560 }
561 String n = method.name();
562 int pos = n.lastIndexOf('.');
563 if (pos >= 0) {
564 n = n.substring(pos + 1);
565 }
566 stream.print(n + "(");
567 comma = "";
568 int count = 1;
569 int size = method.parameters().length;
570 for (ParameterInfo param: method.parameters()) {
571 stream.print(comma + fullParameterTypeName(method, param.type(), count == size)
572 + " " + param.name());
573 comma = ", ";
574 count++;
575 }
576 stream.print(")");
577
578 comma = "";
579 if (method.thrownExceptions().length > 0) {
580 stream.print(" throws ");
581 for (ClassInfo thrown: method.thrownExceptions()) {
582 stream.print(comma + thrown.qualifiedName());
583 comma = ", ";
584 }
585 }
586 if (method.isAbstract() || method.isNative() || method.containingClass().isInterface()) {
587 stream.println(";");
588 } else {
589 stream.print(" { ");
590 if (isConstructor) {
591 stream.print(superCtorCall(method.containingClass(), method.thrownExceptions()));
592 }
593 stream.println("throw new RuntimeException(\"Stub!\"); }");
594 }
595 }
596
597 static void writeField(PrintStream stream, FieldInfo field) {
598 stream.print(DroidDoc.scope(field) + " ");
599 if (field.isStatic()) {
600 stream.print("static ");
601 }
602 if (field.isFinal()) {
603 stream.print("final ");
604 }
605 if (field.isTransient()) {
606 stream.print("transient ");
607 }
608 if (field.isVolatile()) {
609 stream.print("volatile ");
610 }
611
612 stream.print(field.type().fullName());
613 stream.print(" ");
614 stream.print(field.name());
615
616 if (fieldIsInitialized(field)) {
617 stream.print(" = " + field.constantLiteralValue());
618 }
619
620 stream.println(";");
621 }
622
623 static boolean fieldIsInitialized(FieldInfo field) {
624 return (field.isFinal() && field.constantValue() != null)
625 || !field.type().dimension().equals("")
626 || field.containingClass().isInterface();
627 }
628
629 // Returns 'true' if the method is an @Override of a visible parent
630 // method implementation, and thus does not affect the API.
631 static boolean methodIsOverride(MethodInfo mi) {
632 // Abstract/static/final methods are always listed in the API description
633 if (mi.isAbstract() || mi.isStatic() || mi.isFinal()) {
634 return false;
635 }
636
637 // Find any relevant ancestor declaration and inspect it
638 MethodInfo om = mi.findSuperclassImplementation(notStrippable);
639 if (om != null) {
640 // Visibility mismatch is an API change, so check for it
641 if (mi.mIsPrivate == om.mIsPrivate
642 && mi.mIsPublic == om.mIsPublic
643 && mi.mIsProtected == om.mIsProtected) {
644 // Look only for overrides of an ancestor class implementation,
645 // not of e.g. an abstract or interface method declaration
646 if (!om.isAbstract()) {
647 // If the parent is hidden, we can't rely on it to provide
648 // the API
649 if (!om.isHidden()) {
650 // If the only "override" turns out to be in our own class
651 // (which sometimes happens in concrete subclasses of
652 // abstract base classes), it's not really an override
653 if (!mi.mContainingClass.equals(om.mContainingClass)) {
654 return true;
655 }
656 }
657 }
658 }
659 }
660 return false;
661 }
662
663 static boolean canCallMethod(ClassInfo from, MethodInfo m) {
664 if (m.isPublic() || m.isProtected()) {
665 return true;
666 }
667 if (m.isPackagePrivate()) {
668 String fromPkg = from.containingPackage().name();
669 String pkg = m.containingClass().containingPackage().name();
670 if (fromPkg.equals(pkg)) {
671 return true;
672 }
673 }
674 return false;
675 }
676
677 // call a constructor, any constructor on this class's superclass.
678 static String superCtorCall(ClassInfo cl, ClassInfo[] thrownExceptions) {
679 ClassInfo base = cl.realSuperclass();
680 if (base == null) {
681 return "";
682 }
683 HashSet<String> exceptionNames = new HashSet<String>();
684 if (thrownExceptions != null ){
685 for (ClassInfo thrown : thrownExceptions){
686 exceptionNames.add(thrown.name());
687 }
688 }
689 MethodInfo[] ctors = base.constructors();
690 MethodInfo ctor = null;
691 //bad exception indicates that the exceptions thrown by the super constructor
692 //are incompatible with the constructor we're using for the sub class.
693 Boolean badException = false;
694 for (MethodInfo m: ctors) {
695 if (canCallMethod(cl, m)) {
696 if (m.thrownExceptions() != null){
697 for (ClassInfo thrown : m.thrownExceptions()){
698 if (!exceptionNames.contains(thrown.name())){
699 badException = true;
700 }
701 }
702 }
703 if (badException){
704 badException = false;
705 continue;
706 }
707 // if it has no args, we're done
708 if (m.parameters().length == 0) {
709 return "";
710 }
711 ctor = m;
712 }
713 }
714 if (ctor != null) {
715 String result = "";
716 result+= "super(";
717 ParameterInfo[] params = ctor.parameters();
718 int N = params.length;
719 for (int i=0; i<N; i++) {
720 TypeInfo t = params[i].type();
721 if (t.isPrimitive() && t.dimension().equals("")) {
722 String n = t.simpleTypeName();
723 if (("byte".equals(n)
724 || "short".equals(n)
725 || "int".equals(n)
726 || "long".equals(n)
727 || "float".equals(n)
728 || "double".equals(n)) && t.dimension().equals("")) {
729 result += "0";
730 }
731 else if ("char".equals(n)) {
732 result += "'\\0'";
733 }
734 else if ("boolean".equals(n)) {
735 result += "false";
736 }
737 else {
738 result += "<<unknown-" + n + ">>";
739 }
740 } else {
741 //put null in each super class method. Cast null to the correct type
742 //to avoid collisions with other constructors. If the type is generic
743 //don't cast it
744 result += (!t.isTypeVariable() ? "(" + t.qualifiedTypeName() + t.dimension() +
745 ")" : "") + "null";
746 }
747 if (i != N-1) {
748 result += ",";
749 }
750 }
751 result += "); ";
752 return result;
753 } else {
754 return "";
755 }
756 }
757
758 static void writeAnnotations(PrintStream stream, AnnotationInstanceInfo[] annotations) {
759 for (AnnotationInstanceInfo ann: annotations) {
760 if (!ann.type().isHidden()) {
761 stream.println(ann.toString());
762 }
763 }
764 }
765
766 static void writeAnnotationElement(PrintStream stream, MethodInfo ann) {
767 stream.print(ann.returnType().fullName());
768 stream.print(" ");
769 stream.print(ann.name());
770 stream.print("()");
771 AnnotationValueInfo def = ann.defaultAnnotationElementValue();
772 if (def != null) {
773 stream.print(" default ");
774 stream.print(def.valueString());
775 }
776 stream.println(";");
777 }
778
779 static void writeXML(PrintStream xmlWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses,
780 HashSet notStrippable) {
781 // extract the set of packages, sort them by name, and write them out in that order
782 Set<PackageInfo> allClassKeys = allClasses.keySet();
783 PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
784 Arrays.sort(allPackages, PackageInfo.comparator);
785
786 xmlWriter.println("<api>");
787 for (PackageInfo pack : allPackages) {
788 writePackageXML(xmlWriter, pack, allClasses.get(pack), notStrippable);
789 }
790 xmlWriter.println("</api>");
791 }
792
793 static void writePackageXML(PrintStream xmlWriter, PackageInfo pack, List<ClassInfo> classList,
794 HashSet notStrippable) {
795 ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
796 Arrays.sort(classes, ClassInfo.comparator);
Brian Carlstrom7dc35a32010-08-04 23:17:47 -0700797 // Work around the bogus "Array" class we invent for
798 // Arrays.copyOf's Class<? extends T[]> newType parameter. (http://b/2715505)
799 if (pack.name().equals("")) {
800 return;
801 }
The Android Open Source Project88b60792009-03-03 19:28:42 -0800802 xmlWriter.println("<package name=\"" + pack.name() + "\"\n"
803 //+ " source=\"" + pack.position() + "\"\n"
804 + ">");
805 for (ClassInfo cl : classes) {
806 writeClassXML(xmlWriter, cl, notStrippable);
807 }
808 xmlWriter.println("</package>");
809
810
811 }
812
813 static void writeClassXML(PrintStream xmlWriter, ClassInfo cl, HashSet notStrippable) {
814 String scope = DroidDoc.scope(cl);
815 String deprecatedString = "";
816 String declString = (cl.isInterface()) ? "interface" : "class";
817 if (cl.isDeprecated()) {
818 deprecatedString = "deprecated";
819 } else {
820 deprecatedString = "not deprecated";
821 }
822 xmlWriter.println("<" + declString + " name=\"" + cl.name() + "\"");
823 if (!cl.isInterface() && !cl.qualifiedName().equals("java.lang.Object")) {
824 xmlWriter.println(" extends=\"" + ((cl.realSuperclass() == null)
825 ? "java.lang.Object"
826 : cl.realSuperclass().qualifiedName()) + "\"");
827 }
828 xmlWriter.println(" abstract=\"" + cl.isAbstract() + "\"\n"
829 + " static=\"" + cl.isStatic() + "\"\n"
830 + " final=\"" + cl.isFinal() + "\"\n"
831 + " deprecated=\"" + deprecatedString + "\"\n"
832 + " visibility=\"" + scope + "\"\n"
833 //+ " source=\"" + cl.position() + "\"\n"
834 + ">");
835
836 ClassInfo[] interfaces = cl.realInterfaces();
837 Arrays.sort(interfaces, ClassInfo.comparator);
838 for (ClassInfo iface : interfaces) {
839 if (notStrippable.contains(iface)) {
840 xmlWriter.println("<implements name=\"" + iface.qualifiedName() + "\">");
841 xmlWriter.println("</implements>");
842 }
843 }
844
845 MethodInfo[] constructors = cl.constructors();
846 Arrays.sort(constructors, MethodInfo.comparator);
847 for (MethodInfo mi : constructors) {
848 writeConstructorXML(xmlWriter, mi);
849 }
850
851 MethodInfo[] methods = cl.allSelfMethods();
852 Arrays.sort(methods, MethodInfo.comparator);
853 for (MethodInfo mi : methods) {
854 if (!methodIsOverride(mi)) {
855 writeMethodXML(xmlWriter, mi);
856 }
857 }
858
859 FieldInfo[] fields = cl.allSelfFields();
860 Arrays.sort(fields, FieldInfo.comparator);
861 for (FieldInfo fi : fields) {
862 writeFieldXML(xmlWriter, fi);
863 }
864 xmlWriter.println("</" + declString + ">");
865
866 }
867
868 static void writeMethodXML(PrintStream xmlWriter, MethodInfo mi) {
869 String scope = DroidDoc.scope(mi);
870
871 String deprecatedString = "";
872 if (mi.isDeprecated()) {
873 deprecatedString = "deprecated";
874 } else {
875 deprecatedString = "not deprecated";
876 }
877 xmlWriter.println("<method name=\"" + mi.name() + "\"\n"
878 + ((mi.returnType() != null)
879 ? " return=\"" + makeXMLcompliant(fullParameterTypeName(mi, mi.returnType(), false)) + "\"\n"
880 : "")
881 + " abstract=\"" + mi.isAbstract() + "\"\n"
882 + " native=\"" + mi.isNative() + "\"\n"
883 + " synchronized=\"" + mi.isSynchronized() + "\"\n"
884 + " static=\"" + mi.isStatic() + "\"\n"
885 + " final=\"" + mi.isFinal() + "\"\n"
886 + " deprecated=\""+ deprecatedString + "\"\n"
887 + " visibility=\"" + scope + "\"\n"
888 //+ " source=\"" + mi.position() + "\"\n"
889 + ">");
890
891 // write parameters in declaration order
892 int numParameters = mi.parameters().length;
893 int count = 0;
894 for (ParameterInfo pi : mi.parameters()) {
895 count++;
896 writeParameterXML(xmlWriter, mi, pi, count == numParameters);
897 }
898
899 // but write exceptions in canonicalized order
900 ClassInfo[] exceptions = mi.thrownExceptions();
901 Arrays.sort(exceptions, ClassInfo.comparator);
902 for (ClassInfo pi : exceptions) {
903 xmlWriter.println("<exception name=\"" + pi.name() +"\" type=\"" + pi.qualifiedName()
904 + "\">");
905 xmlWriter.println("</exception>");
906 }
907 xmlWriter.println("</method>");
908 }
909
910 static void writeConstructorXML(PrintStream xmlWriter, MethodInfo mi) {
911 String scope = DroidDoc.scope(mi);
912 String deprecatedString = "";
913 if (mi.isDeprecated()) {
914 deprecatedString = "deprecated";
915 } else {
916 deprecatedString = "not deprecated";
917 }
918 xmlWriter.println("<constructor name=\"" + mi.name() + "\"\n"
919 + " type=\"" + mi.containingClass().qualifiedName() + "\"\n"
920 + " static=\"" + mi.isStatic() + "\"\n"
921 + " final=\"" + mi.isFinal() + "\"\n"
922 + " deprecated=\"" + deprecatedString + "\"\n"
923 + " visibility=\"" + scope +"\"\n"
924 //+ " source=\"" + mi.position() + "\"\n"
925 + ">");
926
927 int numParameters = mi.parameters().length;
928 int count = 0;
929 for (ParameterInfo pi : mi.parameters()) {
930 count++;
931 writeParameterXML(xmlWriter, mi, pi, count == numParameters);
932 }
933
934 ClassInfo[] exceptions = mi.thrownExceptions();
935 Arrays.sort(exceptions, ClassInfo.comparator);
936 for (ClassInfo pi : exceptions) {
937 xmlWriter.println("<exception name=\"" + pi.name() +"\" type=\"" + pi.qualifiedName()
938 + "\">");
939 xmlWriter.println("</exception>");
940 }
941 xmlWriter.println("</constructor>");
942 }
943
944 static void writeParameterXML(PrintStream xmlWriter, MethodInfo method,
945 ParameterInfo pi, boolean isLast) {
946 xmlWriter.println("<parameter name=\"" + pi.name() + "\" type=\"" +
947 makeXMLcompliant(fullParameterTypeName(method, pi.type(), isLast)) + "\">");
948 xmlWriter.println("</parameter>");
949 }
950
951 static void writeFieldXML(PrintStream xmlWriter, FieldInfo fi) {
952 String scope = DroidDoc.scope(fi);
953 String deprecatedString = "";
954 if (fi.isDeprecated()) {
955 deprecatedString = "deprecated";
956 } else {
957 deprecatedString = "not deprecated";
958 }
959 //need to make sure value is valid XML
960 String value = makeXMLcompliant(fi.constantLiteralValue());
961
962 String fullTypeName = makeXMLcompliant(fi.type().qualifiedTypeName())
963 + fi.type().dimension();
964
965 xmlWriter.println("<field name=\"" + fi.name() +"\"\n"
966 + " type=\"" + fullTypeName + "\"\n"
967 + " transient=\"" + fi.isTransient() + "\"\n"
968 + " volatile=\"" + fi.isVolatile() + "\"\n"
969 + (fieldIsInitialized(fi) ? " value=\"" + value + "\"\n" : "")
970 + " static=\"" + fi.isStatic() + "\"\n"
971 + " final=\"" + fi.isFinal() + "\"\n"
972 + " deprecated=\"" + deprecatedString + "\"\n"
973 + " visibility=\"" + scope + "\"\n"
974 //+ " source=\"" + fi.position() + "\"\n"
975 + ">");
976 xmlWriter.println("</field>");
977 }
978
979 static String makeXMLcompliant(String s) {
980 String returnString = "";
981 returnString = s.replaceAll("&", "&amp;");
982 returnString = returnString.replaceAll("<", "&lt;");
983 returnString = returnString.replaceAll(">", "&gt;");
984 returnString = returnString.replaceAll("\"", "&quot;");
985 returnString = returnString.replaceAll("'", "&pos;");
986 return returnString;
987 }
988
989 static String fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast) {
990 String fullTypeName = type.fullName(method.typeVariables());
991 if (isLast && method.isVarArgs()) {
992 // TODO: note that this does not attempt to handle hypothetical
993 // vararg methods whose last parameter is a list of arrays, e.g.
994 // "Object[]...".
995 fullTypeName = type.fullNameNoDimension(method.typeVariables()) + "...";
996 }
997 return fullTypeName;
998 }
999}