1 13 package org.netbeans.modules.javadoc.hints; 14 15 import com.sun.javadoc.Doc; 16 import com.sun.javadoc.MethodDoc; 17 import com.sun.javadoc.ParamTag; 18 import com.sun.javadoc.Tag; 19 import com.sun.javadoc.ThrowsTag; 20 import com.sun.source.tree.ClassTree; 21 import com.sun.source.tree.MethodTree; 22 import com.sun.source.tree.Tree; 23 import com.sun.source.tree.VariableTree; 24 import java.util.ArrayList ; 25 import java.util.HashSet ; 26 import java.util.Set ; 27 import javax.lang.model.element.AnnotationMirror; 28 import javax.lang.model.element.Element; 29 import javax.lang.model.element.ElementKind; 30 import javax.lang.model.element.ElementKind; 31 import javax.lang.model.element.ExecutableElement; 32 import javax.lang.model.element.TypeElement; 33 import javax.lang.model.type.DeclaredType; 34 import javax.lang.model.type.TypeKind; 35 import javax.lang.model.type.TypeMirror; 36 import javax.swing.text.BadLocationException ; 37 import javax.swing.text.Document ; 38 import javax.swing.text.Position ; 39 import org.netbeans.api.java.lexer.JavaTokenId; 40 import org.netbeans.api.java.lexer.JavadocTokenId; 41 import org.netbeans.api.java.source.CompilationInfo; 42 import org.netbeans.api.lexer.Token; 43 import org.netbeans.api.lexer.TokenId; 44 import org.netbeans.api.lexer.TokenSequence; 45 import org.netbeans.api.lexer.TokenSequence; 46 47 51 public class JavadocUtilities { 52 53 private JavadocUtilities() { 54 } 55 56 62 private static TokenSequence<JavadocTokenId> findTokenSequence(CompilationInfo javac, Doc doc) { 63 Element e = javac.getElementUtilities().elementFor(doc); 64 65 if (e == null) 66 return null; 67 68 Tree tree = javac.getTrees().getTree(e); 69 if (tree == null) 70 return null; 71 72 int elementStartOffset = (int) javac.getTrees().getSourcePositions().getStartPosition(javac.getCompilationUnit(), tree); 73 TokenSequence<? extends TokenId> s = javac.getTokenHierarchy().tokenSequence(); 74 s.move(elementStartOffset); 75 while (s.movePrevious() && IGNORE_TOKES.contains(s.token().id())) 76 ; 77 if (s.token().id() != JavaTokenId.JAVADOC_COMMENT) 78 return null; 79 80 return s.embedded(JavadocTokenId.language()); 81 } 82 83 89 private static void moveToTag(TokenSequence<JavadocTokenId> es, Tag tag) { 90 Doc doc = tag.holder(); 91 Tag[] tags = doc.tags(); 92 int index = findIndex(tags, tag); 93 94 if (index == (-1)) { 95 tags = doc.inlineTags(); 97 index = findIndex(tags, tag); 98 99 assert index >= 0; 100 101 index = computeTagsWithSameNumberBefore(tags, tag); 102 } else { 103 index = computeTagsWithSameNumberBefore(tags, tag); 104 } 105 106 assert index >=0; 107 108 boolean wasNext = false; 109 110 while (index >= 0 && (wasNext = es.moveNext())) { 111 if (es.token().id() == JavadocTokenId.TAG && 112 tag.name().contentEquals(es.token().text()) && 113 --index < 0) { 114 return; 115 } 116 } 117 118 throw new IllegalStateException ("cannot match the tag: " + tag.toString()); } 120 121 public static TokenSequence<JavadocTokenId> tokensFor(CompilationInfo javac, Tag tag) { 122 Doc doc = tag.holder(); 123 TokenSequence<JavadocTokenId> es = findTokenSequence(javac, doc); 124 assert es != null; 125 126 moveToTag(es, tag); 127 128 int offset = es.offset(); 129 130 int length = tag.text().length(); 131 length = length > 0? length: tag.name().length(); 132 TokenSequence<? extends TokenId> s = javac.getTokenHierarchy().tokenSequence(); 133 return s.embedded(JavadocTokenId.language()).subSequence(offset , offset + length); 134 } 135 136 144 public static Position [] findTagNameBounds(CompilationInfo info, Document doc, Tag tag) throws BadLocationException { 145 TokenSequence<JavadocTokenId> tseq = findTokenSequence(info, tag.holder()); 146 if (tseq == null) 147 return null; 148 moveToTag(tseq, tag); 149 Position [] positions = new Position [2]; 150 positions[0] = doc.createPosition(tseq.offset()); 151 positions[1] = doc.createPosition(tseq.offset() + tseq.token().length()); 152 return positions; 153 } 154 155 public static Position [] findDocBounds(CompilationInfo javac, Document doc, Doc jdoc) throws BadLocationException { 156 Element e = javac.getElementUtilities().elementFor(jdoc); 157 158 if (e == null) 159 return null; 160 161 Tree tree = javac.getTrees().getTree(e); 162 if (tree == null) 163 return null; 164 165 int elementStartOffset = (int) javac.getTrees().getSourcePositions().getStartPosition(javac.getCompilationUnit(), tree); 166 TokenSequence<? extends TokenId> tseq = javac.getTokenHierarchy().tokenSequence(); 167 tseq.move(elementStartOffset); 168 while (tseq.movePrevious() && IGNORE_TOKES.contains(tseq.token().id())) 169 ; 170 if (tseq.token().id() != JavaTokenId.JAVADOC_COMMENT) 171 return null; 172 173 Position [] positions = new Position [2]; 174 positions[0] = doc.createPosition(tseq.offset()); 175 positions[1] = doc.createPosition(tseq.offset() + tseq.token().length()); 176 return positions; 177 } 178 179 182 public static Position [] findTagBounds(CompilationInfo javac, Document doc, Tag tag) throws BadLocationException { 183 return findTagBounds(javac, doc, tag, null); 184 } 185 186 public static Position [] findTagBounds(CompilationInfo javac, Document doc, Tag tag, boolean[] isLastToken) throws BadLocationException { 187 TokenSequence<JavadocTokenId> tseq = findTokenSequence(javac, tag.holder()); 188 if (tseq == null) 189 return null; 190 moveToTag(tseq, tag); 191 192 int start = tseq.offset(); 193 194 Token<JavadocTokenId> token = null; 195 Token<JavadocTokenId> last = null; 196 while (tseq.moveNext()) { 197 if (tseq.token().id() == JavadocTokenId.TAG && 198 last != null && !(last.id() == JavadocTokenId.OTHER_TEXT && 199 last.text().charAt(last.text().length() - 1) == '{')) { token = tseq.token(); 201 break; 202 } 203 last = tseq.token(); 204 } 205 206 int lastTokenCleanUp = 0; 207 if (token == null) { 208 tseq.moveEnd(); 209 tseq.movePrevious(); 210 lastTokenCleanUp = tseq.token().text().toString().indexOf('\n'); 211 lastTokenCleanUp = lastTokenCleanUp > 0? lastTokenCleanUp: 0; 212 if (isLastToken != null && isLastToken.length > 0) { 213 isLastToken[0] = true; 214 } else if (isLastToken != null && isLastToken.length > 0) { 215 isLastToken[0] = false; 216 } 217 } 218 219 Position [] positions = new Position [2]; 220 positions[0] = doc.createPosition(start); 221 positions[1] = doc.createPosition(tseq.offset() + lastTokenCleanUp); 222 return positions; 223 } 224 225 228 public static Position [] findLastTokenBounds(CompilationInfo javac, Document doc, Doc jdoc) throws BadLocationException { 229 TokenSequence<JavadocTokenId> tseq = findTokenSequence(javac, jdoc); 230 if (tseq == null) 231 return null; 232 233 tseq.moveEnd(); 234 if (tseq.movePrevious()) { 235 Position [] positions = new Position [2]; 236 positions[0] = doc.createPosition(tseq.offset()); 237 positions[1] = doc.createPosition(tseq.offset() + tseq.token().length()); 238 return positions; 239 } 240 241 return null; 242 } 243 244 public static TokenSequence<JavaTokenId> findMethodNameToken(CompilationInfo javac, ClassTree enclosing, MethodTree t) { 245 TokenSequence<JavaTokenId> tseq = javac.getTokenHierarchy().tokenSequence(JavaTokenId.language()); 246 Tree retType = t.getReturnType(); 247 if (retType == null) { 248 int offset = (int) javac.getTrees().getSourcePositions().getStartPosition(javac.getCompilationUnit(), t); 250 tseq.move(offset + 1); 251 Token<JavaTokenId> tok = null; 252 String name = enclosing.getSimpleName().toString(); 253 int index = -1; 254 while (tseq.moveNext()) { 256 if (tok != null && tseq.token().id() == JavaTokenId.LPAREN && name.contentEquals(tok.text())) { 258 tseq.moveIndex(index); 259 tseq.moveNext(); 260 break; 262 } 263 if (tseq.token().id() == JavaTokenId.IDENTIFIER) { 264 tok = tseq.token(); 265 index = tseq.index(); 266 } 267 } 268 } else { 270 int offset = (int) javac.getTrees().getSourcePositions().getEndPosition(javac.getCompilationUnit(), retType); 271 tseq.move(offset + 1); 272 while (tseq.moveNext() && tseq.token().id() != JavaTokenId.IDENTIFIER); 273 } 274 return tseq; 275 } 276 277 public static TokenSequence<JavaTokenId> findClassNameToken(CompilationInfo javac, ClassTree t) { 278 TokenSequence<JavaTokenId> tseq = javac.getTokenHierarchy().tokenSequence(JavaTokenId.language()); 279 Tree modifs = t.getModifiers(); 280 assert modifs != null; 281 int offset = (int) javac.getTrees().getSourcePositions().getEndPosition(javac.getCompilationUnit(), modifs); 282 tseq.move(offset + 1); 283 while (tseq.moveNext() && tseq.token().id() != JavaTokenId.IDENTIFIER); 284 285 return tseq; 286 } 287 288 public static TokenSequence<JavaTokenId> findVariableNameToken(CompilationInfo javac, VariableTree t, boolean isEnum) { 289 TokenSequence<JavaTokenId> tseq = javac.getTokenHierarchy().tokenSequence(JavaTokenId.language()); 290 Tree start = isEnum? t: t.getType(); 291 if (start == null) { start = t.getModifiers(); 293 } 294 assert start != null; 295 int offset = isEnum? 296 (int) javac.getTrees().getSourcePositions().getStartPosition(javac.getCompilationUnit(), start): 297 (int) javac.getTrees().getSourcePositions().getEndPosition(javac.getCompilationUnit(), start); 298 tseq.move(offset); 299 String name = t.getName().toString(); 300 while (tseq.moveNext() ) { 301 if (tseq.token().id() == JavaTokenId.IDENTIFIER && name.contentEquals(tseq.token().text())) { 302 break; 303 } 304 } 305 306 return tseq; 307 } 308 309 private static int computeTagsWithSameNumberBefore(Tag[] tags, Tag tag) { 310 int index = 0; 311 312 for (Tag t : tags) { 313 if (t == tag) 314 return index; 315 if (t.name().equals(tag.name())) 316 index++; 317 } 318 319 return -1; 320 } 321 322 private static int findIndex(Tag[] tags, Tag tag) { 323 for (int i = 0; i < tags.length; i++) { 324 if (tag == tags[i]) { 325 return i; 326 } 327 } 328 return -1; 329 } 330 331 public static boolean isDeprecated(CompilationInfo javac, Element elm) { 332 return findDeprecated(javac, elm) != null; 333 } 334 335 public static AnnotationMirror findDeprecated(CompilationInfo javac, Element elm) { 336 TypeElement deprAnn = javac.getElements().getTypeElement("java.lang.Deprecated"); assert deprAnn != null; 338 for (AnnotationMirror annotationMirror : javac.getElements().getAllAnnotationMirrors(elm)) { 339 if (deprAnn.equals(annotationMirror.getAnnotationType().asElement())) { 340 return annotationMirror; 341 } 342 } 343 return null; 344 } 345 346 public static boolean hasInheritedDoc(CompilationInfo javac, Element elm) { 347 return findInheritedDoc(javac, elm) != null; 348 } 349 350 public static MethodDoc findInheritedDoc(CompilationInfo javac, Element elm) { 351 if (elm.getKind() == ElementKind.METHOD) { 352 TypeElement clazz = (TypeElement) elm.getEnclosingElement(); 353 return searchInInterfaces(javac, clazz, clazz, 354 (ExecutableElement) elm, new HashSet <TypeElement>()); 355 } 356 return null; 357 } 358 359 366 private static MethodDoc searchInInterfaces( 367 CompilationInfo javac, TypeElement class2query, TypeElement overriderClass, 368 ExecutableElement overrider, Set <TypeElement> exclude) { 369 370 for (TypeMirror ifceMirror : class2query.getInterfaces()) { 372 if (ifceMirror.getKind() == TypeKind.DECLARED) { 373 TypeElement ifceEl = (TypeElement) ((DeclaredType) ifceMirror).asElement(); 374 if (exclude.contains(ifceEl)) { 375 continue; 376 } 377 MethodDoc jdoc = searchInMethods(javac, ifceEl, overriderClass, overrider); 379 if (jdoc != null) { 380 return jdoc; 381 } 382 exclude.add(ifceEl); 383 } 384 } 385 for (TypeMirror ifceMirror : class2query.getInterfaces()) { 387 if (ifceMirror.getKind() == TypeKind.DECLARED) { 388 TypeElement ifceEl = (TypeElement) ((DeclaredType) ifceMirror).asElement(); 389 MethodDoc jdoc = searchInInterfaces(javac, ifceEl, overriderClass, overrider, exclude); 390 if (jdoc != null) { 391 return jdoc; 392 } 393 } 394 } 395 return searchInSuperclass(javac, class2query, overriderClass, overrider, exclude); 397 } 398 399 private static MethodDoc searchInSuperclass( 400 CompilationInfo javac, TypeElement class2query, TypeElement overriderClass, 401 ExecutableElement overrider, Set <TypeElement> exclude) { 402 403 TypeMirror superclassMirror = class2query.getSuperclass(); 405 if (superclassMirror.getKind() != TypeKind.DECLARED) { 406 return null; 407 } 408 TypeElement superclass = (TypeElement) ((DeclaredType) superclassMirror).asElement(); 409 MethodDoc jdoc = searchInMethods(javac, superclass, overriderClass, overrider); 411 if (jdoc != null) { 412 return jdoc; 413 } 414 415 return searchInInterfaces(javac, superclass, overriderClass, overrider, exclude); 417 } 418 419 private static MethodDoc searchInMethods( 420 CompilationInfo javac, TypeElement class2query, 421 TypeElement overriderClass, ExecutableElement overrider) { 422 423 for (Element elm : class2query.getEnclosedElements()) { 424 if (elm.getKind() == ElementKind.METHOD && 425 javac.getElements().overrides(overrider, (ExecutableElement) elm, overriderClass)) { 426 Doc jdoc = javac.getElementUtilities().javaDocFor(elm); 427 return (jdoc != null && jdoc.getRawCommentText().length() > 0)? 428 (MethodDoc) jdoc: null; 429 } 430 } 431 return null; 432 } 433 434 public static ParamTag findParamTag(CompilationInfo javac, MethodDoc doc, String paramName, boolean inherited) { 435 ExecutableElement overrider = (ExecutableElement) javac.getElementUtilities().elementFor(doc); 436 TypeElement overriderClass = (TypeElement) overrider.getEnclosingElement(); 437 TypeElement class2query = null; 438 Set <TypeElement> exclude = null; 439 while (doc != null) { 440 for (ParamTag paramTag : doc.paramTags()) { 441 if (paramName.equals(paramTag.parameterName())) { 442 return paramTag; 443 } 444 } 445 if (inherited) { 446 if (exclude == null) { 447 exclude = new HashSet <TypeElement>(); 448 } 449 450 if (class2query == null) { 451 class2query = overriderClass; 452 } else { 453 Element melm = javac.getElementUtilities().elementFor(doc); 454 class2query = (TypeElement) melm.getEnclosingElement(); 455 } 456 457 doc = searchInInterfaces(javac, class2query, overriderClass, overrider, exclude); 458 } else { 459 break; 460 } 461 } 462 return null; 463 } 464 465 public static ThrowsTag findThrowsTag(CompilationInfo javac, MethodDoc doc, String fqn, boolean inherited) { 466 ExecutableElement overrider = (ExecutableElement) javac.getElementUtilities().elementFor(doc); 467 TypeElement overriderClass = (TypeElement) overrider.getEnclosingElement(); 468 TypeElement class2query = null; 469 Set <TypeElement> exclude = null; 470 while (doc != null) { 471 for (ThrowsTag throwsTag : doc.throwsTags()) { 472 com.sun.javadoc.Type tagType = throwsTag.exceptionType(); 473 String tagFQN = null; 474 if (tagType != null) { 475 tagFQN = throwsTag.exceptionType().qualifiedTypeName(); 476 } else { tagFQN = throwsTag.exceptionName(); 478 } 479 if (tagFQN.equals(fqn)) { 480 return throwsTag; 481 } 482 } 483 if (inherited) { 484 if (exclude == null) { 485 exclude = new HashSet <TypeElement>(); 486 } 487 488 if (class2query == null) { 489 class2query = overriderClass; 490 } else { 491 Element melm = javac.getElementUtilities().elementFor(doc); 492 class2query = (TypeElement) melm.getEnclosingElement(); 493 } 494 495 doc = searchInInterfaces(javac, class2query, overriderClass, overrider, exclude); 496 } else { 497 break; 498 } 499 } 500 return null; 501 } 502 503 public static Tag findReturnTag(CompilationInfo javac, MethodDoc doc, boolean inherited) { 504 ExecutableElement overrider = (ExecutableElement) javac.getElementUtilities().elementFor(doc); 505 TypeElement overriderClass = (TypeElement) overrider.getEnclosingElement(); 506 TypeElement class2query = null; 507 Set <TypeElement> exclude = null; 508 while (doc != null) { 509 Tag[] tags = doc.tags("@return"); if (tags.length > 0) { 511 return tags[0]; 512 } 513 if (inherited) { 514 if (exclude == null) { 515 exclude = new HashSet <TypeElement>(); 516 } 517 518 if (class2query == null) { 519 class2query = overriderClass; 520 } else { 521 Element melm = javac.getElementUtilities().elementFor(doc); 522 class2query = (TypeElement) melm.getEnclosingElement(); 523 } 524 525 doc = searchInInterfaces(javac, class2query, overriderClass, overrider, exclude); 526 } else { 527 break; 528 } 529 } 530 return null; 531 } 532 533 public static final class TagHandle { 534 private final String name; 535 private final String text; 536 private final int index; 537 538 private TagHandle(Tag tag) { 539 this.name = tag.name(); 540 this.text = tag.text(); 541 this.index = findIndex(tag.holder().tags(), tag); 542 } 543 544 public static TagHandle create(Tag tag) { 545 return new TagHandle(tag); 546 } 547 548 public Tag resolve(Doc doc) { 549 Tag[] tags = doc.tags(); 550 if (this.index < tags.length && 551 this.name.equals(tags[this.index].name()) && 552 this.text.equals(tags[this.index].text())) { 553 return tags[this.index]; 554 } 555 556 for (Tag tag : tags) { 558 if (this.name.equals(tags[this.index].name()) && 559 this.text.equals(tags[this.index].text())) { 560 return tag; 561 } 562 } 563 564 return null; 565 } 566 567 @Override 568 public String toString() { 569 return super.toString() + "[index: " + this.index + "name: " + this.name + "text: " + this.text + ']'; } 572 573 574 } 575 576 private static Set <TokenId> IGNORE_TOKES = null; 577 578 static { 579 IGNORE_TOKES = new HashSet <TokenId>(); 580 IGNORE_TOKES.add(JavaTokenId.WHITESPACE); 581 IGNORE_TOKES.add(JavaTokenId.BLOCK_COMMENT); 582 } 583 584 } 585 | Popular Tags |