blob: eb360cd3071dbe1694221956761b31d4c1f37212 [file] [log] [blame]
Ben Dodson920dbbb2010-08-04 15:21:06 -07001/*
2 * Copyright (C) 2010 Google Inc.
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
17package com.google.doclava;
18
19import com.google.clearsilver.jsilver.data.Data;
20import com.google.doclava.apicheck.AbstractMethodInfo;
Mathieu11b17962013-06-07 17:24:18 -050021import com.google.doclava.apicheck.ApiInfo;
Ben Dodson920dbbb2010-08-04 15:21:06 -070022
23import java.util.*;
24
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -070025public class MethodInfo extends MemberInfo implements AbstractMethodInfo, Resolvable {
Ben Dodson920dbbb2010-08-04 15:21:06 -070026 public static final Comparator<MethodInfo> comparator = new Comparator<MethodInfo>() {
27 public int compare(MethodInfo a, MethodInfo b) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070028 return a.name().compareTo(b.name());
Ben Dodson920dbbb2010-08-04 15:21:06 -070029 }
30 };
31
32 private class InlineTags implements InheritedTags {
33 public TagInfo[] tags() {
34 return comment().tags();
35 }
36
37 public InheritedTags inherited() {
38 MethodInfo m = findOverriddenMethod(name(), signature());
39 if (m != null) {
40 return m.inlineTags();
41 } else {
42 return null;
43 }
44 }
45 }
46
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070047 private static void addInterfaces(ArrayList<ClassInfo> ifaces, ArrayList<ClassInfo> queue) {
Ben Dodson920dbbb2010-08-04 15:21:06 -070048 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 if (mReturnType == null) {
60 // ctor
61 return null;
62 }
63 if (mOverriddenMethod != null) {
64 return mOverriddenMethod;
65 }
66
67 ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
68 addInterfaces(containingClass().interfaces(), queue);
69 for (ClassInfo iface : queue) {
70 for (MethodInfo me : iface.methods()) {
71 if (me.name().equals(name) && me.signature().equals(signature)
72 && me.inlineTags().tags() != null && me.inlineTags().tags().length > 0) {
73 return me;
74 }
75 }
76 }
77 return null;
78 }
79
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -070080 private static void addRealInterfaces(ArrayList<ClassInfo> ifaces, ArrayList<ClassInfo> queue) {
Ben Dodson920dbbb2010-08-04 15:21:06 -070081 for (ClassInfo i : ifaces) {
82 queue.add(i);
83 if (i.realSuperclass() != null && i.realSuperclass().isAbstract()) {
84 queue.add(i.superclass());
85 }
86 }
87 for (ClassInfo i : ifaces) {
88 addInterfaces(i.realInterfaces(), queue);
89 }
90 }
91
92 public MethodInfo findRealOverriddenMethod(String name, String signature, HashSet notStrippable) {
93 if (mReturnType == null) {
94 // ctor
95 return null;
96 }
97 if (mOverriddenMethod != null) {
98 return mOverriddenMethod;
99 }
100
101 ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
102 if (containingClass().realSuperclass() != null
103 && containingClass().realSuperclass().isAbstract()) {
104 queue.add(containingClass());
105 }
106 addInterfaces(containingClass().realInterfaces(), queue);
107 for (ClassInfo iface : queue) {
108 for (MethodInfo me : iface.methods()) {
109 if (me.name().equals(name) && me.signature().equals(signature)
110 && me.inlineTags().tags() != null && me.inlineTags().tags().length > 0
111 && notStrippable.contains(me.containingClass())) {
112 return me;
113 }
114 }
115 }
116 return null;
117 }
118
119 public MethodInfo findSuperclassImplementation(HashSet notStrippable) {
120 if (mReturnType == null) {
121 // ctor
122 return null;
123 }
124 if (mOverriddenMethod != null) {
125 // Even if we're told outright that this was the overridden method, we want to
126 // be conservative and ignore mismatches of parameter types -- they arise from
127 // extending generic specializations, and we want to consider the derived-class
128 // method to be a non-override.
129 if (this.signature().equals(mOverriddenMethod.signature())) {
130 return mOverriddenMethod;
131 }
132 }
133
134 ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
135 if (containingClass().realSuperclass() != null
136 && containingClass().realSuperclass().isAbstract()) {
137 queue.add(containingClass());
138 }
139 addInterfaces(containingClass().realInterfaces(), queue);
140 for (ClassInfo iface : queue) {
141 for (MethodInfo me : iface.methods()) {
142 if (me.name().equals(this.name()) && me.signature().equals(this.signature())
143 && notStrippable.contains(me.containingClass())) {
144 return me;
145 }
146 }
147 }
148 return null;
149 }
150
151 public ClassInfo findRealOverriddenClass(String name, String signature) {
152 if (mReturnType == null) {
153 // ctor
154 return null;
155 }
156 if (mOverriddenMethod != null) {
157 return mOverriddenMethod.mRealContainingClass;
158 }
159
160 ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
161 if (containingClass().realSuperclass() != null
162 && containingClass().realSuperclass().isAbstract()) {
163 queue.add(containingClass());
164 }
165 addInterfaces(containingClass().realInterfaces(), queue);
166 for (ClassInfo iface : queue) {
167 for (MethodInfo me : iface.methods()) {
168 if (me.name().equals(name) && me.signature().equals(signature)
169 && me.inlineTags().tags() != null && me.inlineTags().tags().length > 0) {
170 return iface;
171 }
172 }
173 }
174 return null;
175 }
176
177 private class FirstSentenceTags implements InheritedTags {
178 public TagInfo[] tags() {
179 return comment().briefTags();
180 }
181
182 public InheritedTags inherited() {
183 MethodInfo m = findOverriddenMethod(name(), signature());
184 if (m != null) {
185 return m.firstSentenceTags();
186 } else {
187 return null;
188 }
189 }
190 }
191
192 private class ReturnTags implements InheritedTags {
193 public TagInfo[] tags() {
194 return comment().returnTags();
195 }
196
197 public InheritedTags inherited() {
198 MethodInfo m = findOverriddenMethod(name(), signature());
199 if (m != null) {
200 return m.returnTags();
201 } else {
202 return null;
203 }
204 }
205 }
206
207 public boolean isDeprecated() {
208 boolean deprecated = false;
209 if (!mDeprecatedKnown) {
210 boolean commentDeprecated = comment().isDeprecated();
211 boolean annotationDeprecated = false;
212 for (AnnotationInstanceInfo annotation : annotations()) {
213 if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
214 annotationDeprecated = true;
215 break;
216 }
217 }
218
219 if (commentDeprecated != annotationDeprecated) {
220 Errors.error(Errors.DEPRECATION_MISMATCH, position(), "Method "
221 + mContainingClass.qualifiedName() + "." + name()
222 + ": @Deprecated annotation and @deprecated doc tag do not match");
223 }
224
225 mIsDeprecated = commentDeprecated | annotationDeprecated;
226 mDeprecatedKnown = true;
227 }
228 return mIsDeprecated;
229 }
230
231 public void setDeprecated(boolean deprecated) {
232 mDeprecatedKnown = true;
233 mIsDeprecated = deprecated;
234 }
235
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700236 public ArrayList<TypeInfo> getTypeParameters() {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700237 return mTypeParameters;
238 }
239
Jeff Arnesond6570b02014-10-29 15:46:51 -0700240 /**
241 * Clone this MethodInfo as if it belonged to the specified ClassInfo and apply the
242 * typeArgumentMapping to the parameters and return types.
243 */
244 public MethodInfo cloneForClass(ClassInfo newContainingClass,
245 Map<String, TypeInfo> typeArgumentMapping) {
246 TypeInfo returnType = mReturnType.getTypeWithArguments(typeArgumentMapping);
247 ArrayList<ParameterInfo> parameters = new ArrayList<ParameterInfo>();
248 for (ParameterInfo pi : mParameters) {
249 parameters.add(pi.cloneWithTypeArguments(typeArgumentMapping));
250 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700251 MethodInfo result =
252 new MethodInfo(getRawCommentText(), mTypeParameters, name(), signature(),
253 newContainingClass, realContainingClass(), isPublic(), isProtected(),
254 isPackagePrivate(), isPrivate(), isFinal(), isStatic(), isSynthetic(), mIsAbstract,
255 mIsSynchronized, mIsNative, mIsAnnotationElement, kind(), mFlatSignature,
Jeff Arnesond6570b02014-10-29 15:46:51 -0700256 mOverriddenMethod, returnType, mParameters, mThrownExceptions, position(),
Ben Dodson920dbbb2010-08-04 15:21:06 -0700257 annotations());
258 result.init(mDefaultAnnotationElementValue);
259 return result;
260 }
261
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700262 public MethodInfo(String rawCommentText, ArrayList<TypeInfo> typeParameters, String name,
Ben Dodson920dbbb2010-08-04 15:21:06 -0700263 String signature, ClassInfo containingClass, ClassInfo realContainingClass, boolean isPublic,
264 boolean isProtected, boolean isPackagePrivate, boolean isPrivate, boolean isFinal,
265 boolean isStatic, boolean isSynthetic, boolean isAbstract, boolean isSynchronized,
266 boolean isNative, boolean isAnnotationElement, String kind, String flatSignature,
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700267 MethodInfo overriddenMethod, TypeInfo returnType, ArrayList<ParameterInfo> parameters,
268 ArrayList<ClassInfo> thrownExceptions, SourcePositionInfo position,
269 ArrayList<AnnotationInstanceInfo> annotations) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700270 // Explicitly coerce 'final' state of Java6-compiled enum values() method, to match
271 // the Java5-emitted base API description.
272 super(rawCommentText, name, signature, containingClass, realContainingClass, isPublic,
Joe Onorato04099252011-03-09 13:34:18 -0800273 isProtected, isPackagePrivate, isPrivate,
274 ((name.equals("values") && containingClass.isEnum()) ? true : isFinal),
275 isStatic, isSynthetic, kind, position, annotations);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700276
277 // The underlying MethodDoc for an interface's declared methods winds up being marked
278 // non-abstract. Correct that here by looking at the immediate-parent class, and marking
279 // this method abstract if it is an unimplemented interface method.
280 if (containingClass.isInterface()) {
281 isAbstract = true;
282 }
283
284 mReasonOpened = "0:0";
285 mIsAnnotationElement = isAnnotationElement;
286 mTypeParameters = typeParameters;
287 mIsAbstract = isAbstract;
288 mIsSynchronized = isSynchronized;
289 mIsNative = isNative;
290 mFlatSignature = flatSignature;
291 mOverriddenMethod = overriddenMethod;
292 mReturnType = returnType;
293 mParameters = parameters;
294 mThrownExceptions = thrownExceptions;
295 }
296
297 public void init(AnnotationValueInfo defaultAnnotationElementValue) {
298 mDefaultAnnotationElementValue = defaultAnnotationElementValue;
299 }
300
301 public boolean isAbstract() {
302 return mIsAbstract;
303 }
304
305 public boolean isSynchronized() {
306 return mIsSynchronized;
307 }
308
309 public boolean isNative() {
310 return mIsNative;
311 }
312
313 public String flatSignature() {
314 return mFlatSignature;
315 }
316
317 public InheritedTags inlineTags() {
318 return new InlineTags();
319 }
320
321 public InheritedTags firstSentenceTags() {
322 return new FirstSentenceTags();
323 }
324
325 public InheritedTags returnTags() {
326 return new ReturnTags();
327 }
328
329 public TypeInfo returnType() {
330 return mReturnType;
331 }
332
333 public String prettySignature() {
334 return name() + prettyParameters();
335 }
336
337 /**
338 * Returns a printable version of the parameters of this method's signature.
339 */
340 public String prettyParameters() {
341 StringBuilder params = new StringBuilder("(");
342 for (ParameterInfo pInfo : mParameters) {
343 if (params.length() > 1) {
344 params.append(",");
345 }
346 params.append(pInfo.type().simpleTypeName());
347 }
348
349 params.append(")");
350 return params.toString();
351 }
352
353 /**
354 * Returns a name consistent with the {@link com.google.doclava.MethodInfo#getHashableName()}.
355 */
356 public String getHashableName() {
357 StringBuilder result = new StringBuilder();
358 result.append(name());
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700359
360 if (mParameters == null) {
361 return result.toString();
362 }
363
364 int i = 0;
365 for (ParameterInfo param : mParameters) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700366 result.append(":");
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700367 if (i == (mParameters.size()-1) && isVarArgs()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700368 // TODO: note that this does not attempt to handle hypothetical
369 // vararg methods whose last parameter is a list of arrays, e.g.
370 // "Object[]...".
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700371 result.append(param.type().fullNameNoDimension(typeVariables())).append("...");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700372 } else {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700373 result.append(param.type().fullName(typeVariables()));
Ben Dodson920dbbb2010-08-04 15:21:06 -0700374 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700375 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700376 }
377 return result.toString();
378 }
379
380 private boolean inList(ClassInfo item, ThrowsTagInfo[] list) {
381 int len = list.length;
382 String qn = item.qualifiedName();
383 for (int i = 0; i < len; i++) {
384 ClassInfo ex = list[i].exception();
385 if (ex != null && ex.qualifiedName().equals(qn)) {
386 return true;
387 }
388 }
389 return false;
390 }
391
392 public ThrowsTagInfo[] throwsTags() {
393 if (mThrowsTags == null) {
394 ThrowsTagInfo[] documented = comment().throwsTags();
395 ArrayList<ThrowsTagInfo> rv = new ArrayList<ThrowsTagInfo>();
396
397 int len = documented.length;
398 for (int i = 0; i < len; i++) {
399 rv.add(documented[i]);
400 }
401
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700402 for (ClassInfo cl : mThrownExceptions) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700403 if (documented == null || !inList(cl, documented)) {
404 rv.add(new ThrowsTagInfo("@throws", "@throws", cl.qualifiedName(), cl, "",
405 containingClass(), position()));
406 }
407 }
408 mThrowsTags = rv.toArray(new ThrowsTagInfo[rv.size()]);
409 }
410 return mThrowsTags;
411 }
412
413 private static int indexOfParam(String name, String[] list) {
414 final int N = list.length;
415 for (int i = 0; i < N; i++) {
416 if (name.equals(list[i])) {
417 return i;
418 }
419 }
420 return -1;
421 }
422
423 public ParamTagInfo[] paramTags() {
424 if (mParamTags == null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700425 final int N = mParameters.size();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700426
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.
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700433 int i = 0;
434 for (ParameterInfo param : mParameters) {
435 names[i] = param.name();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700436 comments[i] = "";
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700437 positions[i] = param.position();
438 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700439 }
440
441 // gather our comments, and complain about misnamed @param tags
442 for (ParamTagInfo tag : comment().paramTags()) {
443 int index = indexOfParam(tag.parameterName(), names);
444 if (index >= 0) {
445 comments[index] = tag.parameterComment();
446 positions[index] = tag.position();
447 } else {
448 Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
449 "@param tag with name that doesn't match the parameter list: '" + tag.parameterName()
450 + "'");
451 }
452 }
453
454 // get our parent's tags to fill in the blanks
455 MethodInfo overridden = this.findOverriddenMethod(name(), signature());
456 if (overridden != null) {
457 ParamTagInfo[] maternal = overridden.paramTags();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700458 for (i = 0; i < N; i++) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700459 if (comments[i].equals("")) {
460 comments[i] = maternal[i].parameterComment();
461 positions[i] = maternal[i].position();
462 }
463 }
464 }
465
466 // construct the results, and cache them for next time
467 mParamTags = new ParamTagInfo[N];
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700468 for (i = 0; i < N; i++) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700469 mParamTags[i] =
470 new ParamTagInfo("@param", "@param", names[i] + " " + comments[i], parent(),
471 positions[i]);
472
473 // while we're here, if we find any parameters that are still undocumented at this
474 // point, complain. (this warning is off by default, because it's really, really
475 // common; but, it's good to be able to enforce it)
476 if (comments[i].equals("")) {
477 Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i], "Undocumented parameter '"
478 + names[i] + "' on method '" + name() + "'");
479 }
480 }
481 }
482 return mParamTags;
483 }
484
485 public SeeTagInfo[] seeTags() {
486 SeeTagInfo[] result = comment().seeTags();
487 if (result == null) {
488 if (mOverriddenMethod != null) {
489 result = mOverriddenMethod.seeTags();
490 }
491 }
492 return result;
493 }
494
495 public TagInfo[] deprecatedTags() {
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
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700507 public ArrayList<ParameterInfo> parameters() {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700508 return mParameters;
509 }
510
511
512 public boolean matchesParams(String[] params, String[] dimensions, boolean varargs) {
513 if (mParamStrings == null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700514 if (mParameters.size() != params.length) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700515 return false;
516 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700517 int i = 0;
518 for (ParameterInfo mine : mParameters) {
519 if (!mine.matchesDimension(dimensions[i], varargs)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700520 return false;
521 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700522 TypeInfo myType = mine.type();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700523 String qualifiedName = myType.qualifiedTypeName();
524 String realType = myType.isPrimitive() ? "" : myType.asClassInfo().qualifiedName();
525 String s = params[i];
526 int slen = s.length();
527 int qnlen = qualifiedName.length();
528
529 // Check for a matching generic name or best known type
530 if (!matchesType(qualifiedName, s) && !matchesType(realType, s)) {
531 return false;
532 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700533 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700534 }
535 }
536 return true;
537 }
538
539 /**
540 * Checks to see if a parameter from a method signature is
541 * compatible with a parameter given in a {@code @link} tag.
542 */
543 private boolean matchesType(String signatureParam, String callerParam) {
544 int signatureLength = signatureParam.length();
545 int callerLength = callerParam.length();
546 return ((signatureParam.equals(callerParam) || ((callerLength + 1) < signatureLength
547 && signatureParam.charAt(signatureLength - callerLength - 1) == '.'
548 && signatureParam.endsWith(callerParam))));
549 }
550
551 public void makeHDF(Data data, String base) {
Jeff Arnesond6570b02014-10-29 15:46:51 -0700552 makeHDF(data, base, Collections.<String, TypeInfo>emptyMap());
553 }
554
555 public void makeHDF(Data data, String base, Map<String, TypeInfo> typeMapping) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700556 data.setValue(base + ".kind", kind());
557 data.setValue(base + ".name", name());
558 data.setValue(base + ".href", htmlPage());
559 data.setValue(base + ".anchor", anchor());
560
561 if (mReturnType != null) {
Jeff Arnesond6570b02014-10-29 15:46:51 -0700562 returnType().getTypeWithArguments(typeMapping).makeHDF(
563 data, base + ".returnType", false, typeVariables());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700564 data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
565 }
566
567 data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
568 data.setValue(base + ".final", isFinal() ? "final" : "");
569 data.setValue(base + ".static", isStatic() ? "static" : "");
570
571 TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
572 TagInfo.makeHDF(data, base + ".descr", inlineTags());
573 TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
574 TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
575 data.setValue(base + ".since", getSince());
Scott Main40ad1472012-10-24 18:19:17 -0700576 if (isDeprecated()) {
577 data.setValue(base + ".deprecatedsince", getDeprecatedSince());
578 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700579 ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
580 AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
581 ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
Jeff Arnesond6570b02014-10-29 15:46:51 -0700582 ParameterInfo.makeHDF(data, base + ".params", mParameters.toArray(
583 new ParameterInfo[mParameters.size()]), isVarArgs(), typeVariables(), typeMapping);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700584 if (isProtected()) {
585 data.setValue(base + ".scope", "protected");
586 } else if (isPublic()) {
587 data.setValue(base + ".scope", "public");
588 }
589 TagInfo.makeHDF(data, base + ".returns", returnTags());
590
591 if (mTypeParameters != null) {
592 TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
593 }
Jeff Arnesonb84c41b2014-08-12 15:22:00 -0700594
595 AnnotationInstanceInfo.makeLinkListHDF(
596 data,
597 base + ".showAnnotations",
598 showAnnotations().toArray(new AnnotationInstanceInfo[showAnnotations().size()]));
599
Ben Dodson920dbbb2010-08-04 15:21:06 -0700600 setFederatedReferences(data, base);
601 }
602
603 public HashSet<String> typeVariables() {
604 HashSet<String> result = TypeInfo.typeVariables(mTypeParameters);
605 ClassInfo cl = containingClass();
606 while (cl != null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700607 ArrayList<TypeInfo> types = cl.asTypeInfo().typeArguments();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700608 if (types != null) {
609 TypeInfo.typeVariables(types, result);
610 }
611 cl = cl.containingClass();
612 }
613 return result;
614 }
615
616 @Override
617 public boolean isExecutable() {
618 return true;
619 }
620
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700621 public ArrayList<ClassInfo> thrownExceptions() {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700622 return mThrownExceptions;
623 }
624
625 public String typeArgumentsName(HashSet<String> typeVars) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700626 if (mTypeParameters == null || mTypeParameters.isEmpty()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700627 return "";
628 } else {
629 return TypeInfo.typeArgumentsName(mTypeParameters, typeVars);
630 }
631 }
632
633 public boolean isAnnotationElement() {
634 return mIsAnnotationElement;
635 }
636
637 public AnnotationValueInfo defaultAnnotationElementValue() {
638 return mDefaultAnnotationElementValue;
639 }
640
641 public void setVarargs(boolean set) {
642 mIsVarargs = set;
643 }
644
645 public boolean isVarArgs() {
646 return mIsVarargs;
647 }
648
Jeff Brown722ac6a2013-04-01 17:07:37 -0700649 public boolean isEffectivelyFinal() {
650 if (mIsFinal) {
651 return true;
652 }
653 ClassInfo containingClass = containingClass();
654 if (containingClass != null && containingClass.isEffectivelyFinal()) {
655 return true;
656 }
657 return false;
Jeff Brown43d2ac82013-04-01 13:40:01 -0700658 }
659
Ben Dodson920dbbb2010-08-04 15:21:06 -0700660 @Override
661 public String toString() {
662 return this.name();
663 }
664
665 public void setReason(String reason) {
666 mReasonOpened = reason;
667 }
668
669 public String getReason() {
670 return mReasonOpened;
671 }
672
673 public void addException(String exec) {
674 ClassInfo exceptionClass = new ClassInfo(exec);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700675
676 mThrownExceptions.add(exceptionClass);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700677 }
678
679 public void addParameter(ParameterInfo p) {
680 // Name information
Ben Dodson920dbbb2010-08-04 15:21:06 -0700681 if (mParameters == null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700682 mParameters = new ArrayList<ParameterInfo>();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700683 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700684
685 mParameters.add(p);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700686 }
687
688 private String mFlatSignature;
689 private MethodInfo mOverriddenMethod;
690 private TypeInfo mReturnType;
691 private boolean mIsAnnotationElement;
692 private boolean mIsAbstract;
693 private boolean mIsSynchronized;
694 private boolean mIsNative;
695 private boolean mIsVarargs;
696 private boolean mDeprecatedKnown;
697 private boolean mIsDeprecated;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700698 private ArrayList<ParameterInfo> mParameters;
699 private ArrayList<ClassInfo> mThrownExceptions;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700700 private String[] mParamStrings;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700701 private ThrowsTagInfo[] mThrowsTags;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700702 private ParamTagInfo[] mParamTags;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700703 private ArrayList<TypeInfo> mTypeParameters;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700704 private AnnotationValueInfo mDefaultAnnotationElementValue;
705 private String mReasonOpened;
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700706 private ArrayList<Resolution> mResolutions;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700707
708 // TODO: merge with droiddoc version (above)
709 public String qualifiedName() {
710 String parentQName = (containingClass() != null)
711 ? (containingClass().qualifiedName() + ".") : "";
712 return parentQName + name();
713 }
714
715 @Override
716 public String signature() {
717 if (mSignature == null) {
718 StringBuilder params = new StringBuilder("(");
719 for (ParameterInfo pInfo : mParameters) {
720 if (params.length() > 1) {
721 params.append(", ");
722 }
723 params.append(pInfo.type().fullName());
724 }
725
726 params.append(")");
727 mSignature = params.toString();
728 }
729 return mSignature;
730 }
731
732 public boolean matches(MethodInfo other) {
Ben Dodsonbeef1452010-08-13 16:46:35 -0700733 return prettySignature().equals(other.prettySignature());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700734 }
735
736 public boolean throwsException(ClassInfo exception) {
737 for (ClassInfo e : mThrownExceptions) {
738 if (e.qualifiedName().equals(exception.qualifiedName())) {
739 return true;
740 }
741 }
742 return false;
743 }
Mathieu11b17962013-06-07 17:24:18 -0500744
Ben Dodson920dbbb2010-08-04 15:21:06 -0700745 public boolean isConsistent(MethodInfo mInfo) {
746 boolean consistent = true;
Joe Onorato04099252011-03-09 13:34:18 -0800747 if (this.mReturnType != mInfo.mReturnType && !this.mReturnType.equals(mInfo.mReturnType)) {
Mathieu11b17962013-06-07 17:24:18 -0500748 if (!mReturnType.isPrimitive() && !mInfo.mReturnType.isPrimitive()) {
749 // Check to see if our class extends the old class.
750 ApiInfo infoApi = mInfo.containingClass().containingPackage().containingApi();
751 ClassInfo infoReturnClass = infoApi.findClass(mInfo.mReturnType.qualifiedTypeName());
752 // Find the classes.
753 consistent = infoReturnClass != null &&
754 infoReturnClass.isAssignableTo(mReturnType.qualifiedTypeName());
755 } else {
756 consistent = false;
757 }
758
759 if (!consistent) {
760 Errors.error(Errors.CHANGED_TYPE, mInfo.position(), "Method " + mInfo.qualifiedName()
761 + " has changed return type from " + mReturnType + " to " + mInfo.mReturnType);
762 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700763 }
764
765 if (mIsAbstract != mInfo.mIsAbstract) {
766 consistent = false;
767 Errors.error(Errors.CHANGED_ABSTRACT, mInfo.position(), "Method " + mInfo.qualifiedName()
768 + " has changed 'abstract' qualifier");
769 }
770
771 if (mIsNative != mInfo.mIsNative) {
772 consistent = false;
773 Errors.error(Errors.CHANGED_NATIVE, mInfo.position(), "Method " + mInfo.qualifiedName()
774 + " has changed 'native' qualifier");
775 }
776
Jeff Brown43d2ac82013-04-01 13:40:01 -0700777 if (!mIsStatic) {
778 // Compiler-generated methods vary in their 'final' qualifier between versions of
Ben Dodson920dbbb2010-08-04 15:21:06 -0700779 // the compiler, so this check needs to be quite narrow. A change in 'final'
780 // status of a method is only relevant if (a) the method is not declared 'static'
Jeff Brown43d2ac82013-04-01 13:40:01 -0700781 // and (b) the method is not already inferred to be 'final' by virtue of its class.
Jeff Brown722ac6a2013-04-01 17:07:37 -0700782 if (!isEffectivelyFinal() && mInfo.isEffectivelyFinal()) {
Jeff Brown43d2ac82013-04-01 13:40:01 -0700783 consistent = false;
784 Errors.error(Errors.ADDED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
785 + " has added 'final' qualifier");
Jeff Brown722ac6a2013-04-01 17:07:37 -0700786 } else if (isEffectivelyFinal() && !mInfo.isEffectivelyFinal()) {
Jeff Brown43d2ac82013-04-01 13:40:01 -0700787 consistent = false;
788 Errors.error(Errors.REMOVED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
789 + " has removed 'final' qualifier");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700790 }
791 }
792
793 if (mIsStatic != mInfo.mIsStatic) {
794 consistent = false;
795 Errors.error(Errors.CHANGED_STATIC, mInfo.position(), "Method " + mInfo.qualifiedName()
796 + " has changed 'static' qualifier");
797 }
798
799 if (!scope().equals(mInfo.scope())) {
800 consistent = false;
801 Errors.error(Errors.CHANGED_SCOPE, mInfo.position(), "Method " + mInfo.qualifiedName()
802 + " changed scope from " + scope() + " to " + mInfo.scope());
803 }
804
805 if (!isDeprecated() == mInfo.isDeprecated()) {
806 Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(), "Method " + mInfo.qualifiedName()
Joe Onorato04099252011-03-09 13:34:18 -0800807 + " has changed deprecation state " + isDeprecated() + " --> " + mInfo.isDeprecated());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700808 consistent = false;
809 }
810
Glenn Kastencd020c32011-06-07 08:10:16 -0700811 // see JLS 3 13.4.20 "Adding or deleting a synchronized modifier of a method does not break "
812 // "compatibility with existing binaries."
813 /*
Ben Dodson920dbbb2010-08-04 15:21:06 -0700814 if (mIsSynchronized != mInfo.mIsSynchronized) {
815 Errors.error(Errors.CHANGED_SYNCHRONIZED, mInfo.position(), "Method " + mInfo.qualifiedName()
816 + " has changed 'synchronized' qualifier from " + mIsSynchronized + " to "
817 + mInfo.mIsSynchronized);
818 consistent = false;
819 }
Glenn Kastencd020c32011-06-07 08:10:16 -0700820 */
Ben Dodson920dbbb2010-08-04 15:21:06 -0700821
822 for (ClassInfo exception : thrownExceptions()) {
823 if (!mInfo.throwsException(exception)) {
824 // exclude 'throws' changes to finalize() overrides with no arguments
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700825 if (!name().equals("finalize") || (!mParameters.isEmpty())) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700826 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method " + mInfo.qualifiedName()
827 + " no longer throws exception " + exception.qualifiedName());
828 consistent = false;
829 }
830 }
831 }
832
833 for (ClassInfo exec : mInfo.thrownExceptions()) {
834 // exclude 'throws' changes to finalize() overrides with no arguments
835 if (!throwsException(exec)) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700836 if (!name().equals("finalize") || (!mParameters.isEmpty())) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700837 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method " + mInfo.qualifiedName()
838 + " added thrown exception " + exec.qualifiedName());
839 consistent = false;
840 }
841 }
842 }
843
844 return consistent;
845 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700846
847 public void printResolutions() {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700848 if (mResolutions == null || mResolutions.isEmpty()) {
849 return;
850 }
851
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700852 System.out.println("Resolutions for Method " + mName + mFlatSignature + ":");
853
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700854 for (Resolution r : mResolutions) {
855 System.out.println(r);
856 }
857 }
858
859 public void addResolution(Resolution resolution) {
860 if (mResolutions == null) {
861 mResolutions = new ArrayList<Resolution>();
862 }
863
864 mResolutions.add(resolution);
865 }
866
867 public boolean resolveResolutions() {
868 ArrayList<Resolution> resolutions = mResolutions;
869 mResolutions = new ArrayList<Resolution>();
870
871 boolean allResolved = true;
872 for (Resolution resolution : resolutions) {
873 StringBuilder qualifiedClassName = new StringBuilder();
874 InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName,
875 resolution.getInfoBuilder());
876
877 // if we still couldn't resolve it, save it for the next pass
878 if ("".equals(qualifiedClassName.toString())) {
879 mResolutions.add(resolution);
880 allResolved = false;
881 } else if ("thrownException".equals(resolution.getVariable())) {
882 mThrownExceptions.add(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
883 }
884 }
885
886 return allResolved;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700887 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700888}