blob: 6eb761a6db7af04cee9c9cc344bddd6a3f1afd4e [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,
262 isProtected, isPackagePrivate, isPrivate, ((name.equals("values") && containingClass
263 .isEnum()) ? true : isFinal), isStatic, isSynthetic, kind, position, annotations);
264
265 // The underlying MethodDoc for an interface's declared methods winds up being marked
266 // non-abstract. Correct that here by looking at the immediate-parent class, and marking
267 // this method abstract if it is an unimplemented interface method.
268 if (containingClass.isInterface()) {
269 isAbstract = true;
270 }
271
272 mReasonOpened = "0:0";
273 mIsAnnotationElement = isAnnotationElement;
274 mTypeParameters = typeParameters;
275 mIsAbstract = isAbstract;
276 mIsSynchronized = isSynchronized;
277 mIsNative = isNative;
278 mFlatSignature = flatSignature;
279 mOverriddenMethod = overriddenMethod;
280 mReturnType = returnType;
281 mParameters = parameters;
282 mThrownExceptions = thrownExceptions;
283 }
284
285 public void init(AnnotationValueInfo defaultAnnotationElementValue) {
286 mDefaultAnnotationElementValue = defaultAnnotationElementValue;
287 }
288
289 public boolean isAbstract() {
290 return mIsAbstract;
291 }
292
293 public boolean isSynchronized() {
294 return mIsSynchronized;
295 }
296
297 public boolean isNative() {
298 return mIsNative;
299 }
300
301 public String flatSignature() {
302 return mFlatSignature;
303 }
304
305 public InheritedTags inlineTags() {
306 return new InlineTags();
307 }
308
309 public InheritedTags firstSentenceTags() {
310 return new FirstSentenceTags();
311 }
312
313 public InheritedTags returnTags() {
314 return new ReturnTags();
315 }
316
317 public TypeInfo returnType() {
318 return mReturnType;
319 }
320
321 public String prettySignature() {
322 return name() + prettyParameters();
323 }
324
325 /**
326 * Returns a printable version of the parameters of this method's signature.
327 */
328 public String prettyParameters() {
329 StringBuilder params = new StringBuilder("(");
330 for (ParameterInfo pInfo : mParameters) {
331 if (params.length() > 1) {
332 params.append(",");
333 }
334 params.append(pInfo.type().simpleTypeName());
335 }
336
337 params.append(")");
338 return params.toString();
339 }
340
341 /**
342 * Returns a name consistent with the {@link com.google.doclava.MethodInfo#getHashableName()}.
343 */
344 public String getHashableName() {
345 StringBuilder result = new StringBuilder();
346 result.append(name());
347 for (int p = 0; p < mParameters.length; p++) {
348 result.append(":");
349 if (p == mParameters.length - 1 && isVarArgs()) {
350 // TODO: note that this does not attempt to handle hypothetical
351 // vararg methods whose last parameter is a list of arrays, e.g.
352 // "Object[]...".
353 result.append(mParameters[p].type().fullNameNoDimension(typeVariables())).append("...");
354 } else {
355 result.append(mParameters[p].type().fullName(typeVariables()));
356 }
357 }
358 return result.toString();
359 }
360
361 private boolean inList(ClassInfo item, ThrowsTagInfo[] list) {
362 int len = list.length;
363 String qn = item.qualifiedName();
364 for (int i = 0; i < len; i++) {
365 ClassInfo ex = list[i].exception();
366 if (ex != null && ex.qualifiedName().equals(qn)) {
367 return true;
368 }
369 }
370 return false;
371 }
372
373 public ThrowsTagInfo[] throwsTags() {
374 if (mThrowsTags == null) {
375 ThrowsTagInfo[] documented = comment().throwsTags();
376 ArrayList<ThrowsTagInfo> rv = new ArrayList<ThrowsTagInfo>();
377
378 int len = documented.length;
379 for (int i = 0; i < len; i++) {
380 rv.add(documented[i]);
381 }
382
383 ClassInfo[] all = mThrownExceptions;
384 len = all.length;
385 for (int i = 0; i < len; i++) {
386 ClassInfo cl = all[i];
387 if (documented == null || !inList(cl, documented)) {
388 rv.add(new ThrowsTagInfo("@throws", "@throws", cl.qualifiedName(), cl, "",
389 containingClass(), position()));
390 }
391 }
392 mThrowsTags = rv.toArray(new ThrowsTagInfo[rv.size()]);
393 }
394 return mThrowsTags;
395 }
396
397 private static int indexOfParam(String name, String[] list) {
398 final int N = list.length;
399 for (int i = 0; i < N; i++) {
400 if (name.equals(list[i])) {
401 return i;
402 }
403 }
404 return -1;
405 }
406
407 public ParamTagInfo[] paramTags() {
408 if (mParamTags == null) {
409 final int N = mParameters.length;
410
411 String[] names = new String[N];
412 String[] comments = new String[N];
413 SourcePositionInfo[] positions = new SourcePositionInfo[N];
414
415 // get the right names so we can handle our names being different from
416 // our parent's names.
417 for (int i = 0; i < N; i++) {
418 names[i] = mParameters[i].name();
419 comments[i] = "";
420 positions[i] = mParameters[i].position();
421 }
422
423 // gather our comments, and complain about misnamed @param tags
424 for (ParamTagInfo tag : comment().paramTags()) {
425 int index = indexOfParam(tag.parameterName(), names);
426 if (index >= 0) {
427 comments[index] = tag.parameterComment();
428 positions[index] = tag.position();
429 } else {
430 Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
431 "@param tag with name that doesn't match the parameter list: '" + tag.parameterName()
432 + "'");
433 }
434 }
435
436 // get our parent's tags to fill in the blanks
437 MethodInfo overridden = this.findOverriddenMethod(name(), signature());
438 if (overridden != null) {
439 ParamTagInfo[] maternal = overridden.paramTags();
440 for (int i = 0; i < N; i++) {
441 if (comments[i].equals("")) {
442 comments[i] = maternal[i].parameterComment();
443 positions[i] = maternal[i].position();
444 }
445 }
446 }
447
448 // construct the results, and cache them for next time
449 mParamTags = new ParamTagInfo[N];
450 for (int i = 0; i < N; i++) {
451 mParamTags[i] =
452 new ParamTagInfo("@param", "@param", names[i] + " " + comments[i], parent(),
453 positions[i]);
454
455 // while we're here, if we find any parameters that are still undocumented at this
456 // point, complain. (this warning is off by default, because it's really, really
457 // common; but, it's good to be able to enforce it)
458 if (comments[i].equals("")) {
459 Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i], "Undocumented parameter '"
460 + names[i] + "' on method '" + name() + "'");
461 }
462 }
463 }
464 return mParamTags;
465 }
466
467 public SeeTagInfo[] seeTags() {
468 SeeTagInfo[] result = comment().seeTags();
469 if (result == null) {
470 if (mOverriddenMethod != null) {
471 result = mOverriddenMethod.seeTags();
472 }
473 }
474 return result;
475 }
476
477 public TagInfo[] deprecatedTags() {
478 TagInfo[] result = comment().deprecatedTags();
479 if (result.length == 0) {
480 if (comment().undeprecateTags().length == 0) {
481 if (mOverriddenMethod != null) {
482 result = mOverriddenMethod.deprecatedTags();
483 }
484 }
485 }
486 return result;
487 }
488
489 public ParameterInfo[] parameters() {
490 return mParameters;
491 }
492
493
494 public boolean matchesParams(String[] params, String[] dimensions, boolean varargs) {
495 if (mParamStrings == null) {
496 ParameterInfo[] mine = mParameters;
497 int len = mine.length;
498 if (len != params.length) {
499 return false;
500 }
501 for (int i = 0; i < len; i++) {
502 if (!mine[i].matchesDimension(dimensions[i], varargs)) {
503 return false;
504 }
505 TypeInfo myType = mine[i].type();
506 String qualifiedName = myType.qualifiedTypeName();
507 String realType = myType.isPrimitive() ? "" : myType.asClassInfo().qualifiedName();
508 String s = params[i];
509 int slen = s.length();
510 int qnlen = qualifiedName.length();
511
512 // Check for a matching generic name or best known type
513 if (!matchesType(qualifiedName, s) && !matchesType(realType, s)) {
514 return false;
515 }
516 }
517 }
518 return true;
519 }
520
521 /**
522 * Checks to see if a parameter from a method signature is
523 * compatible with a parameter given in a {@code @link} tag.
524 */
525 private boolean matchesType(String signatureParam, String callerParam) {
526 int signatureLength = signatureParam.length();
527 int callerLength = callerParam.length();
528 return ((signatureParam.equals(callerParam) || ((callerLength + 1) < signatureLength
529 && signatureParam.charAt(signatureLength - callerLength - 1) == '.'
530 && signatureParam.endsWith(callerParam))));
531 }
532
533 public void makeHDF(Data data, String base) {
534 data.setValue(base + ".kind", kind());
535 data.setValue(base + ".name", name());
536 data.setValue(base + ".href", htmlPage());
537 data.setValue(base + ".anchor", anchor());
538
539 if (mReturnType != null) {
540 returnType().makeHDF(data, base + ".returnType", false, typeVariables());
541 data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
542 }
543
544 data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
545 data.setValue(base + ".final", isFinal() ? "final" : "");
546 data.setValue(base + ".static", isStatic() ? "static" : "");
547
548 TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
549 TagInfo.makeHDF(data, base + ".descr", inlineTags());
550 TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
551 TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
552 data.setValue(base + ".since", getSince());
553 ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
554 AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
555 ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
556 ParameterInfo.makeHDF(data, base + ".params", parameters(), isVarArgs(), typeVariables());
557 if (isProtected()) {
558 data.setValue(base + ".scope", "protected");
559 } else if (isPublic()) {
560 data.setValue(base + ".scope", "public");
561 }
562 TagInfo.makeHDF(data, base + ".returns", returnTags());
563
564 if (mTypeParameters != null) {
565 TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
566 }
567
568 setFederatedReferences(data, base);
569 }
570
571 public HashSet<String> typeVariables() {
572 HashSet<String> result = TypeInfo.typeVariables(mTypeParameters);
573 ClassInfo cl = containingClass();
574 while (cl != null) {
575 TypeInfo[] types = cl.asTypeInfo().typeArguments();
576 if (types != null) {
577 TypeInfo.typeVariables(types, result);
578 }
579 cl = cl.containingClass();
580 }
581 return result;
582 }
583
584 @Override
585 public boolean isExecutable() {
586 return true;
587 }
588
589 public ClassInfo[] thrownExceptions() {
590 return mThrownExceptions;
591 }
592
593 public String typeArgumentsName(HashSet<String> typeVars) {
594 if (mTypeParameters == null || mTypeParameters.length == 0) {
595 return "";
596 } else {
597 return TypeInfo.typeArgumentsName(mTypeParameters, typeVars);
598 }
599 }
600
601 public boolean isAnnotationElement() {
602 return mIsAnnotationElement;
603 }
604
605 public AnnotationValueInfo defaultAnnotationElementValue() {
606 return mDefaultAnnotationElementValue;
607 }
608
609 public void setVarargs(boolean set) {
610 mIsVarargs = set;
611 }
612
613 public boolean isVarArgs() {
614 return mIsVarargs;
615 }
616
617 @Override
618 public String toString() {
619 return this.name();
620 }
621
622 public void setReason(String reason) {
623 mReasonOpened = reason;
624 }
625
626 public String getReason() {
627 return mReasonOpened;
628 }
629
630 public void addException(String exec) {
631 ClassInfo exceptionClass = new ClassInfo(exec);
632 List<ClassInfo> exceptions = new ArrayList<ClassInfo>(mThrownExceptions.length + 1);
633 exceptions.addAll(Arrays.asList(mThrownExceptions));
634 exceptions.add(exceptionClass);
635 mThrownExceptions = new ClassInfo[exceptions.size()];
636 exceptions.toArray(mThrownExceptions);
637 }
638
639 public void addParameter(ParameterInfo p) {
640 // Name information
641 ParameterInfo[] newParams;
642 int i = 0;
643
644 if (mParameters == null) {
645 newParams = new ParameterInfo[1];
646 } else {
647 newParams = new ParameterInfo[mParameters.length+1];
648 for (ParameterInfo info : mParameters) {
649 newParams[i++] = info;
650 }
651 }
652 newParams[i] = p;
653 mParameters = newParams;
654
655 // Type information
656 TypeInfo[] newTypes;
657 i = 0;
658
659 if (mTypeParameters == null) {
660 newTypes = new TypeInfo[1];
661 } else {
662 newTypes = new TypeInfo[mTypeParameters.length+1];
663 for (TypeInfo info : mTypeParameters) {
664 newTypes[i++] = info;
665 }
666 }
667 newTypes[i] = p.mType;
668 mTypeParameters = newTypes;
669 }
670
671 private String mFlatSignature;
672 private MethodInfo mOverriddenMethod;
673 private TypeInfo mReturnType;
674 private boolean mIsAnnotationElement;
675 private boolean mIsAbstract;
676 private boolean mIsSynchronized;
677 private boolean mIsNative;
678 private boolean mIsVarargs;
679 private boolean mDeprecatedKnown;
680 private boolean mIsDeprecated;
681 private ParameterInfo[] mParameters;
682 private ClassInfo[] mThrownExceptions;
683 private String[] mParamStrings;
684 ThrowsTagInfo[] mThrowsTags;
685 private ParamTagInfo[] mParamTags;
686 private TypeInfo[] mTypeParameters;
687 private AnnotationValueInfo mDefaultAnnotationElementValue;
688 private String mReasonOpened;
689
690 // TODO: merge with droiddoc version (above)
691 public String qualifiedName() {
692 String parentQName = (containingClass() != null)
693 ? (containingClass().qualifiedName() + ".") : "";
694 return parentQName + name();
695 }
696
697 @Override
698 public String signature() {
699 if (mSignature == null) {
700 StringBuilder params = new StringBuilder("(");
701 for (ParameterInfo pInfo : mParameters) {
702 if (params.length() > 1) {
703 params.append(", ");
704 }
705 params.append(pInfo.type().fullName());
706 }
707
708 params.append(")");
709 mSignature = params.toString();
710 }
711 return mSignature;
712 }
713
714 public boolean matches(MethodInfo other) {
Ben Dodsonbeef1452010-08-13 16:46:35 -0700715 return prettySignature().equals(other.prettySignature());
Ben Dodson920dbbb2010-08-04 15:21:06 -0700716 }
717
718 public boolean throwsException(ClassInfo exception) {
719 for (ClassInfo e : mThrownExceptions) {
720 if (e.qualifiedName().equals(exception.qualifiedName())) {
721 return true;
722 }
723 }
724 return false;
725 }
726
727 public boolean isConsistent(MethodInfo mInfo) {
728 boolean consistent = true;
729 if (!this.mReturnType.equals(mInfo.mReturnType)) {
730 consistent = false;
731 Errors.error(Errors.CHANGED_TYPE, mInfo.position(), "Method " + mInfo.qualifiedName()
732 + " has changed return type from " + mReturnType + " to " + mInfo.mReturnType);
733 }
734
735 if (mIsAbstract != mInfo.mIsAbstract) {
736 consistent = false;
737 Errors.error(Errors.CHANGED_ABSTRACT, mInfo.position(), "Method " + mInfo.qualifiedName()
738 + " has changed 'abstract' qualifier");
739 }
740
741 if (mIsNative != mInfo.mIsNative) {
742 consistent = false;
743 Errors.error(Errors.CHANGED_NATIVE, mInfo.position(), "Method " + mInfo.qualifiedName()
744 + " has changed 'native' qualifier");
745 }
746
747 if (mIsFinal != mInfo.mIsFinal) {
748 // Compiler-generated methods vary in their 'final' qual between versions of
749 // the compiler, so this check needs to be quite narrow. A change in 'final'
750 // status of a method is only relevant if (a) the method is not declared 'static'
751 // and (b) the method's class is not itself 'final'.
752 if (!mIsStatic) {
753 if ((containingClass() == null) || (!containingClass().isFinal())) {
754 consistent = false;
755 Errors.error(Errors.CHANGED_FINAL, mInfo.position(), "Method " + mInfo.qualifiedName()
756 + " has changed 'final' qualifier");
757 }
758 }
759 }
760
761 if (mIsStatic != mInfo.mIsStatic) {
762 consistent = false;
763 Errors.error(Errors.CHANGED_STATIC, mInfo.position(), "Method " + mInfo.qualifiedName()
764 + " has changed 'static' qualifier");
765 }
766
767 if (!scope().equals(mInfo.scope())) {
768 consistent = false;
769 Errors.error(Errors.CHANGED_SCOPE, mInfo.position(), "Method " + mInfo.qualifiedName()
770 + " changed scope from " + scope() + " to " + mInfo.scope());
771 }
772
773 if (!isDeprecated() == mInfo.isDeprecated()) {
774 Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(), "Method " + mInfo.qualifiedName()
775 + " has changed deprecation state");
776 consistent = false;
777 }
778
779 if (mIsSynchronized != mInfo.mIsSynchronized) {
780 Errors.error(Errors.CHANGED_SYNCHRONIZED, mInfo.position(), "Method " + mInfo.qualifiedName()
781 + " has changed 'synchronized' qualifier from " + mIsSynchronized + " to "
782 + mInfo.mIsSynchronized);
783 consistent = false;
784 }
785
786 for (ClassInfo exception : thrownExceptions()) {
787 if (!mInfo.throwsException(exception)) {
788 // exclude 'throws' changes to finalize() overrides with no arguments
789 if (!name().equals("finalize") || (mParameters.length > 0)) {
790 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method " + mInfo.qualifiedName()
791 + " no longer throws exception " + exception.qualifiedName());
792 consistent = false;
793 }
794 }
795 }
796
797 for (ClassInfo exec : mInfo.thrownExceptions()) {
798 // exclude 'throws' changes to finalize() overrides with no arguments
799 if (!throwsException(exec)) {
800 if (!name().equals("finalize") || (mParameters.length > 0)) {
801 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method " + mInfo.qualifiedName()
802 + " added thrown exception " + exec.qualifiedName());
803 consistent = false;
804 }
805 }
806 }
807
808 return consistent;
809 }
810}