blob: 5dc217c2c5b43f5d8271be94c71433df2252c068 [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 }
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500336
337 public String prettyQualifiedSignature() {
338 return qualifiedName() + prettyParameters();
339 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700340
341 /**
342 * Returns a printable version of the parameters of this method's signature.
343 */
344 public String prettyParameters() {
345 StringBuilder params = new StringBuilder("(");
346 for (ParameterInfo pInfo : mParameters) {
347 if (params.length() > 1) {
348 params.append(",");
349 }
350 params.append(pInfo.type().simpleTypeName());
351 }
352
353 params.append(")");
354 return params.toString();
355 }
356
357 /**
358 * Returns a name consistent with the {@link com.google.doclava.MethodInfo#getHashableName()}.
359 */
360 public String getHashableName() {
361 StringBuilder result = new StringBuilder();
362 result.append(name());
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700363
364 if (mParameters == null) {
365 return result.toString();
366 }
367
368 int i = 0;
369 for (ParameterInfo param : mParameters) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700370 result.append(":");
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700371 if (i == (mParameters.size()-1) && isVarArgs()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700372 // TODO: note that this does not attempt to handle hypothetical
373 // vararg methods whose last parameter is a list of arrays, e.g.
374 // "Object[]...".
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700375 result.append(param.type().fullNameNoDimension(typeVariables())).append("...");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700376 } else {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700377 result.append(param.type().fullName(typeVariables()));
Ben Dodson920dbbb2010-08-04 15:21:06 -0700378 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700379 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700380 }
381 return result.toString();
382 }
383
384 private boolean inList(ClassInfo item, ThrowsTagInfo[] list) {
385 int len = list.length;
386 String qn = item.qualifiedName();
387 for (int i = 0; i < len; i++) {
388 ClassInfo ex = list[i].exception();
389 if (ex != null && ex.qualifiedName().equals(qn)) {
390 return true;
391 }
392 }
393 return false;
394 }
395
396 public ThrowsTagInfo[] throwsTags() {
397 if (mThrowsTags == null) {
398 ThrowsTagInfo[] documented = comment().throwsTags();
399 ArrayList<ThrowsTagInfo> rv = new ArrayList<ThrowsTagInfo>();
400
401 int len = documented.length;
402 for (int i = 0; i < len; i++) {
403 rv.add(documented[i]);
404 }
405
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700406 for (ClassInfo cl : mThrownExceptions) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700407 if (documented == null || !inList(cl, documented)) {
408 rv.add(new ThrowsTagInfo("@throws", "@throws", cl.qualifiedName(), cl, "",
409 containingClass(), position()));
410 }
411 }
C. Sean Youngda4b2e22015-05-18 14:12:30 -0500412
413 mThrowsTags = rv.toArray(ThrowsTagInfo.getArray(rv.size()));
Ben Dodson920dbbb2010-08-04 15:21:06 -0700414 }
415 return mThrowsTags;
416 }
417
418 private static int indexOfParam(String name, String[] list) {
419 final int N = list.length;
420 for (int i = 0; i < N; i++) {
421 if (name.equals(list[i])) {
422 return i;
423 }
424 }
425 return -1;
426 }
427
428 public ParamTagInfo[] paramTags() {
429 if (mParamTags == null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700430 final int N = mParameters.size();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700431
C. Sean Youngda4b2e22015-05-18 14:12:30 -0500432 if (N == 0) {
433 // Early out for empty case.
434 mParamTags = ParamTagInfo.EMPTY_ARRAY;
435 return ParamTagInfo.EMPTY_ARRAY;
436 }
437
Ben Dodson920dbbb2010-08-04 15:21:06 -0700438 String[] names = new String[N];
439 String[] comments = new String[N];
440 SourcePositionInfo[] positions = new SourcePositionInfo[N];
441
442 // get the right names so we can handle our names being different from
443 // our parent's names.
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700444 int i = 0;
445 for (ParameterInfo param : mParameters) {
446 names[i] = param.name();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700447 comments[i] = "";
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700448 positions[i] = param.position();
449 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700450 }
451
452 // gather our comments, and complain about misnamed @param tags
453 for (ParamTagInfo tag : comment().paramTags()) {
454 int index = indexOfParam(tag.parameterName(), names);
455 if (index >= 0) {
456 comments[index] = tag.parameterComment();
457 positions[index] = tag.position();
458 } else {
459 Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
460 "@param tag with name that doesn't match the parameter list: '" + tag.parameterName()
461 + "'");
462 }
463 }
464
465 // get our parent's tags to fill in the blanks
466 MethodInfo overridden = this.findOverriddenMethod(name(), signature());
467 if (overridden != null) {
468 ParamTagInfo[] maternal = overridden.paramTags();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700469 for (i = 0; i < N; i++) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700470 if (comments[i].equals("")) {
471 comments[i] = maternal[i].parameterComment();
472 positions[i] = maternal[i].position();
473 }
474 }
475 }
476
477 // construct the results, and cache them for next time
C. Sean Youngda4b2e22015-05-18 14:12:30 -0500478 mParamTags = ParamTagInfo.getArray(N);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700479 for (i = 0; i < N; i++) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700480 mParamTags[i] =
481 new ParamTagInfo("@param", "@param", names[i] + " " + comments[i], parent(),
482 positions[i]);
483
484 // while we're here, if we find any parameters that are still undocumented at this
485 // point, complain. (this warning is off by default, because it's really, really
486 // common; but, it's good to be able to enforce it)
487 if (comments[i].equals("")) {
488 Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i], "Undocumented parameter '"
489 + names[i] + "' on method '" + name() + "'");
490 }
491 }
492 }
493 return mParamTags;
494 }
495
496 public SeeTagInfo[] seeTags() {
497 SeeTagInfo[] result = comment().seeTags();
498 if (result == null) {
499 if (mOverriddenMethod != null) {
500 result = mOverriddenMethod.seeTags();
501 }
502 }
503 return result;
504 }
505
506 public TagInfo[] deprecatedTags() {
507 TagInfo[] result = comment().deprecatedTags();
508 if (result.length == 0) {
509 if (comment().undeprecateTags().length == 0) {
510 if (mOverriddenMethod != null) {
511 result = mOverriddenMethod.deprecatedTags();
512 }
513 }
514 }
515 return result;
516 }
517
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700518 public ArrayList<ParameterInfo> parameters() {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700519 return mParameters;
520 }
521
522
523 public boolean matchesParams(String[] params, String[] dimensions, boolean varargs) {
524 if (mParamStrings == null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700525 if (mParameters.size() != params.length) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700526 return false;
527 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700528 int i = 0;
529 for (ParameterInfo mine : mParameters) {
530 if (!mine.matchesDimension(dimensions[i], varargs)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700531 return false;
532 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700533 TypeInfo myType = mine.type();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700534 String qualifiedName = myType.qualifiedTypeName();
535 String realType = myType.isPrimitive() ? "" : myType.asClassInfo().qualifiedName();
536 String s = params[i];
537 int slen = s.length();
538 int qnlen = qualifiedName.length();
539
540 // Check for a matching generic name or best known type
541 if (!matchesType(qualifiedName, s) && !matchesType(realType, s)) {
542 return false;
543 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700544 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700545 }
546 }
547 return true;
548 }
549
550 /**
551 * Checks to see if a parameter from a method signature is
552 * compatible with a parameter given in a {@code @link} tag.
553 */
554 private boolean matchesType(String signatureParam, String callerParam) {
555 int signatureLength = signatureParam.length();
556 int callerLength = callerParam.length();
557 return ((signatureParam.equals(callerParam) || ((callerLength + 1) < signatureLength
558 && signatureParam.charAt(signatureLength - callerLength - 1) == '.'
559 && signatureParam.endsWith(callerParam))));
560 }
561
562 public void makeHDF(Data data, String base) {
Jeff Arnesond6570b02014-10-29 15:46:51 -0700563 makeHDF(data, base, Collections.<String, TypeInfo>emptyMap());
564 }
565
566 public void makeHDF(Data data, String base, Map<String, TypeInfo> typeMapping) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700567 data.setValue(base + ".kind", kind());
568 data.setValue(base + ".name", name());
569 data.setValue(base + ".href", htmlPage());
570 data.setValue(base + ".anchor", anchor());
571
572 if (mReturnType != null) {
Jeff Arnesond6570b02014-10-29 15:46:51 -0700573 returnType().getTypeWithArguments(typeMapping).makeHDF(
574 data, base + ".returnType", false, typeVariables());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700575 data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
576 }
577
578 data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
579 data.setValue(base + ".final", isFinal() ? "final" : "");
580 data.setValue(base + ".static", isStatic() ? "static" : "");
581
582 TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
583 TagInfo.makeHDF(data, base + ".descr", inlineTags());
584 TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
585 TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
586 data.setValue(base + ".since", getSince());
Scott Main40ad1472012-10-24 18:19:17 -0700587 if (isDeprecated()) {
588 data.setValue(base + ".deprecatedsince", getDeprecatedSince());
589 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700590 ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
591 AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
592 ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
Jeff Arnesond6570b02014-10-29 15:46:51 -0700593 ParameterInfo.makeHDF(data, base + ".params", mParameters.toArray(
594 new ParameterInfo[mParameters.size()]), isVarArgs(), typeVariables(), typeMapping);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700595 if (isProtected()) {
596 data.setValue(base + ".scope", "protected");
597 } else if (isPublic()) {
598 data.setValue(base + ".scope", "public");
599 }
600 TagInfo.makeHDF(data, base + ".returns", returnTags());
601
602 if (mTypeParameters != null) {
603 TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
604 }
Jeff Arnesonb84c41b2014-08-12 15:22:00 -0700605
Jeff Arnesonb2a6c042015-03-27 14:54:47 -0700606 int numAnnotationDocumentation = 0;
607 for (AnnotationInstanceInfo aii : annotations()) {
608 String annotationDocumentation = Doclava.getDocumentationStringForAnnotation(
609 aii.type().qualifiedName());
610 if (annotationDocumentation != null) {
611 data.setValue(base + ".annotationdocumentation." + numAnnotationDocumentation + ".text",
612 annotationDocumentation);
613 numAnnotationDocumentation++;
614 }
615 }
616
617
Jeff Arnesonb84c41b2014-08-12 15:22:00 -0700618 AnnotationInstanceInfo.makeLinkListHDF(
619 data,
620 base + ".showAnnotations",
621 showAnnotations().toArray(new AnnotationInstanceInfo[showAnnotations().size()]));
622
Ben Dodson920dbbb2010-08-04 15:21:06 -0700623 setFederatedReferences(data, base);
624 }
625
626 public HashSet<String> typeVariables() {
627 HashSet<String> result = TypeInfo.typeVariables(mTypeParameters);
628 ClassInfo cl = containingClass();
629 while (cl != null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700630 ArrayList<TypeInfo> types = cl.asTypeInfo().typeArguments();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700631 if (types != null) {
632 TypeInfo.typeVariables(types, result);
633 }
634 cl = cl.containingClass();
635 }
636 return result;
637 }
638
639 @Override
640 public boolean isExecutable() {
641 return true;
642 }
643
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700644 public ArrayList<ClassInfo> thrownExceptions() {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700645 return mThrownExceptions;
646 }
647
648 public String typeArgumentsName(HashSet<String> typeVars) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700649 if (mTypeParameters == null || mTypeParameters.isEmpty()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700650 return "";
651 } else {
652 return TypeInfo.typeArgumentsName(mTypeParameters, typeVars);
653 }
654 }
655
656 public boolean isAnnotationElement() {
657 return mIsAnnotationElement;
658 }
659
660 public AnnotationValueInfo defaultAnnotationElementValue() {
661 return mDefaultAnnotationElementValue;
662 }
663
664 public void setVarargs(boolean set) {
665 mIsVarargs = set;
666 }
667
668 public boolean isVarArgs() {
669 return mIsVarargs;
670 }
671
Jeff Brown722ac6a2013-04-01 17:07:37 -0700672 public boolean isEffectivelyFinal() {
673 if (mIsFinal) {
674 return true;
675 }
676 ClassInfo containingClass = containingClass();
677 if (containingClass != null && containingClass.isEffectivelyFinal()) {
678 return true;
679 }
680 return false;
Jeff Brown43d2ac82013-04-01 13:40:01 -0700681 }
682
Ben Dodson920dbbb2010-08-04 15:21:06 -0700683 @Override
684 public String toString() {
685 return this.name();
686 }
687
688 public void setReason(String reason) {
689 mReasonOpened = reason;
690 }
691
692 public String getReason() {
693 return mReasonOpened;
694 }
695
696 public void addException(String exec) {
697 ClassInfo exceptionClass = new ClassInfo(exec);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700698
699 mThrownExceptions.add(exceptionClass);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700700 }
701
702 public void addParameter(ParameterInfo p) {
703 // Name information
Ben Dodson920dbbb2010-08-04 15:21:06 -0700704 if (mParameters == null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700705 mParameters = new ArrayList<ParameterInfo>();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700706 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700707
708 mParameters.add(p);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700709 }
710
711 private String mFlatSignature;
712 private MethodInfo mOverriddenMethod;
713 private TypeInfo mReturnType;
714 private boolean mIsAnnotationElement;
715 private boolean mIsAbstract;
716 private boolean mIsSynchronized;
717 private boolean mIsNative;
718 private boolean mIsVarargs;
719 private boolean mDeprecatedKnown;
720 private boolean mIsDeprecated;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700721 private ArrayList<ParameterInfo> mParameters;
722 private ArrayList<ClassInfo> mThrownExceptions;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700723 private String[] mParamStrings;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700724 private ThrowsTagInfo[] mThrowsTags;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700725 private ParamTagInfo[] mParamTags;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700726 private ArrayList<TypeInfo> mTypeParameters;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700727 private AnnotationValueInfo mDefaultAnnotationElementValue;
728 private String mReasonOpened;
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700729 private ArrayList<Resolution> mResolutions;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700730
731 // TODO: merge with droiddoc version (above)
732 public String qualifiedName() {
733 String parentQName = (containingClass() != null)
734 ? (containingClass().qualifiedName() + ".") : "";
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500735 // TODO: This logic doesn't work well with constructors, as name() for constructors already
736 // contains the containingClass's name, leading to things like A.B.B() being rendered as A.B.A.B()
Ben Dodson920dbbb2010-08-04 15:21:06 -0700737 return parentQName + name();
738 }
739
740 @Override
741 public String signature() {
742 if (mSignature == null) {
743 StringBuilder params = new StringBuilder("(");
744 for (ParameterInfo pInfo : mParameters) {
745 if (params.length() > 1) {
746 params.append(", ");
747 }
748 params.append(pInfo.type().fullName());
749 }
750
751 params.append(")");
752 mSignature = params.toString();
753 }
754 return mSignature;
755 }
756
757 public boolean matches(MethodInfo other) {
Ben Dodsonbeef1452010-08-13 16:46:35 -0700758 return prettySignature().equals(other.prettySignature());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700759 }
760
761 public boolean throwsException(ClassInfo exception) {
762 for (ClassInfo e : mThrownExceptions) {
763 if (e.qualifiedName().equals(exception.qualifiedName())) {
764 return true;
765 }
766 }
767 return false;
768 }
Mathieu11b17962013-06-07 17:24:18 -0500769
Ben Dodson920dbbb2010-08-04 15:21:06 -0700770 public boolean isConsistent(MethodInfo mInfo) {
771 boolean consistent = true;
Joe Onorato04099252011-03-09 13:34:18 -0800772 if (this.mReturnType != mInfo.mReturnType && !this.mReturnType.equals(mInfo.mReturnType)) {
Mathieu11b17962013-06-07 17:24:18 -0500773 if (!mReturnType.isPrimitive() && !mInfo.mReturnType.isPrimitive()) {
774 // Check to see if our class extends the old class.
775 ApiInfo infoApi = mInfo.containingClass().containingPackage().containingApi();
776 ClassInfo infoReturnClass = infoApi.findClass(mInfo.mReturnType.qualifiedTypeName());
777 // Find the classes.
778 consistent = infoReturnClass != null &&
779 infoReturnClass.isAssignableTo(mReturnType.qualifiedTypeName());
780 } else {
781 consistent = false;
782 }
783
784 if (!consistent) {
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500785 Errors.error(Errors.CHANGED_TYPE, mInfo.position(), "Method "
786 + mInfo.prettyQualifiedSignature() + " has changed return type from " + mReturnType
787 + " to " + mInfo.mReturnType);
Mathieu11b17962013-06-07 17:24:18 -0500788 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700789 }
790
791 if (mIsAbstract != mInfo.mIsAbstract) {
792 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500793 Errors.error(Errors.CHANGED_ABSTRACT, mInfo.position(), "Method "
794 + mInfo.prettyQualifiedSignature() + " has changed 'abstract' qualifier");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700795 }
796
797 if (mIsNative != mInfo.mIsNative) {
798 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500799 Errors.error(Errors.CHANGED_NATIVE, mInfo.position(), "Method "
800 + mInfo.prettyQualifiedSignature() + " has changed 'native' qualifier");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700801 }
802
Jeff Brown43d2ac82013-04-01 13:40:01 -0700803 if (!mIsStatic) {
804 // Compiler-generated methods vary in their 'final' qualifier between versions of
Ben Dodson920dbbb2010-08-04 15:21:06 -0700805 // the compiler, so this check needs to be quite narrow. A change in 'final'
806 // status of a method is only relevant if (a) the method is not declared 'static'
Jeff Brown43d2ac82013-04-01 13:40:01 -0700807 // and (b) the method is not already inferred to be 'final' by virtue of its class.
Jeff Brown722ac6a2013-04-01 17:07:37 -0700808 if (!isEffectivelyFinal() && mInfo.isEffectivelyFinal()) {
Jeff Brown43d2ac82013-04-01 13:40:01 -0700809 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500810 Errors.error(Errors.ADDED_FINAL, mInfo.position(), "Method "
811 + mInfo.prettyQualifiedSignature() + " has added 'final' qualifier");
Jeff Brown722ac6a2013-04-01 17:07:37 -0700812 } else if (isEffectivelyFinal() && !mInfo.isEffectivelyFinal()) {
Jeff Brown43d2ac82013-04-01 13:40:01 -0700813 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500814 Errors.error(Errors.REMOVED_FINAL, mInfo.position(), "Method "
815 + mInfo.prettyQualifiedSignature() + " has removed 'final' qualifier");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700816 }
817 }
818
819 if (mIsStatic != mInfo.mIsStatic) {
820 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500821 Errors.error(Errors.CHANGED_STATIC, mInfo.position(), "Method "
822 + mInfo.prettyQualifiedSignature() + " has changed 'static' qualifier");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700823 }
824
825 if (!scope().equals(mInfo.scope())) {
826 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500827 Errors.error(Errors.CHANGED_SCOPE, mInfo.position(), "Method "
828 + mInfo.prettyQualifiedSignature() + " changed scope from " + scope()
829 + " to " + mInfo.scope());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700830 }
831
832 if (!isDeprecated() == mInfo.isDeprecated()) {
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500833 Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(), "Method "
834 + mInfo.prettyQualifiedSignature() + " has changed deprecation state " + isDeprecated()
835 + " --> " + mInfo.isDeprecated());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700836 consistent = false;
837 }
838
Glenn Kastencd020c32011-06-07 08:10:16 -0700839 // see JLS 3 13.4.20 "Adding or deleting a synchronized modifier of a method does not break "
840 // "compatibility with existing binaries."
841 /*
Ben Dodson920dbbb2010-08-04 15:21:06 -0700842 if (mIsSynchronized != mInfo.mIsSynchronized) {
843 Errors.error(Errors.CHANGED_SYNCHRONIZED, mInfo.position(), "Method " + mInfo.qualifiedName()
844 + " has changed 'synchronized' qualifier from " + mIsSynchronized + " to "
845 + mInfo.mIsSynchronized);
846 consistent = false;
847 }
Glenn Kastencd020c32011-06-07 08:10:16 -0700848 */
Ben Dodson920dbbb2010-08-04 15:21:06 -0700849
850 for (ClassInfo exception : thrownExceptions()) {
851 if (!mInfo.throwsException(exception)) {
852 // exclude 'throws' changes to finalize() overrides with no arguments
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700853 if (!name().equals("finalize") || (!mParameters.isEmpty())) {
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500854 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method "
855 + mInfo.prettyQualifiedSignature() + " no longer throws exception "
856 + exception.qualifiedName());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700857 consistent = false;
858 }
859 }
860 }
861
862 for (ClassInfo exec : mInfo.thrownExceptions()) {
863 // exclude 'throws' changes to finalize() overrides with no arguments
864 if (!throwsException(exec)) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700865 if (!name().equals("finalize") || (!mParameters.isEmpty())) {
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500866 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method "
867 + mInfo.prettyQualifiedSignature() + " added thrown exception "
868 + exec.qualifiedName());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700869 consistent = false;
870 }
871 }
872 }
873
874 return consistent;
875 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700876
877 public void printResolutions() {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700878 if (mResolutions == null || mResolutions.isEmpty()) {
879 return;
880 }
881
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700882 System.out.println("Resolutions for Method " + mName + mFlatSignature + ":");
883
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700884 for (Resolution r : mResolutions) {
885 System.out.println(r);
886 }
887 }
888
889 public void addResolution(Resolution resolution) {
890 if (mResolutions == null) {
891 mResolutions = new ArrayList<Resolution>();
892 }
893
894 mResolutions.add(resolution);
895 }
896
897 public boolean resolveResolutions() {
898 ArrayList<Resolution> resolutions = mResolutions;
899 mResolutions = new ArrayList<Resolution>();
900
901 boolean allResolved = true;
902 for (Resolution resolution : resolutions) {
903 StringBuilder qualifiedClassName = new StringBuilder();
904 InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName,
905 resolution.getInfoBuilder());
906
907 // if we still couldn't resolve it, save it for the next pass
908 if ("".equals(qualifiedClassName.toString())) {
909 mResolutions.add(resolution);
910 allResolved = false;
911 } else if ("thrownException".equals(resolution.getVariable())) {
912 mThrownExceptions.add(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
913 }
914 }
915
916 return allResolved;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700917 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700918}