• 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 org.clearsilver.HDF;
18 
19 import java.util.*;
20 
21 public class MethodInfo extends MemberInfo
22 {
23     public static final Comparator<MethodInfo> comparator = new Comparator<MethodInfo>() {
24         public int compare(MethodInfo a, MethodInfo b) {
25             return a.name().compareTo(b.name());
26         }
27     };
28 
29     private class InlineTags implements InheritedTags
30     {
tags()31         public TagInfo[] tags()
32         {
33             return comment().tags();
34         }
inherited()35         public InheritedTags inherited()
36         {
37             MethodInfo m = findOverriddenMethod(name(), signature());
38             if (m != null) {
39                 return m.inlineTags();
40             } else {
41                 return null;
42             }
43         }
44     }
45 
addInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue)46     private static void addInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue)
47     {
48         for (ClassInfo i: ifaces) {
49             queue.add(i);
50         }
51         for (ClassInfo i: ifaces) {
52             addInterfaces(i.interfaces(), queue);
53         }
54     }
55 
56     // first looks for a superclass, and then does a breadth first search to
57     // find the least far away match
findOverriddenMethod(String name, String signature)58     public MethodInfo findOverriddenMethod(String name, String signature)
59     {
60         if (mReturnType == null) {
61             // ctor
62             return null;
63         }
64         if (mOverriddenMethod != null) {
65             return mOverriddenMethod;
66         }
67 
68         ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
69         addInterfaces(containingClass().interfaces(), queue);
70         for (ClassInfo iface: queue) {
71             for (MethodInfo me: iface.methods()) {
72                 if (me.name().equals(name)
73                         && me.signature().equals(signature)
74                         && me.inlineTags().tags() != null
75                         && me.inlineTags().tags().length > 0) {
76                     return me;
77                 }
78             }
79         }
80         return null;
81     }
82 
addRealInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue)83     private static void addRealInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue)
84     {
85         for (ClassInfo i: ifaces) {
86             queue.add(i);
87             if (i.realSuperclass() != null &&  i.realSuperclass().isAbstract()) {
88                 queue.add(i.superclass());
89             }
90         }
91         for (ClassInfo i: ifaces) {
92             addInterfaces(i.realInterfaces(), queue);
93         }
94     }
95 
findRealOverriddenMethod(String name, String signature, HashSet notStrippable)96     public MethodInfo findRealOverriddenMethod(String name, String signature, HashSet notStrippable) {
97         if (mReturnType == null) {
98         // ctor
99         return null;
100         }
101         if (mOverriddenMethod != null) {
102             return mOverriddenMethod;
103         }
104 
105         ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
106         if (containingClass().realSuperclass() != null &&
107             containingClass().realSuperclass().isAbstract()) {
108             queue.add(containingClass());
109         }
110         addInterfaces(containingClass().realInterfaces(), queue);
111         for (ClassInfo iface: queue) {
112             for (MethodInfo me: iface.methods()) {
113                 if (me.name().equals(name)
114                     && me.signature().equals(signature)
115                     && me.inlineTags().tags() != null
116                     && me.inlineTags().tags().length > 0
117                     && notStrippable.contains(me.containingClass())) {
118                 return me;
119                 }
120             }
121         }
122         return null;
123     }
124 
findSuperclassImplementation(HashSet notStrippable)125     public MethodInfo findSuperclassImplementation(HashSet notStrippable) {
126         if (mReturnType == null) {
127             // ctor
128             return null;
129         }
130         if (mOverriddenMethod != null) {
131             // Even if we're told outright that this was the overridden method, we want to
132             // be conservative and ignore mismatches of parameter types -- they arise from
133             // extending generic specializations, and we want to consider the derived-class
134             // method to be a non-override.
135             if (this.signature().equals(mOverriddenMethod.signature())) {
136                 return mOverriddenMethod;
137             }
138         }
139 
140         ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
141         if (containingClass().realSuperclass() != null &&
142                 containingClass().realSuperclass().isAbstract()) {
143             queue.add(containingClass());
144         }
145         addInterfaces(containingClass().realInterfaces(), queue);
146         for (ClassInfo iface: queue) {
147             for (MethodInfo me: iface.methods()) {
148                 if (me.name().equals(this.name())
149                         && me.signature().equals(this.signature())
150                         && notStrippable.contains(me.containingClass())) {
151                     return me;
152                 }
153             }
154         }
155         return null;
156     }
157 
findRealOverriddenClass(String name, String signature)158     public ClassInfo findRealOverriddenClass(String name, String signature) {
159         if (mReturnType == null) {
160         // ctor
161         return null;
162         }
163         if (mOverriddenMethod != null) {
164             return mOverriddenMethod.mRealContainingClass;
165         }
166 
167         ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
168         if (containingClass().realSuperclass() != null &&
169             containingClass().realSuperclass().isAbstract()) {
170             queue.add(containingClass());
171         }
172         addInterfaces(containingClass().realInterfaces(), queue);
173         for (ClassInfo iface: queue) {
174             for (MethodInfo me: iface.methods()) {
175                 if (me.name().equals(name)
176                     && me.signature().equals(signature)
177                     && me.inlineTags().tags() != null
178                     && me.inlineTags().tags().length > 0) {
179                 return iface;
180                 }
181             }
182         }
183         return null;
184     }
185 
186     private class FirstSentenceTags implements InheritedTags
187     {
tags()188         public TagInfo[] tags()
189         {
190             return comment().briefTags();
191         }
inherited()192         public InheritedTags inherited()
193         {
194             MethodInfo m = findOverriddenMethod(name(), signature());
195             if (m != null) {
196                 return m.firstSentenceTags();
197             } else {
198                 return null;
199             }
200         }
201     }
202 
203     private class ReturnTags implements InheritedTags {
tags()204         public TagInfo[] tags() {
205             return comment().returnTags();
206         }
inherited()207         public InheritedTags inherited() {
208             MethodInfo m = findOverriddenMethod(name(), signature());
209             if (m != null) {
210                 return m.returnTags();
211             } else {
212                 return null;
213             }
214         }
215     }
216 
isDeprecated()217     public boolean isDeprecated() {
218         boolean deprecated = false;
219         if (!mDeprecatedKnown) {
220             boolean commentDeprecated = (comment().deprecatedTags().length > 0);
221             boolean annotationDeprecated = false;
222             for (AnnotationInstanceInfo annotation : annotations()) {
223                 if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
224                     annotationDeprecated = true;
225                     break;
226                 }
227             }
228 
229             if (commentDeprecated != annotationDeprecated) {
230                 Errors.error(Errors.DEPRECATION_MISMATCH, position(),
231                         "Method " + mContainingClass.qualifiedName() + "." + name()
232                         + ": @Deprecated annotation and @deprecated doc tag do not match");
233             }
234 
235             mIsDeprecated = commentDeprecated | annotationDeprecated;
236             mDeprecatedKnown = true;
237         }
238         return mIsDeprecated;
239     }
240 
getTypeParameters()241     public TypeInfo[] getTypeParameters(){
242         return mTypeParameters;
243     }
244 
cloneForClass(ClassInfo newContainingClass)245     public MethodInfo cloneForClass(ClassInfo newContainingClass) {
246         MethodInfo result =  new MethodInfo(getRawCommentText(), mTypeParameters,
247                 name(), signature(), newContainingClass, realContainingClass(),
248                 isPublic(), isProtected(), isPackagePrivate(), isPrivate(), isFinal(), isStatic(),
249                 isSynthetic(), mIsAbstract, mIsSynchronized, mIsNative, mIsAnnotationElement,
250                 kind(), mFlatSignature, mOverriddenMethod,
251                 mReturnType, mParameters, mThrownExceptions, position(), annotations());
252         result.init(mDefaultAnnotationElementValue);
253         return result;
254     }
255 
MethodInfo(String rawCommentText, TypeInfo[] typeParameters, String name, String signature, ClassInfo containingClass, ClassInfo realContainingClass, boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate, boolean isFinal, boolean isStatic, boolean isSynthetic, boolean isAbstract, boolean isSynchronized, boolean isNative, boolean isAnnotationElement, String kind, String flatSignature, MethodInfo overriddenMethod, TypeInfo returnType, ParameterInfo[] parameters, ClassInfo[] thrownExceptions, SourcePositionInfo position, AnnotationInstanceInfo[] annotations)256     public MethodInfo(String rawCommentText, TypeInfo[] typeParameters, String name,
257                         String signature, ClassInfo containingClass, ClassInfo realContainingClass,
258                         boolean isPublic, boolean isProtected,
259                         boolean isPackagePrivate, boolean isPrivate,
260                         boolean isFinal, boolean isStatic, boolean isSynthetic,
261                         boolean isAbstract, boolean isSynchronized, boolean isNative,
262                         boolean isAnnotationElement, String kind,
263                         String flatSignature, MethodInfo overriddenMethod,
264                         TypeInfo returnType, ParameterInfo[] parameters,
265                         ClassInfo[] thrownExceptions, SourcePositionInfo position,
266                         AnnotationInstanceInfo[] annotations)
267     {
268         // Explicitly coerce 'final' state of Java6-compiled enum values() method, to match
269         // the Java5-emitted base API description.
270         super(rawCommentText, name, signature, containingClass, realContainingClass,
271                 isPublic, isProtected, isPackagePrivate, isPrivate,
272                 ((name.equals("values") && containingClass.isEnum()) ? true : isFinal),
273                 isStatic, isSynthetic, kind, position, annotations);
274 
275         // The underlying MethodDoc for an interface's declared methods winds up being marked
276         // non-abstract.  Correct that here by looking at the immediate-parent class, and marking
277         // this method abstract if it is an unimplemented interface method.
278         if (containingClass.isInterface()) {
279             isAbstract = true;
280         }
281 
282         mReasonOpened = "0:0";
283         mIsAnnotationElement = isAnnotationElement;
284         mTypeParameters = typeParameters;
285         mIsAbstract = isAbstract;
286         mIsSynchronized = isSynchronized;
287         mIsNative = isNative;
288         mFlatSignature = flatSignature;
289         mOverriddenMethod = overriddenMethod;
290         mReturnType = returnType;
291         mParameters = parameters;
292         mThrownExceptions = thrownExceptions;
293     }
294 
init(AnnotationValueInfo defaultAnnotationElementValue)295     public void init(AnnotationValueInfo defaultAnnotationElementValue)
296     {
297         mDefaultAnnotationElementValue = defaultAnnotationElementValue;
298     }
299 
isAbstract()300     public boolean isAbstract()
301     {
302         return mIsAbstract;
303     }
304 
isSynchronized()305     public boolean isSynchronized()
306     {
307         return mIsSynchronized;
308     }
309 
isNative()310     public boolean isNative()
311     {
312         return mIsNative;
313     }
314 
flatSignature()315     public String flatSignature()
316     {
317         return mFlatSignature;
318     }
319 
inlineTags()320     public InheritedTags inlineTags()
321     {
322         return new InlineTags();
323     }
324 
firstSentenceTags()325     public InheritedTags firstSentenceTags()
326     {
327         return new FirstSentenceTags();
328     }
329 
returnTags()330     public InheritedTags returnTags() {
331         return new ReturnTags();
332     }
333 
returnType()334     public TypeInfo returnType()
335     {
336         return mReturnType;
337     }
338 
prettySignature()339     public String prettySignature()
340     {
341         String s = "(";
342         int N = mParameters.length;
343         for (int i=0; i<N; i++) {
344             ParameterInfo p = mParameters[i];
345             TypeInfo t = p.type();
346             if (t.isPrimitive()) {
347                 s += t.simpleTypeName();
348             } else {
349                 s += t.asClassInfo().name();
350             }
351             if (i != N-1) {
352                 s += ',';
353             }
354         }
355         s += ')';
356         return s;
357     }
358 
359     /**
360      * Returns a name consistent with the {@link
361      * com.android.apicheck.MethodInfo#getHashableName()}.
362      */
getHashableName()363     public String getHashableName() {
364         StringBuilder result = new StringBuilder();
365         result.append(name());
366         for (int p = 0; p < mParameters.length; p++) {
367             result.append(":");
368             if (p == mParameters.length - 1 && isVarArgs()) {
369                 // TODO: note that this does not attempt to handle hypothetical
370                 // vararg methods whose last parameter is a list of arrays, e.g.
371                 // "Object[]...".
372                 result.append(mParameters[p].type().fullNameNoDimension(typeVariables()))
373                         .append("...");
374             } else {
375                 result.append(mParameters[p].type().fullName(typeVariables()));
376             }
377         }
378         return result.toString();
379     }
380 
inList(ClassInfo item, ThrowsTagInfo[] list)381     private boolean inList(ClassInfo item, ThrowsTagInfo[] list)
382     {
383         int len = list.length;
384         String qn = item.qualifiedName();
385         for (int i=0; i<len; i++) {
386             ClassInfo ex = list[i].exception();
387             if (ex != null && ex.qualifiedName().equals(qn)) {
388                 return true;
389             }
390         }
391         return false;
392     }
393 
throwsTags()394     public ThrowsTagInfo[] throwsTags()
395     {
396         if (mThrowsTags == null) {
397             ThrowsTagInfo[] documented = comment().throwsTags();
398             ArrayList<ThrowsTagInfo> rv = new ArrayList<ThrowsTagInfo>();
399 
400             int len = documented.length;
401             for (int i=0; i<len; i++) {
402                 rv.add(documented[i]);
403             }
404 
405             ClassInfo[] all = mThrownExceptions;
406             len = all.length;
407             for (int i=0; i<len; i++) {
408                 ClassInfo cl = all[i];
409                 if (documented == null || !inList(cl, documented)) {
410                     rv.add(new ThrowsTagInfo("@throws", "@throws",
411                                         cl.qualifiedName(), cl, "",
412                                         containingClass(), position()));
413                 }
414             }
415             mThrowsTags = rv.toArray(new ThrowsTagInfo[rv.size()]);
416         }
417         return mThrowsTags;
418     }
419 
indexOfParam(String name, String[] list)420     private static int indexOfParam(String name, String[] list)
421     {
422         final int N = list.length;
423         for (int i=0; i<N; i++) {
424             if (name.equals(list[i])) {
425                 return i;
426             }
427         }
428         return -1;
429     }
430 
paramTags()431     public ParamTagInfo[] paramTags()
432     {
433         if (mParamTags == null) {
434             final int N = mParameters.length;
435 
436             String[] names = new String[N];
437             String[] comments = new String[N];
438             SourcePositionInfo[] positions = new SourcePositionInfo[N];
439 
440             // get the right names so we can handle our names being different from
441             // our parent's names.
442             for (int i=0; i<N; i++) {
443                 names[i] = mParameters[i].name();
444                 comments[i] = "";
445                 positions[i] = mParameters[i].position();
446             }
447 
448             // gather our comments, and complain about misnamed @param tags
449             for (ParamTagInfo tag: comment().paramTags()) {
450                 int index = indexOfParam(tag.parameterName(), names);
451                 if (index >= 0) {
452                     comments[index] = tag.parameterComment();
453                     positions[index] = tag.position();
454                 } else {
455                     Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
456                             "@param tag with name that doesn't match the parameter list: '"
457                             + tag.parameterName() + "'");
458                 }
459             }
460 
461             // get our parent's tags to fill in the blanks
462             MethodInfo overridden = this.findOverriddenMethod(name(), signature());
463             if (overridden != null) {
464                 ParamTagInfo[] maternal = overridden.paramTags();
465                 for (int i=0; i<N; i++) {
466                     if (comments[i].equals("")) {
467                         comments[i] = maternal[i].parameterComment();
468                         positions[i] = maternal[i].position();
469                     }
470                 }
471             }
472 
473             // construct the results, and cache them for next time
474             mParamTags = new ParamTagInfo[N];
475             for (int i=0; i<N; i++) {
476                 mParamTags[i] = new ParamTagInfo("@param", "@param", names[i] + " " + comments[i],
477                         parent(), positions[i]);
478 
479                 // while we're here, if we find any parameters that are still undocumented at this
480                 // point, complain. (this warning is off by default, because it's really, really
481                 // common; but, it's good to be able to enforce it)
482                 if (comments[i].equals("")) {
483                     Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i],
484                             "Undocumented parameter '" + names[i] + "' on method '"
485                             + name() + "'");
486                 }
487             }
488         }
489         return mParamTags;
490     }
491 
seeTags()492     public SeeTagInfo[] seeTags()
493     {
494         SeeTagInfo[] result = comment().seeTags();
495         if (result == null) {
496             if (mOverriddenMethod != null) {
497                 result = mOverriddenMethod.seeTags();
498             }
499         }
500         return result;
501     }
502 
deprecatedTags()503     public TagInfo[] deprecatedTags()
504     {
505         TagInfo[] result = comment().deprecatedTags();
506         if (result.length == 0) {
507             if (comment().undeprecateTags().length == 0) {
508                 if (mOverriddenMethod != null) {
509                     result = mOverriddenMethod.deprecatedTags();
510                 }
511             }
512         }
513         return result;
514     }
515 
parameters()516     public ParameterInfo[] parameters()
517     {
518         return mParameters;
519     }
520 
521 
matchesParams(String[] params, String[] dimensions)522     public boolean matchesParams(String[] params, String[] dimensions)
523     {
524         if (mParamStrings == null) {
525             ParameterInfo[] mine = mParameters;
526             int len = mine.length;
527             if (len != params.length) {
528                 return false;
529             }
530             for (int i=0; i<len; i++) {
531                 TypeInfo t = mine[i].type();
532                 if (!t.dimension().equals(dimensions[i])) {
533                     return false;
534                 }
535                 String qn = t.qualifiedTypeName();
536                 String s = params[i];
537                 int slen = s.length();
538                 int qnlen = qn.length();
539                 if (!(qn.equals(s) ||
540                         ((slen+1)<qnlen && qn.charAt(qnlen-slen-1)=='.'
541                          && qn.endsWith(s)))) {
542                     return false;
543                 }
544             }
545         }
546         return true;
547     }
548 
makeHDF(HDF data, String base)549     public void makeHDF(HDF data, String base)
550     {
551         data.setValue(base + ".kind", kind());
552         data.setValue(base + ".name", name());
553         data.setValue(base + ".href", htmlPage());
554         data.setValue(base + ".anchor", anchor());
555 
556         if (mReturnType != null) {
557             returnType().makeHDF(data, base + ".returnType", false, typeVariables());
558             data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
559         }
560 
561         data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
562         data.setValue(base + ".final", isFinal() ? "final" : "");
563         data.setValue(base + ".static", isStatic() ? "static" : "");
564 
565         TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
566         TagInfo.makeHDF(data, base + ".descr", inlineTags());
567         TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
568         TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
569         data.setValue(base + ".since", getSince());
570         ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
571         AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
572         ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
573         ParameterInfo.makeHDF(data, base + ".params", parameters(), isVarArgs(), typeVariables());
574         if (isProtected()) {
575             data.setValue(base + ".scope", "protected");
576         }
577         else if (isPublic()) {
578             data.setValue(base + ".scope", "public");
579         }
580         TagInfo.makeHDF(data, base + ".returns", returnTags());
581 
582         if (mTypeParameters != null) {
583             TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
584         }
585     }
586 
typeVariables()587     public HashSet<String> typeVariables()
588     {
589         HashSet<String> result = TypeInfo.typeVariables(mTypeParameters);
590         ClassInfo cl = containingClass();
591         while (cl != null) {
592             TypeInfo[] types = cl.asTypeInfo().typeArguments();
593             if (types != null) {
594                 TypeInfo.typeVariables(types, result);
595             }
596             cl = cl.containingClass();
597         }
598         return result;
599     }
600 
601     @Override
isExecutable()602     public boolean isExecutable()
603     {
604         return true;
605     }
606 
thrownExceptions()607     public ClassInfo[] thrownExceptions()
608     {
609         return mThrownExceptions;
610     }
611 
typeArgumentsName(HashSet<String> typeVars)612     public String typeArgumentsName(HashSet<String> typeVars)
613     {
614         if (mTypeParameters == null || mTypeParameters.length == 0) {
615             return "";
616         } else {
617             return TypeInfo.typeArgumentsName(mTypeParameters, typeVars);
618         }
619     }
620 
isAnnotationElement()621     public boolean isAnnotationElement()
622     {
623         return mIsAnnotationElement;
624     }
625 
defaultAnnotationElementValue()626     public AnnotationValueInfo defaultAnnotationElementValue()
627     {
628         return mDefaultAnnotationElementValue;
629     }
630 
setVarargs(boolean set)631     public void setVarargs(boolean set){
632         mIsVarargs = set;
633     }
isVarArgs()634     public boolean isVarArgs(){
635       return mIsVarargs;
636     }
637 
638     @Override
toString()639     public String toString(){
640       return this.name();
641     }
642 
setReason(String reason)643     public void setReason(String reason) {
644         mReasonOpened = reason;
645     }
646 
getReason()647     public String getReason() {
648         return mReasonOpened;
649     }
650 
651     private String mFlatSignature;
652     private MethodInfo mOverriddenMethod;
653     private TypeInfo mReturnType;
654     private boolean mIsAnnotationElement;
655     private boolean mIsAbstract;
656     private boolean mIsSynchronized;
657     private boolean mIsNative;
658     private boolean mIsVarargs;
659     private boolean mDeprecatedKnown;
660     private boolean mIsDeprecated;
661     private ParameterInfo[] mParameters;
662     private ClassInfo[] mThrownExceptions;
663     private String[] mParamStrings;
664     ThrowsTagInfo[] mThrowsTags;
665     private ParamTagInfo[] mParamTags;
666     private TypeInfo[] mTypeParameters;
667     private AnnotationValueInfo mDefaultAnnotationElementValue;
668     private String mReasonOpened;
669 }
670 
671