OSDN Git Service

eclair snapshot
[android-x86/build.git] / tools / droiddoc / src / MethodInfo.java
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     {
31         public TagInfo[] tags()
32         {
33             return comment().tags();
34         }
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
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
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
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
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
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
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     {
188         public TagInfo[] tags()
189         {
190             return comment().briefTags();
191         }
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 {
204         public TagInfo[] tags() {
205             return comment().returnTags();
206         }
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
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
241     public TypeInfo[] getTypeParameters(){
242         return mTypeParameters;
243     }
244
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
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
295     public void init(AnnotationValueInfo defaultAnnotationElementValue)
296     {
297         mDefaultAnnotationElementValue = defaultAnnotationElementValue;
298     }
299
300     public boolean isAbstract()
301     {
302         return mIsAbstract;
303     }
304
305     public boolean isSynchronized()
306     {
307         return mIsSynchronized;
308     }
309
310     public boolean isNative()
311     {
312         return mIsNative;
313     }
314
315     public String flatSignature()
316     {
317         return mFlatSignature;
318     }
319
320     public InheritedTags inlineTags()
321     {
322         return new InlineTags();
323     }
324
325     public InheritedTags firstSentenceTags()
326     {
327         return new FirstSentenceTags();
328     }
329
330     public InheritedTags returnTags() {
331         return new ReturnTags();
332     }
333
334     public TypeInfo returnType()
335     {
336         return mReturnType;
337     }
338
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      */
363     public String getHashableName() {
364         StringBuilder result = new StringBuilder();
365         result.append(name());
366         for (ParameterInfo pInfo : mParameters) {
367             result.append(":").append(pInfo.type().fullName());
368         }
369         return result.toString();
370     }
371
372     private boolean inList(ClassInfo item, ThrowsTagInfo[] list)
373     {
374         int len = list.length;
375         String qn = item.qualifiedName();
376         for (int i=0; i<len; i++) {
377             ClassInfo ex = list[i].exception();
378             if (ex != null && ex.qualifiedName().equals(qn)) {
379                 return true;
380             }
381         }
382         return false;
383     }
384
385     public ThrowsTagInfo[] throwsTags()
386     {
387         if (mThrowsTags == null) {
388             ThrowsTagInfo[] documented = comment().throwsTags();
389             ArrayList<ThrowsTagInfo> rv = new ArrayList<ThrowsTagInfo>();
390
391             int len = documented.length;
392             for (int i=0; i<len; i++) {
393                 rv.add(documented[i]);
394             }
395
396             ClassInfo[] all = mThrownExceptions;
397             len = all.length;
398             for (int i=0; i<len; i++) {
399                 ClassInfo cl = all[i];
400                 if (documented == null || !inList(cl, documented)) {
401                     rv.add(new ThrowsTagInfo("@throws", "@throws",
402                                         cl.qualifiedName(), cl, "",
403                                         containingClass(), position()));
404                 }
405             }
406             mThrowsTags = rv.toArray(new ThrowsTagInfo[rv.size()]);
407         }
408         return mThrowsTags;
409     }
410
411     private static int indexOfParam(String name, String[] list)
412     {
413         final int N = list.length;
414         for (int i=0; i<N; i++) {
415             if (name.equals(list[i])) {
416                 return i;
417             }
418         }
419         return -1;
420     }
421
422     public ParamTagInfo[] paramTags()
423     {
424         if (mParamTags == null) {
425             final int N = mParameters.length;
426
427             String[] names = new String[N];
428             String[] comments = new String[N];
429             SourcePositionInfo[] positions = new SourcePositionInfo[N];
430
431             // get the right names so we can handle our names being different from
432             // our parent's names.
433             for (int i=0; i<N; i++) {
434                 names[i] = mParameters[i].name();
435                 comments[i] = "";
436                 positions[i] = mParameters[i].position();
437             }
438
439             // gather our comments, and complain about misnamed @param tags
440             for (ParamTagInfo tag: comment().paramTags()) {
441                 int index = indexOfParam(tag.parameterName(), names);
442                 if (index >= 0) {
443                     comments[index] = tag.parameterComment();
444                     positions[index] = tag.position();
445                 } else {
446                     Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
447                             "@param tag with name that doesn't match the parameter list: '"
448                             + tag.parameterName() + "'");
449                 }
450             }
451
452             // get our parent's tags to fill in the blanks
453             MethodInfo overridden = this.findOverriddenMethod(name(), signature());
454             if (overridden != null) {
455                 ParamTagInfo[] maternal = overridden.paramTags();
456                 for (int i=0; i<N; i++) {
457                     if (comments[i].equals("")) {
458                         comments[i] = maternal[i].parameterComment();
459                         positions[i] = maternal[i].position();
460                     }
461                 }
462             }
463
464             // construct the results, and cache them for next time
465             mParamTags = new ParamTagInfo[N];
466             for (int i=0; i<N; i++) {
467                 mParamTags[i] = new ParamTagInfo("@param", "@param", names[i] + " " + comments[i],
468                         parent(), positions[i]);
469
470                 // while we're here, if we find any parameters that are still undocumented at this
471                 // point, complain. (this warning is off by default, because it's really, really
472                 // common; but, it's good to be able to enforce it)
473                 if (comments[i].equals("")) {
474                     Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i],
475                             "Undocumented parameter '" + names[i] + "' on method '"
476                             + name() + "'");
477                 }
478             }
479         }
480         return mParamTags;
481     }
482
483     public SeeTagInfo[] seeTags()
484     {
485         SeeTagInfo[] result = comment().seeTags();
486         if (result == null) {
487             if (mOverriddenMethod != null) {
488                 result = mOverriddenMethod.seeTags();
489             }
490         }
491         return result;
492     }
493
494     public TagInfo[] deprecatedTags()
495     {
496         TagInfo[] result = comment().deprecatedTags();
497         if (result.length == 0) {
498             if (comment().undeprecateTags().length == 0) {
499                 if (mOverriddenMethod != null) {
500                     result = mOverriddenMethod.deprecatedTags();
501                 }
502             }
503         }
504         return result;
505     }
506
507     public ParameterInfo[] parameters()
508     {
509         return mParameters;
510     }
511
512
513     public boolean matchesParams(String[] params, String[] dimensions)
514     {
515         if (mParamStrings == null) {
516             ParameterInfo[] mine = mParameters;
517             int len = mine.length;
518             if (len != params.length) {
519                 return false;
520             }
521             for (int i=0; i<len; i++) {
522                 TypeInfo t = mine[i].type();
523                 if (!t.dimension().equals(dimensions[i])) {
524                     return false;
525                 }
526                 String qn = t.qualifiedTypeName();
527                 String s = params[i];
528                 int slen = s.length();
529                 int qnlen = qn.length();
530                 if (!(qn.equals(s) ||
531                         ((slen+1)<qnlen && qn.charAt(qnlen-slen-1)=='.'
532                          && qn.endsWith(s)))) {
533                     return false;
534                 }
535             }
536         }
537         return true;
538     }
539
540     public void makeHDF(HDF data, String base)
541     {
542         data.setValue(base + ".kind", kind());
543         data.setValue(base + ".name", name());
544         data.setValue(base + ".href", htmlPage());
545         data.setValue(base + ".anchor", anchor());
546
547         if (mReturnType != null) {
548             returnType().makeHDF(data, base + ".returnType", false, typeVariables());
549             data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
550         }
551
552         data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
553         data.setValue(base + ".final", isFinal() ? "final" : "");
554         data.setValue(base + ".static", isStatic() ? "static" : "");
555
556         TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
557         TagInfo.makeHDF(data, base + ".descr", inlineTags());
558         TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
559         TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
560         data.setValue(base + ".since", getSince());
561         ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
562         AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
563         ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
564         ParameterInfo.makeHDF(data, base + ".params", parameters(), isVarArgs(), typeVariables());
565         if (isProtected()) {
566             data.setValue(base + ".scope", "protected");
567         }
568         else if (isPublic()) {
569             data.setValue(base + ".scope", "public");
570         }
571         TagInfo.makeHDF(data, base + ".returns", returnTags());
572
573         if (mTypeParameters != null) {
574             TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
575         }
576     }
577
578     public HashSet<String> typeVariables()
579     {
580         HashSet<String> result = TypeInfo.typeVariables(mTypeParameters);
581         ClassInfo cl = containingClass();
582         while (cl != null) {
583             TypeInfo[] types = cl.asTypeInfo().typeArguments();
584             if (types != null) {
585                 TypeInfo.typeVariables(types, result);
586             }
587             cl = cl.containingClass();
588         }
589         return result;
590     }
591
592     @Override
593     public boolean isExecutable()
594     {
595         return true;
596     }
597
598     public ClassInfo[] thrownExceptions()
599     {
600         return mThrownExceptions;
601     }
602
603     public String typeArgumentsName(HashSet<String> typeVars)
604     {
605         if (mTypeParameters == null || mTypeParameters.length == 0) {
606             return "";
607         } else {
608             return TypeInfo.typeArgumentsName(mTypeParameters, typeVars);
609         }
610     }
611
612     public boolean isAnnotationElement()
613     {
614         return mIsAnnotationElement;
615     }
616
617     public AnnotationValueInfo defaultAnnotationElementValue()
618     {
619         return mDefaultAnnotationElementValue;
620     }
621
622     public void setVarargs(boolean set){
623         mIsVarargs = set;
624     }
625     public boolean isVarArgs(){
626       return mIsVarargs;
627     }
628
629     @Override
630     public String toString(){
631       return this.name();
632     }
633
634     public void setReason(String reason) {
635         mReasonOpened = reason;
636     }
637
638     public String getReason() {
639         return mReasonOpened;
640     }
641
642     private String mFlatSignature;
643     private MethodInfo mOverriddenMethod;
644     private TypeInfo mReturnType;
645     private boolean mIsAnnotationElement;
646     private boolean mIsAbstract;
647     private boolean mIsSynchronized;
648     private boolean mIsNative;
649     private boolean mIsVarargs;
650     private boolean mDeprecatedKnown;
651     private boolean mIsDeprecated;
652     private ParameterInfo[] mParameters;
653     private ClassInfo[] mThrownExceptions;
654     private String[] mParamStrings;
655     ThrowsTagInfo[] mThrowsTags;
656     private ParamTagInfo[] mParamTags;
657     private TypeInfo[] mTypeParameters;
658     private AnnotationValueInfo mDefaultAnnotationElementValue;
659     private String mReasonOpened;
660 }
661