blob: d52dcd8f1e9f1566e1ad27dd53a235a20c9523df [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 }
412 mThrowsTags = rv.toArray(new ThrowsTagInfo[rv.size()]);
413 }
414 return mThrowsTags;
415 }
416
417 private static int indexOfParam(String name, String[] list) {
418 final int N = list.length;
419 for (int i = 0; i < N; i++) {
420 if (name.equals(list[i])) {
421 return i;
422 }
423 }
424 return -1;
425 }
426
427 public ParamTagInfo[] paramTags() {
428 if (mParamTags == null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700429 final int N = mParameters.size();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700430
431 String[] names = new String[N];
432 String[] comments = new String[N];
433 SourcePositionInfo[] positions = new SourcePositionInfo[N];
434
435 // get the right names so we can handle our names being different from
436 // our parent's names.
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700437 int i = 0;
438 for (ParameterInfo param : mParameters) {
439 names[i] = param.name();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700440 comments[i] = "";
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700441 positions[i] = param.position();
442 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700443 }
444
445 // gather our comments, and complain about misnamed @param tags
446 for (ParamTagInfo tag : comment().paramTags()) {
447 int index = indexOfParam(tag.parameterName(), names);
448 if (index >= 0) {
449 comments[index] = tag.parameterComment();
450 positions[index] = tag.position();
451 } else {
452 Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
453 "@param tag with name that doesn't match the parameter list: '" + tag.parameterName()
454 + "'");
455 }
456 }
457
458 // get our parent's tags to fill in the blanks
459 MethodInfo overridden = this.findOverriddenMethod(name(), signature());
460 if (overridden != null) {
461 ParamTagInfo[] maternal = overridden.paramTags();
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700462 for (i = 0; i < N; i++) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700463 if (comments[i].equals("")) {
464 comments[i] = maternal[i].parameterComment();
465 positions[i] = maternal[i].position();
466 }
467 }
468 }
469
470 // construct the results, and cache them for next time
471 mParamTags = new ParamTagInfo[N];
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700472 for (i = 0; i < N; i++) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700473 mParamTags[i] =
474 new ParamTagInfo("@param", "@param", names[i] + " " + comments[i], parent(),
475 positions[i]);
476
477 // while we're here, if we find any parameters that are still undocumented at this
478 // point, complain. (this warning is off by default, because it's really, really
479 // common; but, it's good to be able to enforce it)
480 if (comments[i].equals("")) {
481 Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i], "Undocumented parameter '"
482 + names[i] + "' on method '" + name() + "'");
483 }
484 }
485 }
486 return mParamTags;
487 }
488
489 public SeeTagInfo[] seeTags() {
490 SeeTagInfo[] result = comment().seeTags();
491 if (result == null) {
492 if (mOverriddenMethod != null) {
493 result = mOverriddenMethod.seeTags();
494 }
495 }
496 return result;
497 }
498
499 public TagInfo[] deprecatedTags() {
500 TagInfo[] result = comment().deprecatedTags();
501 if (result.length == 0) {
502 if (comment().undeprecateTags().length == 0) {
503 if (mOverriddenMethod != null) {
504 result = mOverriddenMethod.deprecatedTags();
505 }
506 }
507 }
508 return result;
509 }
510
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700511 public ArrayList<ParameterInfo> parameters() {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700512 return mParameters;
513 }
514
515
516 public boolean matchesParams(String[] params, String[] dimensions, boolean varargs) {
517 if (mParamStrings == null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700518 if (mParameters.size() != params.length) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700519 return false;
520 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700521 int i = 0;
522 for (ParameterInfo mine : mParameters) {
523 if (!mine.matchesDimension(dimensions[i], varargs)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700524 return false;
525 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700526 TypeInfo myType = mine.type();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700527 String qualifiedName = myType.qualifiedTypeName();
528 String realType = myType.isPrimitive() ? "" : myType.asClassInfo().qualifiedName();
529 String s = params[i];
530 int slen = s.length();
531 int qnlen = qualifiedName.length();
532
533 // Check for a matching generic name or best known type
534 if (!matchesType(qualifiedName, s) && !matchesType(realType, s)) {
535 return false;
536 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700537 i++;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700538 }
539 }
540 return true;
541 }
542
543 /**
544 * Checks to see if a parameter from a method signature is
545 * compatible with a parameter given in a {@code @link} tag.
546 */
547 private boolean matchesType(String signatureParam, String callerParam) {
548 int signatureLength = signatureParam.length();
549 int callerLength = callerParam.length();
550 return ((signatureParam.equals(callerParam) || ((callerLength + 1) < signatureLength
551 && signatureParam.charAt(signatureLength - callerLength - 1) == '.'
552 && signatureParam.endsWith(callerParam))));
553 }
554
555 public void makeHDF(Data data, String base) {
Jeff Arnesond6570b02014-10-29 15:46:51 -0700556 makeHDF(data, base, Collections.<String, TypeInfo>emptyMap());
557 }
558
559 public void makeHDF(Data data, String base, Map<String, TypeInfo> typeMapping) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700560 data.setValue(base + ".kind", kind());
561 data.setValue(base + ".name", name());
562 data.setValue(base + ".href", htmlPage());
563 data.setValue(base + ".anchor", anchor());
564
565 if (mReturnType != null) {
Jeff Arnesond6570b02014-10-29 15:46:51 -0700566 returnType().getTypeWithArguments(typeMapping).makeHDF(
567 data, base + ".returnType", false, typeVariables());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700568 data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
569 }
570
571 data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
572 data.setValue(base + ".final", isFinal() ? "final" : "");
573 data.setValue(base + ".static", isStatic() ? "static" : "");
574
575 TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
576 TagInfo.makeHDF(data, base + ".descr", inlineTags());
577 TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
578 TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
579 data.setValue(base + ".since", getSince());
Scott Main40ad1472012-10-24 18:19:17 -0700580 if (isDeprecated()) {
581 data.setValue(base + ".deprecatedsince", getDeprecatedSince());
582 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700583 ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
584 AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
585 ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
Jeff Arnesond6570b02014-10-29 15:46:51 -0700586 ParameterInfo.makeHDF(data, base + ".params", mParameters.toArray(
587 new ParameterInfo[mParameters.size()]), isVarArgs(), typeVariables(), typeMapping);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700588 if (isProtected()) {
589 data.setValue(base + ".scope", "protected");
590 } else if (isPublic()) {
591 data.setValue(base + ".scope", "public");
592 }
593 TagInfo.makeHDF(data, base + ".returns", returnTags());
594
595 if (mTypeParameters != null) {
596 TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
597 }
Jeff Arnesonb84c41b2014-08-12 15:22:00 -0700598
Jeff Arnesonb2a6c042015-03-27 14:54:47 -0700599 int numAnnotationDocumentation = 0;
600 for (AnnotationInstanceInfo aii : annotations()) {
601 String annotationDocumentation = Doclava.getDocumentationStringForAnnotation(
602 aii.type().qualifiedName());
603 if (annotationDocumentation != null) {
604 data.setValue(base + ".annotationdocumentation." + numAnnotationDocumentation + ".text",
605 annotationDocumentation);
606 numAnnotationDocumentation++;
607 }
608 }
609
610
Jeff Arnesonb84c41b2014-08-12 15:22:00 -0700611 AnnotationInstanceInfo.makeLinkListHDF(
612 data,
613 base + ".showAnnotations",
614 showAnnotations().toArray(new AnnotationInstanceInfo[showAnnotations().size()]));
615
Ben Dodson920dbbb2010-08-04 15:21:06 -0700616 setFederatedReferences(data, base);
617 }
618
619 public HashSet<String> typeVariables() {
620 HashSet<String> result = TypeInfo.typeVariables(mTypeParameters);
621 ClassInfo cl = containingClass();
622 while (cl != null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700623 ArrayList<TypeInfo> types = cl.asTypeInfo().typeArguments();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700624 if (types != null) {
625 TypeInfo.typeVariables(types, result);
626 }
627 cl = cl.containingClass();
628 }
629 return result;
630 }
631
632 @Override
633 public boolean isExecutable() {
634 return true;
635 }
636
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700637 public ArrayList<ClassInfo> thrownExceptions() {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700638 return mThrownExceptions;
639 }
640
641 public String typeArgumentsName(HashSet<String> typeVars) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700642 if (mTypeParameters == null || mTypeParameters.isEmpty()) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700643 return "";
644 } else {
645 return TypeInfo.typeArgumentsName(mTypeParameters, typeVars);
646 }
647 }
648
649 public boolean isAnnotationElement() {
650 return mIsAnnotationElement;
651 }
652
653 public AnnotationValueInfo defaultAnnotationElementValue() {
654 return mDefaultAnnotationElementValue;
655 }
656
657 public void setVarargs(boolean set) {
658 mIsVarargs = set;
659 }
660
661 public boolean isVarArgs() {
662 return mIsVarargs;
663 }
664
Jeff Brown722ac6a2013-04-01 17:07:37 -0700665 public boolean isEffectivelyFinal() {
666 if (mIsFinal) {
667 return true;
668 }
669 ClassInfo containingClass = containingClass();
670 if (containingClass != null && containingClass.isEffectivelyFinal()) {
671 return true;
672 }
673 return false;
Jeff Brown43d2ac82013-04-01 13:40:01 -0700674 }
675
Ben Dodson920dbbb2010-08-04 15:21:06 -0700676 @Override
677 public String toString() {
678 return this.name();
679 }
680
681 public void setReason(String reason) {
682 mReasonOpened = reason;
683 }
684
685 public String getReason() {
686 return mReasonOpened;
687 }
688
689 public void addException(String exec) {
690 ClassInfo exceptionClass = new ClassInfo(exec);
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700691
692 mThrownExceptions.add(exceptionClass);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700693 }
694
695 public void addParameter(ParameterInfo p) {
696 // Name information
Ben Dodson920dbbb2010-08-04 15:21:06 -0700697 if (mParameters == null) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700698 mParameters = new ArrayList<ParameterInfo>();
Ben Dodson920dbbb2010-08-04 15:21:06 -0700699 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700700
701 mParameters.add(p);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700702 }
703
704 private String mFlatSignature;
705 private MethodInfo mOverriddenMethod;
706 private TypeInfo mReturnType;
707 private boolean mIsAnnotationElement;
708 private boolean mIsAbstract;
709 private boolean mIsSynchronized;
710 private boolean mIsNative;
711 private boolean mIsVarargs;
712 private boolean mDeprecatedKnown;
713 private boolean mIsDeprecated;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700714 private ArrayList<ParameterInfo> mParameters;
715 private ArrayList<ClassInfo> mThrownExceptions;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700716 private String[] mParamStrings;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700717 private ThrowsTagInfo[] mThrowsTags;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700718 private ParamTagInfo[] mParamTags;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700719 private ArrayList<TypeInfo> mTypeParameters;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700720 private AnnotationValueInfo mDefaultAnnotationElementValue;
721 private String mReasonOpened;
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700722 private ArrayList<Resolution> mResolutions;
Ben Dodson920dbbb2010-08-04 15:21:06 -0700723
724 // TODO: merge with droiddoc version (above)
725 public String qualifiedName() {
726 String parentQName = (containingClass() != null)
727 ? (containingClass().qualifiedName() + ".") : "";
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500728 // TODO: This logic doesn't work well with constructors, as name() for constructors already
729 // 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 -0700730 return parentQName + name();
731 }
732
733 @Override
734 public String signature() {
735 if (mSignature == null) {
736 StringBuilder params = new StringBuilder("(");
737 for (ParameterInfo pInfo : mParameters) {
738 if (params.length() > 1) {
739 params.append(", ");
740 }
741 params.append(pInfo.type().fullName());
742 }
743
744 params.append(")");
745 mSignature = params.toString();
746 }
747 return mSignature;
748 }
749
750 public boolean matches(MethodInfo other) {
Ben Dodsonbeef1452010-08-13 16:46:35 -0700751 return prettySignature().equals(other.prettySignature());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700752 }
753
754 public boolean throwsException(ClassInfo exception) {
755 for (ClassInfo e : mThrownExceptions) {
756 if (e.qualifiedName().equals(exception.qualifiedName())) {
757 return true;
758 }
759 }
760 return false;
761 }
Mathieu11b17962013-06-07 17:24:18 -0500762
Ben Dodson920dbbb2010-08-04 15:21:06 -0700763 public boolean isConsistent(MethodInfo mInfo) {
764 boolean consistent = true;
Joe Onorato04099252011-03-09 13:34:18 -0800765 if (this.mReturnType != mInfo.mReturnType && !this.mReturnType.equals(mInfo.mReturnType)) {
Mathieu11b17962013-06-07 17:24:18 -0500766 if (!mReturnType.isPrimitive() && !mInfo.mReturnType.isPrimitive()) {
767 // Check to see if our class extends the old class.
768 ApiInfo infoApi = mInfo.containingClass().containingPackage().containingApi();
769 ClassInfo infoReturnClass = infoApi.findClass(mInfo.mReturnType.qualifiedTypeName());
770 // Find the classes.
771 consistent = infoReturnClass != null &&
772 infoReturnClass.isAssignableTo(mReturnType.qualifiedTypeName());
773 } else {
774 consistent = false;
775 }
776
777 if (!consistent) {
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500778 Errors.error(Errors.CHANGED_TYPE, mInfo.position(), "Method "
779 + mInfo.prettyQualifiedSignature() + " has changed return type from " + mReturnType
780 + " to " + mInfo.mReturnType);
Mathieu11b17962013-06-07 17:24:18 -0500781 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700782 }
783
784 if (mIsAbstract != mInfo.mIsAbstract) {
785 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500786 Errors.error(Errors.CHANGED_ABSTRACT, mInfo.position(), "Method "
787 + mInfo.prettyQualifiedSignature() + " has changed 'abstract' qualifier");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700788 }
789
790 if (mIsNative != mInfo.mIsNative) {
791 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500792 Errors.error(Errors.CHANGED_NATIVE, mInfo.position(), "Method "
793 + mInfo.prettyQualifiedSignature() + " has changed 'native' qualifier");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700794 }
795
Jeff Brown43d2ac82013-04-01 13:40:01 -0700796 if (!mIsStatic) {
797 // Compiler-generated methods vary in their 'final' qualifier between versions of
Ben Dodson920dbbb2010-08-04 15:21:06 -0700798 // the compiler, so this check needs to be quite narrow. A change in 'final'
799 // status of a method is only relevant if (a) the method is not declared 'static'
Jeff Brown43d2ac82013-04-01 13:40:01 -0700800 // and (b) the method is not already inferred to be 'final' by virtue of its class.
Jeff Brown722ac6a2013-04-01 17:07:37 -0700801 if (!isEffectivelyFinal() && mInfo.isEffectivelyFinal()) {
Jeff Brown43d2ac82013-04-01 13:40:01 -0700802 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500803 Errors.error(Errors.ADDED_FINAL, mInfo.position(), "Method "
804 + mInfo.prettyQualifiedSignature() + " has added 'final' qualifier");
Jeff Brown722ac6a2013-04-01 17:07:37 -0700805 } else if (isEffectivelyFinal() && !mInfo.isEffectivelyFinal()) {
Jeff Brown43d2ac82013-04-01 13:40:01 -0700806 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500807 Errors.error(Errors.REMOVED_FINAL, mInfo.position(), "Method "
808 + mInfo.prettyQualifiedSignature() + " has removed 'final' qualifier");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700809 }
810 }
811
812 if (mIsStatic != mInfo.mIsStatic) {
813 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500814 Errors.error(Errors.CHANGED_STATIC, mInfo.position(), "Method "
815 + mInfo.prettyQualifiedSignature() + " has changed 'static' qualifier");
Ben Dodson920dbbb2010-08-04 15:21:06 -0700816 }
817
818 if (!scope().equals(mInfo.scope())) {
819 consistent = false;
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500820 Errors.error(Errors.CHANGED_SCOPE, mInfo.position(), "Method "
821 + mInfo.prettyQualifiedSignature() + " changed scope from " + scope()
822 + " to " + mInfo.scope());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700823 }
824
825 if (!isDeprecated() == mInfo.isDeprecated()) {
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500826 Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(), "Method "
827 + mInfo.prettyQualifiedSignature() + " has changed deprecation state " + isDeprecated()
828 + " --> " + mInfo.isDeprecated());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700829 consistent = false;
830 }
831
Glenn Kastencd020c32011-06-07 08:10:16 -0700832 // see JLS 3 13.4.20 "Adding or deleting a synchronized modifier of a method does not break "
833 // "compatibility with existing binaries."
834 /*
Ben Dodson920dbbb2010-08-04 15:21:06 -0700835 if (mIsSynchronized != mInfo.mIsSynchronized) {
836 Errors.error(Errors.CHANGED_SYNCHRONIZED, mInfo.position(), "Method " + mInfo.qualifiedName()
837 + " has changed 'synchronized' qualifier from " + mIsSynchronized + " to "
838 + mInfo.mIsSynchronized);
839 consistent = false;
840 }
Glenn Kastencd020c32011-06-07 08:10:16 -0700841 */
Ben Dodson920dbbb2010-08-04 15:21:06 -0700842
843 for (ClassInfo exception : thrownExceptions()) {
844 if (!mInfo.throwsException(exception)) {
845 // exclude 'throws' changes to finalize() overrides with no arguments
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700846 if (!name().equals("finalize") || (!mParameters.isEmpty())) {
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500847 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method "
848 + mInfo.prettyQualifiedSignature() + " no longer throws exception "
849 + exception.qualifiedName());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700850 consistent = false;
851 }
852 }
853 }
854
855 for (ClassInfo exec : mInfo.thrownExceptions()) {
856 // exclude 'throws' changes to finalize() overrides with no arguments
857 if (!throwsException(exec)) {
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700858 if (!name().equals("finalize") || (!mParameters.isEmpty())) {
C. Sean Younga7e5c7e2015-05-08 15:35:06 -0500859 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method "
860 + mInfo.prettyQualifiedSignature() + " added thrown exception "
861 + exec.qualifiedName());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700862 consistent = false;
863 }
864 }
865 }
866
867 return consistent;
868 }
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700869
870 public void printResolutions() {
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700871 if (mResolutions == null || mResolutions.isEmpty()) {
872 return;
873 }
874
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700875 System.out.println("Resolutions for Method " + mName + mFlatSignature + ":");
876
Andrew Sapperstein6ba612e2011-06-20 18:41:24 -0700877 for (Resolution r : mResolutions) {
878 System.out.println(r);
879 }
880 }
881
882 public void addResolution(Resolution resolution) {
883 if (mResolutions == null) {
884 mResolutions = new ArrayList<Resolution>();
885 }
886
887 mResolutions.add(resolution);
888 }
889
890 public boolean resolveResolutions() {
891 ArrayList<Resolution> resolutions = mResolutions;
892 mResolutions = new ArrayList<Resolution>();
893
894 boolean allResolved = true;
895 for (Resolution resolution : resolutions) {
896 StringBuilder qualifiedClassName = new StringBuilder();
897 InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName,
898 resolution.getInfoBuilder());
899
900 // if we still couldn't resolve it, save it for the next pass
901 if ("".equals(qualifiedClassName.toString())) {
902 mResolutions.add(resolution);
903 allResolved = false;
904 } else if ("thrownException".equals(resolution.getVariable())) {
905 mThrownExceptions.add(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString()));
906 }
907 }
908
909 return allResolved;
Andrew Sappersteind6eaacb2011-05-20 13:14:56 -0700910 }
Ben Dodson920dbbb2010-08-04 15:21:06 -0700911}