KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.javadoc.hints;
21
22 import com.sun.javadoc.AnnotationTypeElementDoc;
23 import com.sun.javadoc.AnnotationTypeElementDoc;
24 import com.sun.javadoc.ClassDoc;
25 import com.sun.javadoc.Doc;
26 import com.sun.javadoc.ExecutableMemberDoc;
27 import com.sun.javadoc.MethodDoc;
28 import com.sun.javadoc.ParamTag;
29 import com.sun.javadoc.Tag;
30 import com.sun.javadoc.ThrowsTag;
31 import com.sun.source.tree.ClassTree;
32 import com.sun.source.tree.CompilationUnitTree;
33 import com.sun.source.tree.ErroneousTree;
34 import com.sun.source.tree.ExpressionTree;
35 import com.sun.source.tree.MethodTree;
36 import com.sun.source.tree.Tree;
37 import com.sun.source.tree.TypeParameterTree;
38 import com.sun.source.tree.VariableTree;
39 import com.sun.source.util.TreePath;
40 import com.sun.source.util.TreePathScanner;
41 import java.awt.EventQueue JavaDoc;
42 import java.beans.PropertyChangeListener JavaDoc;
43 import java.io.IOException JavaDoc;
44 import java.util.ArrayList JavaDoc;
45 import java.util.Collections JavaDoc;
46 import java.util.HashMap JavaDoc;
47 import java.util.List JavaDoc;
48 import java.util.Map JavaDoc;
49 import java.util.Set JavaDoc;
50 import java.util.logging.Level JavaDoc;
51 import java.util.logging.Logger JavaDoc;
52 import javax.lang.model.SourceVersion;
53 import javax.lang.model.element.AnnotationMirror;
54 import javax.lang.model.element.Element;
55 import javax.lang.model.element.ElementKind;
56 import javax.lang.model.element.ExecutableElement;
57 import javax.lang.model.element.Modifier;
58 import javax.lang.model.element.TypeElement;
59 import javax.lang.model.element.TypeParameterElement;
60 import javax.lang.model.type.TypeKind;
61 import javax.lang.model.type.TypeMirror;
62 import javax.swing.text.BadLocationException JavaDoc;
63 import javax.swing.text.Document JavaDoc;
64 import javax.swing.text.Position JavaDoc;
65 import javax.swing.text.StyledDocument JavaDoc;
66 import org.netbeans.api.editor.guards.GuardedSection;
67 import org.netbeans.api.editor.guards.GuardedSectionManager;
68 import org.netbeans.api.java.lexer.JavaTokenId;
69 import org.netbeans.api.java.queries.SourceLevelQuery;
70 import org.netbeans.api.java.source.CancellableTask;
71 import org.netbeans.api.java.source.Comment;
72 import org.netbeans.api.java.source.CompilationInfo;
73 import org.netbeans.api.java.source.ElementHandle;
74 import org.netbeans.api.java.source.JavaSource;
75 import org.netbeans.api.java.source.SourceUtils;
76 import org.netbeans.api.java.source.WorkingCopy;
77 import org.netbeans.api.lexer.TokenSequence;
78 import org.netbeans.modules.javadoc.hints.JavadocUtilities.TagHandle;
79 import org.netbeans.spi.editor.hints.ChangeInfo;
80 import org.netbeans.spi.editor.hints.ErrorDescription;
81 import org.netbeans.spi.editor.hints.ErrorDescriptionFactory;
82 import org.netbeans.spi.editor.hints.Fix;
83 import org.netbeans.spi.editor.hints.HintsController;
84 import org.netbeans.spi.editor.hints.LazyFixList;
85 import org.netbeans.spi.editor.hints.Severity;
86 import org.openide.cookies.EditorCookie;
87 import org.openide.cookies.LineCookie;
88 import org.openide.filesystems.FileObject;
89 import org.openide.loaders.DataObject;
90 import org.openide.text.Line;
91 import org.openide.text.NbDocument;
92 import org.openide.util.NbBundle;
93
94 /**
95  * Checks:
96  * - missing javadoc
97  * - @param duplicate, missing, unknown
98  * - @throws duplicate, missing, unknown
99  * - @return duplicate, missing, void
100  * - if @Deprecated annotation, check for @deprecated tag
101  * - if inheritance in place check for superclass javadoc;
102  * - javadoc and its parts may be inherited
103  * @see <a HREF="http://java.sun.com/javase/6/docs/technotes/tools/solaris/javadoc.html#inheritingcomments">Automatic Copying of Method Comments</a>
104  * @see <a HREF="http://java.sun.com/javase/6/docs/technotes/tools/solaris/javadoc.html#javadoctags">Javadoc Tags</a>
105  * @see <a HREF="http://java.sun.com/javase/6/docs/technotes/tools/solaris/javadoc.html#wheretags">Where Tags Can Be Used</a>
106  * @see <a HREF="http://java.sun.com/javase/6/docs/technotes/guides/javadoc/deprecation/index.html">Deprecation of APIs</a>
107  * @author Jan Pokorsky
108  */

