KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > javadoc > hints > JavadocUtilities


1 /*
2  * Sun Public License Notice
3  *
4  * The contents of this file are subject to the Sun Public License
5  * Version 1.0 (the "License"). You may not use this file except in
6  * compliance with the License. A copy of the License is available at
7  * http://www.sun.com/
8  *
9  * The Original Code is NetBeans. The Initial Developer of the Original
10  * Code is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
11  * Microsystems, Inc. All Rights Reserved.
12  */

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 JavaDoc;
25 import java.util.HashSet JavaDoc;
26 import java.util.Set JavaDoc;
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 JavaDoc;
37 import javax.swing.text.Document JavaDoc;
38 import javax.swing.text.Position JavaDoc;
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 /**
48  *
49  * @author Jan Pokorsky
50  */

51 public class JavadocUtilities {
52     
53     private JavadocUtilities() {
54     }
55     
56     /**
57      * Finds javadoc token sequence.
58      * @param javac compilation info
59      * @param doc javadoc for which the tokens are queried
60      * @return javadoc token sequence or null.
61      */

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     /**
84      * Moves the passed token sequence to the first token that correspond to
85      * the javadoc tag.
86      * @param es javadoc token sequence
87      * @param tag javadoc tag to find in the token sequence
88      */

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             //probably an inline tag:
96
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 JavaDoc("cannot match the tag: " + tag.toString()); // NOI18N
119
}
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 /*+ 1 */, offset + length);
134     }
135     
136     /**
137      * finds positions of tag name inside a document.
138      * @param info compilation info
139      * @param doc document
140      * @param tag javadoc tag
141      * @return Position[] {starOffset, endOffset}
142      * @throws javax.swing.text.BadLocationException
143      */

144     public static Position JavaDoc[] findTagNameBounds(CompilationInfo info, Document JavaDoc doc, Tag tag) throws BadLocationException JavaDoc {
145         TokenSequence<JavadocTokenId> tseq = findTokenSequence(info, tag.holder());
146         if (tseq == null)
147             return null;
148         moveToTag(tseq, tag);
149         Position JavaDoc[] positions = new Position JavaDoc[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 JavaDoc[] findDocBounds(CompilationInfo javac, Document JavaDoc doc, Doc jdoc) throws BadLocationException JavaDoc {
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 JavaDoc[] positions = new Position JavaDoc[2];
174         positions[0] = doc.createPosition(tseq.offset());
175         positions[1] = doc.createPosition(tseq.offset() + tseq.token().length());
176         return positions;
177     }
178     
179     /**
180      * finds block tag bounds
181      */

182     public static Position JavaDoc[] findTagBounds(CompilationInfo javac, Document JavaDoc doc, Tag tag) throws BadLocationException JavaDoc {
183         return findTagBounds(javac, doc, tag, null);
184     }
185     
186     public static Position JavaDoc[] findTagBounds(CompilationInfo javac, Document JavaDoc doc, Tag tag, boolean[] isLastToken) throws BadLocationException JavaDoc {
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) == '{')) { // hack for inline tags
200
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 JavaDoc[] positions = new Position JavaDoc[2];
220         positions[0] = doc.createPosition(start);
221         positions[1] = doc.createPosition(tseq.offset() + lastTokenCleanUp);
222         return positions;
223     }
224     
225     /**
226      * finds last javadoc token bounds.
227      */

