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