109 public final class JavadocHintProvider implements CancellableTask<CompilationInfo> {
110     
111     private static final Severity hintSeverity = Severity.WARNING;
112     private static final int NOPOS = -2; // XXX copied from jackpot; should be in api
113
private final FileObject file;
114     private boolean cancel = false;
115     
116     // settings
117
// max count of Create Missing Javadoc hints to process
118
private static int CREATE_JAVADOC_HINT_LIMIT = Integer.MAX_VALUE;
119     // max count of Add/Remove Tag hints to process
120
private static int FIX_JAVADOC_HINT_LIMIT = Integer.MAX_VALUE;
121     
122     private static Access access;
123     
124     /** Creates a new instance of JavadocHintProvider */
125     public JavadocHintProvider(FileObject file) {
126         this.file = file;
127     }
128     
129     public void cancel() {
130         this.cancel = true;
131     }
132     
133     boolean isCanceled() {
134         return cancel;
135     }
136
137     public void run(CompilationInfo javac) throws Exception JavaDoc {
138         readSettings();
139         if (CREATE_JAVADOC_HINT_LIMIT <= 0 && FIX_JAVADOC_HINT_LIMIT <= 0) {
140             return;
141         }
142         
143         Document JavaDoc doc = javac.getDocument();
144         if (doc == null) {
145             return;
146         }
147         List JavaDoc<ErrorDescription> errors = new ArrayList JavaDoc<ErrorDescription>();
148         Analyzer an = new Analyzer(javac, doc);
149         try {
150             an.scan(javac.getCompilationUnit(), errors);
151         } catch (CancelException ex) {
152             // task was cancelled
153
return;
154         }
155         HintsController.setErrors(file, "javadoc", errors);
156     }
157     
158     // XXX Since there are no editor hint options yet, read the settings as system properties.
159
private static void readSettings() {
160         if (access != null) {
161             return;
162         }
163         String JavaDoc s = System.getProperty("org.netbeans.modules.javadoc.hints.MissingJavadocLimit"); // NOI18N
164
try {
165             if (s != null) {
166                 CREATE_JAVADOC_HINT_LIMIT = Integer.parseInt(s);
167             }
168         } catch (NumberFormatException JavaDoc ex) {
169             Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
170         }
171         
172         s = System.getProperty("org.netbeans.modules.javadoc.hints.FixJavadocTagLimit"); // NOI18N
173
try {
174             if (s != null) {
175                 FIX_JAVADOC_HINT_LIMIT = Integer.parseInt(s);
176             }
177         } catch (NumberFormatException JavaDoc ex) {
178             Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
179         }
180         
181         // accept [public|protected|package|private], default is protected
182
// according to http://java.sun.com/javase/6/docs/technotes/tools/solaris/javadoc.html#javadocoptions
183
s = System.getProperty("org.netbeans.modules.javadoc.hints.Visibility"); // NOI18N
184
access = Access.resolve(s);
185     }
186     
187     private static SourceVersion resolveSourceVersion(FileObject file) {
188         String JavaDoc sourceLevel = SourceLevelQuery.getSourceLevel(file);
189         if (sourceLevel == null) {
190             return SourceVersion.latest();
191         } else if (sourceLevel.startsWith("1.6")) {
192             return SourceVersion.RELEASE_6;
193         } else if (sourceLevel.startsWith("1.5")) {
194             return SourceVersion.RELEASE_5;
195         } else if (sourceLevel.startsWith("1.4")) {
196             return SourceVersion.RELEASE_4;
197         } else if (sourceLevel.startsWith("1.3")) {
198             return SourceVersion.RELEASE_3;
199         } else if (sourceLevel.startsWith("1.2")) {
200             return SourceVersion.RELEASE_2;
201         } else if (sourceLevel.startsWith("1.1")) {
202             return SourceVersion.RELEASE_1;
203         } else if (sourceLevel.startsWith("1.0")) {
204             return SourceVersion.RELEASE_1;
205         }
206         
207         return SourceVersion.latest();
208         
209     }
210     
211     private final class Analyzer extends TreePathScanner<Void JavaDoc, List JavaDoc<ErrorDescription>> {
212         
213         private final CompilationInfo javac;
214         private final SourceVersion spec;
215         private final FixAll fixAll = new FixAll();
216         private final Document JavaDoc doc;
217         private int createJavadocHintCounter = CREATE_JAVADOC_HINT_LIMIT;
218         private int fixJavadocHintCounter = FIX_JAVADOC_HINT_LIMIT;
219         
220         Analyzer(CompilationInfo javac, Document JavaDoc doc) {
221             this.javac = javac;
222             this.doc = doc;
223             this.spec = resolveSourceVersion(javac.getFileObject());
224         }
225         
226         @Override JavaDoc
227         public Void JavaDoc visitCompilationUnit(CompilationUnitTree node, List JavaDoc<ErrorDescription> arg1) {
228             return scan(node.getTypeDecls(), arg1);
229         }
230
231         @Override JavaDoc
232         public Void JavaDoc visitClass(ClassTree node, List JavaDoc<ErrorDescription> arg1) {
233             if (access.isAccesible(node.getModifiers().getFlags())) {
234                 processNode(node, arg1);
235                 // scan enclosed members
236
return scan(node.getMembers(), arg1);
237             }
238             return null;
239         }
240         
241         @Override JavaDoc
242         public Void JavaDoc visitMethod(MethodTree node, List JavaDoc<ErrorDescription> arg1) {
243             Tree clazz = getCurrentPath().getParentPath().getLeaf();
244             if (clazz.getKind() == Tree.Kind.CLASS &&
245                     (javac.getTreeUtilities().isInterface((ClassTree) clazz) ||
246                     javac.getTreeUtilities().isAnnotation((ClassTree) clazz) ||
247                     access.isAccesible(node.getModifiers().getFlags()))) {
248                 processNode(node, arg1);
249             }
250             return null;
251         }
252         
253         @Override JavaDoc
254         public Void JavaDoc visitVariable(VariableTree node, List JavaDoc<ErrorDescription> arg1) {
255             if (access.isAccesible(node.getModifiers().getFlags())) {
256                 processNode(node, arg1);
257             }
258             return null;
259         }
260
261         @Override JavaDoc
262         public Void JavaDoc visitErroneous(ErroneousTree node,
263                                    List JavaDoc<ErrorDescription> p) {
264             // ignore error nodes
265
return null;
266         }
267
268         void processNode(Tree node, List JavaDoc<ErrorDescription> errors) {
269             checkTaskState();
270             
271             if (javac.getTreeUtilities().isSynthetic(getCurrentPath())) {
272                 return;
273             }
274             // check javadoc
275
Element elm = javac.getTrees().getElement(getCurrentPath());
276             
277             if (elm == null) {
278                 Logger.getLogger(JavadocHintProvider.class.getName()).log(
279                         Level.INFO, "Cannot resolve element for " + node + " in " + file); // NOI18N
280
return;
281             } else if (isGuarded(node)) {
282                 return;
283             }
284             
285             String JavaDoc jdText = javac.getElements().getDocComment(elm);
286             // create hint descriptor + prepare javadoc
287
if (jdText == null) {
288                 if (createJavadocHintCounter <= 0 || JavadocUtilities.hasInheritedDoc(javac, elm)) {
289                     return;
290                 }
291                 
292                 try {
293                     Position JavaDoc[] positions = createSignaturePositions(node);
294                     ErrorDescription err = ErrorDescriptionFactory.createErrorDescription(
295                             hintSeverity,
296                             NbBundle.getMessage(JavadocHintProvider.class, "MISSING_JAVADOC_DESC"), // NOI18N
297
createGenerateFixes(elm),
298                             doc,
299                             positions[0],
300                             positions[1]);
301                     errors.add(err);
302                     if (--createJavadocHintCounter <= 0) {
303                         ErrorDescription warning = ErrorDescriptionFactory.createErrorDescription(
304                                 Severity.WARNING,
305                                 NbBundle.getMessage(JavadocHintProvider.class, "OUT_OF_MISSING_JD_LIMIT_DESC", CREATE_JAVADOC_HINT_LIMIT), // NOI18N
306
doc,
307                                 positions[0],
308                                 positions[1]);
309                         errors.add(warning);
310                     }
311                 } catch (BadLocationException JavaDoc ex) {
312                     Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
313                 }
314             } else {
315                 if (fixJavadocHintCounter <= 0) {
316                     return;
317                 }
318                 
319                 try {
320                     Doc jDoc = javac.getElementUtilities().javaDocFor(elm);
321                     if (jDoc.isMethod() || jDoc.isConstructor()) {
322                         ExecutableMemberDoc methDoc = (ExecutableMemberDoc) jDoc;
323                         ExecutableElement methodEl = (ExecutableElement) elm;
324                         MethodTree methodTree = (MethodTree) node;
325                         processParameters(methodEl, methodTree, methDoc, errors);
326                         processReturn(methodEl, methodTree, methDoc, errors);
327                         processThrows(methodEl, methodTree, methDoc, errors);
328                     } else if(jDoc.isClass() || jDoc.isInterface()) {
329                         TypeElement classEl = (TypeElement) elm;
330                         ClassDoc classDoc = (ClassDoc) jDoc;
331                         ClassTree classTree = (ClassTree) node;
332                         processTypeParameters(classEl, classTree, classDoc, errors);
333                     } else if (jDoc.isAnnotationType()) {
334                         processAnnTypeParameters(elm, node, jDoc, errors);
335                     } else if (jDoc.isAnnotationTypeElement()) {
336                         AnnotationTypeElementDoc annDoc = (AnnotationTypeElementDoc) jDoc;
337                         ExecutableElement methodEl = (ExecutableElement) elm;
338                         MethodTree methodTree = (MethodTree) node;
339                         processAnnTypeParameters(methodEl, methodTree, annDoc, errors);
340                         processReturn(methodEl, methodTree, annDoc, errors);
341                         processAnnTypeThrows(methodEl, methodTree, annDoc, errors);
342                     }
343
344                     processDeprecatedAnnotation(elm, jDoc, errors);
345                 } catch (OutOfLimitException ex) {
346                     // fixJavadocHintCounter run out, next jd errors will be ignored
347
}
348                 
349             }
350         }
351         
352         private void processDeprecatedAnnotation(Element elm, Doc jDoc, List JavaDoc<ErrorDescription> errors) {
353             if (SourceVersion.RELEASE_5.compareTo(spec) > 0) {
354                 // jdks older than 1.5 do not support annotations
355
return;
356             }
357             
358             Tag[] deprTags = jDoc.tags("@deprecated"); // NOI18N
359
AnnotationMirror annMirror = JavadocUtilities.findDeprecated(javac, elm);
360             
361             if (annMirror != null) {
362                 // is deprecated
363
if (deprTags.length == 0) {
364                     // missing tag
365
try {
366                         Position JavaDoc[] poss = createPositions(javac.getTrees().getTree(elm, annMirror));
367                         ErrorDescription err = ErrorDescriptionFactory.createErrorDescription(
368                                 hintSeverity,
369                                 NbBundle.getMessage(JavadocHintProvider.class, "MISSING_DEPRECATED_DESC"), // NOI18N
370
Collections.<Fix>singletonList(AddTagFix.createAddDeprecatedTagFix(elm, file, spec)),
371                                 doc, poss[0], poss[1]);
372                         addTagHint(errors, err);
373                     } catch (BadLocationException JavaDoc ex) {
374                         Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
375                     }
376                 } else if (deprTags.length > 1) {
377                     // duplicates
378
boolean isFirst = true;
379                     for (Tag tag : deprTags) {
380                         if (isFirst) {
381                             isFirst = false;
382                             continue;
383                         }
384                         addRemoveTagFix(tag,
385                                 NbBundle.getMessage(JavadocHintProvider.class, "DUPLICATE_DEPRECATED_DESC"), // NOI18N
386
elm, errors);
387                     }
388                 }
389             } else {
390                 // not annotated
391
if (deprTags.length > 1) {
392                     // duplicates
393
boolean isFirst = true;
394                     for (Tag tag : deprTags) {
395                         if (isFirst) {
396                             isFirst = false;
397                             continue;
398                         }
399                         addRemoveTagFix(tag,
400                                 NbBundle.getMessage(JavadocHintProvider.class, "DUPLICATE_DEPRECATED_DESC"), // NOI18N
401
elm, errors);
402                     }
403                 }
404                 if (deprTags.length > 0) {
405                     // XXX ignore for now; we could offer to annotate the element if @deprecate tag exists
406
// or remove tag
407
}
408             }
409         }
410         
411         private void processReturn(ExecutableElement exec, MethodTree node, ExecutableMemberDoc jdoc, List JavaDoc<ErrorDescription> errors) {
412             final TypeMirror returnType = exec.getReturnType();
413             final Tree returnTree = node.getReturnType();
414             final Tag[] tags = jdoc.tags("@return"); // NOI18N
415

416             if (returnType.getKind() == TypeKind.VOID) {
417                 // void has @return
418
for (int i = 0; i < tags.length; i++) {
419                     Tag tag = tags[i];
420                     addRemoveTagFix(tag,
421                             NbBundle.getMessage(JavadocHintProvider.class,
422                                 jdoc.isMethod()? "WRONG_RETURN_DESC": "WRONG_CONSTRUCTOR_RETURN_DESC"), // NOI18N
423
exec, errors);
424                 }
425             } else {
426                 for (int i = 0; i < tags.length; i++) {
427                     // check duplicate @return
428
Tag tag = tags[i];
429                     if (i > 0) {
430                         addRemoveTagFix(tag,
431                                 NbBundle.getMessage(JavadocHintProvider.class, "DUPLICATE_RETURN_DESC"), // NOI18N
432
exec, errors);
433                     }
434                 }
435             }
436             
437             if (returnType.getKind() != TypeKind.VOID && tags.length == 0 &&
438                     JavadocUtilities.findReturnTag(javac, (MethodDoc) jdoc, true) == null) {
439                 // missing @return
440
try {
441                     Position JavaDoc[] poss = createPositions(returnTree);
442                     ErrorDescription err = ErrorDescriptionFactory.createErrorDescription(
443                             hintSeverity,
444                             NbBundle.getMessage(JavadocHintProvider.class, "MISSING_RETURN_DESC"), // NOI18N
445
Collections.<Fix>singletonList(AddTagFix.createAddReturnTagFix(exec, file, spec)),
446                             doc, poss[0], poss[1]);
447                     addTagHint(errors, err);
448                 } catch (BadLocationException JavaDoc ex) {
449                     Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
450                 }
451             }
452
453         }
454         
455         private void processThrows(ExecutableElement exec, MethodTree node, ExecutableMemberDoc jdoc, List JavaDoc<ErrorDescription> errors) {
456             final List JavaDoc<? extends ExpressionTree> throwz = node.getThrows();
457             final ThrowsTag[] tags = jdoc.throwsTags();
458             
459             Map JavaDoc<String JavaDoc, ThrowsTag> tagNames = new HashMap JavaDoc<String JavaDoc, ThrowsTag>();
460             for (ThrowsTag throwsTag : tags) {
461                 com.sun.javadoc.Type tagType = throwsTag.exceptionType();
462                 String JavaDoc tagFQN = null;
463                 if (tagType != null) { // unresolvable type
464
tagFQN = throwsTag.exceptionType().qualifiedTypeName();
465                 } else {
466                     tagFQN = throwsTag.exceptionName();
467                 }
468                 if (tagNames.containsKey(tagFQN)) {
469                     // duplicate throws error
470
addRemoveTagFix(throwsTag,
471                             NbBundle.getMessage(JavadocHintProvider.class, "DUPLICATE_THROWS_DESC", throwsTag.name(), throwsTag.exceptionName()), // NOI18N
472
exec, errors);
473                 } else {
474                     tagNames.put(tagFQN, throwsTag);
475                 }
476             }
477             
478             // resolve existing and missing tags
479
int index = 0;
480             for (ExpressionTree throwTree : throwz) {
481                 ++index;
482                 TreePath path = new TreePath(getCurrentPath(), throwTree);
483                 Element el = javac.getTrees().getElement(path);
484                 TypeElement tel = (TypeElement) el;
485                 boolean exists = tagNames.remove(tel.getQualifiedName().toString()) != null;
486                 if (!exists && (jdoc.isConstructor() ||
487                         jdoc.isMethod() &&
488                         JavadocUtilities.findThrowsTag(javac, (MethodDoc) jdoc, tel.getQualifiedName().toString(), true) == null)) {
489                     // missing @throws
490
try {
491                         Position JavaDoc[] poss = createPositions(throwTree);
492                         ErrorDescription err = ErrorDescriptionFactory.createErrorDescription(
493                                 hintSeverity,
494                                 NbBundle.getMessage(JavadocHintProvider.class, "MISSING_THROWS_DESC", tel.getQualifiedName().toString()), // NOI18N
495
Collections.<Fix>singletonList(AddTagFix.createAddThrowsTagFix(exec, tel.getQualifiedName().toString(), index, file, spec)),
496                                 doc, poss[0], poss[1]);
497                         addTagHint(errors, err);
498                     } catch (BadLocationException JavaDoc ex) {
499                         Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
500                     }
501                 }
502             }
503             
504             TypeMirror rtException = javac.getElements().getTypeElement("java.lang.RuntimeException").asType(); // NOI18N
505

506             // resolve leftovers
507
for (ThrowsTag throwsTag : tagNames.values()) {
508                 // redundant @throws
509
com.sun.javadoc.Type throwsType = throwsTag.exceptionType();
510                 Doc throwClassDoc = null;
511                 if (throwsType != null) {
512                     throwClassDoc = throwsType.asClassDoc();
513                 }
514                 if (throwClassDoc != null) {
515                     Element throwEl = javac.getElementUtilities().elementFor(throwClassDoc);
516                     if (throwEl != null && javac.getTypes().isSubtype(throwEl.asType(), rtException)) {
517                         // ignore RuntimeExceptions
518
break;
519                     }
520                 }
521                 addRemoveTagFix(throwsTag,
522                         NbBundle.getMessage(JavadocHintProvider.class, "UNKNOWN_THROWABLE_DESC", throwsTag.name(), throwsTag.exceptionName()), // NOI18N
523
exec, errors);
524             }
525
526         }
527         
528         private void processAnnTypeThrows(ExecutableElement exec, MethodTree node, AnnotationTypeElementDoc jdoc, List JavaDoc<ErrorDescription> errors) {
529             // this surprisingly gets both @throws and @exception tags
530
Tag[] tags = jdoc.tags("@throws"); //NOI18N
531

532             for (Tag tag : tags) {
533                 // annotation type element cannot contain throwables
534
ThrowsTag throwsTag = (ThrowsTag) tag;
535                 addRemoveTagFix(throwsTag,
536                         NbBundle.getMessage(JavadocHintProvider.class, "ILLEGAL_ANNOTATION_TYPE_THROWS_DESC", // NOI18N
537
throwsTag.name(),
538                         throwsTag.exceptionName()),
539                         exec, errors);
540             }
541         }
542         
543         private void processAnnTypeParameters(Element elm, Tree node, Doc jdoc, List JavaDoc<ErrorDescription> errors) {
544             final Tag[] tags = jdoc.tags("@param"); //NOI18N
545

546             for (Tag tag : tags) {
547                 // annotation type element cannot contain params
548
ParamTag paramTag = (ParamTag) tag;
549                 addRemoveTagFix(paramTag,
550                         NbBundle.getMessage(JavadocHintProvider.class, "ILLEGAL_ANNOTATION_TYPE_PARAM_DESC", paramTag.parameterName()), // NOI18N
551
elm, errors);
552             }
553         }
554         
555         private void processParameters(ExecutableElement exec, MethodTree node, ExecutableMemberDoc jdoc, List JavaDoc<ErrorDescription> errors) {
556             final List JavaDoc<? extends VariableTree> params = node.getParameters();
557 // final ParamTag[] tags = doc.paramTags();
558
final Tag[] tags = jdoc.tags("@param"); //NOI18N
559

560             Map JavaDoc<String JavaDoc, ParamTag> tagNames = new HashMap JavaDoc<String JavaDoc, ParamTag>();
561             // create param tag names set and reveal duplicates
562
for (Tag tag : tags) {
563                 ParamTag paramTag = (ParamTag) tag;
564                 if (paramTag.isTypeParameter()) {
565                     // javadoc does not support type parameters of methods yet
566
// and isTypeParameter does not seem to be working. Let's
567
// work around this as leftover params below.
568
continue;
569                 }
570                 
571                 if (tagNames.containsKey(paramTag.parameterName())) {
572                     // duplicate @param error
573
addRemoveTagFix(paramTag,
574                             NbBundle.getMessage(JavadocHintProvider.class, "DUPLICATE_PARAM_DESC", paramTag.parameterName()), // NOI18N
575
exec, errors);
576                 } else {
577                     tagNames.put(paramTag.parameterName(), paramTag);
578                 }
579             }
580             
581             // resolve existing and missing tags
582
for (VariableTree param : params) {
583                 boolean exists = tagNames.remove(param.getName().toString()) != null;
584                 if (!exists && (jdoc.isConstructor() ||
585                         jdoc.isMethod() &&
586                         JavadocUtilities.findParamTag(javac, (MethodDoc) jdoc, param.getName().toString(), true) == null)) {
587                     // missing @param
588
try {
589                         Position JavaDoc[] poss = createPositions(param);
590                         ErrorDescription err = ErrorDescriptionFactory.createErrorDescription(
591                                 hintSeverity,
592                                 NbBundle.getMessage(JavadocHintProvider.class, "MISSING_PARAM_DESC", param.getName()), // NOI18N
593
Collections.<Fix>singletonList(AddTagFix.createAddParamTagFix(exec, param.getName().toString(), file, spec)),
594                                 doc, poss[0], poss[1]);
595                         addTagHint(errors, err);
596                     } catch (BadLocationException JavaDoc ex) {
597                         Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
598                     }
599                 }
600             }
601             
602             // resolve leftovers
603
for (ParamTag paramTag : tagNames.values()) {
604                 // XXX workaround: check if not type param
605
boolean isTypeParam = false;
606                 for (TypeParameterElement typeParameterElement : exec.getTypeParameters()) {
607                     if (paramTag.parameterName().equals(typeParameterElement.getSimpleName().toString())) {
608                         isTypeParam = true;
609                         break;
610                     }
611                 }
612                 if (isTypeParam) {
613                     continue;
614                 }
615                 // end of workaround
616

617                 // redundant @param
618
addRemoveTagFix(paramTag,
619                         NbBundle.getMessage(JavadocHintProvider.class, "UNKNOWN_PARAM_DESC", paramTag.parameterName()), // NOI18N
620
exec, errors);
621             }
622
623         }
624         
625         private void processTypeParameters(TypeElement elm, ClassTree node, ClassDoc jdoc, List JavaDoc<ErrorDescription> errors) {
626             final List JavaDoc<? extends TypeParameterTree> params = node.getTypeParameters();
627 // final ParamTag[] tags = doc.typeParamTags();
628
final Tag[] tags = jdoc.tags("@param"); // NOI18N
629

630             Map JavaDoc<String JavaDoc, ParamTag> tagNames = new HashMap JavaDoc<String JavaDoc, ParamTag>();
631             // create param tag names set and reveal duplicates
632
for (Tag tag : tags) {
633                 ParamTag paramTag = (ParamTag) tag;
634                 if (tagNames.containsKey(paramTag.parameterName())) {
635                     // duplicate @param error
636
addRemoveTagFix(paramTag,
637                             NbBundle.getMessage(JavadocHintProvider.class, "DUPLICATE_TYPEPARAM_DESC", paramTag.parameterName()), // NOI18N
638
elm, errors);
639                 } else {
640                     tagNames.put(paramTag.parameterName(), paramTag);
641                 }
642             }
643             
644             // resolve existing and missing tags
645
for (TypeParameterTree param : params) {
646                 boolean exists = tagNames.remove(param.getName().toString()) != null;
647                 if (!exists /*&& doc.isMethod() &&
648                         JavadocUtilities.findParamTag((MethodDoc) doc, param.getName().toString(), true) == null*/
) {
649                     // missing @param
650
try {
651                         Position JavaDoc[] poss = createPositions(param);
652                         ErrorDescription err = ErrorDescriptionFactory.createErrorDescription(
653                                 hintSeverity,
654                                 NbBundle.getMessage(JavadocHintProvider.class, "MISSING_TYPEPARAM_DESC", param.getName()), // NOI18N
655
Collections.<Fix>singletonList(AddTagFix.createAddTypeParamTagFix(elm, param.getName().toString(), file, spec)),
656                                 doc, poss[0], poss[1]);
657                         addTagHint(errors, err);
658                     } catch (BadLocationException JavaDoc ex) {
659                         Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
660                     }
661                 }
662             }
663             
664             // resolve leftovers
665
for (ParamTag paramTag : tagNames.values()) {
666                 // redundant @param
667
addRemoveTagFix(paramTag,
668                         NbBundle.getMessage(JavadocHintProvider.class, "UNKNOWN_TYPEPARAM_DESC", paramTag.parameterName()), // NOI18N
669
elm, errors);
670             }
671         }
672         
673         Position JavaDoc[] createPositions(Tree t) throws BadLocationException JavaDoc {
674             final Position JavaDoc[] poss = new Position JavaDoc[2];
675             final int start = (int) javac.getTrees().getSourcePositions().
676                     getStartPosition(javac.getCompilationUnit(), t);
677             final int end = (int) javac.getTrees().getSourcePositions().
678                     getEndPosition(javac.getCompilationUnit(), t);
679             
680             // XXX needs document lock?
681
poss[0] = doc.createPosition(start);
682             poss[1] = doc.createPosition(end);
683             return poss;
684         }
685         
686         /**
687          * creates start and end positions of the tree
688          */

689         Position JavaDoc[] createSignaturePositions(final Tree t) throws BadLocationException JavaDoc {
690             final Position JavaDoc[] pos = new Position JavaDoc[2];
691             final BadLocationException JavaDoc[] blex = new BadLocationException JavaDoc[1];
692             NbDocument.runAtomic((StyledDocument JavaDoc) doc, new Runnable JavaDoc() {
693                 public void run() {
694                     try {
695                         TokenSequence<JavaTokenId> tseq = null;
696                         if (t.getKind() == Tree.Kind.METHOD) { // method + constructor
697
tseq = JavadocUtilities.findMethodNameToken(javac, (ClassTree) getCurrentPath().getParentPath().getLeaf(), (MethodTree) t);
698                         } else if (t.getKind() == Tree.Kind.CLASS) {
699                             tseq = JavadocUtilities.findClassNameToken(javac, (ClassTree) t);
700                         } else if (Tree.Kind.VARIABLE == t.getKind()) {
701                             tseq = JavadocUtilities.findVariableNameToken(javac, (VariableTree) t,
702                                     javac.getTreeUtilities().isEnum((ClassTree) getCurrentPath().getParentPath().getLeaf()));
703                         }
704                         
705                         if (tseq != null) {
706                             pos[0] = doc.createPosition(tseq.offset());
707                             pos[1] = doc.createPosition(tseq.offset() + tseq.token().length());
708                             return;
709                         }
710                         
711                         assert true: t.toString();
712                     } catch (BadLocationException JavaDoc ex) {
713                         blex[0] = ex;
714                     }
715                 }
716             });
717             if (blex[0] != null)
718                 throw (BadLocationException JavaDoc) new BadLocationException JavaDoc(blex[0].getMessage(), blex[0].offsetRequested()).initCause(blex[0]);
719             return pos;
720         }
721         
722         private boolean isGuarded(Tree node) {
723             GuardedSectionManager guards = GuardedSectionManager.getInstance((StyledDocument JavaDoc) doc);
724             if (guards != null) {
725                 try {
726                     final int startOff = (int) javac.getTrees().getSourcePositions().
727                             getStartPosition(javac.getCompilationUnit(), node);
728                     final Position JavaDoc startPos = doc.createPosition(startOff);
729
730                     for (GuardedSection guard : guards.getGuardedSections()) {
731                         if (guard.contains(startPos, false)) {
732                             return true;
733                         }
734                     }
735                 } catch (BadLocationException JavaDoc ex) {
736                     Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
737                     // consider it as guarded
738
return true;
739                 }
740             }
741             return false;
742         }
743         
744         private void addRemoveTagFix(Tag tag, String JavaDoc description, Element elm, List JavaDoc<ErrorDescription> errors) {
745             try {
746                 Position JavaDoc[] poss = JavadocUtilities.findTagNameBounds(javac, doc, tag);
747                 if (poss == null) {
748                     throw new BadLocationException JavaDoc("no position for " + tag, -1); // NOI18N
749
}
750                 ErrorDescription err = ErrorDescriptionFactory.createErrorDescription(
751                         hintSeverity,
752                         description,
753                         Collections.<Fix>singletonList(new RemoveTagFix(tag.name(), TagHandle.create(tag), ElementHandle.create(elm), file, spec)),
754                         doc, poss[0], poss[1]);
755                 addTagHint(errors, err);
756             } catch (BadLocationException JavaDoc ex) {
757                 Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
758             }
759         }
760         
761         JavadocLazyFixList createGenerateFixes(Element elm) {
762             List JavaDoc<Fix> fixes = new ArrayList JavaDoc<Fix>(3);
763             ElementHandle handle = ElementHandle.create(elm);
764             
765             String JavaDoc description;
766             if (elm.getKind() == ElementKind.CONSTRUCTOR) {
767                 description = elm.getEnclosingElement().getSimpleName().toString();
768             } else {
769                 description = elm.getSimpleName().toString();
770             }
771             
772             JavadocLazyFixList fixList = new JavadocLazyFixList(fixes, fixAll);
773             
774             GenerateJavadocFix jdFix = new GenerateJavadocFix(description, handle, javac.getFileObject(), this.spec);
775             
776             fixes.add(jdFix);
777             fixAll.addFix(jdFix);
778             
779             // XXX add Inherit javadoc
780

781 // Fix fixInherit = new JavadocFix("Inherit javadoc");
782
// fixes.add(fixInherit);
783
// fixes.add(new JavadocFix("Create missing javadoc"));
784
// fixes.add(new JavadocFix("Fix all missing javadocs"));
785
return fixList;
786         }
787         
788         private void addTagHint(List JavaDoc<ErrorDescription> errors, ErrorDescription desc) {
789             errors.add(desc);
790             checkTaskState();
791             if (--fixJavadocHintCounter <= 0) {
792                 try {
793                     ErrorDescription warning = ErrorDescriptionFactory.createErrorDescription(
794                             Severity.WARNING,
795                             NbBundle.getMessage(JavadocHintProvider.class, "OUT_OF_TAG_FIXES_LIMIT_DESC", FIX_JAVADOC_HINT_LIMIT), // NOI18N
796
doc,
797                             desc.getRange().getBegin().getPosition(),
798                             desc.getRange().getEnd().getPosition());
799                     errors.add(warning);
800                 } catch (IOException JavaDoc ex) {
801                     Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.INFO, ex.getMessage(), ex);
802                 }
803                 throw new OutOfLimitException();
804             }
805         }
806         
807         private void checkTaskState() {
808             if (isCanceled()) {
809                 throw new CancelException();
810             }
811         }
812     }
813     
814     private static final class JavadocLazyFixList implements LazyFixList {
815         
816         private List JavaDoc<Fix> contexFixes;
817         private FixAll fixAll;
818         
819         public JavadocLazyFixList(List JavaDoc<Fix> contexFixes, FixAll fixAll) {
820             this.contexFixes = contexFixes;
821             this.fixAll = fixAll;
822         }
823         
824         public void addPropertyChangeListener(PropertyChangeListener JavaDoc l) {
825         }
826
827         public void removePropertyChangeListener(PropertyChangeListener JavaDoc l) {
828         }
829
830         public boolean probablyContainsFixes() {
831             return true;
832         }
833
834         public List JavaDoc<Fix> getFixes() {
835             if (fixAll.isReady()) {
836                 contexFixes.add(fixAll);
837             }
838             return contexFixes;
839         }
840
841         public boolean isComputed() {
842             return true;
843         }
844         
845     }
846     
847     private static final class FixAll implements Fix {
848         
849         private List JavaDoc<GenerateJavadocFix> allJavadocFixes = new ArrayList JavaDoc<GenerateJavadocFix>();
850         
851         public void addFix(GenerateJavadocFix f) {
852             allJavadocFixes.add(f);
853         }
854         
855         public boolean isReady() {
856             return allJavadocFixes.size() > 1;
857         }
858     
859         public String JavaDoc getText() {
860             return NbBundle.getMessage(JavadocHintProvider.class, "FIX_ALL_HINT"); // NOI18N
861
}
862
863         public ChangeInfo implement() {
864             for (GenerateJavadocFix javadocFix : allJavadocFixes) {
865                 javadocFix.implement(false);
866             }
867
868             return null;
869         }
870         
871     }
872
873     private static final class GenerateJavadocFix implements Fix {
874         private String JavaDoc name;
875         private final ElementHandle handle;
876         private final FileObject file;
877         private Position JavaDoc position;
878         private final SourceVersion spec;
879
880         GenerateJavadocFix(String JavaDoc name, ElementHandle handle, FileObject file, SourceVersion spec) {
881             this.name = name;
882             this.handle = handle;
883             this.file = file;
884             this.spec = spec;
885         }
886
887         public String JavaDoc getText() {
888             return NbBundle.getMessage(JavadocHintProvider.class, "MISSING_JAVADOC_HINT", name); // NOI18N
889
}
890
891         public ChangeInfo implement() {
892             return implement(true);
893         }
894         
895         public ChangeInfo implement(final boolean open) {
896             final String JavaDoc[] javadocForDocument = new String JavaDoc[1];
897             final Document JavaDoc[] docs = new Document JavaDoc[1];
898             JavaSource js = JavaSource.forFileObject(file);
899             try {
900                 js.runModificationTask(new CancellableTask<WorkingCopy>() {
901                     public void cancel() {
902                     }
903                     
904                     public void run(WorkingCopy wc) throws Exception JavaDoc {
905                         wc.toPhase(JavaSource.Phase.RESOLVED);
906                         Element elm = handle.resolve(wc);
907                         Tree t = null;
908                         if (elm != null) {
909                             t = SourceUtils.treeFor(wc, elm);
910                         }
911                         if (t != null) {
912                             JavadocGenerator gen = new JavadocGenerator(GenerateJavadocFix.this.spec);
913                             String JavaDoc javadocTxt = gen.generateComment(elm, wc);
914                             Comment javadoc = Comment.create(Comment.Style.JAVADOC, NOPOS, NOPOS, 0, javadocTxt);
915                             wc.getTreeMaker().addComment(t, javadoc, true);
916                             
917                             // XXX workaround until the generator start to do its job
918
javadocForDocument[0] = javadocTxt;
919                             docs[0] = wc.getDocument();
920                             if (docs[0] == null) {
921                                 return;
922                             }
923                             position = docs[0].createPosition((int) wc.getTrees().getSourcePositions().getStartPosition(wc.getCompilationUnit(), t));
924                         }
925                     }
926                     
927                 }).commit();
928                 
929             } catch (IOException JavaDoc ex) {
930                 Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
931             }
932             
933             // XXX follows workaround until the generator starts to do its job
934
try {
935                 if (docs[0] == null) {
936                     // nothing to do; TreeMaker did his job likely.
937
return null;
938                 }
939                 
940                 NbDocument.runAtomicAsUser((StyledDocument JavaDoc) docs[0], new Runnable JavaDoc() {
941                     public void run() {
942                         try {
943                             String JavaDoc tab = JavadocGenerator.guessIndentation(docs[0], position);
944                             String JavaDoc iJavadoc = JavadocGenerator.indentJavadoc(javadocForDocument[0], tab);
945                             docs[0].insertString(position.getOffset(), iJavadoc, null);
946                             // move the caret to proper position
947
// System.out.println("javadoc:'" + iJavadoc + '\'');
948
int offset = iJavadoc.indexOf("\n");
949 // System.out.println("offset1: " + offset);
950
offset = iJavadoc.indexOf("\n", offset + 1);
951 // System.out.println("offset2: " + offset);
952
offset = position.getOffset() + offset - iJavadoc.length();
953                             if (open) doOpen(file, offset);
954                         } catch (BadLocationException JavaDoc ex) {
955                             ex.printStackTrace();
956                         }
957                     }
958                 });
959             } catch (BadLocationException JavaDoc ex) {
960                 Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
961             }
962             return null;
963         }
964     }
965
966     private static final class RemoveTagFix implements Fix, CancellableTask<WorkingCopy> {
967         
968         private String JavaDoc tagName;
969         private final TagHandle tagHandle;
970         private final ElementHandle handle;
971         private final FileObject file;
972         private final SourceVersion spec;
973         
974         private Position JavaDoc[] tagBounds;
975         private Document JavaDoc doc;
976
977         RemoveTagFix(String JavaDoc tagName, TagHandle tagHandle, ElementHandle elmHandle, FileObject file, SourceVersion spec) {
978             this.tagName = tagName;
979             this.tagHandle = tagHandle;
980             this.handle = elmHandle;
981             this.file = file;
982             this.spec = spec;
983         }
984
985         public String JavaDoc getText() {
986             return NbBundle.getMessage(JavadocHintProvider.class, "REMOVE_TAG_HINT", tagName); // NOI18N
987
}
988
989         public ChangeInfo implement() {
990             return implement(true);
991         }
992         
993         private void removeTag(final CompilationInfo ci, Element elm) throws IOException JavaDoc, BadLocationException JavaDoc {
994             final Doc jdoc = ci.getElementUtilities().javaDocFor(elm);
995             if (jdoc != null) {
996                 final Tag tag = tagHandle.resolve(jdoc);
997                 if (tag == null) {
998                     return;
999                 }
1000                
1001                final Document JavaDoc doc = ci.getDocument();
1002                if (doc == null) {
1003                    return;
1004                }
1005                NbDocument.runAtomicAsUser((StyledDocument JavaDoc) doc, new Runnable JavaDoc() {
1006                    public void run() {
1007                        try {
1008                            tagBounds = JavadocUtilities.findTagBounds(ci, doc, tag);
1009                        } catch (BadLocationException JavaDoc ex) {
1010                            Logger.getLogger(JavadocHintProvider.class.getName()).
1011                                    log(Level.SEVERE, ex.getMessage(), ex);
1012                        }
1013                    }
1014                });
1015            }
1016        }
1017        
1018        private void removeTag() throws BadLocationException JavaDoc {
1019            if (tagBounds == null || doc == null) {
1020                return;
1021            }
1022            NbDocument.runAtomicAsUser((StyledDocument JavaDoc) doc, new Runnable JavaDoc() {
1023                public void run() {
1024                    try {
1025                        doc.remove(tagBounds[0].getOffset(), tagBounds[1].getOffset() - tagBounds[0].getOffset());
1026                    } catch (BadLocationException JavaDoc ex) {
1027                        Logger.getLogger(JavadocHintProvider.class.getName()).
1028                                log(Level.SEVERE, ex.getMessage(), ex);
1029                    }
1030                }
1031            });
1032        }
1033        
1034        public ChangeInfo implement(final boolean open) {
1035            JavaSource js = JavaSource.forFileObject(file);
1036            try {
1037                js.runModificationTask(this).commit();
1038                // XXX follows workaround until the generator starts to do its job
1039
removeTag();
1040            } catch (BadLocationException JavaDoc ex) {
1041                Logger.getLogger(JavadocHintProvider.class.getName()).
1042                        log(Level.SEVERE, ex.getMessage(), ex);
1043            } catch (IOException JavaDoc ex) {
1044                Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
1045            }
1046            
1047            return null;
1048        }
1049        
1050        public void cancel() {
1051        }
1052
1053        public void run(WorkingCopy wc) throws Exception JavaDoc {
1054            wc.toPhase(JavaSource.Phase.RESOLVED);
1055            Element elm = handle.resolve(wc);
1056            Tree t = null;
1057            if (elm != null) {
1058                t = SourceUtils.treeFor(wc, elm);
1059            }
1060            if (t != null) {
1061                removeTag(wc, elm);
1062                doc = wc.getDocument();
1063            }
1064        }
1065        
1066    }
1067    
1068    private static final class AddTagFix implements Fix, CancellableTask<WorkingCopy> {
1069        
1070        private enum Kind {PARAM, RETURN, THROWS, TYPEPARAM, DEPRECATED}
1071        private final ElementHandle methodHandle;
1072        private final String JavaDoc paramName;
1073        /** index of throwable in throwables list */
1074        private final int index;
1075        private final FileObject file;
1076        private final SourceVersion spec;
1077        private final String JavaDoc descKey;
1078        private final Kind kind;
1079        
1080        private Position JavaDoc insertPosition;
1081        String JavaDoc insertJavadoc;
1082        private int openOffset;
1083        Document JavaDoc doc;
1084    
1085        private AddTagFix(ElementHandle methodHandle, String JavaDoc paramName, int index,
1086                FileObject file, SourceVersion spec, String JavaDoc descKey, Kind tagKind) {
1087            this.methodHandle = methodHandle;
1088            this.paramName = paramName;
1089            this.index = index;
1090            this.file = file;
1091            this.spec = spec;
1092            this.descKey = descKey;
1093            this.kind = tagKind;
1094        }
1095        
1096        public static Fix createAddParamTagFix(ExecutableElement elm,
1097                String JavaDoc paramName, FileObject file, SourceVersion spec) {
1098            return new AddTagFix(ElementHandle.create(elm), paramName, -1, file, spec, "MISSING_PARAM_HINT", Kind.PARAM); // NOI18N
1099
}
1100        
1101        public static Fix createAddTypeParamTagFix(TypeElement elm,
1102                String JavaDoc paramName, FileObject file, SourceVersion spec) {
1103            return new AddTagFix(ElementHandle.create(elm), paramName, -1, file, spec, "MISSING_TYPEPARAM_HINT", Kind.TYPEPARAM); // NOI18N
1104
}
1105        
1106        public static Fix createAddReturnTagFix(ExecutableElement elm,
1107                FileObject file, SourceVersion spec) {
1108            return new AddTagFix(ElementHandle.create(elm), "", -1, file, spec, "MISSING_RETURN_HINT", Kind.RETURN); // NOI18N
1109
}
1110        
1111        public static Fix createAddThrowsTagFix(ExecutableElement elm,
1112                String JavaDoc fqn, int throwIndex, FileObject file, SourceVersion spec) {
1113            return new AddTagFix(ElementHandle.create(elm), fqn, throwIndex, file, spec, "MISSING_THROWS_HINT", Kind.THROWS); // NOI18N
1114
}
1115        
1116        public static Fix createAddDeprecatedTagFix(Element elm,
1117                FileObject file, SourceVersion spec) {
1118            return new AddTagFix(ElementHandle.create(elm), "", -1, file, spec, "MISSING_DEPRECATED_HINT", Kind.DEPRECATED); // NOI18N
1119
}
1120
1121        public String JavaDoc getText() {
1122            return NbBundle.getMessage(JavadocHintProvider.class, descKey, this.paramName);
1123        }
1124
1125        public ChangeInfo implement() {
1126            JavaSource js = JavaSource.forFileObject(file);
1127            try {
1128                js.runModificationTask(this).commit();
1129                if (doc == null || insertPosition == null || insertJavadoc == null) {
1130                    return null;
1131                }
1132                int open = insertPosition.getOffset() + openOffset;
1133                insertJavadoc();
1134                doOpen(file, open);
1135            } catch (BadLocationException JavaDoc ex) {
1136                Logger.getLogger(JavadocHintProvider.class.getName()).
1137                        log(Level.SEVERE, ex.getMessage(), ex);
1138            } catch (IOException JavaDoc ex) {
1139                Logger.getLogger(JavadocHintProvider.class.getName()).
1140                        log(Level.SEVERE, ex.getMessage(), ex);
1141            }
1142            return null;
1143        }
1144
1145        public void run(final WorkingCopy wc) throws Exception JavaDoc {
1146            wc.toPhase(JavaSource.Phase.RESOLVED);
1147            final Element elm = methodHandle.resolve(wc);
1148            if (elm == null) {
1149                return;
1150            }
1151            
1152            final Doc jdoc = wc.getElementUtilities().javaDocFor(elm);
1153            doc = wc.getDocument();
1154            if (doc == null) {
1155                return;
1156            }
1157            
1158            NbDocument.runAtomicAsUser((StyledDocument JavaDoc) doc, new Runnable JavaDoc() {
1159                public void run() {
1160                    try {
1161                        computeInsertPositionAndJavadoc(wc, elm, jdoc);
1162                    } catch (BadLocationException JavaDoc ex) {
1163                        Logger.getLogger(JavadocHintProvider.class.getName()).
1164                                log(Level.SEVERE, ex.getMessage(), ex);
1165                    }
1166                }
1167            });
1168        }
1169        
1170        private void computeInsertPositionAndJavadoc(CompilationInfo wc, Element elm, Doc jdoc) throws BadLocationException JavaDoc {
1171            // find position where to add
1172
boolean[] isLastTag = new boolean[1];
1173            switch (this.kind) {
1174                case PARAM:
1175                    insertPosition = getParamInsertPosition(wc, doc, (ExecutableElement) elm, jdoc, isLastTag);
1176                    insertJavadoc = "@param " + paramName + " "; // NOI18N
1177
break;
1178                case TYPEPARAM:
1179                    insertPosition = getTypeParamInsertPosition(wc, doc, (TypeElement) elm, jdoc, isLastTag);
1180                    insertJavadoc = "@param " + paramName + " "; // NOI18N
1181
break;
1182                case RETURN:
1183                    insertPosition = getReturnInsertPosition(wc, doc, jdoc, isLastTag);
1184                    insertJavadoc = "@return "; // NOI18N
1185
break;
1186                case THROWS:
1187                    insertPosition = getThrowsInsertPosition(wc, doc, (ExecutableMemberDoc) jdoc, isLastTag);
1188                    insertJavadoc = "@throws " + paramName + " "; // NOI18N
1189
break;
1190                case DEPRECATED:
1191                    insertPosition = getDeprecatedInsertPosition(wc, doc, jdoc, isLastTag);
1192                    insertJavadoc = "@deprecated "; // NOI18N
1193
break;
1194                default:
1195                    throw new IllegalStateException JavaDoc();
1196            }
1197            
1198            // create tag string
1199
// resolve indentation
1200
// take start of javadoc and find /** and compute distance od \n and first *
1201
Position JavaDoc[] jdBounds = JavadocUtilities.findDocBounds(wc, doc, jdoc);
1202            int jdBeginLine = NbDocument.findLineNumber((StyledDocument JavaDoc) doc, jdBounds[0].getOffset());
1203            int jdEndLine = NbDocument.findLineNumber((StyledDocument JavaDoc) doc, jdBounds[1].getOffset());
1204            int insertLine = NbDocument.findLineNumber((StyledDocument JavaDoc) doc, insertPosition.getOffset());
1205            
1206            String JavaDoc indentation = JavadocGenerator.guessJavadocIndentation(wc, doc, jdoc); // NOI18N
1207
if (jdBeginLine == insertLine && insertLine == jdEndLine) {
1208                // one line javadoc
1209
insertJavadoc = '\n' + indentation + "* " + insertJavadoc; // NOI18N
1210
openOffset = insertJavadoc.length();
1211                insertJavadoc += '\n' + indentation;
1212            } else if (insertLine == jdEndLine && !isLastTag[0]) {
1213                // multiline javadoc but empty
1214
openOffset = 2 + insertJavadoc.length();
1215                insertJavadoc = "* " + insertJavadoc + '\n' + indentation; // NOI18N
1216
} else if (isLastTag[0]) {
1217                // insert after the last block tag
1218
insertJavadoc = '\n' + indentation + "* " + insertJavadoc; // NOI18N
1219
openOffset = insertJavadoc.length();
1220            } else {
1221                // insert before some block tag
1222
openOffset = insertJavadoc.length();
1223                insertJavadoc = insertJavadoc + '\n' + indentation + "* "; // NOI18N
1224
}
1225        }
1226        
1227        private void insertJavadoc() throws BadLocationException JavaDoc {
1228            NbDocument.runAtomicAsUser((StyledDocument JavaDoc) doc, new Runnable JavaDoc() {
1229                public void run() {
1230                    try {
1231                        // insert indented string to text
1232
doc.insertString(insertPosition.getOffset(), insertJavadoc, null);
1233                    } catch (BadLocationException JavaDoc ex) {
1234                        Logger.getLogger(JavadocHintProvider.class.getName()).
1235                                log(Level.SEVERE, ex.getMessage(), ex);
1236                    }
1237                }
1238            });
1239        }
1240
1241        public void cancel() {
1242        }
1243        
1244        private Position JavaDoc getDeprecatedInsertPosition(CompilationInfo wc, Document JavaDoc doc, Doc jdoc, boolean[] isLastTag) throws BadLocationException JavaDoc {
1245            // find last javadoc token position
1246
return getTagInsertPosition(wc, doc, jdoc, null, false, isLastTag);
1247        }
1248        
1249        private Position JavaDoc getTypeParamInsertPosition(CompilationInfo wc, Document JavaDoc doc, TypeElement elm, Doc jdoc, boolean[] isLastTag) throws BadLocationException JavaDoc {
1250                // 1. find @param tags + find index of param and try to apply on @param array
1251
Tag[] tags = jdoc.tags("@param"); // NOI18N
1252
Tag where = null;
1253            boolean insertBefore = true;
1254            if (tags.length > 0) {
1255                int index = findParamIndex(elm.getTypeParameters(), paramName);
1256                where = index < tags.length? tags[index]: tags[tags.length - 1];
1257                insertBefore = index < tags.length;
1258            } else {
1259                // 2. if not, find first tag + insert before
1260
tags = jdoc.tags();
1261                if (tags.length > 0) {
1262                    where = tags[0];
1263                }
1264            }
1265            return getTagInsertPosition(wc, doc, jdoc, where, insertBefore, isLastTag);
1266        }
1267        
1268        private Position JavaDoc getThrowsInsertPosition(CompilationInfo wc, Document JavaDoc doc, ExecutableMemberDoc jdoc, boolean[] isLastTag) throws BadLocationException JavaDoc {
1269                // 1. find @param tags + find index of param and try to apply on @param array
1270
Tag[] tags = jdoc.throwsTags(); // NOI18N
1271
// XXX filter type params?
1272
Tag where = null;
1273            boolean insertBefore = true;
1274            if (tags.length > 0) {
1275                int index = this.index;
1276                where = index < tags.length? tags[index]: tags[tags.length - 1];
1277                insertBefore = index < tags.length;
1278            } else {
1279                // 2. if not, find first tag + insert before
1280
tags = jdoc.tags("@return"); // NOI18N
1281
if (tags.length == 0) {
1282                    tags = jdoc.tags("@param"); // NOI18N
1283
}
1284                if (tags.length == 0) {
1285                    tags = jdoc.tags();
1286                } else {
1287                    // in case @return or @param
1288
insertBefore = false;
1289                }
1290                if (tags.length > 0) {
1291                    where = tags[0];
1292                }
1293            }
1294            return getTagInsertPosition(wc, doc, jdoc, where, insertBefore, isLastTag);
1295        }
1296        
1297        private Position JavaDoc getReturnInsertPosition(CompilationInfo wc, Document JavaDoc doc, Doc jdoc, boolean[] isLastTag) throws BadLocationException JavaDoc {
1298                // 1. find @param tags
1299
Tag[] tags = jdoc.tags("@param"); // NOI18N
1300
Tag where = null;
1301            boolean insertBefore = true;
1302            if (tags.length > 0) {
1303                where = tags[tags.length - 1];
1304                insertBefore = false;
1305            } else {
1306                // 2. if not, find first tag + insert before
1307
tags = jdoc.tags();
1308                if (tags.length > 0) {
1309                    where = tags[0];
1310                }
1311            }
1312            return getTagInsertPosition(wc, doc, jdoc, where, insertBefore, isLastTag);
1313        }
1314        
1315        private Position JavaDoc getParamInsertPosition(CompilationInfo wc, Document JavaDoc doc, ExecutableElement elm, Doc jdoc, boolean[] isLastTag) throws BadLocationException JavaDoc {
1316                // 1. find @param tags + find index of param and try to apply on @param array
1317
Tag[] tags = jdoc.tags("@param"); // NOI18N
1318
// XXX filter type params?
1319
Tag where = null;
1320            boolean insertBefore = true;
1321            if (tags.length > 0) {
1322                int index = findParamIndex(elm.getParameters(), paramName);
1323                where = index < tags.length? tags[index]: tags[tags.length - 1];
1324                insertBefore = index < tags.length;
1325            } else {
1326                // 2. if not, find first tag + insert before
1327
tags = jdoc.tags();
1328                if (tags.length > 0) {
1329                    where = tags[0];
1330                }
1331            }
1332            return getTagInsertPosition(wc, doc, jdoc, where, insertBefore, isLastTag);
1333        }
1334        
1335        private Position JavaDoc getTagInsertPosition(CompilationInfo wc, Document JavaDoc doc, Doc jdoc, Tag where, boolean insertBefore, boolean[] isLastTag) throws BadLocationException JavaDoc {
1336            // find insert position
1337
Position JavaDoc[] bounds = null;
1338            if (where != null) {
1339                bounds = JavadocUtilities.findTagBounds(wc, doc, where, isLastTag);
1340                if (insertBefore) {
1341                    isLastTag[0] = false;
1342                }
1343            } else {
1344                // 3. if not, insert at the last token; resolve \n and /***/ cases
1345
bounds = JavadocUtilities.findLastTokenBounds(wc, doc, jdoc);
1346                insertBefore = false;
1347                isLastTag[0] = false;
1348            }
1349            
1350            return insertBefore? bounds[0]: bounds[1];
1351        }
1352        
1353        private int findParamIndex(List JavaDoc<? extends Element> params, String JavaDoc name) {
1354            int i = 0;
1355            for (Element param : params) {
1356                if (name.contentEquals(param.getSimpleName())) {
1357                    return i;
1358                }
1359                i++;
1360            }
1361            throw new IllegalArgumentException JavaDoc("Unknown param: " + name); // NOI18N
1362
}
1363    }
1364    
1365    private static void doOpen(final FileObject fo, final int offset) {
1366        EventQueue.invokeLater(new Runnable JavaDoc() {
1367            public void run() {
1368                doOpenImpl(fo, offset);
1369            }
1370        });
1371    }
1372    
1373    private static boolean doOpenImpl(FileObject fo, int offset) {
1374        try {
1375            DataObject od = DataObject.find(fo);
1376            EditorCookie ec = (EditorCookie) od.getCookie(EditorCookie.class);
1377            LineCookie lc = (LineCookie) od.getCookie(LineCookie.class);
1378            
1379            if (ec != null && lc != null && offset != -1) {
1380                StyledDocument JavaDoc doc = ec.openDocument();
1381                if (doc != null) {
1382                    int line = NbDocument.findLineNumber(doc, offset);
1383                    int lineOffset = NbDocument.findLineOffset(doc, line);
1384                    int column = offset - lineOffset;
1385                    
1386                    if (line != -1) {
1387                        Line l = lc.getLineSet().getCurrent(line);
1388                        
1389                        if (l != null) {
1390                            l.show(Line.SHOW_GOTO, column);
1391                            return true;
1392                        }
1393                    }
1394                }
1395            }
1396        } catch (IOException JavaDoc ex) {
1397            Logger.getLogger(JavadocHintProvider.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
1398        }
1399        return false;
1400    }
1401    
1402    /**
1403     * according to http://java.sun.com/javase/6/docs/technotes/tools/solaris/javadoc.html#javadocoptions
1404     */

1405    private enum Access {
1406        PUBLIC, PROTECTED, PACKAGE, PRIVATE;
1407        
1408        /**
1409         * accept [public|protected|package|private], default(null or other) is protected
1410         */

1411        public static Access resolve(String JavaDoc s) {
1412            if (s != null) {
1413                s = s.trim().toLowerCase();
1414                if ("public".equals(s)) { // NOI18N
1415
return Access.PUBLIC;
1416                } else if ("protected".equals(s)) { // NOI18N
1417
return Access.PROTECTED;
1418                } else if ("private".equals(s)) { // NOI18N
1419
return Access.PRIVATE;
1420                } else if ("package".equals(s)) { // NOI18N
1421
return Access.PACKAGE;
1422                }
1423            }
1424            return Access.PROTECTED;
1425        }
1426        
1427        public boolean isAccesible(Set JavaDoc<Modifier> flags) {
1428            switch(this) {
1429                case PRIVATE:
1430                    return true;
1431                case PACKAGE:
1432                    return !flags.contains(Modifier.PRIVATE);
1433                case PROTECTED:
1434                    return flags.contains(Modifier.PUBLIC) || flags.contains(Modifier.PROTECTED);
1435                case PUBLIC:
1436                    return flags.contains(Modifier.PUBLIC);
1437                default:
1438                    throw new IllegalStateException JavaDoc();
1439            }
1440        }
1441    }
1442    /**
1443     * This is not regular exception! It allows to promtly cancel
1444     * javadoc hints computation on CancellableTask request.
1445     * NO STACK TRACE IS FILLED! performance reason
1446     */

1447    private static final class CancelException extends RuntimeException JavaDoc {
1448        @Override JavaDoc
1449        public synchronized Throwable JavaDoc fillInStackTrace() {
1450            return null;
1451        }
1452    }
1453    
1454    /**
1455     * This is not regular throwable! It allows to promtly cancel
1456     * javadoc add/remove tag hints computation on running
1457     * out of the FIX_JAVADOC_HINT_LIMIT.
1458     * NO STACK TRACE IS FILLED! performance reason
1459     */

1460    private static final class OutOfLimitException extends RuntimeException JavaDoc {
1461        @Override JavaDoc
1462        public synchronized Throwable JavaDoc fillInStackTrace() {
1463            return null;
1464        }
1465    }
1466}
1467
Popular Tags