cfe4330d0d18d55fab39fa4a898229c163219024
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / daemon / impl / quickfix / CreateFromUsageBaseFix.java
blobcfe4330d0d18d55fab39fa4a898229c163219024
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.CodeInsightUtilBase;
19 import com.intellij.codeInsight.daemon.QuickFixBundle;
20 import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
21 import com.intellij.codeInsight.template.Template;
22 import com.intellij.codeInsight.template.TemplateEditingListener;
23 import com.intellij.codeInsight.template.TemplateManager;
24 import com.intellij.ide.util.PsiClassListCellRenderer;
25 import com.intellij.ide.util.PsiElementListCellRenderer;
26 import com.intellij.openapi.application.ApplicationManager;
27 import com.intellij.openapi.command.CommandProcessor;
28 import com.intellij.openapi.diagnostic.Logger;
29 import com.intellij.openapi.editor.Editor;
30 import com.intellij.openapi.fileEditor.FileEditorManager;
31 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
32 import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.openapi.ui.popup.PopupChooserBuilder;
35 import com.intellij.openapi.util.TextRange;
36 import com.intellij.psi.*;
37 import com.intellij.psi.util.PsiTreeUtil;
38 import com.intellij.util.IncorrectOperationException;
39 import com.intellij.util.VisibilityUtil;
40 import org.jetbrains.annotations.NonNls;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
44 import javax.swing.*;
45 import java.util.ArrayList;
46 import java.util.Collections;
47 import java.util.List;
48 import java.util.Vector;
50 /**
51 * @author Mike
53 public abstract class CreateFromUsageBaseFix extends BaseIntentionAction {
54 private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageBaseFix");
56 protected CreateFromUsageBaseFix() {
59 public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
60 int offset = editor.getCaretModel().getOffset();
61 PsiElement element = getElement();
62 if (element == null) {
63 return false;
66 List<PsiClass> targetClasses = getTargetClasses(element);
67 return !targetClasses.isEmpty() && !isValidElement(element) && isAvailableImpl(offset);
70 protected abstract boolean isAvailableImpl(int offset);
72 protected abstract void invokeImpl(PsiClass targetClass);
74 protected abstract boolean isValidElement(PsiElement result);
76 public void invoke(@NotNull Project project, Editor editor, PsiFile file) {
77 PsiDocumentManager.getInstance(project).commitAllDocuments();
79 PsiElement element = getElement();
81 if (LOG.isDebugEnabled()) {
82 LOG.debug("CreateFromUsage: element =" + element);
85 if (element == null) {
86 return;
89 List<PsiClass> targetClasses = getTargetClasses(element);
90 if (targetClasses.isEmpty()) return;
92 if (targetClasses.size() == 1) {
93 doInvoke(project, targetClasses.get(0));
94 } else {
95 chooseTargetClass(targetClasses, editor);
99 private void doInvoke(Project project, PsiClass targetClass) {
100 if (!CodeInsightUtilBase.prepareFileForWrite(targetClass.getContainingFile())) {
101 return;
104 IdeDocumentHistory.getInstance(project).includeCurrentPlaceAsChangePlace();
105 invokeImpl(targetClass);
108 @Nullable
109 protected abstract PsiElement getElement();
111 private void chooseTargetClass(List<PsiClass> classes, final Editor editor) {
112 final Project project = classes.get(0).getProject();
114 final JList list = new JList(new Vector<PsiClass>(classes));
115 PsiElementListCellRenderer renderer = new PsiClassListCellRenderer();
116 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
117 list.setCellRenderer(renderer);
118 final PopupChooserBuilder builder = new PopupChooserBuilder(list);
119 renderer.installSpeedSearch(builder);
121 Runnable runnable = new Runnable() {
122 public void run() {
123 int index = list.getSelectedIndex();
124 if (index < 0) return;
125 final PsiClass aClass = (PsiClass) list.getSelectedValue();
126 CommandProcessor.getInstance().executeCommand(project, new Runnable() {
127 public void run() {
128 ApplicationManager.getApplication().runWriteAction(new Runnable() {
129 public void run() {
130 doInvoke(project, aClass);
135 }, getText(), null);
139 builder.
140 setTitle(QuickFixBundle.message("target.class.chooser.title")).
141 setItemChoosenCallback(runnable).
142 createPopup().
143 showInBestPositionFor(editor);
146 protected static Editor positionCursor(Project project, @NotNull PsiFile targetFile, @NotNull PsiElement element) {
147 TextRange range = element.getTextRange();
148 int textOffset = range.getStartOffset();
150 OpenFileDescriptor descriptor = new OpenFileDescriptor(project, targetFile.getVirtualFile(), textOffset);
151 return FileEditorManager.getInstance(project).openTextEditor(descriptor, true);
154 protected void setupVisibility(PsiClass parentClass, PsiClass targetClass, PsiModifierList list) throws IncorrectOperationException {
155 if (targetClass.isInterface()) {
156 list.deleteChildRange(list.getFirstChild(), list.getLastChild());
157 return;
159 VisibilityUtil.setVisibility(list, getVisibility(parentClass, targetClass));
162 protected String getVisibility(PsiClass parentClass, PsiClass targetClass) {
163 if (parentClass != null && (parentClass.equals(targetClass) || PsiTreeUtil.isAncestor(targetClass, parentClass, true))) {
164 return PsiModifier.PRIVATE;
165 } else {
166 return PsiModifier.PUBLIC;
170 protected static boolean shouldCreateStaticMember(PsiReferenceExpression ref, PsiClass targetClass) {
171 if (targetClass.isInterface()) {
172 return false;
175 PsiExpression qualifierExpression = ref.getQualifierExpression();
176 while (qualifierExpression instanceof PsiParenthesizedExpression) {
177 qualifierExpression = ((PsiParenthesizedExpression) qualifierExpression).getExpression();
180 if (qualifierExpression instanceof PsiReferenceExpression) {
181 PsiReferenceExpression referenceExpression = (PsiReferenceExpression) qualifierExpression;
183 PsiElement resolvedElement = referenceExpression.resolve();
185 return resolvedElement instanceof PsiClass;
186 } else if (qualifierExpression != null) {
187 return false;
188 } else {
189 assert PsiTreeUtil.isAncestor(targetClass, ref, true);
190 PsiModifierListOwner owner = PsiTreeUtil.getParentOfType(ref, PsiModifierListOwner.class);
191 if (owner instanceof PsiMethod && ((PsiMethod)owner).isConstructor()) {
192 //usages inside delegating constructor call
193 PsiExpression run = ref;
194 while (true) {
195 if (!(run.getParent() instanceof PsiExpression)) break;
196 run = (PsiExpression)run.getParent();
198 if (run.getParent() instanceof PsiExpressionList &&
199 run.getParent().getParent() instanceof PsiMethodCallExpression) {
200 @NonNls String calleeText = ((PsiMethodCallExpression)run.getParent().getParent()).getMethodExpression().getText();
201 if (calleeText.equals("this") || calleeText.equals("super")) return true;
205 while (owner != null && owner != targetClass) {
206 if (owner.hasModifierProperty(PsiModifier.STATIC)) return true;
207 owner = PsiTreeUtil.getParentOfType(owner, PsiModifierListOwner.class);
211 return false;
214 @Nullable
215 private static PsiExpression getQualifier (PsiElement element) {
216 if (element instanceof PsiNewExpression) {
217 PsiJavaCodeReferenceElement ref = ((PsiNewExpression) element).getClassReference();
218 if (ref instanceof PsiReferenceExpression) {
219 return ((PsiReferenceExpression) ref).getQualifierExpression();
221 } else if (element instanceof PsiReferenceExpression) {
222 return ((PsiReferenceExpression) element).getQualifierExpression();
223 } else if (element instanceof PsiMethodCallExpression) {
224 return ((PsiMethodCallExpression) element).getMethodExpression().getQualifierExpression();
227 return null;
230 protected static PsiSubstitutor getTargetSubstitutor (PsiElement element) {
231 if (element instanceof PsiNewExpression) {
232 JavaResolveResult result = ((PsiNewExpression)element).getClassOrAnonymousClassReference().advancedResolve(false);
233 PsiSubstitutor substitutor = result.getSubstitutor();
234 return substitutor == null ? PsiSubstitutor.EMPTY : substitutor;
237 PsiExpression qualifier = getQualifier(element);
238 if (qualifier != null) {
239 PsiType type = qualifier.getType();
240 if (type instanceof PsiClassType) {
241 return ((PsiClassType)type).resolveGenerics().getSubstitutor();
245 return PsiSubstitutor.EMPTY;
248 protected boolean isAllowOuterTargetClass() {
249 return true;
252 //Should return only valid inproject classes
253 @NotNull
254 protected List<PsiClass> getTargetClasses(PsiElement element) {
255 PsiClass psiClass = null;
256 PsiExpression qualifier = null;
258 if (element instanceof PsiNewExpression) {
259 final PsiNewExpression newExpression = (PsiNewExpression)element;
260 PsiJavaCodeReferenceElement ref = newExpression.getClassOrAnonymousClassReference();
261 if (ref != null) {
262 PsiElement refElement = ref.resolve();
263 if (refElement instanceof PsiClass) psiClass = (PsiClass)refElement;
266 else if (element instanceof PsiReferenceExpression) {
267 qualifier = ((PsiReferenceExpression)element).getQualifierExpression();
269 else if (element instanceof PsiMethodCallExpression) {
270 final PsiReferenceExpression methodExpression = ((PsiMethodCallExpression)element).getMethodExpression();
271 qualifier = methodExpression.getQualifierExpression();
272 @NonNls final String referenceName = methodExpression.getReferenceName();
273 if (referenceName == null) return Collections.emptyList();
275 boolean allowOuterClasses = false;
276 if (qualifier != null) {
277 PsiType type = qualifier.getType();
278 if (type instanceof PsiClassType) {
279 psiClass = ((PsiClassType)type).resolve();
282 if (qualifier instanceof PsiJavaCodeReferenceElement) {
283 final PsiElement resolved = ((PsiJavaCodeReferenceElement)qualifier).resolve();
284 if (resolved instanceof PsiClass) {
285 if (psiClass == null) psiClass = (PsiClass)resolved;
288 } else if (psiClass == null) {
289 psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class);
290 allowOuterClasses = true;
293 if (psiClass instanceof PsiTypeParameter) {
294 PsiClass[] supers = psiClass.getSupers();
295 List<PsiClass> filtered = new ArrayList<PsiClass>();
296 for (PsiClass aSuper : supers) {
297 if (!aSuper.getManager().isInProject(aSuper)) continue;
298 if (!(aSuper instanceof PsiTypeParameter)) filtered.add(aSuper);
300 return filtered;
302 else {
303 if (psiClass == null || !psiClass.getManager().isInProject(psiClass)) {
304 return Collections.emptyList();
307 if (!allowOuterClasses ||
308 !isAllowOuterTargetClass() ||
309 ApplicationManager.getApplication().isUnitTestMode())
310 return Collections.singletonList(psiClass);
312 List<PsiClass> result = new ArrayList<PsiClass>();
314 while (psiClass != null) {
315 result.add(psiClass);
316 if (psiClass.hasModifierProperty(PsiModifier.STATIC)) break;
317 psiClass = PsiTreeUtil.getParentOfType(psiClass, PsiClass.class);
319 return result;
323 protected static void startTemplate (@NotNull Editor editor, final Template template, @NotNull final Project project) {
324 startTemplate(editor, template, project, null);
327 protected static void startTemplate (@NotNull final Editor editor, final Template template, @NotNull final Project project, final TemplateEditingListener listener) {
328 Runnable runnable = new Runnable() {
329 public void run() {
330 if (project.isDisposed() || editor.isDisposed()) return;
331 TemplateManager.getInstance(project).startTemplate(editor, template, listener, true);
334 if (ApplicationManager.getApplication().isUnitTestMode()) {
335 runnable.run();
337 else {
338 ApplicationManager.getApplication().invokeLater(runnable);