blob: ed98378f2cc5d02582fd4a79f2df2b8c336281ed [file] [log] [blame]
The Android Open Source Projectb6c1cf62008-10-21 07:00:00 -07001/*
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 super(rawCommentText, name, signature, containingClass, realContainingClass,
270 isPublic, isProtected, isPackagePrivate, isPrivate,
271 isFinal, isStatic, isSynthetic, kind, position, annotations);
272
273 // The underlying MethodDoc for an interface's declared methods winds up being marked
274 // non-abstract. Correct that here by looking at the immediate-parent class, and marking
275 // this method abstract if it is an unimplemented interface method.
276 if (containingClass.isInterface()) {
277 isAbstract = true;
278 }
279
280 mReasonOpened = "0:0";
281 mIsAnnotationElement = isAnnotationElement;
282 mTypeParameters = typeParameters;
283 mIsAbstract = isAbstract;
284 mIsSynchronized = isSynchronized;
285 mIsNative = isNative;
286 mFlatSignature = flatSignature;
287 mOverriddenMethod = overriddenMethod;
288 mReturnType = returnType;
289 mParameters = parameters;
290 mThrownExceptions = thrownExceptions;
291 }
292
293 public void init(AnnotationValueInfo defaultAnnotationElementValue)
294 {
295 mDefaultAnnotationElementValue = defaultAnnotationElementValue;
296 }
297
298 public boolean isAbstract()
299 {
300 return mIsAbstract;
301 }
302
303 public boolean isSynchronized()
304 {
305 return mIsSynchronized;
306 }
307
308 public boolean isNative()
309 {
310 return mIsNative;
311 }
312
313 public String flatSignature()
314 {
315 return mFlatSignature;
316 }
317
318 public InheritedTags inlineTags()
319 {
320 return new InlineTags();
321 }
322
323 public InheritedTags firstSentenceTags()
324 {
325 return new FirstSentenceTags();
326 }
327
328 public InheritedTags returnTags() {
329 return new ReturnTags();
330 }
331
332 public TypeInfo returnType()
333 {
334 return mReturnType;
335 }
336
337 public String prettySignature()
338 {
339 String s = "(";
340 int N = mParameters.length;
341 for (int i=0; i<N; i++) {
342 ParameterInfo p = mParameters[i];
343 TypeInfo t = p.type();
344 if (t.isPrimitive()) {
345 s += t.simpleTypeName();
346 } else {
347 s += t.asClassInfo().name();
348 }
349 if (i != N-1) {
350 s += ',';
351 }
352 }
353 s += ')';
354 return s;
355 }
356
357 private boolean inList(ClassInfo item, ThrowsTagInfo[] list)
358 {
359 int len = list.length;
360 String qn = item.qualifiedName();
361 for (int i=0; i<len; i++) {
362 ClassInfo ex = list[i].exception();
363 if (ex != null && ex.qualifiedName().equals(qn)) {
364 return true;
365 }
366 }
367 return false;
368 }
369
370 public ThrowsTagInfo[] throwsTags()
371 {
372 if (mThrowsTags == null) {
373 ThrowsTagInfo[] documented = comment().throwsTags();
374 ArrayList<ThrowsTagInfo> rv = new ArrayList<ThrowsTagInfo>();
375
376 int len = documented.length;
377 for (int i=0; i<len; i++) {
378 rv.add(documented[i]);
379 }
380
381 ClassInfo[] all = mThrownExceptions;
382 len = all.length;
383 for (int i=0; i<len; i++) {
384 ClassInfo cl = all[i];
385 if (documented == null || !inList(cl, documented)) {
386 rv.add(new ThrowsTagInfo("@throws", "@throws",
387 cl.qualifiedName(), cl, "",
388 containingClass(), position()));
389 }
390 }
391 mThrowsTags = rv.toArray(new ThrowsTagInfo[rv.size()]);
392 }
393 return mThrowsTags;
394 }
395
396 private static int indexOfParam(String name, String[] list)
397 {
398 final int N = list.length;
399 for (int i=0; i<N; i++) {
400 if (name.equals(list[i])) {
401 return i;
402 }
403 }
404 return -1;
405 }
406
407 public ParamTagInfo[] paramTags()
408 {
409 if (mParamTags == null) {
410 final int N = mParameters.length;
411
412 String[] names = new String[N];
413 String[] comments = new String[N];
414 SourcePositionInfo[] positions = new SourcePositionInfo[N];
415
416 // get the right names so we can handle our names being different from
417 // our parent's names.
418 for (int i=0; i<N; i++) {
419 names[i] = mParameters[i].name();
420 comments[i] = "";
421 positions[i] = mParameters[i].position();
422 }
423
424 // gather our comments, and complain about misnamed @param tags
425 for (ParamTagInfo tag: comment().paramTags()) {
426 int index = indexOfParam(tag.parameterName(), names);
427 if (index >= 0) {
428 comments[index] = tag.parameterComment();
429 positions[index] = tag.position();
430 } else {
431 Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
432 "@param tag with name that doesn't match the parameter list: '"
433 + tag.parameterName() + "'");
434 }
435 }
436
437 // get our parent's tags to fill in the blanks
438 MethodInfo overridden = this.findOverriddenMethod(name(), signature());
439 if (overridden != null) {
440 ParamTagInfo[] maternal = overridden.paramTags();
441 for (int i=0; i<N; i++) {
442 if (comments[i].equals("")) {
443 comments[i] = maternal[i].parameterComment();
444 positions[i] = maternal[i].position();
445 }
446 }
447 }
448
449 // construct the results, and cache them for next time
450 mParamTags = new ParamTagInfo[N];
451 for (int i=0; i<N; i++) {
452 mParamTags[i] = new ParamTagInfo("@param", "@param", names[i] + " " + comments[i],
453 parent(), positions[i]);
454
455 // while we're here, if we find any parameters that are still undocumented at this
456 // point, complain. (this warning is off by default, because it's really, really
457 // common; but, it's good to be able to enforce it)
458 if (comments[i].equals("")) {
459 Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i],
460 "Undocumented parameter '" + names[i] + "' on method '"
461 + name() + "'");
462 }
463 }
464 }
465 return mParamTags;
466 }
467
468 public SeeTagInfo[] seeTags()
469 {
470 SeeTagInfo[] result = comment().seeTags();
471 if (result == null) {
472 if (mOverriddenMethod != null) {
473 result = mOverriddenMethod.seeTags();
474 }
475 }
476 return result;
477 }
478
479 public TagInfo[] deprecatedTags()
480 {
481 TagInfo[] result = comment().deprecatedTags();
482 if (result.length == 0) {
483 if (comment().undeprecateTags().length == 0) {
484 if (mOverriddenMethod != null) {
485 result = mOverriddenMethod.deprecatedTags();
486 }
487 }
488 }
489 return result;
490 }
491
492 public ParameterInfo[] parameters()
493 {
494 return mParameters;
495 }
496
497
498 public boolean matchesParams(String[] params, String[] dimensions)
499 {
500 if (mParamStrings == null) {
501 ParameterInfo[] mine = mParameters;
502 int len = mine.length;
503 if (len != params.length) {
504 return false;
505 }
506 for (int i=0; i<len; i++) {
507 TypeInfo t = mine[i].type();
508 if (!t.dimension().equals(dimensions[i])) {
509 return false;
510 }
511 String qn = t.qualifiedTypeName();
512 String s = params[i];
513 int slen = s.length();
514 int qnlen = qn.length();
515 if (!(qn.equals(s) ||
516 ((slen+1)<qnlen && qn.charAt(qnlen-slen-1)=='.'
517 && qn.endsWith(s)))) {
518 return false;
519 }
520 }
521 }
522 return true;
523 }
524
525 public void makeHDF(HDF data, String base)
526 {
527 data.setValue(base + ".kind", kind());
528 data.setValue(base + ".name", name());
529 data.setValue(base + ".href", htmlPage());
530 data.setValue(base + ".anchor", anchor());
531
532 if (mReturnType != null) {
533 returnType().makeHDF(data, base + ".returnType", false, typeVariables());
534 data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
535 }
536
537 data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
538 data.setValue(base + ".final", isFinal() ? "final" : "");
539 data.setValue(base + ".static", isStatic() ? "static" : "");
540
541 TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
542 TagInfo.makeHDF(data, base + ".descr", inlineTags());
543 TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
544 TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
545 ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
546 AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
547 ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
548 ParameterInfo.makeHDF(data, base + ".params", parameters(), isVarArgs(), typeVariables());
549 if (isProtected()) {
550 data.setValue(base + ".scope", "protected");
551 }
552 else if (isPublic()) {
553 data.setValue(base + ".scope", "public");
554 }
555 TagInfo.makeHDF(data, base + ".returns", returnTags());
556
557 if (mTypeParameters != null) {
558 TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
559 }
560 }
561
562 public HashSet<String> typeVariables()
563 {
564 HashSet<String> result = TypeInfo.typeVariables(mTypeParameters);
565 ClassInfo cl = containingClass();
566 while (cl != null) {
567 TypeInfo[] types = cl.asTypeInfo().typeArguments();
568 if (types != null) {
569 TypeInfo.typeVariables(types, result);
570 }
571 cl = cl.containingClass();
572 }
573 return result;
574 }
575
576 public boolean isExecutable()
577 {
578 return true;
579 }
580
581 public ClassInfo[] thrownExceptions()
582 {
583 return mThrownExceptions;
584 }
585
586 public String typeArgumentsName(HashSet<String> typeVars)
587 {
588 if (mTypeParameters == null || mTypeParameters.length == 0) {
589 return "";
590 } else {
591 return TypeInfo.typeArgumentsName(mTypeParameters, typeVars);
592 }
593 }
594
595 public boolean isAnnotationElement()
596 {
597 return mIsAnnotationElement;
598 }
599
600 public AnnotationValueInfo defaultAnnotationElementValue()
601 {
602 return mDefaultAnnotationElementValue;
603 }
604
605 public void setVarargs(boolean set){
606 mIsVarargs = set;
607 }
608 public boolean isVarArgs(){
609 return mIsVarargs;
610 }
611 public String toString(){
612 return this.name();
613 }
614
615 public void setReason(String reason) {
616 mReasonOpened = reason;
617 }
618
619 public String getReason() {
620 return mReasonOpened;
621 }
622
623 private String mFlatSignature;
624 private MethodInfo mOverriddenMethod;
625 private TypeInfo mReturnType;
626 private boolean mIsAnnotationElement;
627 private boolean mIsAbstract;
628 private boolean mIsSynchronized;
629 private boolean mIsNative;
630 private boolean mIsVarargs;
631 private boolean mDeprecatedKnown;
632 private boolean mIsDeprecated;
633 private ParameterInfo[] mParameters;
634 private ClassInfo[] mThrownExceptions;
635 private String[] mParamStrings;
636 ThrowsTagInfo[] mThrowsTags;
637 private ParamTagInfo[] mParamTags;
638 private TypeInfo[] mTypeParameters;
639 private AnnotationValueInfo mDefaultAnnotationElementValue;
640 private String mReasonOpened;
641}
642