update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInspection / javaDoc / JavaDocReferenceInspection.java
blob2032924f7a07f55aa363f8b5b8785c079b2e6bbc
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.codeInspection.javaDoc;
18 import com.intellij.codeHighlighting.HighlightDisplayLevel;
19 import com.intellij.codeInsight.daemon.QuickFixBundle;
20 import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix;
21 import com.intellij.codeInspection.*;
22 import com.intellij.codeInspection.ex.BaseLocalInspectionTool;
23 import com.intellij.ide.DataManager;
24 import com.intellij.ide.util.FQNameCellRenderer;
25 import com.intellij.openapi.actionSystem.PlatformDataKeys;
26 import com.intellij.openapi.application.Result;
27 import com.intellij.openapi.command.WriteCommandAction;
28 import com.intellij.openapi.editor.Editor;
29 import com.intellij.openapi.project.Project;
30 import com.intellij.openapi.ui.popup.PopupChooserBuilder;
31 import com.intellij.psi.*;
32 import com.intellij.psi.javadoc.*;
33 import com.intellij.psi.util.proximity.PsiProximityComparator;
34 import org.jetbrains.annotations.NonNls;
35 import org.jetbrains.annotations.NotNull;
36 import org.jetbrains.annotations.Nullable;
38 import javax.swing.*;
39 import java.util.*;
41 public class JavaDocReferenceInspection extends BaseLocalInspectionTool {
42 @NonNls public static final String SHORT_NAME = "JavadocReference";
43 public static final String DISPLAY_NAME = InspectionsBundle.message("inspection.javadoc.ref.display.name");
46 private static ProblemDescriptor createDescriptor(@NotNull PsiElement element, String template, InspectionManager manager) {
47 return manager.createProblemDescriptor(element, template, (LocalQuickFix [])null, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL);
50 @Nullable
51 public ProblemDescriptor[] checkMethod(@NotNull PsiMethod psiMethod, @NotNull InspectionManager manager, boolean isOnTheFly) {
52 return checkMember(psiMethod, manager, isOnTheFly);
55 @Nullable
56 public ProblemDescriptor[] checkField(@NotNull PsiField field, @NotNull InspectionManager manager, boolean isOnTheFly) {
57 return checkMember(field, manager, isOnTheFly);
60 @Nullable
61 private ProblemDescriptor[] checkMember(final PsiDocCommentOwner docCommentOwner, final InspectionManager manager, final boolean isOnTheFly) {
62 ArrayList<ProblemDescriptor> problems = new ArrayList<ProblemDescriptor>();
63 final PsiDocComment docComment = docCommentOwner.getDocComment();
64 if (docComment == null) return null;
66 final Set<PsiJavaCodeReferenceElement> references = new HashSet<PsiJavaCodeReferenceElement>();
67 docComment.accept(getVisitor(references, docCommentOwner, problems, manager));
68 for (PsiJavaCodeReferenceElement reference : references) {
69 final List<PsiClass> classesToImport = new ImportClassFix(reference).getClassesToImport();
70 problems.add(manager.createProblemDescriptor(reference, InspectionsBundle.message("inspection.javadoc.problem.cannot.resolve",
71 "<code>" + reference.getText() + "</code>"),
72 !isOnTheFly || classesToImport.isEmpty() ? null : new AddImportFix(classesToImport), ProblemHighlightType.LIKE_UNKNOWN_SYMBOL));
75 return problems.isEmpty()
76 ? null
77 : problems.toArray(new ProblemDescriptor[problems.size()]);
80 @Nullable
81 public ProblemDescriptor[] checkClass(@NotNull PsiClass aClass, @NotNull InspectionManager manager, boolean isOnTheFly) {
82 return checkMember(aClass, manager, isOnTheFly);
86 private PsiElementVisitor getVisitor(final Set<PsiJavaCodeReferenceElement> references,
87 final PsiElement context,
88 final ArrayList<ProblemDescriptor> problems,
89 final InspectionManager manager) {
90 return new JavaElementVisitor() {
91 @Override public void visitReferenceExpression(PsiReferenceExpression expression) {
92 visitElement(expression);
95 @Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
96 super.visitReferenceElement(reference);
97 JavaResolveResult result = reference.advancedResolve(false);
98 if (result.getElement() == null && !result.isPackagePrefixPackageReference()) {
99 references.add(reference);
103 @Override public void visitDocTag(PsiDocTag tag) {
104 super.visitDocTag(tag);
105 final JavadocManager javadocManager = JavaPsiFacade.getInstance(tag.getProject()).getJavadocManager();
106 final JavadocTagInfo info = javadocManager.getTagInfo(tag.getName());
107 if (info == null || !info.isInline()) {
108 visitRefInDocTag(tag, javadocManager, context, problems, manager);
112 @Override public void visitInlineDocTag(PsiInlineDocTag tag) {
113 super.visitInlineDocTag(tag);
114 final JavadocManager javadocManager = JavaPsiFacade.getInstance(tag.getProject()).getJavadocManager();
115 visitRefInDocTag(tag, javadocManager, context, problems, manager);
118 @Override public void visitElement(PsiElement element) {
119 PsiElement[] children = element.getChildren();
120 for (PsiElement child : children) {
121 //do not visit method javadoc twice
122 if (!(child instanceof PsiDocCommentOwner)) {
123 child.accept(this);
130 public static void visitRefInDocTag(final PsiDocTag tag,
131 final JavadocManager manager,
132 final PsiElement context,
133 ArrayList<ProblemDescriptor> problems,
134 InspectionManager inspectionManager) {
135 String tagName = tag.getName();
136 PsiDocTagValue value = tag.getValueElement();
137 if (value == null) return;
138 final JavadocTagInfo info = manager.getTagInfo(tagName);
139 if (info != null && !info.isValidInContext(context)) return;
140 String message = info == null || !info.isInline() ? null : info.checkTagValue(value);
141 if (message != null){
142 problems.add(createDescriptor(value, message, inspectionManager));
144 final PsiReference reference = value.getReference();
145 if (reference != null) {
146 PsiElement element = reference.resolve();
147 if (element == null) {
148 final int textOffset = value.getTextOffset();
150 if (textOffset != value.getTextRange().getEndOffset()) {
151 final PsiDocTagValue valueElement = tag.getValueElement();
152 if (valueElement != null) {
153 @NonNls String params = "<code>" + value.getContainingFile().getViewProvider().getContents().subSequence(textOffset, value.getTextRange().getEndOffset()) + "</code>";
154 problems.add(createDescriptor(valueElement, InspectionsBundle.message("inspection.javadoc.problem.cannot.resolve", params), inspectionManager));
162 @NotNull
163 public String getDisplayName() {
164 return DISPLAY_NAME;
167 @NotNull
168 public String getGroupDisplayName() {
169 return "";
172 @NotNull
173 public String getShortName() {
174 return SHORT_NAME;
177 @NotNull
178 public HighlightDisplayLevel getDefaultLevel() {
179 return HighlightDisplayLevel.ERROR;
182 private class AddImportFix implements LocalQuickFix{
183 private final List<PsiClass> myClassesToImport;
185 public AddImportFix(final List<PsiClass> classesToImport) {
186 myClassesToImport = classesToImport;
189 @NotNull
190 public String getName() {
191 return QuickFixBundle.message("import.class.fix");
194 @NotNull
195 public String getFamilyName() {
196 return QuickFixBundle.message("import.class.fix");
199 public void applyFix(@NotNull final Project project, @NotNull final ProblemDescriptor descriptor) {
200 final PsiElement element = descriptor.getPsiElement();
201 if (element instanceof PsiJavaCodeReferenceElement) {
202 final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)element;
203 Collections.sort(myClassesToImport, new PsiProximityComparator(referenceElement.getElement()));
204 final JList list = new JList(myClassesToImport.toArray(new PsiClass[myClassesToImport.size()]));
205 list.setCellRenderer(new FQNameCellRenderer());
206 Runnable runnable = new Runnable() {
207 public void run() {
208 if (!element.isValid()) return;
209 final int index = list.getSelectedIndex();
210 if (index < 0) return;
211 new WriteCommandAction(project, element.getContainingFile()){
212 protected void run(final Result result) throws Throwable {
213 final PsiClass psiClass = myClassesToImport.get(index);
214 if (psiClass.isValid()) {
215 PsiDocumentManager.getInstance(project).commitAllDocuments();
216 referenceElement.bindToElement(psiClass);
219 }.execute();
222 final Editor editor = PlatformDataKeys.EDITOR.getData(DataManager.getInstance().getDataContext());
223 assert editor != null; //available for on the fly mode only
224 new PopupChooserBuilder(list).
225 setTitle(QuickFixBundle.message("class.to.import.chooser.title")).
226 setItemChoosenCallback(runnable).
227 createPopup().
228 showInBestPositionFor(editor);