• 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         // 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 
324         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 
writeClassFile(PrintStream stream, ClassInfo cl)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 
writeClass(PrintStream stream, ClassInfo cl)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 
writeMethod(PrintStream stream, MethodInfo method, boolean isConstructor)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 
writeField(PrintStream stream, FieldInfo field)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 
fieldIsInitialized(FieldInfo field)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.
methodIsOverride(MethodInfo mi)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 
canCallMethod(ClassInfo from, MethodInfo m)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.
superCtorCall(ClassInfo cl, ClassInfo[] thrownExceptions)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 
writeAnnotations(PrintStream stream, AnnotationInstanceInfo[] annotations)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 
writeAnnotationElement(PrintStream stream, MethodInfo ann)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 
writeXML(PrintStream xmlWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses, HashSet notStrippable)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 
writePackageXML(PrintStream xmlWriter, PackageInfo pack, List<ClassInfo> classList, HashSet notStrippable)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);
797         // 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         }
802         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 
writeClassXML(PrintStream xmlWriter, ClassInfo cl, HashSet notStrippable)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 
writeMethodXML(PrintStream xmlWriter, MethodInfo mi)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 
writeConstructorXML(PrintStream xmlWriter, MethodInfo mi)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 
writeParameterXML(PrintStream xmlWriter, MethodInfo method, ParameterInfo pi, boolean isLast)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 
writeFieldXML(PrintStream xmlWriter, FieldInfo fi)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 
makeXMLcompliant(String s)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 
fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast)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 }
1000