228     public static Position JavaDoc[] findLastTokenBounds(CompilationInfo javac, Document JavaDoc doc, Doc jdoc) throws BadLocationException JavaDoc {
229         TokenSequence<JavadocTokenId> tseq = findTokenSequence(javac, jdoc);
230         if (tseq == null)
231             return null;
232         
233         tseq.moveEnd();
234         if (tseq.movePrevious()) {
235             Position JavaDoc[] positions = new Position JavaDoc[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             // constructor
249
int offset = (int) javac.getTrees().getSourcePositions().getStartPosition(javac.getCompilationUnit(), t);
250             tseq.move(offset + 1);
251             Token<JavaTokenId> tok = null;
252             String JavaDoc name = enclosing.getSimpleName().toString();
253             int index = -1;
254 // System.out.println("Dump: " + t.getKind() + ", " + t.toString());
255
while (tseq.moveNext()) {
256 // System.out.println("dump.t: " + tseq.token().id() + ", '" + tseq.token().text() + "'");
257
if (tok != null && tseq.token().id() == JavaTokenId.LPAREN && name.contentEquals(tok.text())) {
258                     tseq.moveIndex(index);
259                     tseq.moveNext();
260 // System.out.println("@@@@@@BINGO");
261
break;
262                 }
263                 if (tseq.token().id() == JavaTokenId.IDENTIFIER) {
264                     tok = tseq.token();
265                     index = tseq.index();
266                 }
267             }
268 // System.out.println();
269
} 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) { // enum constant
292
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 JavaDoc name = t.getName().toString();
300         while (tseq.moveNext() /*&& !(tseq.token().id() == JavaTokenId.IDENTIFIER && name.contentEquals(tseq.token().text()))*/) {
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"); //NOI18N
337
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 JavaDoc<TypeElement>());
355         }
356         return null;
357     }
358     
359     /**
360      * <a HREF="http://java.sun.com/javase/6/docs/technotes/tools/solaris/javadoc.html#inheritingcomments">
361      * Algorithm for Inheriting Method Comments
362      * </a>
363      * <p>Do not use MethodDoc.overriddenMethod() instead since it fails for
364      * interfaces!
365      */

366     private static MethodDoc searchInInterfaces(
367             CompilationInfo javac, TypeElement class2query, TypeElement overriderClass,
368             ExecutableElement overrider, Set JavaDoc<TypeElement> exclude) {
369         
370         // Step 1
371
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                 // check methods
378
MethodDoc jdoc = searchInMethods(javac, ifceEl, overriderClass, overrider);
379                 if (jdoc != null) {
380                     return jdoc;
381                 }
382                 exclude.add(ifceEl);
383             }
384         }
385         // Step 2
386
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         // Step 3
396
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 JavaDoc<TypeElement> exclude) {
402         
403         // Step 3a
404
TypeMirror superclassMirror = class2query.getSuperclass();
405         if (superclassMirror.getKind() != TypeKind.DECLARED) {
406             return null;
407         }
408         TypeElement superclass = (TypeElement) ((DeclaredType) superclassMirror).asElement();
409         // check methods
410
MethodDoc jdoc = searchInMethods(javac, superclass, overriderClass, overrider);
411         if (jdoc != null) {
412             return jdoc;
413         }
414         
415         // Step 3b
416
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 JavaDoc paramName, boolean inherited) {
435         ExecutableElement overrider = (ExecutableElement) javac.getElementUtilities().elementFor(doc);
436         TypeElement overriderClass = (TypeElement) overrider.getEnclosingElement();
437         TypeElement class2query = null;
438         Set JavaDoc<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 JavaDoc<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 JavaDoc fqn, boolean inherited) {
466         ExecutableElement overrider = (ExecutableElement) javac.getElementUtilities().elementFor(doc);
467         TypeElement overriderClass = (TypeElement) overrider.getEnclosingElement();
468         TypeElement class2query = null;
469         Set JavaDoc<TypeElement> exclude = null;
470         while (doc != null) {
471             for (ThrowsTag throwsTag : doc.throwsTags()) {
472                 com.sun.javadoc.Type tagType = throwsTag.exceptionType();
473                 String JavaDoc tagFQN = null;
474                 if (tagType != null) {
475                     tagFQN = throwsTag.exceptionType().qualifiedTypeName();
476                 } else { // unresolvable type
477
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 JavaDoc<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 JavaDoc<TypeElement> exclude = null;
508         while (doc != null) {
509             Tag[] tags = doc.tags("@return"); // NOI18N
510
if (tags.length > 0) {
511                 return tags[0];
512             }
513             if (inherited) {
514                 if (exclude == null) {
515                     exclude = new HashSet JavaDoc<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 JavaDoc name;
535         private final String JavaDoc 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             // javadoc was changed
557
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 JavaDoc
568         public String JavaDoc toString() {
569             return super.toString() + "[index: " + this.index + // NOI18N
570
"name: " + this.name + "text: " + this.text + ']'; // NOI18N
571
}
572
573         
574     }
575     
576     private static Set JavaDoc<TokenId> IGNORE_TOKES = null;
577     
578     static {
579         IGNORE_TOKES = new HashSet JavaDoc<TokenId>();
580         IGNORE_TOKES.add(JavaTokenId.WHITESPACE);
581         IGNORE_TOKES.add(JavaTokenId.BLOCK_COMMENT);
582     }
583
584 }
585
Popular Tags