b30cc9d57d2e42b0e26f30f8154d3a2568471872
[idea/community.git] / java / java-impl / src / com / intellij / codeInspection / javaDoc / JavaDocLocalInspection.java
1 /*
2  * Copyright (c) 2005 Jet Brains. All Rights Reserved.
3  */
4 package com.intellij.codeInspection.javaDoc;
5
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.openapi.diagnostic.Logger;
14 import com.intellij.openapi.editor.Editor;
15 import com.intellij.openapi.editor.ScrollType;
16 import com.intellij.openapi.extensions.ExtensionPoint;
17 import com.intellij.openapi.extensions.Extensions;
18 import com.intellij.openapi.fileEditor.FileEditorManager;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.util.*;
21 import com.intellij.profile.codeInspection.InspectionProfileManager;
22 import com.intellij.profile.codeInspection.InspectionProjectProfileManager;
23 import com.intellij.psi.*;
24 import com.intellij.psi.impl.source.javadoc.PsiDocParamRef;
25 import com.intellij.psi.impl.source.jsp.jspJava.JspClass;
26 import com.intellij.psi.impl.source.jsp.jspJava.JspHolderMethod;
27 import com.intellij.psi.javadoc.*;
28 import com.intellij.psi.util.InheritanceUtil;
29 import com.intellij.psi.util.PsiTreeUtil;
30 import com.intellij.ui.DocumentAdapter;
31 import com.intellij.ui.FieldPanel;
32 import com.intellij.ui.IdeBorderFactory;
33 import com.intellij.util.IJSwingUtilities;
34 import com.intellij.util.IncorrectOperationException;
35 import com.intellij.util.ui.UIUtil;
36 import org.jdom.Element;
37 import org.jetbrains.annotations.NonNls;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.annotations.Nullable;
40
41 import javax.swing.*;
42 import javax.swing.event.ChangeEvent;
43 import javax.swing.event.ChangeListener;
44 import javax.swing.event.DocumentEvent;
45 import javax.swing.text.BadLocationException;
46 import javax.swing.text.Document;
47 import java.awt.*;
48 import java.awt.event.ActionEvent;
49 import java.awt.event.ActionListener;
50 import java.util.*;
51
52 public class JavaDocLocalInspection extends BaseLocalInspectionTool {
53   private static final String REQUIRED_JAVADOC_IS_ABSENT = InspectionsBundle.message("inspection.javadoc.problem.descriptor");
54
55   @NonNls private static final String NONE = "none";
56   @NonNls private static final String PUBLIC = "public";
57   @NonNls private static final String PROTECTED = "protected";
58   @NonNls private static final String PACKAGE_LOCAL = "package";
59   @NonNls private static final String PRIVATE = "private";
60   @NonNls private static final Set<String> ourUniqueTags = new HashSet<String>();
61   @NonNls public static final String SHORT_NAME = "JavaDoc";
62
63   static {
64     ourUniqueTags.add("return");
65     ourUniqueTags.add("deprecated");
66     ourUniqueTags.add("serial");
67     ourUniqueTags.add("serialData");
68   }
69
70
71   public static class Options implements JDOMExternalizable {
72     @NonNls public String ACCESS_JAVADOC_REQUIRED_FOR = NONE;
73     @NonNls public String REQUIRED_TAGS = "";
74
75     public Options() {
76     }
77
78     public Options(String ACCESS_JAVADOC_REQUIRED_FOR, String REQUIRED_TAGS) {
79       this.ACCESS_JAVADOC_REQUIRED_FOR = ACCESS_JAVADOC_REQUIRED_FOR;
80       this.REQUIRED_TAGS = REQUIRED_TAGS;
81     }
82
83     public void readExternal(Element element) throws InvalidDataException {
84       DefaultJDOMExternalizer.readExternal(this, element);
85     }
86
87     public void writeExternal(Element element) throws WriteExternalException {
88       DefaultJDOMExternalizer.writeExternal(this, element);
89     }
90   }
91
92   @NonNls public Options TOP_LEVEL_CLASS_OPTIONS = new Options("none", "");
93   @NonNls public Options INNER_CLASS_OPTIONS = new Options("none", "");
94   @NonNls public Options METHOD_OPTIONS = new Options("none", "@return@param@throws or @exception");
95   @NonNls public Options FIELD_OPTIONS = new Options("none", "");
96   public boolean IGNORE_DEPRECATED = false;
97   public boolean IGNORE_JAVADOC_PERIOD = true;
98   public String myAdditionalJavadocTags = "";
99
100   private static final Logger LOG = Logger.getInstance("com.intellij.codeInspection.javaDoc.JavaDocLocalInspection");
101
102   private class OptionsPanel extends JPanel {
103     private JPanel createOptionsPanel(String[] modifiers, String[] tags, Options options) {
104       JPanel pane = new JPanel(new GridLayout(1, tags == null ? 1 : 2));
105
106       pane.add(createScopePanel(modifiers, options));
107       if (tags != null) {
108         pane.add(createTagsPanel(tags, options));
109       }
110
111       pane.validate();
112
113       return pane;
114     }
115
116     private JPanel createTagsPanel(String[] tags, Options options) {
117       JPanel panel = new JPanel(new GridBagLayout());
118       panel.setBorder(BorderFactory.createCompoundBorder(IdeBorderFactory.createTitledBorder(InspectionsBundle.message("inspection.javadoc.required.tags.option.title")),
119                                                          BorderFactory.createEmptyBorder(0, 3, 3, 3)));
120
121       GridBagConstraints gc = new GridBagConstraints();
122       gc.weightx = 1;
123       gc.weighty = 0;
124       gc.fill = GridBagConstraints.HORIZONTAL;
125       gc.anchor = GridBagConstraints.NORTHWEST;
126
127
128       for (int i = 0; i < tags.length; i++) {
129         JCheckBox box = new JCheckBox(tags[i]);
130         gc.gridy = i;
131         if (i == tags.length - 1) gc.weighty = 1;
132         panel.add(box, gc);
133         box.setSelected(isTagRequired(options, tags[i]));
134         box.addChangeListener(new MyChangeListener(box, options, tags[i]));
135       }
136
137       return panel;
138     }
139
140     private class MyChangeListener implements ChangeListener {
141       private final JCheckBox myCheckBox;
142       private final Options myOptions;
143       private final String myTagName;
144
145       public MyChangeListener(JCheckBox checkBox, Options options, String tagName) {
146         myCheckBox = checkBox;
147         myOptions = options;
148         myTagName = tagName;
149       }
150
151       public void stateChanged(ChangeEvent e) {
152         if (myCheckBox.isSelected()) {
153           if (!isTagRequired(myOptions,myTagName)) {
154             myOptions.REQUIRED_TAGS += myTagName;
155           }
156         }
157         else {
158           myOptions.REQUIRED_TAGS = myOptions.REQUIRED_TAGS.replaceAll(myTagName, "");
159         }
160       }
161     }
162
163     private JPanel createScopePanel(final String[] modifiers, final Options options) {
164       JPanel panel = new JPanel(new BorderLayout());
165       panel.setBorder(BorderFactory.createCompoundBorder(IdeBorderFactory.createTitledBorder(InspectionsBundle.message("inspection.scope.for.title")),
166                                                          BorderFactory.createEmptyBorder(0, 3, 3, 3)));
167
168       final Hashtable<Integer, JLabel> sliderLabels = new Hashtable<Integer, JLabel>();
169       for (int i = 0; i < modifiers.length; i++) {
170         sliderLabels.put(i + 1, new JLabel(modifiers[i]));
171       }
172
173       final JSlider slider = new JSlider(SwingConstants.VERTICAL, 1, modifiers.length, 1);
174
175       slider.setLabelTable(sliderLabels);
176       slider.putClientProperty(UIUtil.JSLIDER_ISFILLED, Boolean.TRUE);
177       slider.setPreferredSize(new Dimension(80, 50));
178       slider.setPaintLabels(true);
179       slider.setSnapToTicks(true);
180       slider.addChangeListener(new ChangeListener() {
181         public void stateChanged(ChangeEvent e) {
182           int value = slider.getValue();
183           options.ACCESS_JAVADOC_REQUIRED_FOR = modifiers[value - 1];
184           for (Integer key : sliderLabels.keySet()) {
185             sliderLabels.get(key).setForeground(key.intValue() <= value ? Color.black : new Color(100, 100, 100));
186           }
187         }
188       });
189
190       Color fore = Color.black;
191       for (int i = 0; i < modifiers.length; i++) {
192         sliderLabels.get(i + 1).setForeground(fore);
193
194         if (modifiers[i].equals(options.ACCESS_JAVADOC_REQUIRED_FOR)) {
195           slider.setValue(i + 1);
196           fore = new Color(100, 100, 100);
197         }
198       }
199
200       panel.add(slider, BorderLayout.WEST);
201
202       return panel;
203     }
204
205     public OptionsPanel() {
206       super(new GridBagLayout());
207       GridBagConstraints gc = new GridBagConstraints(0, GridBagConstraints.RELATIVE, 2, 1, 1, 1, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(0,0,0,0),0,0 );
208       gc.weighty = 0;
209       add(createAdditionalJavadocTagsPanel(), gc);
210       JTabbedPane tabs = new JTabbedPane(SwingConstants.BOTTOM);
211       @NonNls String[] tags = new String[]{"@author", "@version", "@since", "@param"};
212       tabs.add(InspectionsBundle.message("inspection.javadoc.option.tab.title"), createOptionsPanel(new String[]{NONE, PUBLIC, PACKAGE_LOCAL},
213                                                                                                     tags,
214                                                                                                     TOP_LEVEL_CLASS_OPTIONS));
215       tags = new String[]{"@return", "@param", InspectionsBundle.message("inspection.javadoc.throws.or.exception.option")};
216       tabs.add(InspectionsBundle.message("inspection.javadoc.option.tab.title.method"), createOptionsPanel(new String[]{NONE, PUBLIC, PROTECTED, PACKAGE_LOCAL, PRIVATE},
217                                                                                                            tags,
218                                                                                                            METHOD_OPTIONS));
219       tabs.add(InspectionsBundle.message("inspection.javadoc.option.tab.title.field"), createOptionsPanel(new String[]{NONE, PUBLIC, PROTECTED, PACKAGE_LOCAL, PRIVATE},
220                                                                                                           null,
221                                                                                                           FIELD_OPTIONS));
222       tabs.add(InspectionsBundle.message("inspection.javadoc.option.tab.title.inner.class"), createOptionsPanel(new String[]{NONE, PUBLIC, PROTECTED, PACKAGE_LOCAL, PRIVATE},
223                                                                                                                 null,
224                                                                                                                 INNER_CLASS_OPTIONS));
225       add(tabs, gc);
226
227       final JCheckBox checkBox = new JCheckBox(InspectionsBundle.message("inspection.javadoc.option.ignore.deprecated"),
228                                                IGNORE_DEPRECATED);
229       checkBox.addActionListener(new ActionListener() {
230         public void actionPerformed(ActionEvent e) {
231           IGNORE_DEPRECATED = checkBox.isSelected();
232         }
233       });
234       gc.gridwidth = 1;
235       add(checkBox, gc);
236       final JCheckBox periodCheckBox = new JCheckBox(InspectionsBundle.message("inspection.javadoc.option.ignore.period"),
237                                                      IGNORE_JAVADOC_PERIOD);
238       periodCheckBox.addActionListener(new ActionListener() {
239         public void actionPerformed(ActionEvent e) {
240           IGNORE_JAVADOC_PERIOD = periodCheckBox.isSelected();
241         }
242       });
243       add(periodCheckBox, gc);
244     }
245
246     public FieldPanel createAdditionalJavadocTagsPanel(){
247       FieldPanel additionalTagsPanel = new FieldPanel(InspectionsBundle.message("inspection.javadoc.label.text"), InspectionsBundle.message("inspection.javadoc.dialog.title"), null, null);
248       additionalTagsPanel.setPreferredSize(new Dimension(150, additionalTagsPanel.getPreferredSize().height));
249       additionalTagsPanel.getTextField().getDocument().addDocumentListener(new DocumentAdapter() {
250         protected void textChanged(DocumentEvent e) {
251           final Document document = e.getDocument();
252           try {
253             final String text = document.getText(0, document.getLength());
254             if (text != null) {
255               myAdditionalJavadocTags = text.trim();
256             }
257           }
258           catch (BadLocationException e1) {
259              LOG.error(e1);
260           }
261         }
262       });
263       additionalTagsPanel.setText(myAdditionalJavadocTags);
264       return additionalTagsPanel;
265     }
266   }
267
268   public JComponent createOptionsPanel() {
269     return new OptionsPanel();
270   }
271
272   private static ProblemDescriptor createDescriptor(@NotNull PsiElement element, String template, InspectionManager manager) {
273     return manager.createProblemDescriptor(element, template, (LocalQuickFix [])null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
274   }
275
276   private static ProblemDescriptor createDescriptor(@NotNull PsiElement element, String template, @NotNull LocalQuickFix fix, InspectionManager manager) {
277     return manager.createProblemDescriptor(element, template, fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
278   }
279
280   private static class AddMissingTagFix implements LocalQuickFix {
281     private final String myTag;
282     private final String myValue;
283
284     public AddMissingTagFix(@NonNls String tag, String value) {
285       myTag = tag;
286       myValue = value;
287     }
288     public AddMissingTagFix(String tag) {
289       this(tag, "");
290     }
291
292     @NotNull
293     public String getName() {
294       return InspectionsBundle.message("inspection.javadoc.problem.add.tag", myTag, myValue);
295     }
296
297     public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
298       final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
299       try {
300         final PsiDocCommentOwner owner = PsiTreeUtil.getParentOfType(descriptor.getEndElement(), PsiDocCommentOwner.class);
301         if (owner != null) {
302           if (!CodeInsightUtil.preparePsiElementsForWrite(owner)) return;
303           final PsiDocComment docComment = owner.getDocComment();
304           final PsiDocTag tag = factory.createDocTagFromText("@" + myTag+" "+myValue, docComment);
305           if (docComment != null) {
306             PsiElement addedTag;
307             final PsiElement anchor = getAnchor();
308             if (anchor != null) {
309               addedTag = docComment.addBefore(tag, anchor);
310             }
311             else {
312               addedTag = docComment.add(tag);
313             }
314             moveCaretTo(addedTag);
315           }
316         }
317       }
318       catch (IncorrectOperationException e) {
319         LOG.error(e);
320       }
321     }
322
323     @Nullable
324     protected PsiElement getAnchor() {
325       return null;
326     }
327
328     private static void moveCaretTo(final PsiElement newCaretPosition) {
329       Project project = newCaretPosition.getProject();
330       final PsiFile psiFile = newCaretPosition.getContainingFile();
331       final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
332       if (editor != null && IJSwingUtilities.hasFocus(editor.getComponent())) {
333         final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
334         if (file == psiFile) {
335           editor.getCaretModel().moveToOffset(newCaretPosition.getTextRange().getEndOffset());
336           editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
337         }
338       }
339     }
340
341     @NotNull
342     public String getFamilyName() {
343       return InspectionsBundle.message("inspection.javadoc.problem.add.tag.family");
344     }
345   }
346   @Nullable
347   public ProblemDescriptor[] checkClass(@NotNull PsiClass psiClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
348     if (psiClass instanceof PsiAnonymousClass) return null;
349     if (psiClass instanceof JspClass) return null;
350     if (psiClass instanceof PsiTypeParameter) return null;
351     if (IGNORE_DEPRECATED && psiClass.isDeprecated()) {
352       return null;
353     }
354     PsiDocComment docComment = psiClass.getDocComment();
355     final PsiIdentifier nameIdentifier = psiClass.getNameIdentifier();
356     final PsiElement elementToHighlight = nameIdentifier != null ? nameIdentifier : psiClass;
357     if (docComment == null) {
358       return isJavaDocRequired(psiClass)
359              ? new ProblemDescriptor[]{createDescriptor(elementToHighlight, REQUIRED_JAVADOC_IS_ABSENT, manager)}
360              : null;
361     }
362
363     PsiDocTag[] tags = docComment.getTags();
364     @NonNls String[] tagsToCheck = {"author", "version", "since"};
365     @NonNls String[] absentDescriptionKeys = {
366       "inspection.javadoc.problem.missing.author.description",
367       "inspection.javadoc.problem.missing.version.description",
368       "inspection.javadoc.problem.missing.since.description"};
369
370     boolean[] isTagRequired = new boolean[tagsToCheck.length];
371     boolean[] isTagPresent = new boolean[tagsToCheck.length];
372
373     boolean someTagsAreRequired = false;
374     for (int i = 0; i < tagsToCheck.length; i++) {
375       final String tag = tagsToCheck[i];
376       someTagsAreRequired |= isTagRequired[i] = isTagRequired(psiClass, tag);
377     }
378
379     if (someTagsAreRequired) {
380       for (PsiDocTag tag : tags) {
381         String tagName = tag.getName();
382         for (int i = 0; i < tagsToCheck.length; i++) {
383           final String tagToCheck = tagsToCheck[i];
384           if (tagToCheck.equals(tagName)) {
385             isTagPresent[i] = true;
386           }
387         }
388       }
389     }
390
391     final ArrayList<ProblemDescriptor> problems = new ArrayList<ProblemDescriptor>(2);
392
393     for (int i = 0; i < tagsToCheck.length; i++) {
394       final String tagToCheck = tagsToCheck[i];
395       if (isTagRequired[i] && !isTagPresent[i]) {
396         problems.add(createMissingTagDescriptor(elementToHighlight, tagToCheck, manager));
397       }
398     }
399     ArrayList<ProblemDescriptor> tagProblems = getTagValuesProblems(psiClass, tags, manager);
400     if (tagProblems != null) {
401       problems.addAll(tagProblems);
402     }
403     checkForPeriodInDoc(docComment, problems, manager);
404     checkInlineTags(manager, problems, docComment.getDescriptionElements(),
405                     JavaPsiFacade.getInstance(docComment.getProject()).getJavadocManager());
406
407     for (PsiDocTag tag : tags) {
408       for (int i = 0; i < tagsToCheck.length; i++) {
409         final String tagToCheck = tagsToCheck[i];
410         if (tagToCheck.equals(tag.getName()) && extractTagDescription(tag).length() == 0) {
411           problems.add(createDescriptor(elementToHighlight, InspectionsBundle.message(absentDescriptionKeys[i]), manager));
412         }
413       }
414     }
415
416     checkDuplicateTags(tags, problems, manager);
417
418     if (isTagRequired(psiClass, "param") && psiClass.hasTypeParameters() && nameIdentifier != null) {
419       ArrayList<PsiTypeParameter> absentParameters = null;
420       final PsiTypeParameter[] typeParameters = psiClass.getTypeParameters();
421       for (PsiTypeParameter typeParameter : typeParameters) {
422         if (!isFound(tags, typeParameter)) {
423           if (absentParameters == null) absentParameters = new ArrayList<PsiTypeParameter>(1);
424           absentParameters.add(typeParameter);
425         }
426       }
427       if (absentParameters != null) {
428         for (PsiTypeParameter psiTypeParameter : absentParameters) {
429           problems.add(createMissingParamTagDescriptor(nameIdentifier, psiTypeParameter, manager));
430         }
431       }
432     }
433
434     return problems.isEmpty()
435            ? null
436            : problems.toArray(new ProblemDescriptor[problems.size()]);
437   }
438
439   private static ProblemDescriptor createMissingParamTagDescriptor(final PsiIdentifier nameIdentifier,
440                                                                    final PsiTypeParameter psiTypeParameter,
441                                                                    final InspectionManager manager) {
442     String message = InspectionsBundle.message("inspection.javadoc.problem.missing.tag", "<code>@param</code>");
443     return createDescriptor(nameIdentifier, message, new AddMissingTagFix("param", "<" + psiTypeParameter.getName() + ">"), manager);
444   }
445
446   @Nullable
447   public ProblemDescriptor[] checkField(@NotNull PsiField psiField, @NotNull InspectionManager manager, boolean isOnTheFly) {
448     if (IGNORE_DEPRECATED && (psiField.isDeprecated() || psiField.getContainingClass().isDeprecated())) {
449       return null;
450     }
451
452     PsiDocComment docComment = psiField.getDocComment();
453     if (docComment == null) {
454       return isJavaDocRequired(psiField)
455              ? new ProblemDescriptor[]{createDescriptor(psiField.getNameIdentifier(), REQUIRED_JAVADOC_IS_ABSENT, manager)}
456              : null;
457     }
458
459     final ArrayList<ProblemDescriptor> problems = new ArrayList<ProblemDescriptor>(2);
460     ArrayList<ProblemDescriptor> tagProblems = getTagValuesProblems(psiField, docComment.getTags(), manager);
461     if (tagProblems != null) {
462       problems.addAll(tagProblems);
463     }
464     checkInlineTags(manager, problems, docComment.getDescriptionElements(),
465                     JavaPsiFacade.getInstance(docComment.getProject()).getJavadocManager());
466     checkForPeriodInDoc(docComment, problems, manager);
467     checkDuplicateTags(docComment.getTags(), problems, manager);
468     return problems.isEmpty()
469            ? null
470            : problems.toArray(new ProblemDescriptor[problems.size()]);
471   }
472
473   @Nullable
474   public ProblemDescriptor[] checkMethod(@NotNull PsiMethod psiMethod, @NotNull InspectionManager manager, boolean isOnTheFly) {
475     if (psiMethod instanceof JspHolderMethod) return null;
476     if (IGNORE_DEPRECATED && (psiMethod.isDeprecated() || psiMethod.getContainingClass().isDeprecated())) {
477       return null;
478     }
479     PsiDocComment docComment = psiMethod.getDocComment();
480     final PsiMethod[] superMethods = psiMethod.findSuperMethods();
481     if (docComment == null) {
482       if (isJavaDocRequired(psiMethod)) {
483         if (superMethods.length > 0) return null;
484         ExtensionPoint<Condition<PsiMember>> point = Extensions.getRootArea().getExtensionPoint(ExtensionPoints.JAVADOC_LOCAL);
485         final Condition<PsiMember>[] addins = point.getExtensions();
486         for (Condition<PsiMember> addin : addins) {
487           if (addin.value(psiMethod)) return null;
488         }
489         if (superMethods.length == 0) {
490           final PsiIdentifier nameIdentifier = psiMethod.getNameIdentifier();
491           return nameIdentifier != null ? new ProblemDescriptor[] { createDescriptor(nameIdentifier, REQUIRED_JAVADOC_IS_ABSENT, manager)} : null;
492         }
493         else {
494           return null;
495         }
496       }
497       else {
498         return null;
499       }
500     }
501
502     final PsiElement[] descriptionElements = docComment.getDescriptionElements();
503     for (PsiElement descriptionElement : descriptionElements) {
504       if (descriptionElement instanceof PsiInlineDocTag) {
505         if ("inheritDoc".equals(((PsiInlineDocTag)descriptionElement).getName())) return null;
506       }
507     }
508
509     final ArrayList<ProblemDescriptor> problems = new ArrayList<ProblemDescriptor>(2);
510
511     checkInlineTags(manager, problems, descriptionElements,
512                     JavaPsiFacade.getInstance(docComment.getProject()).getJavadocManager());
513
514     final PsiDocTag tagByName = docComment.findTagByName("inheritDoc");
515     if (tagByName != null) {
516       final String tagName = tagByName.getName();
517       final JavadocTagInfo tagInfo = JavaPsiFacade.getInstance(tagByName.getProject()).getJavadocManager().getTagInfo(tagName);
518       if (tagInfo != null && tagInfo.isValidInContext(psiMethod)){
519         return null;
520       }
521     }
522
523     PsiDocTag[] tags = docComment.getTags();
524
525     boolean isReturnRequired = false;
526     boolean isReturnAbsent = true;
527     if (superMethods.length == 0 && !psiMethod.isConstructor() && PsiType.VOID != psiMethod.getReturnType() && isTagRequired(psiMethod, "return")) {
528       isReturnRequired = true;
529       for (PsiDocTag tag : tags) {
530         if ("return".equals(tag.getName())) {
531           isReturnAbsent = false;
532           break;
533         }
534       }
535     }
536
537     ArrayList<PsiParameter> absentParameters = null;
538     if (superMethods.length == 0 && isTagRequired(psiMethod, "param") ) {
539       PsiParameter[] params = psiMethod.getParameterList().getParameters();
540       for (PsiParameter param : params) {
541         if (!isFound(tags, param)) {
542           if (absentParameters == null) absentParameters = new ArrayList<PsiParameter>(2);
543           absentParameters.add(param);
544         }
545       }
546     }
547
548
549
550     if (isReturnRequired && isReturnAbsent) {
551       final PsiIdentifier psiIdentifier = psiMethod.getNameIdentifier();
552       if (psiIdentifier != null) {
553         problems.add(createMissingTagDescriptor(psiIdentifier, "return", manager));
554       }
555     }
556
557     if (absentParameters != null) {
558       for (PsiParameter psiParameter : absentParameters) {
559         final PsiIdentifier nameIdentifier = psiMethod.getNameIdentifier();
560         if (nameIdentifier != null) {
561           problems.add(createMissingParamTagDescriptor(nameIdentifier, psiParameter, manager));
562         }
563       }
564     }
565
566     for (PsiDocTag tag : tags) {
567       if ("param".equals(tag.getName())) {
568         final PsiElement[] dataElements = tag.getDataElements();
569         final PsiDocTagValue valueElement = tag.getValueElement();
570         boolean hasProblemsWithTag = dataElements.length < 2;
571         if (!hasProblemsWithTag) {
572           final StringBuilder buf = new StringBuilder();
573           for (PsiElement element : dataElements) {
574             if (element != valueElement){
575               buf.append(element.getText());
576             }
577           }
578           hasProblemsWithTag = buf.toString().trim().length() == 0;
579         }
580         if (hasProblemsWithTag) {
581           if (valueElement != null) {
582             problems.add(createDescriptor(valueElement,
583                                           InspectionsBundle.message("inspection.javadoc.method.problem.missing.tag.description", "<code>@param " + valueElement.getText() + "</code>"),
584                                           manager));
585           }
586
587         }
588       }
589     }
590
591     if (superMethods.length == 0 && isTagRequired(psiMethod, "@throws") && psiMethod.getThrowsList().getReferencedTypes().length > 0) {
592       final Map<PsiClassType, PsiClass> declaredExceptions = new HashMap<PsiClassType, PsiClass>();
593       final PsiClassType[] classTypes = psiMethod.getThrowsList().getReferencedTypes();
594       for (PsiClassType classType : classTypes) {
595         final PsiClass psiClass = classType.resolve();
596         if (psiClass != null){
597           declaredExceptions.put(classType, psiClass);
598         }
599       }
600       processThrowsTags(tags, declaredExceptions, manager, problems);
601       if (!declaredExceptions.isEmpty()) {
602         for (PsiClassType declaredException : declaredExceptions.keySet()) {
603           problems.add(createMissingThrowsTagDescriptor(psiMethod, manager, declaredException));
604         }
605       }
606     }
607
608     ArrayList<ProblemDescriptor> tagProblems = getTagValuesProblems(psiMethod, tags, manager);
609     if (tagProblems != null) {
610       problems.addAll(tagProblems);
611     }
612
613     checkForPeriodInDoc(docComment, problems, manager);
614
615     for (PsiDocTag tag : tags) {
616       if ("param".equals(tag.getName())) {
617         if (extractTagDescription(tag).length() == 0) {
618           PsiDocTagValue value = tag.getValueElement();
619           if (value instanceof PsiDocParamRef) {
620             PsiDocParamRef paramRef = (PsiDocParamRef)value;
621             PsiParameter[] params = psiMethod.getParameterList().getParameters();
622             for (PsiParameter param : params) {
623               if (paramRef.getReference().isReferenceTo(param)) {
624                 problems.add(createDescriptor(value,
625                                               InspectionsBundle.message("inspection.javadoc.method.problem.descriptor", "<code>@param</code>", "<code>" + param.getName() + "</code>"),
626                                               manager));
627               }
628             }
629           }
630         }
631       }
632       else
633         if ("return".equals(tag.getName())) {
634           if (extractTagDescription(tag).length() == 0) {
635             String message = InspectionsBundle.message("inspection.javadoc.method.problem.missing.tag.description", "<code>@return</code>");
636             ProblemDescriptor descriptor = manager.createProblemDescriptor(tag.getNameElement(), message, null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, true);
637             problems.add(descriptor);
638           }
639         }
640     }
641
642     checkDuplicateTags(tags, problems, manager);
643
644     return problems.isEmpty()
645            ? null
646            : problems.toArray(new ProblemDescriptor[problems.size()]);
647   }
648
649   private static boolean isFound(final PsiDocTag[] tags, final PsiElement param) {
650     for (PsiDocTag tag : tags) {
651       if ("param".equals(tag.getName())) {
652         PsiDocTagValue value = tag.getValueElement();
653         if (value instanceof PsiDocParamRef) {
654           PsiDocParamRef paramRef = (PsiDocParamRef)value;
655           final PsiReference psiReference = paramRef.getReference();
656           if (psiReference != null && psiReference.isReferenceTo(param)) {
657             return true;
658           }
659         }
660       }
661     }
662     return false;
663   }
664
665   private static void processThrowsTags(final PsiDocTag[] tags,
666                                         final Map<PsiClassType, PsiClass> declaredExceptions,
667                                         final InspectionManager mananger,
668                                         @NotNull final ArrayList<ProblemDescriptor> problems) {
669     for (PsiDocTag tag : tags) {
670       if ("throws".equals(tag.getName()) || "exception".equals(tag.getName())) {
671         final PsiDocTagValue value = tag.getValueElement();
672         if (value == null) continue;
673         final PsiElement firstChild = value.getFirstChild();
674         if (firstChild == null) continue;
675         final PsiElement psiElement = firstChild.getFirstChild();
676         if (!(psiElement instanceof PsiJavaCodeReferenceElement)) continue;
677         final PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement)psiElement;
678         final PsiElement element = ref.resolve();
679         if (element instanceof PsiClass){
680           final PsiClass exceptionClass = (PsiClass)element;
681           for (Iterator<PsiClassType> it = declaredExceptions.keySet().iterator(); it.hasNext();) {
682             PsiClassType classType = it.next();
683             final PsiClass psiClass = declaredExceptions.get(classType);
684             if (InheritanceUtil.isInheritorOrSelf(exceptionClass, psiClass, true)) {
685               if (extractThrowsTagDescription(tag).length() == 0) {
686                 problems.add(createDescriptor(tag.getNameElement(), InspectionsBundle.message("inspection.javadoc.method.problem.missing.tag.description", "<code>" + tag.getName() + "</code>"), mananger));
687               }
688               it.remove();
689             }
690           }
691         }
692       }
693     }
694   }
695
696   @Nullable
697   private static ProblemDescriptor createMissingThrowsTagDescriptor(final PsiMethod method,
698                                                                     final InspectionManager manager,
699                                                                     final PsiClassType exceptionClassType) {
700     @NonNls String tag = "throws";
701     String message = InspectionsBundle.message("inspection.javadoc.problem.missing.tag", "<code>@" + tag + "</code> " + exceptionClassType.getCanonicalText());
702     final String firstDeclaredException = exceptionClassType.getCanonicalText();
703     final PsiIdentifier nameIdentifier = method.getNameIdentifier();
704     return nameIdentifier != null ? createDescriptor(nameIdentifier, message,new AddMissingTagFix(tag, firstDeclaredException), manager) : null;    
705   }
706
707   private static ProblemDescriptor createMissingTagDescriptor(PsiElement elementToHighlight,
708                                                               @NonNls String tag,
709                                                               final InspectionManager manager) {
710     String message = InspectionsBundle.message("inspection.javadoc.problem.missing.tag", "<code>@" + tag + "</code>");
711     return createDescriptor(elementToHighlight, message,new AddMissingTagFix(tag), manager);
712   }
713   private static ProblemDescriptor createMissingParamTagDescriptor(PsiElement elementToHighlight,
714                                                                    PsiParameter param,
715                                                                    final InspectionManager manager) {
716     String message = InspectionsBundle.message("inspection.javadoc.method.problem.missing.param.tag", "<code>@param</code>", "<code>" + param.getName() + "</code>");
717     return createDescriptor(elementToHighlight, message, new AddMissingParamTagFix(param), manager);
718   }
719
720   private static class AddMissingParamTagFix extends AddMissingTagFix {
721     private final PsiParameter myParam;
722
723     public AddMissingParamTagFix(final PsiParameter param) {
724       super("param", param.getName());
725       myParam = param;
726     }
727
728     @NotNull
729     public String getName() {
730       return InspectionsBundle.message("inspection.javadoc.problem.add.param.tag", myParam.getName());
731     }
732
733     @Nullable
734     protected PsiElement getAnchor() {
735       final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(myParam, PsiMethod.class);
736       LOG.assertTrue(psiMethod != null);
737       final PsiDocComment docComment = psiMethod.getDocComment();
738       LOG.assertTrue(docComment != null);
739       PsiDocTag[] tags = docComment.findTagsByName("param");
740       if (tags.length == 0) { //insert as first tag or append to description
741         tags = docComment.getTags();
742         if (tags.length == 0) return null;
743         return tags[0];
744       }
745
746       PsiParameter nextParam = PsiTreeUtil.getNextSiblingOfType(myParam, PsiParameter.class);
747       while (nextParam != null) {
748         for (PsiDocTag tag : tags) {
749           if (matches(nextParam, tag)) {
750             return tag;
751           }
752         }
753         nextParam = PsiTreeUtil.getNextSiblingOfType(nextParam, PsiParameter.class);
754       }
755
756       PsiParameter prevParam = PsiTreeUtil.getPrevSiblingOfType(myParam, PsiParameter.class);
757       while (prevParam != null) {
758         for (PsiDocTag tag : tags) {
759           if (matches(prevParam, tag)) {
760             return PsiTreeUtil.getNextSiblingOfType(tag, PsiDocTag.class);
761           }
762         }
763         prevParam = PsiTreeUtil.getPrevSiblingOfType(prevParam, PsiParameter.class);
764       }
765
766       return null;
767     }
768
769     private static boolean matches(final PsiParameter param, final PsiDocTag tag) {
770       return tag.getValueElement().getText().trim().startsWith(param.getName());
771     }
772   }
773
774   private static String extractTagDescription(PsiDocTag tag) {
775     StringBuilder buf = new StringBuilder();
776     PsiElement[] children = tag.getChildren();
777     for (PsiElement child : children) {
778       if (child instanceof PsiDocToken) {
779         PsiDocToken token = (PsiDocToken)child;
780         if (token.getTokenType() == JavaDocTokenType.DOC_COMMENT_DATA) {
781           buf.append(token.getText());
782         }
783       }
784       else if (child instanceof PsiDocTagValue) {
785         buf.append(child.getText());
786       } else if (child instanceof PsiInlineDocTag) {
787         buf.append(child.getText());
788       }
789     }
790
791     String s = buf.toString();
792     return s.trim();
793   }
794
795   private static String extractThrowsTagDescription(PsiDocTag tag) {
796     StringBuilder buf = new StringBuilder();
797     PsiElement[] children = tag.getChildren();
798     for (PsiElement child : children) {
799       if (child instanceof PsiDocToken) {
800         PsiDocToken token = (PsiDocToken)child;
801         if (token.getTokenType() == JavaDocTokenType.DOC_COMMENT_DATA) {
802           buf.append(token.getText());
803         }
804       }
805     }
806
807     return buf.toString().trim();
808   }
809
810   private void checkForPeriodInDoc(PsiDocComment docComment,
811                                    ArrayList<ProblemDescriptor> problems,
812                                    InspectionManager manager) {
813     if (IGNORE_JAVADOC_PERIOD) return;
814     PsiDocTag[] tags = docComment.getTags();
815     int dotIndex = docComment.getText().indexOf('.');
816     int tagOffset = 0;
817     if (dotIndex >= 0) {      //need to find first valid tag
818       final PsiDocCommentOwner owner = PsiTreeUtil.getParentOfType(docComment, PsiDocCommentOwner.class);
819       for (PsiDocTag tag : tags) {
820         final String tagName = tag.getName();
821         final JavadocTagInfo tagInfo = JavaPsiFacade.getInstance(tag.getProject()).getJavadocManager().getTagInfo(tagName);
822         if (tagInfo != null && tagInfo.isValidInContext(owner) && !tagInfo.isInline()) {
823           tagOffset = tag.getTextOffset();
824           break;
825         }
826       }
827     }
828
829     if (dotIndex == -1 || tagOffset > 0 && dotIndex + docComment.getTextOffset() > tagOffset) {
830       problems.add(manager.createProblemDescriptor(docComment.getFirstChild(),
831                                                    InspectionsBundle.message("inspection.javadoc.problem.descriptor1"),
832                                                    null,
833                                                    ProblemHighlightType.GENERIC_ERROR_OR_WARNING,
834                                                    false));
835     }
836   }
837
838   @Nullable
839   private ArrayList<ProblemDescriptor> getTagValuesProblems(PsiDocCommentOwner context, PsiDocTag[] tags, InspectionManager inspectionManager) {
840     final ArrayList<ProblemDescriptor> problems = new ArrayList<ProblemDescriptor>(2);
841     nextTag:
842     for (PsiDocTag tag : tags) {
843       final JavadocManager manager = JavaPsiFacade.getInstance(tag.getProject()).getJavadocManager();
844       String tagName = tag.getName();
845       JavadocTagInfo tagInfo = manager.getTagInfo(tagName);
846
847       if (tagInfo == null || !tagInfo.isValidInContext(context)) {
848         final StringTokenizer tokenizer = new StringTokenizer(myAdditionalJavadocTags, ", ");
849         while (tokenizer.hasMoreTokens()) {
850           if (Comparing.strEqual(tagName, tokenizer.nextToken())) continue nextTag;
851         }
852
853         if (tagInfo == null){
854           problems.add(createDescriptor(tag.getNameElement(), InspectionsBundle.message("inspection.javadoc.problem.wrong.tag", "<code>" + tagName + "</code>"), new AddUnknownTagToCustoms(tag), inspectionManager));
855         } else {
856           problems.add(createDescriptor(tag.getNameElement(), InspectionsBundle.message("inspection.javadoc.problem.disallowed.tag", "<code>" + tagName + "</code>"), new AddUnknownTagToCustoms(tag), inspectionManager));
857         }
858
859       }
860
861       PsiDocTagValue value = tag.getValueElement();
862       final JavadocTagInfo info = manager.getTagInfo(tagName);
863       if (info != null && !info.isValidInContext(context)) continue;
864       String message = info == null ? null : info.checkTagValue(value);
865
866       final PsiReference reference = value != null ? value.getReference() : null;
867       if (message == null && reference != null) {
868         PsiElement element = reference.resolve();
869         if (element == null) {
870           final int textOffset = value.getTextOffset();
871
872           if (textOffset == value.getTextRange().getEndOffset()) {
873             problems.add(inspectionManager.createProblemDescriptor(tag, InspectionsBundle.message("inspection.javadoc.problem.name.expected"), null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, true));
874           }
875         }
876       }
877
878       if (message != null) {
879         final PsiDocTagValue valueElement = tag.getValueElement();
880         if (valueElement == null){
881           problems.add(inspectionManager.createProblemDescriptor(tag, InspectionsBundle.message("inspection.javadoc.method.problem.missing.tag.description", "<code>" + tag.getName() + "</code>"), null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, true));
882         } else {
883           problems.add(createDescriptor(valueElement, message, inspectionManager));
884         }
885       }
886       checkInlineTags(inspectionManager, problems, tag.getDataElements(), manager);
887     }
888
889     return problems.isEmpty() ? null : problems;
890   }
891
892   private void checkInlineTags(final InspectionManager inspectionManager,
893                                final ArrayList<ProblemDescriptor> problems,
894                                final PsiElement[] dataElements,
895                                final JavadocManager manager) {
896     for (PsiElement dataElement : dataElements) {
897       if (dataElement instanceof PsiInlineDocTag) {
898         final PsiInlineDocTag inlineDocTag = (PsiInlineDocTag)dataElement;
899         final PsiElement nameElement = inlineDocTag.getNameElement();
900         if (manager.getTagInfo(inlineDocTag.getName()) == null) {
901           if (nameElement != null) {
902             problems.add(createDescriptor(nameElement, InspectionsBundle.message("inspection.javadoc.problem.wrong.tag", "<code>" + inlineDocTag.getName() + "</code>"), new AddUnknownTagToCustoms(inlineDocTag), inspectionManager));
903           }
904         }
905         final PsiDocTagValue value = inlineDocTag.getValueElement();
906         if (value != null) {
907           final PsiReference reference = value.getReference();
908           if (reference != null) {
909             final PsiElement ref = reference.resolve();
910             if (ref != null){
911               if (PsiTreeUtil.getParentOfType(inlineDocTag, PsiDocCommentOwner.class) == PsiTreeUtil.getParentOfType(ref, PsiDocCommentOwner.class, false)) {
912                 if (nameElement != null) {
913                   problems.add(createDescriptor(nameElement, InspectionsBundle.message("inspection.javadoc.problem.pointing.to.itself"), inspectionManager));
914                 }
915               }
916             }
917           }
918         }
919       }
920     }
921   }
922
923   @SuppressWarnings({"SimplifiableIfStatement"})
924   private boolean isTagRequired(PsiElement context, @NonNls String tag) {
925     if (context instanceof PsiClass) {
926       if (PsiTreeUtil.getParentOfType(context, PsiClass.class) != null) {
927         return isTagRequired(INNER_CLASS_OPTIONS, tag);
928       }
929
930       return isTagRequired(TOP_LEVEL_CLASS_OPTIONS, tag);
931     }
932
933     if (context instanceof PsiMethod) {
934       return isTagRequired(METHOD_OPTIONS, tag);
935     }
936
937     if (context instanceof PsiField) {
938       return isTagRequired(FIELD_OPTIONS, tag);
939     }
940
941     return false;
942   }
943
944   private static boolean isTagRequired(Options options, String tag) {
945     return options.REQUIRED_TAGS.contains(tag);
946   }
947
948   private boolean isJavaDocRequired(PsiModifierListOwner psiElement) {
949     final RefJavaUtil refUtil = RefJavaUtil.getInstance();
950     int actualAccess = getAccessNumber(refUtil.getAccessModifier(psiElement));
951     if (psiElement instanceof PsiClass) {
952       PsiClass psiClass = (PsiClass)psiElement;
953       if (PsiTreeUtil.getParentOfType(psiClass, PsiClass.class) != null) {
954         return actualAccess <= getAccessNumber(INNER_CLASS_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR);
955       }
956
957       return actualAccess <= getAccessNumber(TOP_LEVEL_CLASS_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR);
958     }
959
960     if (psiElement instanceof PsiMethod) {
961       psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class);
962       while (psiElement != null) {
963         actualAccess = Math.max(actualAccess, getAccessNumber(refUtil.getAccessModifier(psiElement)));
964         psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class);
965       }
966
967       return actualAccess <= getAccessNumber(METHOD_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR);
968     }
969
970     if (psiElement instanceof PsiField) {
971       psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class);
972       while (psiElement != null) {
973         actualAccess = Math.max(actualAccess, getAccessNumber(refUtil.getAccessModifier(psiElement)));
974         psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class);
975       }
976
977       return actualAccess <= getAccessNumber(FIELD_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR);
978     }
979
980     return false;
981   }
982
983   private static void checkDuplicateTags(final PsiDocTag[] tags,
984                                          ArrayList<ProblemDescriptor> problems,
985                                          final InspectionManager manager) {
986     Set<String> documentedParamNames = null;
987     Set<String> documentedExceptions = null;
988     Set<String> uniqueTags = null;
989     for(PsiDocTag tag: tags) {
990       if ("param".equals(tag.getName())) {
991         PsiDocTagValue value = tag.getValueElement();
992         if (value instanceof PsiDocParamRef) {
993           PsiDocParamRef paramRef = (PsiDocParamRef)value;
994           final PsiReference reference = paramRef.getReference();
995           if (reference != null) {
996             final String paramName = reference.getCanonicalText();
997             if (documentedParamNames == null) {
998               documentedParamNames = new HashSet<String>();
999             }
1000             if (documentedParamNames.contains(paramName)) {
1001               problems.add(createDescriptor(tag.getNameElement(), InspectionsBundle.message("inspection.javadoc.problem.duplicate.param", paramName), manager));
1002             }
1003             documentedParamNames.add(paramName);
1004           }
1005         }
1006       }
1007       else if ("throws".equals(tag.getName()) || "exception".equals(tag.getName())) {
1008         PsiDocTagValue value = tag.getValueElement();
1009         if (value != null) {
1010           final PsiElement firstChild = value.getFirstChild();
1011           if (firstChild != null && firstChild.getFirstChild() instanceof PsiJavaCodeReferenceElement) {
1012             PsiJavaCodeReferenceElement refElement = (PsiJavaCodeReferenceElement) firstChild.getFirstChild();
1013             if (refElement != null) {
1014               PsiElement element = refElement.resolve();
1015               if (element instanceof PsiClass) {
1016                 String fqName = ((PsiClass)element).getQualifiedName();
1017                 if (documentedExceptions == null) {
1018                   documentedExceptions = new HashSet<String>();
1019                 }
1020                 if (documentedExceptions.contains(fqName)) {
1021                   problems.add(createDescriptor(tag.getNameElement(),
1022                                                 InspectionsBundle.message("inspection.javadoc.problem.duplicate.throws", fqName),
1023                                                 manager));
1024                 }
1025                 documentedExceptions.add(fqName);
1026               }
1027             }
1028           }
1029         }
1030       }
1031       else if (JavaDocLocalInspection.ourUniqueTags.contains(tag.getName())) {
1032         if (uniqueTags == null) {
1033           uniqueTags = new HashSet<String>();
1034         }
1035         if (uniqueTags.contains(tag.getName())) {
1036           problems.add(createDescriptor(tag.getNameElement(), InspectionsBundle.message("inspection.javadoc.problem.duplicate.tag", tag.getName()), manager));
1037         }
1038         uniqueTags.add(tag.getName());
1039       }
1040     }
1041   }
1042
1043   private static int getAccessNumber(@NonNls String accessModifier) {
1044     if (accessModifier.startsWith("none")) return 0;
1045     if (accessModifier.startsWith("public")) return 1;
1046     if (accessModifier.startsWith("protected")) return 2;
1047     if (accessModifier.startsWith("package")) return 3;
1048     if (accessModifier.startsWith("private")) return 4;
1049
1050     return 5;
1051   }
1052
1053   @NotNull
1054   public String getDisplayName() {
1055     return InspectionsBundle.message("inspection.javadoc.display.name");
1056   }
1057
1058   @NotNull
1059   public String getGroupDisplayName() {
1060     return "";
1061   }
1062
1063   @NotNull
1064   public String getShortName() {
1065     return SHORT_NAME;
1066   }
1067
1068   private class AddUnknownTagToCustoms implements LocalQuickFix {
1069     PsiDocTag myTag;
1070
1071     public AddUnknownTagToCustoms(PsiDocTag tag) {
1072       myTag = tag;
1073     }
1074
1075     @NotNull
1076     public String getName() {
1077       return QuickFixBundle.message("add.doctag.to.custom.tags", myTag.getName());
1078     }
1079
1080     @NotNull
1081     public String getFamilyName() {
1082      return QuickFixBundle.message("fix.javadoc.family");
1083    }
1084
1085     public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
1086       if (myTag == null || !myTag.isValid()) return;
1087       if (myAdditionalJavadocTags.length() > 0) {
1088         myAdditionalJavadocTags += "," + myTag.getName();
1089       }
1090       else {
1091         myAdditionalJavadocTags = myTag.getName();
1092       }
1093       final InspectionProfile inspectionProfile =
1094         InspectionProjectProfileManager.getInstance(project).getInspectionProfile();
1095       //correct save settings
1096       ((ModifiableModel)inspectionProfile).isProperSetting(HighlightDisplayKey.find(SHORT_NAME));
1097       InspectionProfileManager.getInstance().fireProfileChanged(inspectionProfile);
1098       //TODO lesya
1099
1100       /*
1101
1102       try {
1103         inspectionProfile.save();
1104       }
1105       catch (IOException e) {
1106         Messages.showErrorDialog(project, e.getMessage(), CommonBundle.getErrorTitle());
1107       }
1108
1109       */
1110     }
1111   }
1112 }