blob: b2d083982e6b4298f8694fa3efafd52d650dc28b [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;
21
22import java.util.*;
23
24public class MethodInfo extends MemberInfo implements AbstractMethodInfo {
25 public static final Comparator<MethodInfo> comparator = new Comparator<MethodInfo>() {
26 public int compare(MethodInfo a, MethodInfo b) {
27 return a.name().compareTo(b.name());
28 }
29 };
30
31 private class InlineTags implements InheritedTags {
32 public TagInfo[] tags() {
33 return comment().tags();
34 }
35
36 public InheritedTags inherited() {
37 MethodInfo m = findOverriddenMethod(name(), signature());
38 if (m != null) {
39 return m.inlineTags();
40 } else {
41 return null;
42 }
43 }
44 }
45
46 private static void addInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue) {
47 for (ClassInfo i : ifaces) {
48 queue.add(i);
49 }
50 for (ClassInfo i : ifaces) {
51 addInterfaces(i.interfaces(), queue);
52 }
53 }
54
55 // first looks for a superclass, and then does a breadth first search to
56 // find the least far away match
57 public MethodInfo findOverriddenMethod(String name, String signature) {
58 if (mReturnType == null) {
59 // ctor
60 return null;
61 }
62 if (mOverriddenMethod != null) {
63 return mOverriddenMethod;
64 }
65
66 ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
67 addInterfaces(containingClass().interfaces(), queue);
68 for (ClassInfo iface : queue) {
69 for (MethodInfo me : iface.methods()) {
70 if (me.name().equals(name) && me.signature().equals(signature)
71 && me.inlineTags().tags() != null && me.inlineTags().tags().length > 0) {
72 return me;
73 }
74 }
75 }
76 return null;
77 }
78
79 private static void addRealInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue) {
80 for (ClassInfo i : ifaces) {
81 queue.add(i);
82 if (i.realSuperclass() != null && i.realSuperclass().isAbstract()) {
83 queue.add(i.superclass());
84 }
85 }
86 for (ClassInfo i : ifaces) {
87 addInterfaces(i.realInterfaces(), queue);
88 }
89 }
90
91 public MethodInfo findRealOverriddenMethod(String name, String signature, HashSet notStrippable) {
92 if (mReturnType == null) {
93 // ctor
94 return null;
95 }
96 if (mOverriddenMethod != null) {
97 return mOverriddenMethod;
98 }
99
100 ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
101 if (containingClass().realSuperclass() != null
102 && containingClass().realSuperclass().isAbstract()) {
103 queue.add(containingClass());
104 }
105 addInterfaces(containingClass().realInterfaces(), queue);
106 for (ClassInfo iface : queue) {
107 for (MethodInfo me : iface.methods()) {
108 if (me.name().equals(name) && me.signature().equals(signature)
109 && me.inlineTags().tags() != null && me.inlineTags().tags().length > 0
110 && notStrippable.contains(me.containingClass())) {
111 return me;
112 }
113 }
114 }
115 return null;
116 }
117
118 public MethodInfo findSuperclassImplementation(HashSet notStrippable) {
119 if (mReturnType == null) {
120 // ctor
121 return null;
122 }
123 if (mOverriddenMethod != null) {
124 // Even if we're told outright that this was the overridden method, we want to
125 // be conservative and ignore mismatches of parameter types -- they arise from
126 // extending generic specializations, and we want to consider the derived-class
127 // method to be a non-override.
128 if (this.signature().equals(mOverriddenMethod.signature())) {
129 return mOverriddenMethod;
130 }
131 }
132
133 ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
134 if (containingClass().realSuperclass() != null
135 && containingClass().realSuperclass().isAbstract()) {
136 queue.add(containingClass());
137 }
138 addInterfaces(containingClass().realInterfaces(), queue);
139 for (ClassInfo iface : queue) {
140 for (MethodInfo me : iface.methods()) {
141 if (me.name().equals(this.name()) && me.signature().equals(this.signature())
142 && notStrippable.contains(me.containingClass())) {
143 return me;
144 }
145 }
146 }
147 return null;
148 }
149
150 public ClassInfo findRealOverriddenClass(String name, String signature) {
151 if (mReturnType == null) {
152 // ctor
153 return null;
154 }
155 if (mOverriddenMethod != null) {
156 return mOverriddenMethod.mRealContainingClass;
157 }
158
159 ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
160 if (containingClass().realSuperclass() != null
161 && containingClass().realSuperclass().isAbstract()) {
162 queue.add(containingClass());
163 }
164 addInterfaces(containingClass().realInterfaces(), queue);
165 for (ClassInfo iface : queue) {
166 for (MethodInfo me : iface.methods()) {
167 if (me.name().equals(name) && me.signature().equals(signature)
168 && me.inlineTags().tags() != null && me.inlineTags().tags().length > 0) {
169 return iface;
170 }
171 }
172 }
173 return null;
174 }
175
176 private class FirstSentenceTags implements InheritedTags {
177 public TagInfo[] tags() {
178 return comment().briefTags();
179 }
180
181 public InheritedTags inherited() {
182 MethodInfo m = findOverriddenMethod(name(), signature());
183 if (m != null) {
184 return m.firstSentenceTags();
185 } else {
186 return null;
187 }
188 }
189 }
190
191 private class ReturnTags implements InheritedTags {
192 public TagInfo[] tags() {
193 return comment().returnTags();
194 }
195
196 public InheritedTags inherited() {
197 MethodInfo m = findOverriddenMethod(name(), signature());
198 if (m != null) {
199 return m.returnTags();
200 } else {
201 return null;
202 }
203 }
204 }
205
206 public boolean isDeprecated() {
207 boolean deprecated = false;
208 if (!mDeprecatedKnown) {
209 boolean commentDeprecated = comment().isDeprecated();
210 boolean annotationDeprecated = false;
211 for (AnnotationInstanceInfo annotation : annotations()) {
212 if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
213 annotationDeprecated = true;
214 break;
215 }
216 }
217
218 if (commentDeprecated != annotationDeprecated) {
219 Errors.error(Errors.DEPRECATION_MISMATCH, position(), "Method "
220 + mContainingClass.qualifiedName() + "." + name()
221 + ": @Deprecated annotation and @deprecated doc tag do not match");
222 }
223
224 mIsDeprecated = commentDeprecated | annotationDeprecated;
225 mDeprecatedKnown = true;
226 }
227 return mIsDeprecated;
228 }
229
230 public void setDeprecated(boolean deprecated) {
231 mDeprecatedKnown = true;
232 mIsDeprecated = deprecated;
233 }
234
235 public TypeInfo[] getTypeParameters() {
236 return mTypeParameters;
237 }
238
239 public MethodInfo cloneForClass(ClassInfo newContainingClass) {
240 MethodInfo result =
241 new MethodInfo(getRawCommentText(), mTypeParameters, name(), signature(),
242 newContainingClass, realContainingClass(), isPublic(), isProtected(),
243 isPackagePrivate(), isPrivate(), isFinal(), isStatic(), isSynthetic(), mIsAbstract,
244 mIsSynchronized, mIsNative, mIsAnnotationElement, kind(), mFlatSignature,
245 mOverriddenMethod, mReturnType, mParameters, mThrownExceptions, position(),
246 annotations());
247 result.init(mDefaultAnnotationElementValue);
248 return result;
249 }
250
251 public MethodInfo(String rawCommentText, TypeInfo[] typeParameters, String name,
252 String signature, ClassInfo containingClass, ClassInfo realContainingClass, boolean isPublic,
253 boolean isProtected, boolean isPackagePrivate, boolean isPrivate, boolean isFinal,
254 boolean isStatic, boolean isSynthetic, boolean isAbstract, boolean isSynchronized,
255 boolean isNative, boolean isAnnotationElement, String kind, String flatSignature,
256 MethodInfo overriddenMethod, TypeInfo returnType, ParameterInfo[] parameters,
257 ClassInfo[] thrownExceptions, SourcePositionInfo position,
258 AnnotationInstanceInfo[] annotations) {
259 // Explicitly coerce 'final' state of Java6-compiled enum values() method, to match
260 // the Java5-emitted base API description.
261 super(rawCommentText, name, signature, containingClass, realContainingClass, isPublic,
Joe Onorato04099252011-03-09 13:34:18 -0800262 isProtected, isPackagePrivate, isPrivate,
263 ((name.equals("values") && containingClass.isEnum()) ? true : isFinal),
264 isStatic, isSynthetic, kind, position, annotations);
Ben Dodson920dbbb2010-08-04 15:21:06 -0700265
266 // The underlying MethodDoc for an interface's declared methods winds up being marked
267 // non-abstract. Correct that here by looking at the immediate-parent class, and marking
268 // this method abstract if it is an unimplemented interface method.
269 if (containingClass.isInterface()) {
270 isAbstract = true;
271 }
272
273 mReasonOpened = "0:0";
274 mIsAnnotationElement = isAnnotationElement;
275 mTypeParameters = typeParameters;
276 mIsAbstract = isAbstract;
277 mIsSynchronized = isSynchronized;
278 mIsNative = isNative;
279 mFlatSignature = flatSignature;
280 mOverriddenMethod = overriddenMethod;
281 mReturnType = returnType;
282 mParameters = parameters;
283 mThrownExceptions = thrownExceptions;
284 }
285
286 public void init(AnnotationValueInfo defaultAnnotationElementValue) {
287 mDefaultAnnotationElementValue = defaultAnnotationElementValue;
288 }
289
290 public boolean isAbstract() {
291 return mIsAbstract;
292 }
293
294 public boolean isSynchronized() {
295 return mIsSynchronized;
296 }
297
298 public boolean isNative() {
299 return mIsNative;
300 }
301
302 public String flatSignature() {
303 return mFlatSignature;
304 }
305
306 public InheritedTags inlineTags() {
307 return new InlineTags();
308 }
309
310 public InheritedTags firstSentenceTags() {
311 return new FirstSentenceTags();
312 }
313
314 public InheritedTags returnTags() {
315 return new ReturnTags();
316 }
317
318 public TypeInfo returnType() {
319 return mReturnType;
320 }
321
322 public String prettySignature() {
323 return name() + prettyParameters();
324 }
325
326 /**
327 * Returns a printable version of the parameters of this method's signature.
328 */
329 public String prettyParameters() {
330 StringBuilder params = new StringBuilder("(");
331 for (ParameterInfo pInfo : mParameters) {
332 if (params.length() > 1) {
333 params.append(",");
334 }
335 params.append(pInfo.type().simpleTypeName());
336 }
337
338 params.append(")");
339 return params.toString();
340 }
341
342 /**
343 * Returns a name consistent with the {@link com.google.doclava.MethodInfo#getHashableName()}.
344 */
345 public String getHashableName() {
346 StringBuilder result = new StringBuilder();
347 result.append(name());
348 for (int p = 0; p < mParameters.length; p++) {
349 result.append(":");
350 if (p == mParameters.length - 1 && isVarArgs()) {
351 // TODO: note that this does not attempt to handle hypothetical
352 // vararg methods whose last parameter is a list of arrays, e.g.
353 // "Object[]...".
354 result.append(mParameters[p].type().fullNameNoDimension(typeVariables())).append("...");
355 } else {
356 result.append(mParameters[p].type().fullName(typeVariables()));
357 }
358 }
359 return result.toString();
360 }
361
362 private boolean inList(ClassInfo item, ThrowsTagInfo[] list) {
363 int len = list.length;
364 String qn = item.qualifiedName();
365 for (int i = 0; i < len; i++) {
366 ClassInfo ex = list[i].exception();
367 if (ex != null && ex.qualifiedName().equals(qn)) {
368 return true;
369 }
370 }
371 return false;
372 }
373
374 public ThrowsTagInfo[] throwsTags() {
375 if (mThrowsTags == null) {
376 ThrowsTagInfo[] documented = comment().throwsTags();
377 ArrayList<ThrowsTagInfo> rv = new ArrayList<ThrowsTagInfo>();
378
379 int len = documented.length;
380 for (int i = 0; i < len; i++) {
381 rv.add(documented[i]);
382 }
383
384 ClassInfo[] all = mThrownExceptions;
385 len = all.length;
386 for (int i = 0; i < len; i++) {
387 ClassInfo cl = all[i];
388 if (documented == null || !inList(cl, documented)) {
389 rv.add(new ThrowsTagInfo("@throws", "@throws", cl.qualifiedName(), cl, "",
390 containingClass(), position()));
391 }
392 }
393 mThrowsTags = rv.toArray(new ThrowsTagInfo[rv.size()]);
394 }
395 return mThrowsTags;
396 }
397
398 private static int indexOfParam(String name, String[] list) {
399 final int N = list.length;
400 for (int i = 0; i < N; i++) {
401 if (name.equals(list[i])) {
402 return i;
403 }
404 }
405 return -1;
406 }
407
408 public ParamTagInfo[] paramTags() {
409 if (mParamTags == null) {
410 final int N = mParameters.length;
411
412 String[] names = new String[N];
413 String[] comments = new String[N];
414 SourcePositionInfo[] positions = new SourcePositionInfo[N];
415
416 // get the right names so we can handle our names being different from
417 // our parent's names.
418 for (int i = 0; i < N; i++) {
419 names[i] = mParameters[i].name();
420 comments[i] = "";
421 positions[i] = mParameters[i].position();
422 }
423
424 // gather our comments, and complain about misnamed @param tags
425 for (ParamTagInfo tag : comment().paramTags()) {
426 int index = indexOfParam(tag.parameterName(), names);
427 if (index >= 0) {
428 comments[index] = tag.parameterComment();
429 positions[index] = tag.position();
430 } else {
431 Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
432 "@param tag with name that doesn't match the parameter list: '" + tag.parameterName()
433 + "'");
434 }
435 }
436
437 // get our parent's tags to fill in the blanks
438 MethodInfo overridden = this.findOverriddenMethod(name(), signature());
439 if (overridden != null) {
440 ParamTagInfo[] maternal = overridden.paramTags();
441 for (int i = 0; i < N; i++) {
442 if (comments[i].equals("")) {
443 comments[i] = maternal[i].parameterComment();
444 positions[i] = maternal[i].position();
445 }
446 }
447 }
448
449 // construct the results, and cache them for next time
450 mParamTags = new ParamTagInfo[N];
451 for (int i = 0; i < N; i++) {
452 mParamTags[i] =
453 new ParamTagInfo("@param", "@param", names[i] + " " + comments[i], parent(),
454 positions[i]);
455
456 // while we're here, if we find any parameters that are still undocumented at this
457 // point, complain. (this warning is off by default, because it's really, really
458 // common; but, it's good to be able to enforce it)
459 if (comments[i].equals("")) {
460 Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i], "Undocumented parameter '"
461 + names[i] + "' on method '" + name() + "'");
462 }
463 }
464 }
465 return mParamTags;
466 }
467
468 public SeeTagInfo[] seeTags() {
469 SeeTagInfo[] result = comment().seeTags();
470 if (result == null) {
471 if (mOverriddenMethod != null) {
472 result = mOverriddenMethod.seeTags();
473 }
474 }
475 return result;
476 }
477
478 public TagInfo[] deprecatedTags() {
479 TagInfo[] result = comment().deprecatedTags();
480 if (result.length == 0) {
481 if (comment().undeprecateTags().length == 0) {
482 if (mOverriddenMethod != null) {
483 result = mOverriddenMethod.deprecatedTags();
484 }
485 }
486 }
487 return result;
488 }
489
490 public ParameterInfo[] parameters() {
491 return mParameters;
492 }
493
494
495 public boolean matchesParams(String[] params, String[] dimensions, boolean varargs) {
496 if (mParamStrings == null) {
497 ParameterInfo[] mine = mParameters;
498 int len = mine.length;
499 if (len != params.length) {
500 return false;
501 }
502 for (int i = 0; i < len; i++) {
503 if (!mine[i].matchesDimension(dimensions[i], varargs)) {
504 return false;
505 }
506 TypeInfo myType = mine[i].type();
507 String qualifiedName = myType.qualifiedTypeName();
508 String realType = myType.isPrimitive() ? "" : myType.asClassInfo().qualifiedName();
509 String s = params[i];
510 int slen = s.length();
511 int qnlen = qualifiedName.length();
512
513 // Check for a matching generic name or best known type
514 if (!matchesType(qualifiedName, s) && !matchesType(realType, s)) {
515 return false;
516 }
517 }
518 }
519 return true;
520 }
521
522 /**
523 * Checks to see if a parameter from a method signature is
524 * compatible with a parameter given in a {@code @link} tag.
525 */
526 private boolean matchesType(String signatureParam, String callerParam) {
527 int signatureLength = signatureParam.length();
528 int callerLength = callerParam.length();
529 return ((signatureParam.equals(callerParam) || ((callerLength + 1) < signatureLength
530 && signatureParam.charAt(signatureLength - callerLength - 1) == '.'
531 && signatureParam.endsWith(callerParam))));
532 }
533
534 public void makeHDF(Data data, String base) {
535 data.setValue(base + ".kind", kind());
536 data.setValue(base + ".name", name());
537 data.setValue(base + ".href", htmlPage());
538 data.setValue(base + ".anchor", anchor());
539
540 if (mReturnType != null) {
541 returnType().makeHDF(data, base + ".returnType", false, typeVariables());
542 data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
543 }
544
545 data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
546 data.setValue(base + ".final", isFinal() ? "final" : "");
547 data.setValue(base + ".static", isStatic() ? "static" : "");
548
549 TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
550 TagInfo.makeHDF(data, base + ".descr", inlineTags());
551 TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
552 TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
553 data.setValue(base + ".since", getSince());
554 ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
555 AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
556 ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
557 ParameterInfo.makeHDF(data, base + ".params", parameters(), isVarArgs(), typeVariables());
558 if (isProtected()) {
559 data.setValue(base + ".scope", "protected");
560 } else if (isPublic()) {
561 data.setValue(base + ".scope", "public");
562 }
563 TagInfo.makeHDF(data, base + ".returns", returnTags());
564
565 if (mTypeParameters != null) {
566 TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
567 }
568
569 setFederatedReferences(data, base);
570 }
571
572 public HashSet<String> typeVariables() {
573 HashSet<String> result = TypeInfo.typeVariables(mTypeParameters);
574 ClassInfo cl = containingClass();
575 while (cl != null) {
576 TypeInfo[] types = cl.asTypeInfo().typeArguments();
577 if (types != null) {
578 TypeInfo.typeVariables(types, result);
579 }
580 cl = cl.containingClass();
581 }
582 return result;
583 }
584
585 @Override
586 public boolean isExecutable() {
587 return true;
588 }
589
590 public ClassInfo[] thrownExceptions() {
591 return mThrownExceptions;
592 }
593
594 public String typeArgumentsName(HashSet<String> typeVars) {
595 if (mTypeParameters == null || mTypeParameters.length == 0) {
596 return "";
597 } else {
598 return TypeInfo.typeArgumentsName(mTypeParameters, typeVars);
599 }
600 }
601
602 public boolean isAnnotationElement() {
603 return mIsAnnotationElement;
604 }
605
606 public AnnotationValueInfo defaultAnnotationElementValue() {
607 return mDefaultAnnotationElementValue;
608 }
609
610 public void setVarargs(boolean set) {
611 mIsVarargs = set;
612 }
613
614 public boolean isVarArgs() {
615 return mIsVarargs;
616 }
617
618 @Override
619 public String toString() {
620 return this.name();
621 }
622
623 public void setReason(String reason) {
624 mReasonOpened = reason;
625 }
626
627 public String getReason() {
628 return mReasonOpened;
629 }
630
631 public void addException(String exec) {
632 ClassInfo exceptionClass = new ClassInfo(exec);
633 List<ClassInfo> exceptions = new ArrayList<ClassInfo>(mThrownExceptions.length + 1);
634 exceptions.addAll(Arrays.asList(mThrownExceptions));
635 exceptions.add(exceptionClass);
636 mThrownExceptions = new ClassInfo[exceptions.size()];
637 exceptions.toArray(mThrownExceptions);
638 }
639
640 public void addParameter(ParameterInfo p) {
641 // Name information
642 ParameterInfo[] newParams;
643 int i = 0;
644
645 if (mParameters == null) {
646 newParams = new ParameterInfo[1];
647 } else {
648 newParams = new ParameterInfo[mParameters.length+1];
649 for (ParameterInfo info : mParameters) {
650 newParams[i++] = info;
651 }
652 }
653 newParams[i] = p;
654 mParameters = newParams;
655
656 // Type information
657 TypeInfo[] newTypes;
658 i = 0;
659
660 if (mTypeParameters == null) {
661 newTypes = new TypeInfo[1];
662 } else {
663 newTypes = new TypeInfo[mTypeParameters.length+1];
664 for (TypeInfo info : mTypeParameters) {
665 newTypes[i++] = info;
666 }
667 }
668 newTypes[i] = p.mType;
669 mTypeParameters = newTypes;
670 }
671
672 private String mFlatSignature;
673 private MethodInfo mOverriddenMethod;
674 private TypeInfo mReturnType;
675 private boolean mIsAnnotationElement;
676 private boolean mIsAbstract;
677 private boolean mIsSynchronized;
678 private boolean mIsNative;
679 private boolean mIsVarargs;
680 private boolean mDeprecatedKnown;
681 private boolean mIsDeprecated;
682 private ParameterInfo[] mParameters;
683 private ClassInfo[] mThrownExceptions;
684 private String[] mParamStrings;
685 ThrowsTagInfo[] mThrowsTags;
686 private ParamTagInfo[] mParamTags;
687 private TypeInfo[] mTypeParameters;
688 private AnnotationValueInfo mDefaultAnnotationElementValue;
689 private String mReasonOpened;
690
691 // TODO: merge with droiddoc version (above)
692 public String qualifiedName() {
693 String parentQName = (containingClass() != null)
694 ? (containingClass().qualifiedName() + ".") : "";
695 return parentQName + name();
696 }
697
698 @Override
699 public String signature() {
700 if (mSignature == null) {
701 StringBuilder params = new StringBuilder("(");
702 for (ParameterInfo pInfo : mParameters) {
703 if (params.length() > 1) {
704 params.append(", ");
705 }
706 params.append(pInfo.type().fullName());
707 }
708
709 params.append(")");
710 mSignature = params.toString();
711 }
712 return mSignature;
713 }
714
715 public boolean matches(MethodInfo other) {
Ben Dodsonbeef1452010-08-13 16:46:35 -0700716 return prettySignature().equals(other.prettySignature());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700717 }
718
719 public boolean throwsException(ClassInfo exception) {
720 for (ClassInfo e : mThrownExceptions) {
721 if (e.qualifiedName().equals(exception.qualifiedName())) {
722 return true;
723 }
724 }
725 return false;
726 }
727
728 public boolean isConsistent(MethodInfo mInfo) {
729 boolean consistent = true;
Joe Onorato04099252011-03-09 13:34:18 -0800730 if (this.mReturnType != mInfo.mReturnType && !this.mReturnType.equals(mInfo.mReturnType)) {
Ben Dodson920dbbb2010-08-04 15:21:06 -0700731 consistent = false;
732 Errors.error(Errors.CHANGED_TYPE, mInfo.position(), "Method " + mInfo.qualifiedName()
733 + " has changed return type from " + mReturnType + " to " + mInfo.mReturnType);
734 }
735
736 if (mIsAbstract != mInfo.mIsAbstract) {
737 consistent = false;
738 Errors.error(Errors.CHANGED_ABSTRACT, mInfo.position(), "Method " + mInfo.qualifiedName()
739 + " has changed 'abstract' qualifier");
740 }
741
742 if (mIsNative != mInfo.mIsNative) {
743 consistent = false;
744 Errors.error(Errors.CHANGED_NATIVE, mInfo.position(), "Method " + mInfo.qualifiedName()
745 + " has changed 'native' qualifier");
746 }
747
748 if (mIsFinal != mInfo.mIsFinal) {
749 // Compiler-generated methods vary in their 'final' qual between versions of
750 // the compiler, so this check needs to be quite narrow. A change in 'final'
751 // status of a method is only relevant if (a) the method is not declared 'static'
752 // and (b) the method's class is not itself 'final'.
753 if (!mIsStatic) {
754 if ((containingClass() == null) || (!containingClass().isFinal())) {
755 consistent = false;
756 Errors.error(Errors.CHANGED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
757 + " has changed 'final' qualifier");
758 }
759 }
760 }
761
762 if (mIsStatic != mInfo.mIsStatic) {
763 consistent = false;
764 Errors.error(Errors.CHANGED_STATIC, mInfo.position(), "Method " + mInfo.qualifiedName()
765 + " has changed 'static' qualifier");
766 }
767
768 if (!scope().equals(mInfo.scope())) {
769 consistent = false;
770 Errors.error(Errors.CHANGED_SCOPE, mInfo.position(), "Method " + mInfo.qualifiedName()
771 + " changed scope from " + scope() + " to " + mInfo.scope());
772 }
773
774 if (!isDeprecated() == mInfo.isDeprecated()) {
775 Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(), "Method " + mInfo.qualifiedName()
Joe Onorato04099252011-03-09 13:34:18 -0800776 + " has changed deprecation state " + isDeprecated() + " --> " + mInfo.isDeprecated());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700777 consistent = false;
778 }
779
Glenn Kastencd020c32011-06-07 08:10:16 -0700780 // see JLS 3 13.4.20 "Adding or deleting a synchronized modifier of a method does not break "
781 // "compatibility with existing binaries."
782 /*
Ben Dodson920dbbb2010-08-04 15:21:06 -0700783 if (mIsSynchronized != mInfo.mIsSynchronized) {
784 Errors.error(Errors.CHANGED_SYNCHRONIZED, mInfo.position(), "Method " + mInfo.qualifiedName()
785 + " has changed 'synchronized' qualifier from " + mIsSynchronized + " to "
786 + mInfo.mIsSynchronized);
787 consistent = false;
788 }
Glenn Kastencd020c32011-06-07 08:10:16 -0700789 */
Ben Dodson920dbbb2010-08-04 15:21:06 -0700790
791 for (ClassInfo exception : thrownExceptions()) {
792 if (!mInfo.throwsException(exception)) {
793 // exclude 'throws' changes to finalize() overrides with no arguments
794 if (!name().equals("finalize") || (mParameters.length > 0)) {
795 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method " + mInfo.qualifiedName()
796 + " no longer throws exception " + exception.qualifiedName());
797 consistent = false;
798 }
799 }
800 }
801
802 for (ClassInfo exec : mInfo.thrownExceptions()) {
803 // exclude 'throws' changes to finalize() overrides with no arguments
804 if (!throwsException(exec)) {
805 if (!name().equals("finalize") || (mParameters.length > 0)) {
806 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method " + mInfo.qualifiedName()
807 + " added thrown exception " + exec.qualifiedName());
808 consistent = false;
809 }
810 }
811 }
812
813 return consistent;
814 }
815}