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