1 /* 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 17 package com.google.doclava; 18 19 import com.google.clearsilver.jsilver.data.Data; 20 import com.google.doclava.apicheck.AbstractMethodInfo; 21 import com.google.doclava.apicheck.ApiInfo; 22 23 import java.util.*; 24 25 public class MethodInfo extends MemberInfo implements AbstractMethodInfo, Resolvable { 26 public static final Comparator<MethodInfo> comparator = new Comparator<MethodInfo>() { 27 public int compare(MethodInfo a, MethodInfo b) { 28 return a.name().compareTo(b.name()); 29 } 30 }; 31 32 private class InlineTags implements InheritedTags { tags()33 public TagInfo[] tags() { 34 return comment().tags(); 35 } 36 inherited()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 addInterfaces(ArrayList<ClassInfo> ifaces, ArrayList<ClassInfo> queue)47 private static void addInterfaces(ArrayList<ClassInfo> ifaces, ArrayList<ClassInfo> queue) { 48 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 findOverriddenMethod(String name, String signature)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 addRealInterfaces(ArrayList<ClassInfo> ifaces, ArrayList<ClassInfo> queue)80 private static void addRealInterfaces(ArrayList<ClassInfo> ifaces, ArrayList<ClassInfo> queue) { 81 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 findRealOverriddenMethod(String name, String signature, HashSet notStrippable)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 findSuperclassImplementation(HashSet notStrippable)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 findRealOverriddenClass(String name, String signature)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 { tags()178 public TagInfo[] tags() { 179 return comment().briefTags(); 180 } 181 inherited()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 { tags()193 public TagInfo[] tags() { 194 return comment().returnTags(); 195 } 196 inherited()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 isDeprecated()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 setDeprecated(boolean deprecated)231 public void setDeprecated(boolean deprecated) { 232 mDeprecatedKnown = true; 233 mIsDeprecated = deprecated; 234 } 235 getTypeParameters()236 public ArrayList<TypeInfo> getTypeParameters() { 237 return mTypeParameters; 238 } 239 240 /** 241 * Clone this MethodInfo as if it belonged to the specified ClassInfo and apply the 242 * typeArgumentMapping to the parameters and return types. 243 */ cloneForClass(ClassInfo newContainingClass, Map<String, TypeInfo> typeArgumentMapping)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 } 251 MethodInfo result = 252 new MethodInfo(getRawCommentText(), mTypeParameters, name(), signature(), 253 newContainingClass, realContainingClass(), isPublic(), isProtected(), 254 isPackagePrivate(), isPrivate(), isFinal(), isStatic(), isSynthetic(), mIsAbstract, 255 mIsSynchronized, mIsNative, mIsDefault, mIsAnnotationElement, kind(), mFlatSignature, 256 mOverriddenMethod, returnType, mParameters, mThrownExceptions, position(), 257 annotations()); 258 result.init(mDefaultAnnotationElementValue); 259 return result; 260 } 261 MethodInfo(String rawCommentText, ArrayList<TypeInfo> typeParameters, String name, String signature, ClassInfo containingClass, ClassInfo realContainingClass, boolean isPublic, boolean isProtected, boolean isPackagePrivate, boolean isPrivate, boolean isFinal, boolean isStatic, boolean isSynthetic, boolean isAbstract, boolean isSynchronized, boolean isNative, boolean isDefault, boolean isAnnotationElement, String kind, String flatSignature, MethodInfo overriddenMethod, TypeInfo returnType, ArrayList<ParameterInfo> parameters, ArrayList<ClassInfo> thrownExceptions, SourcePositionInfo position, ArrayList<AnnotationInstanceInfo> annotations)262 public MethodInfo(String rawCommentText, ArrayList<TypeInfo> typeParameters, String name, 263 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 isDefault, boolean isAnnotationElement, String kind, 267 String flatSignature, MethodInfo overriddenMethod, TypeInfo returnType, 268 ArrayList<ParameterInfo> parameters, ArrayList<ClassInfo> thrownExceptions, 269 SourcePositionInfo position, ArrayList<AnnotationInstanceInfo> annotations) { 270 // 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, 273 isProtected, isPackagePrivate, isPrivate, 274 ((name.equals("values") && containingClass.isEnum()) ? true : isFinal), 275 isStatic, isSynthetic, kind, position, annotations); 276 277 mReasonOpened = "0:0"; 278 mIsAnnotationElement = isAnnotationElement; 279 mTypeParameters = typeParameters; 280 mIsAbstract = isAbstract; 281 mIsSynchronized = isSynchronized; 282 mIsNative = isNative; 283 mIsDefault = isDefault; 284 mFlatSignature = flatSignature; 285 mOverriddenMethod = overriddenMethod; 286 mReturnType = returnType; 287 mParameters = parameters; 288 mThrownExceptions = thrownExceptions; 289 } 290 init(AnnotationValueInfo defaultAnnotationElementValue)291 public void init(AnnotationValueInfo defaultAnnotationElementValue) { 292 mDefaultAnnotationElementValue = defaultAnnotationElementValue; 293 } 294 isAbstract()295 public boolean isAbstract() { 296 return mIsAbstract; 297 } 298 isSynchronized()299 public boolean isSynchronized() { 300 return mIsSynchronized; 301 } 302 isNative()303 public boolean isNative() { 304 return mIsNative; 305 } 306 isDefault()307 public boolean isDefault() { 308 return mIsDefault; 309 } 310 flatSignature()311 public String flatSignature() { 312 return mFlatSignature; 313 } 314 inlineTags()315 public InheritedTags inlineTags() { 316 return new InlineTags(); 317 } 318 blockTags()319 public TagInfo[] blockTags() { 320 return comment().blockTags(); 321 } 322 firstSentenceTags()323 public InheritedTags firstSentenceTags() { 324 return new FirstSentenceTags(); 325 } 326 returnTags()327 public InheritedTags returnTags() { 328 return new ReturnTags(); 329 } 330 returnType()331 public TypeInfo returnType() { 332 return mReturnType; 333 } 334 prettySignature()335 public String prettySignature() { 336 return name() + prettyParameters(); 337 } 338 prettyQualifiedSignature()339 public String prettyQualifiedSignature() { 340 return qualifiedName() + prettyParameters(); 341 } 342 343 /** 344 * Returns a printable version of the parameters of this method's signature. 345 */ prettyParameters()346 public String prettyParameters() { 347 StringBuilder params = new StringBuilder("("); 348 for (ParameterInfo pInfo : mParameters) { 349 if (params.length() > 1) { 350 params.append(","); 351 } 352 params.append(pInfo.type().simpleTypeName()); 353 } 354 355 params.append(")"); 356 return params.toString(); 357 } 358 359 /** 360 * Returns a name consistent with the {@link com.google.doclava.MethodInfo#getHashableName()}. 361 */ getHashableName()362 public String getHashableName() { 363 StringBuilder result = new StringBuilder(); 364 result.append(name()); 365 366 if (mParameters == null) { 367 return result.toString(); 368 } 369 370 int i = 0; 371 for (ParameterInfo param : mParameters) { 372 result.append(":"); 373 if (i == (mParameters.size()-1) && isVarArgs()) { 374 // TODO: note that this does not attempt to handle hypothetical 375 // vararg methods whose last parameter is a list of arrays, e.g. 376 // "Object[]...". 377 result.append(param.type().fullNameNoDimension(typeVariables())).append("..."); 378 } else { 379 result.append(param.type().fullName(typeVariables())); 380 } 381 i++; 382 } 383 return result.toString(); 384 } 385 inList(ClassInfo item, ThrowsTagInfo[] list)386 private boolean inList(ClassInfo item, ThrowsTagInfo[] list) { 387 int len = list.length; 388 String qn = item.qualifiedName(); 389 for (int i = 0; i < len; i++) { 390 ClassInfo ex = list[i].exception(); 391 if (ex != null && ex.qualifiedName().equals(qn)) { 392 return true; 393 } 394 } 395 return false; 396 } 397 throwsTags()398 public ThrowsTagInfo[] throwsTags() { 399 if (mThrowsTags == null) { 400 ThrowsTagInfo[] documented = comment().throwsTags(); 401 ArrayList<ThrowsTagInfo> rv = new ArrayList<ThrowsTagInfo>(); 402 403 int len = documented.length; 404 for (int i = 0; i < len; i++) { 405 rv.add(documented[i]); 406 } 407 408 for (ClassInfo cl : mThrownExceptions) { 409 if (documented == null || !inList(cl, documented)) { 410 rv.add(new ThrowsTagInfo("@throws", "@throws", cl.qualifiedName(), cl, "", 411 containingClass(), position())); 412 } 413 } 414 415 mThrowsTags = rv.toArray(ThrowsTagInfo.getArray(rv.size())); 416 } 417 return mThrowsTags; 418 } 419 indexOfParam(String name, ParamTagInfo[] list)420 private static int indexOfParam(String name, ParamTagInfo[] list) { 421 final int N = list.length; 422 for (int i = 0; i < N; i++) { 423 if (name.equals(list[i].parameterName())) { 424 return i; 425 } 426 } 427 return -1; 428 } 429 430 /* Checks whether the name documented with the provided @param tag 431 * actually matches one of the method parameters. */ isParamTagInMethod(ParamTagInfo tag)432 private boolean isParamTagInMethod(ParamTagInfo tag) { 433 for (ParameterInfo paramInfo : mParameters) { 434 if (paramInfo.name().equals(tag.parameterName())) { 435 return true; 436 } 437 } 438 return false; 439 } 440 paramTags()441 public ParamTagInfo[] paramTags() { 442 if (mParamTags == null) { 443 final int N = mParameters.size(); 444 final String DEFAULT_COMMENT = "<!-- no parameter comment -->"; 445 446 if (N == 0) { 447 // Early out for empty case. 448 mParamTags = ParamTagInfo.EMPTY_ARRAY; 449 return ParamTagInfo.EMPTY_ARRAY; 450 } 451 // Where we put each result 452 mParamTags = ParamTagInfo.getArray(N); 453 454 // collect all the @param tag info 455 ParamTagInfo[] paramTags = comment().paramTags(); 456 457 // Complain about misnamed @param tags 458 for (ParamTagInfo tag : paramTags) { 459 if (!isParamTagInMethod(tag) && !tag.isTypeParameter()){ 460 Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(), 461 "@param tag with name that doesn't match the parameter list: '" 462 + tag.parameterName() + "'"); 463 } 464 } 465 466 // Loop the parameters known from the method signature... 467 // Start by getting the known parameter name and data type. Then, if 468 // there's an @param tag that matches the current parameter name, get the 469 // javadoc comments. But if there's no @param comments here, then 470 // check if it's available from the parent class. 471 int i = 0; 472 for (ParameterInfo param : mParameters) { 473 String name = param.name(); 474 String type = param.type().simpleTypeName(); 475 String comment = DEFAULT_COMMENT; 476 SourcePositionInfo position = param.position(); 477 478 // Find the matching param from the @param tags in order to get 479 // the parameter comments 480 int index = indexOfParam(name, paramTags); 481 if (index >= 0) { 482 comment = paramTags[index].parameterComment(); 483 position = paramTags[index].position(); 484 } 485 486 // get our parent's tags to fill in the blanks 487 MethodInfo overridden = this.findOverriddenMethod(name(), signature()); 488 if (overridden != null) { 489 ParamTagInfo[] maternal = overridden.paramTags(); 490 if (comment.equals(DEFAULT_COMMENT)) { 491 comment = maternal[i].parameterComment(); 492 position = maternal[i].position(); 493 } 494 } 495 496 // Okay, now add the collected parameter information to the method data 497 mParamTags[i] = 498 new ParamTagInfo("@param", type, name + " " + comment, parent(), 499 position); 500 501 // while we're here, if we find any parameters that are still 502 // undocumented at this point, complain. This warning is off by 503 // default, because it's really, really common; 504 // but, it's good to be able to enforce it. 505 if (comment.equals(DEFAULT_COMMENT)) { 506 Errors.error(Errors.UNDOCUMENTED_PARAMETER, position, 507 "Undocumented parameter '" + name + "' on method '" 508 + name() + "'"); 509 } 510 i++; 511 } 512 } 513 return mParamTags; 514 } 515 seeTags()516 public SeeTagInfo[] seeTags() { 517 SeeTagInfo[] result = comment().seeTags(); 518 if (result == null) { 519 if (mOverriddenMethod != null) { 520 result = mOverriddenMethod.seeTags(); 521 } 522 } 523 return result; 524 } 525 deprecatedTags()526 public TagInfo[] deprecatedTags() { 527 TagInfo[] result = comment().deprecatedTags(); 528 if (result.length == 0) { 529 if (comment().undeprecateTags().length == 0) { 530 if (mOverriddenMethod != null) { 531 result = mOverriddenMethod.deprecatedTags(); 532 } 533 } 534 } 535 return result; 536 } 537 parameters()538 public ArrayList<ParameterInfo> parameters() { 539 return mParameters; 540 } 541 542 matchesParams(String[] params, String[] dimensions, boolean varargs)543 public boolean matchesParams(String[] params, String[] dimensions, boolean varargs) { 544 if (mParamStrings == null) { 545 if (mParameters.size() != params.length) { 546 return false; 547 } 548 int i = 0; 549 for (ParameterInfo mine : mParameters) { 550 // If the method we're matching against is a varargs method (varargs == true), then 551 // only its last parameter is varargs. 552 if (!mine.matchesDimension(dimensions[i], (i == params.length - 1) ? varargs : false)) { 553 return false; 554 } 555 TypeInfo myType = mine.type(); 556 String qualifiedName = myType.qualifiedTypeName(); 557 String realType = myType.isPrimitive() ? "" : myType.asClassInfo().qualifiedName(); 558 String s = params[i]; 559 560 // Check for a matching generic name or best known type 561 if (!matchesType(qualifiedName, s) && !matchesType(realType, s)) { 562 return false; 563 } 564 i++; 565 } 566 } 567 return true; 568 } 569 570 /** 571 * Checks to see if a parameter from a method signature is 572 * compatible with a parameter given in a {@code @link} tag. 573 */ matchesType(String signatureParam, String callerParam)574 private boolean matchesType(String signatureParam, String callerParam) { 575 int signatureLength = signatureParam.length(); 576 int callerLength = callerParam.length(); 577 return ((signatureParam.equals(callerParam) || ((callerLength + 1) < signatureLength 578 && signatureParam.charAt(signatureLength - callerLength - 1) == '.' 579 && signatureParam.endsWith(callerParam)))); 580 } 581 makeHDF(Data data, String base)582 public void makeHDF(Data data, String base) { 583 makeHDF(data, base, Collections.<String, TypeInfo>emptyMap()); 584 } 585 makeHDF(Data data, String base, Map<String, TypeInfo> typeMapping)586 public void makeHDF(Data data, String base, Map<String, TypeInfo> typeMapping) { 587 data.setValue(base + ".kind", kind()); 588 data.setValue(base + ".name", name()); 589 data.setValue(base + ".href", htmlPage()); 590 data.setValue(base + ".anchor", anchor()); 591 592 if (mReturnType != null) { 593 returnType().getTypeWithArguments(typeMapping).makeHDF( 594 data, base + ".returnType", false, typeVariables()); 595 data.setValue(base + ".abstract", mIsAbstract ? "abstract" : ""); 596 } 597 598 data.setValue(base + ".default", mIsDefault ? "default" : ""); 599 data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : ""); 600 data.setValue(base + ".final", isFinal() ? "final" : ""); 601 data.setValue(base + ".static", isStatic() ? "static" : ""); 602 603 TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags()); 604 TagInfo.makeHDF(data, base + ".descr", inlineTags()); 605 TagInfo.makeHDF(data, base + ".blockTags", blockTags()); 606 TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags()); 607 TagInfo.makeHDF(data, base + ".seeAlso", seeTags()); 608 data.setValue(base + ".since", getSince()); 609 if (isDeprecated()) { 610 data.setValue(base + ".deprecatedsince", getDeprecatedSince()); 611 } 612 ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags()); 613 AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags()); 614 ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags()); 615 ParameterInfo.makeHDF(data, base + ".params", mParameters.toArray( 616 new ParameterInfo[mParameters.size()]), isVarArgs(), typeVariables(), typeMapping); 617 if (isProtected()) { 618 data.setValue(base + ".scope", "protected"); 619 } else if (isPublic()) { 620 data.setValue(base + ".scope", "public"); 621 } 622 TagInfo.makeHDF(data, base + ".returns", returnTags()); 623 624 if (mTypeParameters != null) { 625 TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false); 626 } 627 628 int numAnnotationDocumentation = 0; 629 for (AnnotationInstanceInfo aii : annotations()) { 630 String annotationDocumentation = Doclava.getDocumentationStringForAnnotation( 631 aii.type().qualifiedName()); 632 if (annotationDocumentation != null) { 633 data.setValue(base + ".annotationdocumentation." + numAnnotationDocumentation + ".text", 634 annotationDocumentation); 635 numAnnotationDocumentation++; 636 } 637 } 638 639 640 AnnotationInstanceInfo.makeLinkListHDF( 641 data, 642 base + ".showAnnotations", 643 showAnnotations().toArray(new AnnotationInstanceInfo[showAnnotations().size()])); 644 645 setFederatedReferences(data, base); 646 } 647 typeVariables()648 public HashSet<String> typeVariables() { 649 HashSet<String> result = TypeInfo.typeVariables(mTypeParameters); 650 ClassInfo cl = containingClass(); 651 while (cl != null) { 652 ArrayList<TypeInfo> types = cl.asTypeInfo().typeArguments(); 653 if (types != null) { 654 TypeInfo.typeVariables(types, result); 655 } 656 cl = cl.containingClass(); 657 } 658 return result; 659 } 660 661 @Override isExecutable()662 public boolean isExecutable() { 663 return true; 664 } 665 thrownExceptions()666 public ArrayList<ClassInfo> thrownExceptions() { 667 return mThrownExceptions; 668 } 669 typeArgumentsName(HashSet<String> typeVars)670 public String typeArgumentsName(HashSet<String> typeVars) { 671 if (mTypeParameters == null || mTypeParameters.isEmpty()) { 672 return ""; 673 } else { 674 return TypeInfo.typeArgumentsName(mTypeParameters, typeVars); 675 } 676 } 677 isAnnotationElement()678 public boolean isAnnotationElement() { 679 return mIsAnnotationElement; 680 } 681 defaultAnnotationElementValue()682 public AnnotationValueInfo defaultAnnotationElementValue() { 683 return mDefaultAnnotationElementValue; 684 } 685 setVarargs(boolean set)686 public void setVarargs(boolean set) { 687 mIsVarargs = set; 688 } 689 isVarArgs()690 public boolean isVarArgs() { 691 return mIsVarargs; 692 } 693 isEffectivelyFinal()694 public boolean isEffectivelyFinal() { 695 if (mIsFinal) { 696 return true; 697 } 698 ClassInfo containingClass = containingClass(); 699 if (containingClass != null && containingClass.isEffectivelyFinal()) { 700 return true; 701 } 702 return false; 703 } 704 705 @Override toString()706 public String toString() { 707 return this.name(); 708 } 709 setReason(String reason)710 public void setReason(String reason) { 711 mReasonOpened = reason; 712 } 713 getReason()714 public String getReason() { 715 return mReasonOpened; 716 } 717 addException(String exec)718 public void addException(String exec) { 719 ClassInfo exceptionClass = new ClassInfo(exec); 720 721 mThrownExceptions.add(exceptionClass); 722 } 723 addParameter(ParameterInfo p)724 public void addParameter(ParameterInfo p) { 725 // Name information 726 if (mParameters == null) { 727 mParameters = new ArrayList<ParameterInfo>(); 728 } 729 730 mParameters.add(p); 731 } 732 733 private String mFlatSignature; 734 private MethodInfo mOverriddenMethod; 735 private TypeInfo mReturnType; 736 private boolean mIsAnnotationElement; 737 private boolean mIsAbstract; 738 private boolean mIsSynchronized; 739 private boolean mIsNative; 740 private boolean mIsVarargs; 741 private boolean mDeprecatedKnown; 742 private boolean mIsDeprecated; 743 private boolean mIsDefault; 744 private ArrayList<ParameterInfo> mParameters; 745 private ArrayList<ClassInfo> mThrownExceptions; 746 private String[] mParamStrings; 747 private ThrowsTagInfo[] mThrowsTags; 748 private ParamTagInfo[] mParamTags; 749 private ArrayList<TypeInfo> mTypeParameters; 750 private AnnotationValueInfo mDefaultAnnotationElementValue; 751 private String mReasonOpened; 752 private ArrayList<Resolution> mResolutions; 753 754 // TODO: merge with droiddoc version (above) qualifiedName()755 public String qualifiedName() { 756 String parentQName = (containingClass() != null) 757 ? (containingClass().qualifiedName() + ".") : ""; 758 // TODO: This logic doesn't work well with constructors, as name() for constructors already 759 // contains the containingClass's name, leading to things like A.B.B() being rendered as A.B.A.B() 760 return parentQName + name(); 761 } 762 763 @Override signature()764 public String signature() { 765 if (mSignature == null) { 766 StringBuilder params = new StringBuilder("("); 767 for (ParameterInfo pInfo : mParameters) { 768 if (params.length() > 1) { 769 params.append(", "); 770 } 771 params.append(pInfo.type().fullName()); 772 } 773 774 params.append(")"); 775 mSignature = params.toString(); 776 } 777 return mSignature; 778 } 779 matches(MethodInfo other)780 public boolean matches(MethodInfo other) { 781 return prettySignature().equals(other.prettySignature()); 782 } 783 throwsException(ClassInfo exception)784 public boolean throwsException(ClassInfo exception) { 785 for (ClassInfo e : mThrownExceptions) { 786 if (e.qualifiedName().equals(exception.qualifiedName())) { 787 return true; 788 } 789 } 790 return false; 791 } 792 isConsistent(MethodInfo mInfo)793 public boolean isConsistent(MethodInfo mInfo) { 794 boolean consistent = true; 795 if (this.mReturnType != mInfo.mReturnType && !this.mReturnType.equals(mInfo.mReturnType)) { 796 if (!mReturnType.isPrimitive() && !mInfo.mReturnType.isPrimitive()) { 797 // Check to see if our class extends the old class. 798 ApiInfo infoApi = mInfo.containingClass().containingPackage().containingApi(); 799 ClassInfo infoReturnClass = infoApi.findClass(mInfo.mReturnType.qualifiedTypeName()); 800 // Find the classes. 801 consistent = infoReturnClass != null && 802 infoReturnClass.isAssignableTo(mReturnType.qualifiedTypeName()); 803 } else { 804 consistent = false; 805 } 806 807 if (!consistent) { 808 Errors.error(Errors.CHANGED_TYPE, mInfo.position(), "Method " 809 + mInfo.prettyQualifiedSignature() + " has changed return type from " + mReturnType 810 + " to " + mInfo.mReturnType); 811 } 812 } 813 814 if (mIsAbstract != mInfo.mIsAbstract) { 815 consistent = false; 816 Errors.error(Errors.CHANGED_ABSTRACT, mInfo.position(), "Method " 817 + mInfo.prettyQualifiedSignature() + " has changed 'abstract' qualifier"); 818 } 819 820 if (mIsNative != mInfo.mIsNative) { 821 consistent = false; 822 Errors.error(Errors.CHANGED_NATIVE, mInfo.position(), "Method " 823 + mInfo.prettyQualifiedSignature() + " has changed 'native' qualifier"); 824 } 825 826 if (!mIsStatic) { 827 // Compiler-generated methods vary in their 'final' qualifier between versions of 828 // the compiler, so this check needs to be quite narrow. A change in 'final' 829 // status of a method is only relevant if (a) the method is not declared 'static' 830 // and (b) the method is not already inferred to be 'final' by virtue of its class. 831 if (!isEffectivelyFinal() && mInfo.isEffectivelyFinal()) { 832 consistent = false; 833 Errors.error(Errors.ADDED_FINAL, mInfo.position(), "Method " 834 + mInfo.prettyQualifiedSignature() + " has added 'final' qualifier"); 835 } else if (isEffectivelyFinal() && !mInfo.isEffectivelyFinal()) { 836 consistent = false; 837 Errors.error(Errors.REMOVED_FINAL, mInfo.position(), "Method " 838 + mInfo.prettyQualifiedSignature() + " has removed 'final' qualifier"); 839 } 840 } 841 842 if (mIsStatic != mInfo.mIsStatic) { 843 consistent = false; 844 Errors.error(Errors.CHANGED_STATIC, mInfo.position(), "Method " 845 + mInfo.prettyQualifiedSignature() + " has changed 'static' qualifier"); 846 } 847 848 if (!scope().equals(mInfo.scope())) { 849 consistent = false; 850 Errors.error(Errors.CHANGED_SCOPE, mInfo.position(), "Method " 851 + mInfo.prettyQualifiedSignature() + " changed scope from " + scope() 852 + " to " + mInfo.scope()); 853 } 854 855 if (!isDeprecated() == mInfo.isDeprecated()) { 856 Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(), "Method " 857 + mInfo.prettyQualifiedSignature() + " has changed deprecation state " + isDeprecated() 858 + " --> " + mInfo.isDeprecated()); 859 consistent = false; 860 } 861 862 // see JLS 3 13.4.20 "Adding or deleting a synchronized modifier of a method does not break " 863 // "compatibility with existing binaries." 864 /* 865 if (mIsSynchronized != mInfo.mIsSynchronized) { 866 Errors.error(Errors.CHANGED_SYNCHRONIZED, mInfo.position(), "Method " + mInfo.qualifiedName() 867 + " has changed 'synchronized' qualifier from " + mIsSynchronized + " to " 868 + mInfo.mIsSynchronized); 869 consistent = false; 870 } 871 */ 872 873 for (ClassInfo exception : thrownExceptions()) { 874 if (!mInfo.throwsException(exception)) { 875 // exclude 'throws' changes to finalize() overrides with no arguments 876 if (!name().equals("finalize") || (!mParameters.isEmpty())) { 877 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method " 878 + mInfo.prettyQualifiedSignature() + " no longer throws exception " 879 + exception.qualifiedName()); 880 consistent = false; 881 } 882 } 883 } 884 885 for (ClassInfo exec : mInfo.thrownExceptions()) { 886 // exclude 'throws' changes to finalize() overrides with no arguments 887 if (!throwsException(exec)) { 888 if (!name().equals("finalize") || (!mParameters.isEmpty())) { 889 Errors.error(Errors.CHANGED_THROWS, mInfo.position(), "Method " 890 + mInfo.prettyQualifiedSignature() + " added thrown exception " 891 + exec.qualifiedName()); 892 consistent = false; 893 } 894 } 895 } 896 897 return consistent; 898 } 899 printResolutions()900 public void printResolutions() { 901 if (mResolutions == null || mResolutions.isEmpty()) { 902 return; 903 } 904 905 System.out.println("Resolutions for Method " + mName + mFlatSignature + ":"); 906 907 for (Resolution r : mResolutions) { 908 System.out.println(r); 909 } 910 } 911 addResolution(Resolution resolution)912 public void addResolution(Resolution resolution) { 913 if (mResolutions == null) { 914 mResolutions = new ArrayList<Resolution>(); 915 } 916 917 mResolutions.add(resolution); 918 } 919 resolveResolutions()920 public boolean resolveResolutions() { 921 ArrayList<Resolution> resolutions = mResolutions; 922 mResolutions = new ArrayList<Resolution>(); 923 924 boolean allResolved = true; 925 for (Resolution resolution : resolutions) { 926 StringBuilder qualifiedClassName = new StringBuilder(); 927 InfoBuilder.resolveQualifiedName(resolution.getValue(), qualifiedClassName, 928 resolution.getInfoBuilder()); 929 930 // if we still couldn't resolve it, save it for the next pass 931 if ("".equals(qualifiedClassName.toString())) { 932 mResolutions.add(resolution); 933 allResolved = false; 934 } else if ("thrownException".equals(resolution.getVariable())) { 935 mThrownExceptions.add(InfoBuilder.Caches.obtainClass(qualifiedClassName.toString())); 936 } 937 } 938 939 return allResolved; 940 } 941 } 942