2 * Copyright (c) 2005 Jet Brains. All Rights Reserved.
4 package com
.intellij
.codeInspection
.javaDoc
;
6 import com
.intellij
.ExtensionPoints
;
7 import com
.intellij
.codeInsight
.CodeInsightUtil
;
8 import com
.intellij
.codeInsight
.daemon
.HighlightDisplayKey
;
9 import com
.intellij
.codeInsight
.daemon
.QuickFixBundle
;
10 import com
.intellij
.codeInspection
.*;
11 import com
.intellij
.codeInspection
.ex
.BaseLocalInspectionTool
;
12 import com
.intellij
.codeInspection
.reference
.RefJavaUtil
;
13 import com
.intellij
.lang
.ASTNode
;
14 import com
.intellij
.openapi
.diagnostic
.Logger
;
15 import com
.intellij
.openapi
.editor
.Editor
;
16 import com
.intellij
.openapi
.editor
.ScrollType
;
17 import com
.intellij
.openapi
.extensions
.ExtensionPoint
;
18 import com
.intellij
.openapi
.extensions
.Extensions
;
19 import com
.intellij
.openapi
.fileEditor
.FileEditorManager
;
20 import com
.intellij
.openapi
.project
.Project
;
21 import com
.intellij
.openapi
.util
.*;
22 import com
.intellij
.profile
.codeInspection
.InspectionProfileManager
;
23 import com
.intellij
.profile
.codeInspection
.InspectionProjectProfileManager
;
24 import com
.intellij
.psi
.*;
25 import com
.intellij
.psi
.impl
.source
.javadoc
.PsiDocParamRef
;
26 import com
.intellij
.psi
.impl
.source
.jsp
.jspJava
.JspClass
;
27 import com
.intellij
.psi
.impl
.source
.jsp
.jspJava
.JspHolderMethod
;
28 import com
.intellij
.psi
.javadoc
.*;
29 import com
.intellij
.psi
.util
.InheritanceUtil
;
30 import com
.intellij
.psi
.util
.PsiTreeUtil
;
31 import com
.intellij
.ui
.DocumentAdapter
;
32 import com
.intellij
.ui
.FieldPanel
;
33 import com
.intellij
.ui
.IdeBorderFactory
;
34 import com
.intellij
.util
.IJSwingUtilities
;
35 import com
.intellij
.util
.IncorrectOperationException
;
36 import com
.intellij
.util
.ui
.UIUtil
;
37 import org
.jdom
.Element
;
38 import org
.jetbrains
.annotations
.NonNls
;
39 import org
.jetbrains
.annotations
.NotNull
;
40 import org
.jetbrains
.annotations
.Nullable
;
43 import javax
.swing
.event
.ChangeEvent
;
44 import javax
.swing
.event
.ChangeListener
;
45 import javax
.swing
.event
.DocumentEvent
;
46 import javax
.swing
.text
.BadLocationException
;
47 import javax
.swing
.text
.Document
;
49 import java
.awt
.event
.ActionEvent
;
50 import java
.awt
.event
.ActionListener
;
53 public class JavaDocLocalInspection
extends BaseLocalInspectionTool
{
54 private static final String REQUIRED_JAVADOC_IS_ABSENT
= InspectionsBundle
.message("inspection.javadoc.problem.descriptor");
56 @NonNls private static final String NONE
= "none";
57 @NonNls private static final String PUBLIC
= "public";
58 @NonNls private static final String PROTECTED
= "protected";
59 @NonNls private static final String PACKAGE_LOCAL
= "package";
60 @NonNls private static final String PRIVATE
= "private";
61 @NonNls private static final Set
<String
> ourUniqueTags
= new HashSet
<String
>();
62 @NonNls public static final String SHORT_NAME
= "JavaDoc";
65 ourUniqueTags
.add("return");
66 ourUniqueTags
.add("deprecated");
67 ourUniqueTags
.add("serial");
68 ourUniqueTags
.add("serialData");
72 public static class Options
implements JDOMExternalizable
{
73 @NonNls public String ACCESS_JAVADOC_REQUIRED_FOR
= NONE
;
74 @NonNls public String REQUIRED_TAGS
= "";
79 public Options(String ACCESS_JAVADOC_REQUIRED_FOR
, String REQUIRED_TAGS
) {
80 this.ACCESS_JAVADOC_REQUIRED_FOR
= ACCESS_JAVADOC_REQUIRED_FOR
;
81 this.REQUIRED_TAGS
= REQUIRED_TAGS
;
84 public void readExternal(Element element
) throws InvalidDataException
{
85 DefaultJDOMExternalizer
.readExternal(this, element
);
88 public void writeExternal(Element element
) throws WriteExternalException
{
89 DefaultJDOMExternalizer
.writeExternal(this, element
);
93 @NonNls public Options TOP_LEVEL_CLASS_OPTIONS
= new Options("none", "");
94 @NonNls public Options INNER_CLASS_OPTIONS
= new Options("none", "");
95 @NonNls public Options METHOD_OPTIONS
= new Options("none", "@return@param@throws or @exception");
96 @NonNls public Options FIELD_OPTIONS
= new Options("none", "");
97 public boolean IGNORE_DEPRECATED
= false;
98 public boolean IGNORE_JAVADOC_PERIOD
= true;
99 public boolean IGNORE_DUPLICATED_THROWS
= false;
100 public String myAdditionalJavadocTags
= "";
102 private static final Logger LOG
= Logger
.getInstance("com.intellij.codeInspection.javaDoc.JavaDocLocalInspection");
104 private class OptionsPanel
extends JPanel
{
105 private JPanel
createOptionsPanel(String
[] modifiers
, String
[] tags
, Options options
) {
106 JPanel pane
= new JPanel(new GridLayout(1, tags
== null ?
1 : 2));
108 pane
.add(createScopePanel(modifiers
, options
));
110 pane
.add(createTagsPanel(tags
, options
));
118 private JPanel
createTagsPanel(String
[] tags
, Options options
) {
119 JPanel panel
= new JPanel(new GridBagLayout());
120 panel
.setBorder(BorderFactory
.createCompoundBorder(IdeBorderFactory
.createTitledBorder(InspectionsBundle
.message("inspection.javadoc.required.tags.option.title")),
121 BorderFactory
.createEmptyBorder(0, 3, 3, 3)));
123 GridBagConstraints gc
= new GridBagConstraints();
126 gc
.fill
= GridBagConstraints
.HORIZONTAL
;
127 gc
.anchor
= GridBagConstraints
.NORTHWEST
;
130 for (int i
= 0; i
< tags
.length
; i
++) {
131 JCheckBox box
= new JCheckBox(tags
[i
]);
133 if (i
== tags
.length
- 1) gc
.weighty
= 1;
135 box
.setSelected(isTagRequired(options
, tags
[i
]));
136 box
.addChangeListener(new MyChangeListener(box
, options
, tags
[i
]));
142 private class MyChangeListener
implements ChangeListener
{
143 private final JCheckBox myCheckBox
;
144 private final Options myOptions
;
145 private final String myTagName
;
147 public MyChangeListener(JCheckBox checkBox
, Options options
, String tagName
) {
148 myCheckBox
= checkBox
;
153 public void stateChanged(ChangeEvent e
) {
154 if (myCheckBox
.isSelected()) {
155 if (!isTagRequired(myOptions
,myTagName
)) {
156 myOptions
.REQUIRED_TAGS
+= myTagName
;
160 myOptions
.REQUIRED_TAGS
= myOptions
.REQUIRED_TAGS
.replaceAll(myTagName
, "");
165 private JPanel
createScopePanel(final String
[] modifiers
, final Options options
) {
166 JPanel panel
= new JPanel(new BorderLayout());
167 panel
.setBorder(BorderFactory
.createCompoundBorder(IdeBorderFactory
.createTitledBorder(InspectionsBundle
.message("inspection.scope.for.title")),
168 BorderFactory
.createEmptyBorder(0, 3, 3, 3)));
170 final Hashtable
<Integer
, JLabel
> sliderLabels
= new Hashtable
<Integer
, JLabel
>();
171 for (int i
= 0; i
< modifiers
.length
; i
++) {
172 sliderLabels
.put(i
+ 1, new JLabel(modifiers
[i
]));
175 final JSlider slider
= new JSlider(SwingConstants
.VERTICAL
, 1, modifiers
.length
, 1);
177 slider
.setLabelTable(sliderLabels
);
178 slider
.putClientProperty(UIUtil
.JSLIDER_ISFILLED
, Boolean
.TRUE
);
179 slider
.setPreferredSize(new Dimension(80, 50));
180 slider
.setPaintLabels(true);
181 slider
.setSnapToTicks(true);
182 slider
.addChangeListener(new ChangeListener() {
183 public void stateChanged(ChangeEvent e
) {
184 int value
= slider
.getValue();
185 options
.ACCESS_JAVADOC_REQUIRED_FOR
= modifiers
[value
- 1];
186 for (Integer key
: sliderLabels
.keySet()) {
187 sliderLabels
.get(key
).setForeground(key
.intValue() <= value ? Color
.black
: new Color(100, 100, 100));
192 Color fore
= Color
.black
;
193 for (int i
= 0; i
< modifiers
.length
; i
++) {
194 sliderLabels
.get(i
+ 1).setForeground(fore
);
196 if (modifiers
[i
].equals(options
.ACCESS_JAVADOC_REQUIRED_FOR
)) {
197 slider
.setValue(i
+ 1);
198 fore
= new Color(100, 100, 100);
202 panel
.add(slider
, BorderLayout
.WEST
);
207 public OptionsPanel() {
208 super(new GridBagLayout());
209 GridBagConstraints gc
= new GridBagConstraints(0, GridBagConstraints
.RELATIVE
, 2, 1, 1, 1, GridBagConstraints
.NORTH
, GridBagConstraints
.BOTH
, new Insets(0,0,0,0),0,0 );
211 add(createAdditionalJavadocTagsPanel(), gc
);
212 JTabbedPane tabs
= new JTabbedPane(SwingConstants
.BOTTOM
);
213 @NonNls String
[] tags
= new String
[]{"@author", "@version", "@since", "@param"};
214 tabs
.add(InspectionsBundle
.message("inspection.javadoc.option.tab.title"), createOptionsPanel(new String
[]{NONE
, PUBLIC
, PACKAGE_LOCAL
},
216 TOP_LEVEL_CLASS_OPTIONS
));
217 tags
= new String
[]{"@return", "@param", InspectionsBundle
.message("inspection.javadoc.throws.or.exception.option")};
218 tabs
.add(InspectionsBundle
.message("inspection.javadoc.option.tab.title.method"), createOptionsPanel(new String
[]{NONE
, PUBLIC
, PROTECTED
, PACKAGE_LOCAL
, PRIVATE
},
221 tabs
.add(InspectionsBundle
.message("inspection.javadoc.option.tab.title.field"), createOptionsPanel(new String
[]{NONE
, PUBLIC
, PROTECTED
, PACKAGE_LOCAL
, PRIVATE
},
224 tabs
.add(InspectionsBundle
.message("inspection.javadoc.option.tab.title.inner.class"), createOptionsPanel(new String
[]{NONE
, PUBLIC
, PROTECTED
, PACKAGE_LOCAL
, PRIVATE
},
226 INNER_CLASS_OPTIONS
));
229 final JCheckBox checkBox
= new JCheckBox(InspectionsBundle
.message("inspection.javadoc.option.ignore.deprecated"),
231 checkBox
.addActionListener(new ActionListener() {
232 public void actionPerformed(ActionEvent e
) {
233 IGNORE_DEPRECATED
= checkBox
.isSelected();
238 final JCheckBox periodCheckBox
= new JCheckBox(InspectionsBundle
.message("inspection.javadoc.option.ignore.period"),
239 IGNORE_JAVADOC_PERIOD
);
240 periodCheckBox
.addActionListener(new ActionListener() {
241 public void actionPerformed(ActionEvent e
) {
242 IGNORE_JAVADOC_PERIOD
= periodCheckBox
.isSelected();
245 add(periodCheckBox
, gc
);
247 final JCheckBox ignoreDuplicateThrowsCheckBox
= new JCheckBox("Ignore duplicate throws tag",
248 IGNORE_DUPLICATED_THROWS
);
249 ignoreDuplicateThrowsCheckBox
.addActionListener(new ActionListener() {
250 public void actionPerformed(ActionEvent e
) {
251 IGNORE_DUPLICATED_THROWS
= ignoreDuplicateThrowsCheckBox
.isSelected();
254 add(ignoreDuplicateThrowsCheckBox
, gc
);
257 public FieldPanel
createAdditionalJavadocTagsPanel(){
258 FieldPanel additionalTagsPanel
= new FieldPanel(InspectionsBundle
.message("inspection.javadoc.label.text"), InspectionsBundle
.message("inspection.javadoc.dialog.title"), null, null);
259 additionalTagsPanel
.setPreferredSize(new Dimension(150, additionalTagsPanel
.getPreferredSize().height
));
260 additionalTagsPanel
.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
261 protected void textChanged(DocumentEvent e
) {
262 final Document document
= e
.getDocument();
264 final String text
= document
.getText(0, document
.getLength());
266 myAdditionalJavadocTags
= text
.trim();
269 catch (BadLocationException e1
) {
274 additionalTagsPanel
.setText(myAdditionalJavadocTags
);
275 return additionalTagsPanel
;
279 public JComponent
createOptionsPanel() {
280 return new OptionsPanel();
283 private static ProblemDescriptor
createDescriptor(@NotNull PsiElement element
, String template
, InspectionManager manager
,
285 return manager
.createProblemDescriptor(element
, template
, onTheFly
, (LocalQuickFix
[])null, ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
);
288 private static ProblemDescriptor
createDescriptor(@NotNull PsiElement element
, String template
, @NotNull LocalQuickFix fix
,
289 InspectionManager manager
, boolean onTheFly
) {
290 return manager
.createProblemDescriptor(element
, template
, fix
, ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
, onTheFly
);
293 private static class AddMissingTagFix
implements LocalQuickFix
{
294 private final String myTag
;
295 private final String myValue
;
297 public AddMissingTagFix(@NonNls String tag
, String value
) {
301 public AddMissingTagFix(String tag
) {
306 public String
getName() {
307 return InspectionsBundle
.message("inspection.javadoc.problem.add.tag", myTag
, myValue
);
310 public void applyFix(@NotNull Project project
, @NotNull ProblemDescriptor descriptor
) {
311 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(project
).getElementFactory();
313 final PsiDocCommentOwner owner
= PsiTreeUtil
.getParentOfType(descriptor
.getEndElement(), PsiDocCommentOwner
.class);
315 if (!CodeInsightUtil
.preparePsiElementsForWrite(owner
)) return;
316 final PsiDocComment docComment
= owner
.getDocComment();
317 final PsiDocTag tag
= factory
.createDocTagFromText("@" + myTag
+" "+myValue
, docComment
);
318 if (docComment
!= null) {
320 final PsiElement anchor
= getAnchor();
321 if (anchor
!= null) {
322 addedTag
= docComment
.addBefore(tag
, anchor
);
325 addedTag
= docComment
.add(tag
);
327 moveCaretTo(addedTag
);
331 catch (IncorrectOperationException e
) {
337 protected PsiElement
getAnchor() {
341 private static void moveCaretTo(final PsiElement newCaretPosition
) {
342 Project project
= newCaretPosition
.getProject();
343 final PsiFile psiFile
= newCaretPosition
.getContainingFile();
344 final Editor editor
= FileEditorManager
.getInstance(project
).getSelectedTextEditor();
345 if (editor
!= null && IJSwingUtilities
.hasFocus(editor
.getComponent())) {
346 final PsiFile file
= PsiDocumentManager
.getInstance(project
).getPsiFile(editor
.getDocument());
347 if (file
== psiFile
) {
348 editor
.getCaretModel().moveToOffset(newCaretPosition
.getTextRange().getEndOffset());
349 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
355 public String
getFamilyName() {
356 return InspectionsBundle
.message("inspection.javadoc.problem.add.tag.family");
360 public ProblemDescriptor
[] checkClass(@NotNull PsiClass psiClass
, @NotNull InspectionManager manager
, boolean isOnTheFly
) {
361 if (psiClass
instanceof PsiAnonymousClass
) return null;
362 if (psiClass
instanceof JspClass
) return null;
363 if (psiClass
instanceof PsiTypeParameter
) return null;
364 if (IGNORE_DEPRECATED
&& psiClass
.isDeprecated()) {
367 PsiDocComment docComment
= psiClass
.getDocComment();
368 final PsiIdentifier nameIdentifier
= psiClass
.getNameIdentifier();
369 final PsiElement elementToHighlight
= nameIdentifier
!= null ? nameIdentifier
: psiClass
;
370 if (docComment
== null) {
371 return isJavaDocRequired(psiClass
)
372 ?
new ProblemDescriptor
[]{createDescriptor(elementToHighlight
, REQUIRED_JAVADOC_IS_ABSENT
, manager
, isOnTheFly
)}
376 PsiDocTag
[] tags
= docComment
.getTags();
377 @NonNls String
[] tagsToCheck
= {"author", "version", "since"};
378 @NonNls String
[] absentDescriptionKeys
= {
379 "inspection.javadoc.problem.missing.author.description",
380 "inspection.javadoc.problem.missing.version.description",
381 "inspection.javadoc.problem.missing.since.description"};
383 boolean[] isTagRequired
= new boolean[tagsToCheck
.length
];
384 boolean[] isTagPresent
= new boolean[tagsToCheck
.length
];
386 boolean someTagsAreRequired
= false;
387 for (int i
= 0; i
< tagsToCheck
.length
; i
++) {
388 final String tag
= tagsToCheck
[i
];
389 someTagsAreRequired
|= isTagRequired
[i
] = isTagRequired(psiClass
, tag
);
392 if (someTagsAreRequired
) {
393 for (PsiDocTag tag
: tags
) {
394 String tagName
= tag
.getName();
395 for (int i
= 0; i
< tagsToCheck
.length
; i
++) {
396 final String tagToCheck
= tagsToCheck
[i
];
397 if (tagToCheck
.equals(tagName
)) {
398 isTagPresent
[i
] = true;
404 final ArrayList
<ProblemDescriptor
> problems
= new ArrayList
<ProblemDescriptor
>(2);
406 for (int i
= 0; i
< tagsToCheck
.length
; i
++) {
407 final String tagToCheck
= tagsToCheck
[i
];
408 if (isTagRequired
[i
] && !isTagPresent
[i
]) {
409 problems
.add(createMissingTagDescriptor(elementToHighlight
, tagToCheck
, manager
, isOnTheFly
));
412 ArrayList
<ProblemDescriptor
> tagProblems
= getTagValuesProblems(psiClass
, tags
, manager
, isOnTheFly
);
413 if (tagProblems
!= null) {
414 problems
.addAll(tagProblems
);
416 checkForPeriodInDoc(docComment
, problems
, manager
, isOnTheFly
);
417 checkInlineTags(manager
, problems
, docComment
.getDescriptionElements(),
418 JavaPsiFacade
.getInstance(docComment
.getProject()).getJavadocManager(), isOnTheFly
);
419 checkForBadCharacters(docComment
, problems
, manager
, isOnTheFly
);
420 for (PsiDocTag tag
: tags
) {
421 for (int i
= 0; i
< tagsToCheck
.length
; i
++) {
422 final String tagToCheck
= tagsToCheck
[i
];
423 if (tagToCheck
.equals(tag
.getName()) && extractTagDescription(tag
).length() == 0) {
424 problems
.add(createDescriptor(elementToHighlight
, InspectionsBundle
.message(absentDescriptionKeys
[i
]), manager
, isOnTheFly
));
429 checkDuplicateTags(tags
, problems
, manager
, isOnTheFly
);
431 if (isTagRequired(psiClass
, "param") && psiClass
.hasTypeParameters() && nameIdentifier
!= null) {
432 ArrayList
<PsiTypeParameter
> absentParameters
= null;
433 final PsiTypeParameter
[] typeParameters
= psiClass
.getTypeParameters();
434 for (PsiTypeParameter typeParameter
: typeParameters
) {
435 if (!isFound(tags
, typeParameter
)) {
436 if (absentParameters
== null) absentParameters
= new ArrayList
<PsiTypeParameter
>(1);
437 absentParameters
.add(typeParameter
);
440 if (absentParameters
!= null) {
441 for (PsiTypeParameter psiTypeParameter
: absentParameters
) {
442 problems
.add(createMissingParamTagDescriptor(nameIdentifier
, psiTypeParameter
, manager
, isOnTheFly
));
447 return problems
.isEmpty()
449 : problems
.toArray(new ProblemDescriptor
[problems
.size()]);
452 private static ProblemDescriptor
createMissingParamTagDescriptor(final PsiIdentifier nameIdentifier
,
453 final PsiTypeParameter psiTypeParameter
,
454 final InspectionManager manager
, boolean isOnTheFly
) {
455 String message
= InspectionsBundle
.message("inspection.javadoc.problem.missing.tag", "<code>@param</code>");
456 return createDescriptor(nameIdentifier
, message
, new AddMissingTagFix("param", "<" + psiTypeParameter
.getName() + ">"), manager
,
461 public ProblemDescriptor
[] checkField(@NotNull PsiField psiField
, @NotNull InspectionManager manager
, boolean isOnTheFly
) {
462 if (IGNORE_DEPRECATED
&& (psiField
.isDeprecated() || psiField
.getContainingClass().isDeprecated())) {
466 PsiDocComment docComment
= psiField
.getDocComment();
467 if (docComment
== null) {
468 return isJavaDocRequired(psiField
)
469 ?
new ProblemDescriptor
[]{createDescriptor(psiField
.getNameIdentifier(), REQUIRED_JAVADOC_IS_ABSENT
, manager
, isOnTheFly
)}
473 final ArrayList
<ProblemDescriptor
> problems
= new ArrayList
<ProblemDescriptor
>(2);
474 ArrayList
<ProblemDescriptor
> tagProblems
= getTagValuesProblems(psiField
, docComment
.getTags(), manager
, isOnTheFly
);
475 if (tagProblems
!= null) {
476 problems
.addAll(tagProblems
);
478 checkInlineTags(manager
, problems
, docComment
.getDescriptionElements(),
479 JavaPsiFacade
.getInstance(docComment
.getProject()).getJavadocManager(), isOnTheFly
);
480 checkForPeriodInDoc(docComment
, problems
, manager
, isOnTheFly
);
481 checkDuplicateTags(docComment
.getTags(), problems
, manager
, isOnTheFly
);
482 checkForBadCharacters(docComment
, problems
, manager
, isOnTheFly
);
483 return problems
.isEmpty()
485 : problems
.toArray(new ProblemDescriptor
[problems
.size()]);
489 public ProblemDescriptor
[] checkMethod(@NotNull PsiMethod psiMethod
, @NotNull InspectionManager manager
, boolean isOnTheFly
) {
490 if (psiMethod
instanceof JspHolderMethod
) return null;
491 if (IGNORE_DEPRECATED
&& (psiMethod
.isDeprecated() || psiMethod
.getContainingClass().isDeprecated())) {
494 PsiDocComment docComment
= psiMethod
.getDocComment();
495 final PsiMethod
[] superMethods
= psiMethod
.findSuperMethods();
496 if (docComment
== null) {
497 if (isJavaDocRequired(psiMethod
)) {
498 if (superMethods
.length
> 0) return null;
499 ExtensionPoint
<Condition
<PsiMember
>> point
= Extensions
.getRootArea().getExtensionPoint(ExtensionPoints
.JAVADOC_LOCAL
);
500 final Condition
<PsiMember
>[] addins
= point
.getExtensions();
501 for (Condition
<PsiMember
> addin
: addins
) {
502 if (addin
.value(psiMethod
)) return null;
504 if (superMethods
.length
== 0) {
505 final PsiIdentifier nameIdentifier
= psiMethod
.getNameIdentifier();
506 return nameIdentifier
!= null ?
new ProblemDescriptor
[] { createDescriptor(nameIdentifier
, REQUIRED_JAVADOC_IS_ABSENT
, manager
,
518 final PsiElement
[] descriptionElements
= docComment
.getDescriptionElements();
519 for (PsiElement descriptionElement
: descriptionElements
) {
520 if (descriptionElement
instanceof PsiInlineDocTag
) {
521 if ("inheritDoc".equals(((PsiInlineDocTag
)descriptionElement
).getName())) return null;
525 final ArrayList
<ProblemDescriptor
> problems
= new ArrayList
<ProblemDescriptor
>(2);
527 checkInlineTags(manager
, problems
, descriptionElements
,
528 JavaPsiFacade
.getInstance(docComment
.getProject()).getJavadocManager(), isOnTheFly
);
530 final PsiDocTag tagByName
= docComment
.findTagByName("inheritDoc");
531 if (tagByName
!= null) {
532 final String tagName
= tagByName
.getName();
533 final JavadocTagInfo tagInfo
= JavaPsiFacade
.getInstance(tagByName
.getProject()).getJavadocManager().getTagInfo(tagName
);
534 if (tagInfo
!= null && tagInfo
.isValidInContext(psiMethod
)){
539 PsiDocTag
[] tags
= docComment
.getTags();
541 boolean isReturnRequired
= false;
542 boolean isReturnAbsent
= true;
543 if (superMethods
.length
== 0 && !psiMethod
.isConstructor() && PsiType
.VOID
!= psiMethod
.getReturnType() && isTagRequired(psiMethod
, "return")) {
544 isReturnRequired
= true;
545 for (PsiDocTag tag
: tags
) {
546 if ("return".equals(tag
.getName())) {
547 isReturnAbsent
= false;
553 ArrayList
<PsiParameter
> absentParameters
= null;
554 if (superMethods
.length
== 0 && isTagRequired(psiMethod
, "param") ) {
555 PsiParameter
[] params
= psiMethod
.getParameterList().getParameters();
556 for (PsiParameter param
: params
) {
557 if (!isFound(tags
, param
)) {
558 if (absentParameters
== null) absentParameters
= new ArrayList
<PsiParameter
>(2);
559 absentParameters
.add(param
);
566 if (isReturnRequired
&& isReturnAbsent
) {
567 final PsiIdentifier psiIdentifier
= psiMethod
.getNameIdentifier();
568 if (psiIdentifier
!= null) {
569 problems
.add(createMissingTagDescriptor(psiIdentifier
, "return", manager
, isOnTheFly
));
573 if (absentParameters
!= null) {
574 for (PsiParameter psiParameter
: absentParameters
) {
575 final PsiIdentifier nameIdentifier
= psiMethod
.getNameIdentifier();
576 if (nameIdentifier
!= null) {
577 problems
.add(createMissingParamTagDescriptor(nameIdentifier
, psiParameter
, manager
, isOnTheFly
));
582 for (PsiDocTag tag
: tags
) {
583 if ("param".equals(tag
.getName())) {
584 final PsiElement
[] dataElements
= tag
.getDataElements();
585 final PsiDocTagValue valueElement
= tag
.getValueElement();
586 boolean hasProblemsWithTag
= dataElements
.length
< 2;
587 if (!hasProblemsWithTag
) {
588 final StringBuilder buf
= new StringBuilder();
589 for (PsiElement element
: dataElements
) {
590 if (element
!= valueElement
){
591 buf
.append(element
.getText());
594 hasProblemsWithTag
= buf
.toString().trim().length() == 0;
596 if (hasProblemsWithTag
) {
597 if (valueElement
!= null) {
598 problems
.add(createDescriptor(valueElement
,
599 InspectionsBundle
.message("inspection.javadoc.method.problem.missing.tag.description", "<code>@param " + valueElement
.getText() + "</code>"),
600 manager
, isOnTheFly
));
607 if (superMethods
.length
== 0 && isTagRequired(psiMethod
, "@throws") && psiMethod
.getThrowsList().getReferencedTypes().length
> 0) {
608 final Map
<PsiClassType
, PsiClass
> declaredExceptions
= new HashMap
<PsiClassType
, PsiClass
>();
609 final PsiClassType
[] classTypes
= psiMethod
.getThrowsList().getReferencedTypes();
610 for (PsiClassType classType
: classTypes
) {
611 final PsiClass psiClass
= classType
.resolve();
612 if (psiClass
!= null){
613 declaredExceptions
.put(classType
, psiClass
);
616 processThrowsTags(tags
, declaredExceptions
, manager
, problems
, isOnTheFly
);
617 if (!declaredExceptions
.isEmpty()) {
618 for (PsiClassType declaredException
: declaredExceptions
.keySet()) {
619 problems
.add(createMissingThrowsTagDescriptor(psiMethod
, manager
, declaredException
, isOnTheFly
));
624 ArrayList
<ProblemDescriptor
> tagProblems
= getTagValuesProblems(psiMethod
, tags
, manager
, isOnTheFly
);
625 if (tagProblems
!= null) {
626 problems
.addAll(tagProblems
);
629 checkForPeriodInDoc(docComment
, problems
, manager
, isOnTheFly
);
630 checkForBadCharacters(docComment
, problems
, manager
, isOnTheFly
);
631 for (PsiDocTag tag
: tags
) {
632 if ("param".equals(tag
.getName())) {
633 if (extractTagDescription(tag
).length() == 0) {
634 PsiDocTagValue value
= tag
.getValueElement();
635 if (value
instanceof PsiDocParamRef
) {
636 PsiDocParamRef paramRef
= (PsiDocParamRef
)value
;
637 PsiParameter
[] params
= psiMethod
.getParameterList().getParameters();
638 for (PsiParameter param
: params
) {
639 if (paramRef
.getReference().isReferenceTo(param
)) {
640 problems
.add(createDescriptor(value
,
641 InspectionsBundle
.message("inspection.javadoc.method.problem.descriptor", "<code>@param</code>", "<code>" + param
.getName() + "</code>"),
642 manager
, isOnTheFly
));
649 if ("return".equals(tag
.getName())) {
650 if (extractTagDescription(tag
).length() == 0) {
651 String message
= InspectionsBundle
.message("inspection.javadoc.method.problem.missing.tag.description", "<code>@return</code>");
652 ProblemDescriptor descriptor
= manager
.createProblemDescriptor(tag
.getNameElement(), message
, null, ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
,
654 problems
.add(descriptor
);
659 checkDuplicateTags(tags
, problems
, manager
, isOnTheFly
);
661 return problems
.isEmpty()
663 : problems
.toArray(new ProblemDescriptor
[problems
.size()]);
666 private static boolean isFound(final PsiDocTag
[] tags
, final PsiElement param
) {
667 for (PsiDocTag tag
: tags
) {
668 if ("param".equals(tag
.getName())) {
669 PsiDocTagValue value
= tag
.getValueElement();
670 if (value
instanceof PsiDocParamRef
) {
671 PsiDocParamRef paramRef
= (PsiDocParamRef
)value
;
672 final PsiReference psiReference
= paramRef
.getReference();
673 if (psiReference
!= null && psiReference
.isReferenceTo(param
)) {
682 private static void processThrowsTags(final PsiDocTag
[] tags
,
683 final Map
<PsiClassType
, PsiClass
> declaredExceptions
,
684 final InspectionManager mananger
,
685 @NotNull final ArrayList
<ProblemDescriptor
> problems
, boolean isOnTheFly
) {
686 for (PsiDocTag tag
: tags
) {
687 if ("throws".equals(tag
.getName()) || "exception".equals(tag
.getName())) {
688 final PsiDocTagValue value
= tag
.getValueElement();
689 if (value
== null) continue;
690 final PsiElement firstChild
= value
.getFirstChild();
691 if (firstChild
== null) continue;
692 final PsiElement psiElement
= firstChild
.getFirstChild();
693 if (!(psiElement
instanceof PsiJavaCodeReferenceElement
)) continue;
694 final PsiJavaCodeReferenceElement ref
= (PsiJavaCodeReferenceElement
)psiElement
;
695 final PsiElement element
= ref
.resolve();
696 if (element
instanceof PsiClass
){
697 final PsiClass exceptionClass
= (PsiClass
)element
;
698 for (Iterator
<PsiClassType
> it
= declaredExceptions
.keySet().iterator(); it
.hasNext();) {
699 PsiClassType classType
= it
.next();
700 final PsiClass psiClass
= declaredExceptions
.get(classType
);
701 if (InheritanceUtil
.isInheritorOrSelf(exceptionClass
, psiClass
, true)) {
702 if (extractThrowsTagDescription(tag
).length() == 0) {
703 problems
.add(createDescriptor(tag
.getNameElement(), InspectionsBundle
.message("inspection.javadoc.method.problem.missing.tag.description", "<code>" + tag
.getName() + "</code>"), mananger
,
715 private static ProblemDescriptor
createMissingThrowsTagDescriptor(final PsiMethod method
,
716 final InspectionManager manager
,
717 final PsiClassType exceptionClassType
, boolean isOnTheFly
) {
718 @NonNls String tag
= "throws";
719 String message
= InspectionsBundle
.message("inspection.javadoc.problem.missing.tag", "<code>@" + tag
+ "</code> " + exceptionClassType
.getCanonicalText());
720 final String firstDeclaredException
= exceptionClassType
.getCanonicalText();
721 final PsiIdentifier nameIdentifier
= method
.getNameIdentifier();
722 return nameIdentifier
!= null ?
createDescriptor(nameIdentifier
, message
,new AddMissingTagFix(tag
, firstDeclaredException
), manager
,
726 private static ProblemDescriptor
createMissingTagDescriptor(PsiElement elementToHighlight
,
728 final InspectionManager manager
, boolean isOnTheFly
) {
729 String message
= InspectionsBundle
.message("inspection.javadoc.problem.missing.tag", "<code>@" + tag
+ "</code>");
730 return createDescriptor(elementToHighlight
, message
,new AddMissingTagFix(tag
), manager
, isOnTheFly
);
732 private static ProblemDescriptor
createMissingParamTagDescriptor(PsiElement elementToHighlight
,
734 final InspectionManager manager
, boolean isOnTheFly
) {
735 String message
= InspectionsBundle
.message("inspection.javadoc.method.problem.missing.param.tag", "<code>@param</code>", "<code>" + param
.getName() + "</code>");
736 return createDescriptor(elementToHighlight
, message
, new AddMissingParamTagFix(param
), manager
, isOnTheFly
);
739 private static class AddMissingParamTagFix
extends AddMissingTagFix
{
740 private final PsiParameter myParam
;
742 public AddMissingParamTagFix(final PsiParameter param
) {
743 super("param", param
.getName());
748 public String
getName() {
749 return InspectionsBundle
.message("inspection.javadoc.problem.add.param.tag", myParam
.getName());
753 protected PsiElement
getAnchor() {
754 final PsiMethod psiMethod
= PsiTreeUtil
.getParentOfType(myParam
, PsiMethod
.class);
755 LOG
.assertTrue(psiMethod
!= null);
756 final PsiDocComment docComment
= psiMethod
.getDocComment();
757 LOG
.assertTrue(docComment
!= null);
758 PsiDocTag
[] tags
= docComment
.findTagsByName("param");
759 if (tags
.length
== 0) { //insert as first tag or append to description
760 tags
= docComment
.getTags();
761 if (tags
.length
== 0) return null;
765 PsiParameter nextParam
= PsiTreeUtil
.getNextSiblingOfType(myParam
, PsiParameter
.class);
766 while (nextParam
!= null) {
767 for (PsiDocTag tag
: tags
) {
768 if (matches(nextParam
, tag
)) {
772 nextParam
= PsiTreeUtil
.getNextSiblingOfType(nextParam
, PsiParameter
.class);
775 PsiParameter prevParam
= PsiTreeUtil
.getPrevSiblingOfType(myParam
, PsiParameter
.class);
776 while (prevParam
!= null) {
777 for (PsiDocTag tag
: tags
) {
778 if (matches(prevParam
, tag
)) {
779 return PsiTreeUtil
.getNextSiblingOfType(tag
, PsiDocTag
.class);
782 prevParam
= PsiTreeUtil
.getPrevSiblingOfType(prevParam
, PsiParameter
.class);
788 private static boolean matches(final PsiParameter param
, final PsiDocTag tag
) {
789 return tag
.getValueElement().getText().trim().startsWith(param
.getName());
793 private static String
extractTagDescription(PsiDocTag tag
) {
794 StringBuilder buf
= new StringBuilder();
795 PsiElement
[] children
= tag
.getChildren();
796 for (PsiElement child
: children
) {
797 if (child
instanceof PsiDocToken
) {
798 PsiDocToken token
= (PsiDocToken
)child
;
799 if (token
.getTokenType() == JavaDocTokenType
.DOC_COMMENT_DATA
) {
800 buf
.append(token
.getText());
803 else if (child
instanceof PsiDocTagValue
) {
804 buf
.append(child
.getText());
805 } else if (child
instanceof PsiInlineDocTag
) {
806 buf
.append(child
.getText());
810 String s
= buf
.toString();
814 private static String
extractThrowsTagDescription(PsiDocTag tag
) {
815 StringBuilder buf
= new StringBuilder();
816 PsiElement
[] children
= tag
.getChildren();
817 for (PsiElement child
: children
) {
818 if (child
instanceof PsiDocToken
) {
819 PsiDocToken token
= (PsiDocToken
)child
;
820 if (token
.getTokenType() == JavaDocTokenType
.DOC_COMMENT_DATA
) {
821 buf
.append(token
.getText());
826 return buf
.toString().trim();
829 private void checkForBadCharacters(PsiDocComment docComment
,
830 final ArrayList
<ProblemDescriptor
> problems
,
831 final InspectionManager manager
, final boolean onTheFly
) {
832 docComment
.accept(new PsiRecursiveElementVisitor(){
834 public void visitElement(PsiElement element
) {
835 super.visitElement(element
);
836 final ASTNode node
= element
.getNode();
838 if (node
.getElementType() == JavaDocTokenType
.DOC_COMMENT_BAD_CHARACTER
) {
839 problems
.add(manager
.createProblemDescriptor(element
, "Illegal character", (LocalQuickFix
)null, ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
, onTheFly
));
846 private void checkForPeriodInDoc(PsiDocComment docComment
,
847 ArrayList
<ProblemDescriptor
> problems
,
848 InspectionManager manager
, boolean onTheFly
) {
849 if (IGNORE_JAVADOC_PERIOD
) return;
850 PsiDocTag
[] tags
= docComment
.getTags();
851 int dotIndex
= docComment
.getText().indexOf('.');
853 if (dotIndex
>= 0) { //need to find first valid tag
854 final PsiDocCommentOwner owner
= PsiTreeUtil
.getParentOfType(docComment
, PsiDocCommentOwner
.class);
855 for (PsiDocTag tag
: tags
) {
856 final String tagName
= tag
.getName();
857 final JavadocTagInfo tagInfo
= JavaPsiFacade
.getInstance(tag
.getProject()).getJavadocManager().getTagInfo(tagName
);
858 if (tagInfo
!= null && tagInfo
.isValidInContext(owner
) && !tagInfo
.isInline()) {
859 tagOffset
= tag
.getTextOffset();
865 if (dotIndex
== -1 || tagOffset
> 0 && dotIndex
+ docComment
.getTextOffset() > tagOffset
) {
866 problems
.add(manager
.createProblemDescriptor(docComment
.getFirstChild(),
867 InspectionsBundle
.message("inspection.javadoc.problem.descriptor1"),
869 ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
, onTheFly
, false));
874 private ArrayList
<ProblemDescriptor
> getTagValuesProblems(PsiDocCommentOwner context
, PsiDocTag
[] tags
, InspectionManager inspectionManager
,
875 boolean isOnTheFly
) {
876 final ArrayList
<ProblemDescriptor
> problems
= new ArrayList
<ProblemDescriptor
>(2);
878 for (PsiDocTag tag
: tags
) {
879 final JavadocManager manager
= JavaPsiFacade
.getInstance(tag
.getProject()).getJavadocManager();
880 String tagName
= tag
.getName();
881 JavadocTagInfo tagInfo
= manager
.getTagInfo(tagName
);
883 if (tagInfo
== null || !tagInfo
.isValidInContext(context
)) {
884 final StringTokenizer tokenizer
= new StringTokenizer(myAdditionalJavadocTags
, ", ");
885 while (tokenizer
.hasMoreTokens()) {
886 if (Comparing
.strEqual(tagName
, tokenizer
.nextToken())) continue nextTag
;
889 if (tagInfo
== null){
890 problems
.add(createDescriptor(tag
.getNameElement(), InspectionsBundle
.message("inspection.javadoc.problem.wrong.tag", "<code>" + tagName
+ "</code>"), new AddUnknownTagToCustoms(tag
), inspectionManager
,
893 problems
.add(createDescriptor(tag
.getNameElement(), InspectionsBundle
.message("inspection.javadoc.problem.disallowed.tag", "<code>" + tagName
+ "</code>"), new AddUnknownTagToCustoms(tag
), inspectionManager
,
899 PsiDocTagValue value
= tag
.getValueElement();
900 final JavadocTagInfo info
= manager
.getTagInfo(tagName
);
901 if (info
!= null && !info
.isValidInContext(context
)) continue;
902 String message
= info
== null ?
null : info
.checkTagValue(value
);
904 final PsiReference reference
= value
!= null ? value
.getReference() : null;
905 if (message
== null && reference
!= null) {
906 PsiElement element
= reference
.resolve();
907 if (element
== null) {
908 final int textOffset
= value
.getTextOffset();
910 if (textOffset
== value
.getTextRange().getEndOffset()) {
911 problems
.add(inspectionManager
.createProblemDescriptor(tag
, InspectionsBundle
.message("inspection.javadoc.problem.name.expected"), null, ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
,
917 if (message
!= null) {
918 final PsiDocTagValue valueElement
= tag
.getValueElement();
919 if (valueElement
== null){
920 problems
.add(inspectionManager
.createProblemDescriptor(tag
, InspectionsBundle
.message("inspection.javadoc.method.problem.missing.tag.description", "<code>" + tag
.getName() + "</code>"), null, ProblemHighlightType
.GENERIC_ERROR_OR_WARNING
,
923 problems
.add(createDescriptor(valueElement
, message
, inspectionManager
, isOnTheFly
));
926 checkInlineTags(inspectionManager
, problems
, tag
.getDataElements(), manager
, isOnTheFly
);
929 return problems
.isEmpty() ?
null : problems
;
932 private void checkInlineTags(final InspectionManager inspectionManager
,
933 final ArrayList
<ProblemDescriptor
> problems
,
934 final PsiElement
[] dataElements
,
935 final JavadocManager manager
, boolean isOnTheFly
) {
936 for (PsiElement dataElement
: dataElements
) {
937 if (dataElement
instanceof PsiInlineDocTag
) {
938 final PsiInlineDocTag inlineDocTag
= (PsiInlineDocTag
)dataElement
;
939 final PsiElement nameElement
= inlineDocTag
.getNameElement();
940 if (manager
.getTagInfo(inlineDocTag
.getName()) == null) {
941 if (nameElement
!= null) {
942 problems
.add(createDescriptor(nameElement
, InspectionsBundle
.message("inspection.javadoc.problem.wrong.tag", "<code>" + inlineDocTag
.getName() + "</code>"), new AddUnknownTagToCustoms(inlineDocTag
), inspectionManager
,
946 final PsiDocTagValue value
= inlineDocTag
.getValueElement();
948 final PsiReference reference
= value
.getReference();
949 if (reference
!= null) {
950 final PsiElement ref
= reference
.resolve();
952 if (PsiTreeUtil
.getParentOfType(inlineDocTag
, PsiDocCommentOwner
.class) == PsiTreeUtil
.getParentOfType(ref
, PsiDocCommentOwner
.class, false)) {
953 if (nameElement
!= null) {
954 problems
.add(createDescriptor(nameElement
, InspectionsBundle
.message("inspection.javadoc.problem.pointing.to.itself"), inspectionManager
,
965 @SuppressWarnings({"SimplifiableIfStatement"})
966 private boolean isTagRequired(PsiElement context
, @NonNls String tag
) {
967 if (context
instanceof PsiClass
) {
968 if (PsiTreeUtil
.getParentOfType(context
, PsiClass
.class) != null) {
969 return isTagRequired(INNER_CLASS_OPTIONS
, tag
);
972 return isTagRequired(TOP_LEVEL_CLASS_OPTIONS
, tag
);
975 if (context
instanceof PsiMethod
) {
976 return isTagRequired(METHOD_OPTIONS
, tag
);
979 if (context
instanceof PsiField
) {
980 return isTagRequired(FIELD_OPTIONS
, tag
);
986 private static boolean isTagRequired(Options options
, String tag
) {
987 return options
.REQUIRED_TAGS
.contains(tag
);
990 private boolean isJavaDocRequired(PsiModifierListOwner psiElement
) {
991 final RefJavaUtil refUtil
= RefJavaUtil
.getInstance();
992 int actualAccess
= getAccessNumber(refUtil
.getAccessModifier(psiElement
));
993 if (psiElement
instanceof PsiClass
) {
994 PsiClass psiClass
= (PsiClass
)psiElement
;
995 if (PsiTreeUtil
.getParentOfType(psiClass
, PsiClass
.class) != null) {
996 return actualAccess
<= getAccessNumber(INNER_CLASS_OPTIONS
.ACCESS_JAVADOC_REQUIRED_FOR
);
999 return actualAccess
<= getAccessNumber(TOP_LEVEL_CLASS_OPTIONS
.ACCESS_JAVADOC_REQUIRED_FOR
);
1002 if (psiElement
instanceof PsiMethod
) {
1003 psiElement
= PsiTreeUtil
.getParentOfType(psiElement
, PsiClass
.class);
1004 while (psiElement
!= null) {
1005 actualAccess
= Math
.max(actualAccess
, getAccessNumber(refUtil
.getAccessModifier(psiElement
)));
1006 psiElement
= PsiTreeUtil
.getParentOfType(psiElement
, PsiClass
.class);
1009 return actualAccess
<= getAccessNumber(METHOD_OPTIONS
.ACCESS_JAVADOC_REQUIRED_FOR
);
1012 if (psiElement
instanceof PsiField
) {
1013 psiElement
= PsiTreeUtil
.getParentOfType(psiElement
, PsiClass
.class);
1014 while (psiElement
!= null) {
1015 actualAccess
= Math
.max(actualAccess
, getAccessNumber(refUtil
.getAccessModifier(psiElement
)));
1016 psiElement
= PsiTreeUtil
.getParentOfType(psiElement
, PsiClass
.class);
1019 return actualAccess
<= getAccessNumber(FIELD_OPTIONS
.ACCESS_JAVADOC_REQUIRED_FOR
);
1025 private void checkDuplicateTags(final PsiDocTag
[] tags
,
1026 ArrayList
<ProblemDescriptor
> problems
,
1027 final InspectionManager manager
, boolean isOnTheFly
) {
1028 Set
<String
> documentedParamNames
= null;
1029 Set
<String
> documentedExceptions
= null;
1030 Set
<String
> uniqueTags
= null;
1031 for(PsiDocTag tag
: tags
) {
1032 if ("param".equals(tag
.getName())) {
1033 PsiDocTagValue value
= tag
.getValueElement();
1034 if (value
instanceof PsiDocParamRef
) {
1035 PsiDocParamRef paramRef
= (PsiDocParamRef
)value
;
1036 final PsiReference reference
= paramRef
.getReference();
1037 if (reference
!= null) {
1038 final String paramName
= reference
.getCanonicalText();
1039 if (documentedParamNames
== null) {
1040 documentedParamNames
= new HashSet
<String
>();
1042 if (documentedParamNames
.contains(paramName
)) {
1043 problems
.add(createDescriptor(tag
.getNameElement(), InspectionsBundle
.message("inspection.javadoc.problem.duplicate.param", paramName
), manager
,
1046 documentedParamNames
.add(paramName
);
1050 else if (!IGNORE_DUPLICATED_THROWS
&& ("throws".equals(tag
.getName()) || "exception".equals(tag
.getName()))) {
1051 PsiDocTagValue value
= tag
.getValueElement();
1052 if (value
!= null) {
1053 final PsiElement firstChild
= value
.getFirstChild();
1054 if (firstChild
!= null && firstChild
.getFirstChild() instanceof PsiJavaCodeReferenceElement
) {
1055 PsiJavaCodeReferenceElement refElement
= (PsiJavaCodeReferenceElement
) firstChild
.getFirstChild();
1056 if (refElement
!= null) {
1057 PsiElement element
= refElement
.resolve();
1058 if (element
instanceof PsiClass
) {
1059 String fqName
= ((PsiClass
)element
).getQualifiedName();
1060 if (documentedExceptions
== null) {
1061 documentedExceptions
= new HashSet
<String
>();
1063 if (documentedExceptions
.contains(fqName
)) {
1064 problems
.add(createDescriptor(tag
.getNameElement(),
1065 InspectionsBundle
.message("inspection.javadoc.problem.duplicate.throws", fqName
),
1066 manager
, isOnTheFly
));
1068 documentedExceptions
.add(fqName
);
1074 else if (JavaDocLocalInspection
.ourUniqueTags
.contains(tag
.getName())) {
1075 if (uniqueTags
== null) {
1076 uniqueTags
= new HashSet
<String
>();
1078 if (uniqueTags
.contains(tag
.getName())) {
1079 problems
.add(createDescriptor(tag
.getNameElement(), InspectionsBundle
.message("inspection.javadoc.problem.duplicate.tag", tag
.getName()), manager
,
1082 uniqueTags
.add(tag
.getName());
1087 private static int getAccessNumber(@NonNls String accessModifier
) {
1088 if (accessModifier
.startsWith("none")) return 0;
1089 if (accessModifier
.startsWith("public")) return 1;
1090 if (accessModifier
.startsWith("protected")) return 2;
1091 if (accessModifier
.startsWith("package")) return 3;
1092 if (accessModifier
.startsWith("private")) return 4;
1098 public String
getDisplayName() {
1099 return InspectionsBundle
.message("inspection.javadoc.display.name");
1103 public String
getGroupDisplayName() {
1108 public String
getShortName() {
1112 private class AddUnknownTagToCustoms
implements LocalQuickFix
{
1115 public AddUnknownTagToCustoms(PsiDocTag tag
) {
1120 public String
getName() {
1121 return QuickFixBundle
.message("add.doctag.to.custom.tags", myTag
.getName());
1125 public String
getFamilyName() {
1126 return QuickFixBundle
.message("fix.javadoc.family");
1129 public void applyFix(@NotNull Project project
, @NotNull ProblemDescriptor descriptor
) {
1130 if (myTag
== null || !myTag
.isValid()) return;
1131 if (myAdditionalJavadocTags
.length() > 0) {
1132 myAdditionalJavadocTags
+= "," + myTag
.getName();
1135 myAdditionalJavadocTags
= myTag
.getName();
1137 final InspectionProfile inspectionProfile
=
1138 InspectionProjectProfileManager
.getInstance(project
).getInspectionProfile();
1139 //correct save settings
1140 ((ModifiableModel
)inspectionProfile
).isProperSetting(HighlightDisplayKey
.find(SHORT_NAME
));
1141 InspectionProfileManager
.getInstance().fireProfileChanged(inspectionProfile
);
1147 inspectionProfile.save();
1149 catch (IOException e) {
1150 Messages.showErrorDialog(project, e.getMessage(), CommonBundle.getErrorTitle());