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