'optimize imports on the fly' moved to Settings|editor|auto import
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / quickfix / ImportClassFixBase.java
blob373bd59226c5fed553cb9d121e2a34a2d422ba3d
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.codeInsight.daemon.impl.quickfix;
18 import com.intellij.codeInsight.daemon.QuickFixBundle;
19 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings;
20 import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
21 import com.intellij.codeInsight.daemon.impl.actions.AddImportAction;
22 import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl;
23 import com.intellij.codeInsight.daemon.impl.ShowAutoImportPass;
24 import com.intellij.codeInsight.completion.JavaCompletionUtil;
25 import com.intellij.codeInsight.CodeInsightUtil;
26 import com.intellij.codeInsight.CodeInsightUtilBase;
27 import com.intellij.codeInsight.hint.QuestionAction;
28 import com.intellij.codeInsight.hint.HintManager;
29 import com.intellij.codeInspection.HintAction;
30 import com.intellij.psi.*;
31 import com.intellij.psi.search.PsiShortNamesCache;
32 import com.intellij.psi.search.GlobalSearchScope;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.openapi.editor.Editor;
35 import com.intellij.openapi.command.CommandProcessor;
36 import com.intellij.openapi.util.TextRange;
37 import com.intellij.openapi.application.ApplicationManager;
38 import com.intellij.packageDependencies.DependencyValidationManager;
39 import com.intellij.packageDependencies.DependencyRule;
40 import org.jetbrains.annotations.NotNull;
41 import org.jetbrains.annotations.Nullable;
43 import java.util.List;
44 import java.util.Collections;
45 import java.util.ArrayList;
46 import java.util.regex.Pattern;
47 import java.util.regex.Matcher;
48 import java.util.regex.PatternSyntaxException;
50 /**
51 * @author peter
53 public abstract class ImportClassFixBase<T extends PsiElement & PsiReference> implements HintAction {
54 private final T myRef;
56 protected ImportClassFixBase(T ref) {
57 myRef = ref;
60 public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
61 return myRef.isValid() && file.getManager().isInProject(file) && !getClassesToImport().isEmpty();
64 @Nullable
65 protected abstract String getReferenceName(T reference);
67 protected abstract boolean hasTypeParameters(T reference);
69 public List<PsiClass> getClassesToImport() {
70 PsiManager manager = PsiManager.getInstance(myRef.getProject());
71 PsiShortNamesCache cache = JavaPsiFacade.getInstance(manager.getProject()).getShortNamesCache();
72 String name = getReferenceName(myRef);
73 GlobalSearchScope scope = myRef.getResolveScope();
74 if (name == null) {
75 return Collections.emptyList();
77 boolean referenceHasTypeParameters = hasTypeParameters(myRef);
78 PsiClass[] classes = cache.getClassesByName(name, scope);
79 if (classes.length == 0) return Collections.emptyList();
80 ArrayList<PsiClass> classList = new ArrayList<PsiClass>(classes.length);
81 boolean isAnnotationReference = myRef.getParent() instanceof PsiAnnotation;
82 for (PsiClass aClass : classes) {
83 if (isAnnotationReference && !aClass.isAnnotationType()) continue;
84 if (JavaCompletionUtil.isInExcludedPackage(aClass)) continue;
85 if (referenceHasTypeParameters && !aClass.hasTypeParameters()) continue;
86 String qName = aClass.getQualifiedName();
87 if (qName != null) { //filter local classes
88 if (qName.indexOf('.') == -1) continue; //do not show classes from default package)
89 if (qName.endsWith(name)) {
90 if (isAccessible(aClass, myRef)) {
91 classList.add(aClass);
96 return classList;
99 protected abstract boolean isAccessible(PsiClass aClass, T reference);
101 protected abstract String getQualifiedName(T reference);
103 public boolean doFix(@NotNull final Editor editor, boolean doShow, final boolean allowCaretNearRef) {
104 List<PsiClass> classesToImport = getClassesToImport();
105 if (classesToImport.isEmpty()) return false;
107 try {
108 String name = getQualifiedName(myRef);
109 if (name != null) {
110 Pattern pattern = Pattern.compile(DaemonCodeAnalyzerSettings.getInstance().NO_AUTO_IMPORT_PATTERN);
111 Matcher matcher = pattern.matcher(name);
112 if (matcher.matches()) {
113 return false;
117 catch (PatternSyntaxException e) {
118 //ignore
120 final PsiFile psiFile = myRef.getContainingFile();
121 if (classesToImport.size() > 1) {
122 reduceSuggestedClassesBasedOnDependencyRuleViolation(psiFile, classesToImport);
124 PsiClass[] classes = classesToImport.toArray(new PsiClass[classesToImport.size()]);
125 final Project project = myRef.getProject();
126 CodeInsightUtil.sortIdenticalShortNameClasses(classes, psiFile);
128 final QuestionAction action = createAddImportAction(classes, project, editor);
130 DaemonCodeAnalyzerImpl codeAnalyzer = (DaemonCodeAnalyzerImpl)DaemonCodeAnalyzer.getInstance(project);
132 if (classes.length == 1
133 && com.intellij.codeInsight.CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY
134 && (allowCaretNearRef || !isCaretNearRef(editor, myRef))
135 && !JspPsiUtil.isInJspFile(psiFile)
136 && codeAnalyzer.canChangeFileSilently(psiFile)
137 && !hasUnresolvedImportWhichCanImport(psiFile, classes[0].getName())) {
138 CommandProcessor.getInstance().runUndoTransparentAction(new Runnable() {
139 public void run() {
140 action.execute();
143 return false;
145 if (doShow) {
146 String hintText = ShowAutoImportPass.getMessage(classes.length > 1, classes[0].getQualifiedName());
147 HintManager.getInstance().showQuestionHint(editor, hintText, myRef.getTextOffset(), myRef.getTextRange().getEndOffset(), action);
149 return true;
152 protected abstract boolean isQualified(T reference);
154 public boolean showHint(final Editor editor) {
155 return !isQualified(myRef) && doFix(editor, true, false);
158 @NotNull
159 public String getText() {
160 return QuickFixBundle.message("import.class.fix");
163 @NotNull
164 public String getFamilyName() {
165 return QuickFixBundle.message("import.class.fix");
168 public boolean startInWriteAction() {
169 return false;
172 protected abstract boolean hasUnresolvedImportWhichCanImport(PsiFile psiFile, String name);
174 private static void reduceSuggestedClassesBasedOnDependencyRuleViolation(PsiFile file, List<PsiClass> availableClasses) {
175 final Project project = file.getProject();
176 final DependencyValidationManager validationManager = DependencyValidationManager.getInstance(project);
177 for (int i = availableClasses.size() - 1; i >= 0; i--) {
178 PsiClass psiClass = availableClasses.get(i);
179 PsiFile targetFile = psiClass.getContainingFile();
180 if (targetFile == null) continue;
181 final DependencyRule[] violated = validationManager.getViolatorDependencyRules(file, targetFile);
182 if (violated.length != 0) {
183 availableClasses.remove(i);
184 if (availableClasses.size() == 1) break;
189 private static boolean isCaretNearRef(Editor editor, PsiElement ref) {
190 TextRange range = ref.getTextRange();
191 int offset = editor.getCaretModel().getOffset();
193 return range.grown(1).contains(offset);
196 public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) {
197 if (!CodeInsightUtilBase.prepareFileForWrite(file)) return;
198 ApplicationManager.getApplication().runWriteAction(new Runnable() {
199 public void run() {
200 List<PsiClass> classesToImport = getClassesToImport();
201 PsiClass[] classes = classesToImport.toArray(new PsiClass[classesToImport.size()]);
202 CodeInsightUtil.sortIdenticalShortNameClasses(classes, file);
203 if (classes.length == 0) return;
205 AddImportAction action = createAddImportAction(classes, project, editor);
206 action.execute();
211 protected void bindReference(T reference, PsiClass targetClass) {
212 reference.bindToElement(targetClass);
215 protected AddImportAction createAddImportAction(PsiClass[] classes, Project project, Editor editor) {
216 return new AddImportAction(project, myRef, editor, classes) {
217 @Override
218 protected void bindReference(PsiReference ref, PsiClass targetClass) {
219 ImportClassFixBase.this.bindReference((T)ref, targetClass);