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
.refactoring
.util
.RefactoringConflictsUtil
;
39 import com
.intellij
.util
.IncorrectOperationException
;
40 import org
.jetbrains
.annotations
.NonNls
;
41 import org
.jetbrains
.annotations
.NotNull
;
42 import org
.jetbrains
.annotations
.Nullable
;
45 import java
.util
.ArrayList
;
46 import java
.util
.Collections
;
47 import java
.util
.List
;
48 import java
.util
.Vector
;
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) {
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) {
89 List
<PsiClass
> targetClasses
= getTargetClasses(element
);
90 if (targetClasses
.isEmpty()) return;
92 if (targetClasses
.size() == 1) {
93 doInvoke(project
, targetClasses
.get(0));
95 chooseTargetClass(targetClasses
, editor
);
99 private void doInvoke(Project project
, PsiClass targetClass
) {
100 if (!CodeInsightUtilBase
.prepareFileForWrite(targetClass
.getContainingFile())) {
104 IdeDocumentHistory
.getInstance(project
).includeCurrentPlaceAsChangePlace();
105 invokeImpl(targetClass
);
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 renderer
.installSpeedSearch(list
);
120 Runnable runnable
= new Runnable() {
122 int index
= list
.getSelectedIndex();
123 if (index
< 0) return;
124 final PsiClass aClass
= (PsiClass
) list
.getSelectedValue();
125 CommandProcessor
.getInstance().executeCommand(project
, new Runnable() {
127 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
129 doInvoke(project
, aClass
);
138 new PopupChooserBuilder(list
).
139 setTitle(QuickFixBundle
.message("target.class.chooser.title")).
140 setItemChoosenCallback(runnable
).
142 showInBestPositionFor(editor
);
145 protected static Editor
positionCursor(Project project
, @NotNull PsiFile targetFile
, @NotNull PsiElement element
) {
146 TextRange range
= element
.getTextRange();
147 int textOffset
= range
.getStartOffset();
149 OpenFileDescriptor descriptor
= new OpenFileDescriptor(project
, targetFile
.getVirtualFile(), textOffset
);
150 return FileEditorManager
.getInstance(project
).openTextEditor(descriptor
, true);
153 protected void setupVisibility(PsiClass parentClass
, PsiClass targetClass
, PsiModifierList list
) throws IncorrectOperationException
{
154 if (targetClass
.isInterface()) {
155 list
.deleteChildRange(list
.getFirstChild(), list
.getLastChild());
158 RefactoringConflictsUtil
.setVisibility(list
, getVisibility(parentClass
, targetClass
));
161 protected String
getVisibility(PsiClass parentClass
, PsiClass targetClass
) {
162 if (parentClass
!= null && (parentClass
.equals(targetClass
) || PsiTreeUtil
.isAncestor(targetClass
, parentClass
, true))) {
163 return PsiModifier
.PRIVATE
;
165 return PsiModifier
.PUBLIC
;
169 protected static boolean shouldCreateStaticMember(PsiReferenceExpression ref
, PsiClass targetClass
) {
170 if (targetClass
.isInterface()) {
174 PsiExpression qualifierExpression
= ref
.getQualifierExpression();
175 while (qualifierExpression
instanceof PsiParenthesizedExpression
) {
176 qualifierExpression
= ((PsiParenthesizedExpression
) qualifierExpression
).getExpression();
179 if (qualifierExpression
instanceof PsiReferenceExpression
) {
180 PsiReferenceExpression referenceExpression
= (PsiReferenceExpression
) qualifierExpression
;
182 PsiElement resolvedElement
= referenceExpression
.resolve();
184 return resolvedElement
instanceof PsiClass
;
185 } else if (qualifierExpression
!= null) {
188 assert PsiTreeUtil
.isAncestor(targetClass
, ref
, true);
189 PsiModifierListOwner owner
= PsiTreeUtil
.getParentOfType(ref
, PsiModifierListOwner
.class);
190 if (owner
instanceof PsiMethod
&& ((PsiMethod
)owner
).isConstructor()) {
191 //usages inside delegating constructor call
192 PsiExpression run
= ref
;
194 if (!(run
.getParent() instanceof PsiExpression
)) break;
195 run
= (PsiExpression
)run
.getParent();
197 if (run
.getParent() instanceof PsiExpressionList
&&
198 run
.getParent().getParent() instanceof PsiMethodCallExpression
) {
199 @NonNls String calleeText
= ((PsiMethodCallExpression
)run
.getParent().getParent()).getMethodExpression().getText();
200 if (calleeText
.equals("this") || calleeText
.equals("super")) return true;
204 while (owner
!= null && owner
!= targetClass
) {
205 if (owner
.hasModifierProperty(PsiModifier
.STATIC
)) return true;
206 owner
= PsiTreeUtil
.getParentOfType(owner
, PsiModifierListOwner
.class);
214 private static PsiExpression
getQualifier (PsiElement element
) {
215 if (element
instanceof PsiNewExpression
) {
216 PsiJavaCodeReferenceElement ref
= ((PsiNewExpression
) element
).getClassReference();
217 if (ref
instanceof PsiReferenceExpression
) {
218 return ((PsiReferenceExpression
) ref
).getQualifierExpression();
220 } else if (element
instanceof PsiReferenceExpression
) {
221 return ((PsiReferenceExpression
) element
).getQualifierExpression();
222 } else if (element
instanceof PsiMethodCallExpression
) {
223 return ((PsiMethodCallExpression
) element
).getMethodExpression().getQualifierExpression();
229 protected static PsiSubstitutor
getTargetSubstitutor (PsiElement element
) {
230 if (element
instanceof PsiNewExpression
) {
231 JavaResolveResult result
= ((PsiNewExpression
)element
).getClassOrAnonymousClassReference().advancedResolve(false);
232 PsiSubstitutor substitutor
= result
.getSubstitutor();
233 return substitutor
== null ? PsiSubstitutor
.EMPTY
: substitutor
;
236 PsiExpression qualifier
= getQualifier(element
);
237 if (qualifier
!= null) {
238 PsiType type
= qualifier
.getType();
239 if (type
instanceof PsiClassType
) {
240 return ((PsiClassType
)type
).resolveGenerics().getSubstitutor();
244 return PsiSubstitutor
.EMPTY
;
247 protected boolean isAllowOuterTargetClass() {
251 //Should return only valid inproject classes
253 protected List
<PsiClass
> getTargetClasses(PsiElement element
) {
254 PsiClass psiClass
= null;
255 PsiExpression qualifier
= null;
257 if (element
instanceof PsiNewExpression
) {
258 final PsiNewExpression newExpression
= (PsiNewExpression
)element
;
259 PsiJavaCodeReferenceElement ref
= newExpression
.getClassOrAnonymousClassReference();
261 PsiElement refElement
= ref
.resolve();
262 if (refElement
instanceof PsiClass
) psiClass
= (PsiClass
)refElement
;
265 else if (element
instanceof PsiReferenceExpression
) {
266 qualifier
= ((PsiReferenceExpression
)element
).getQualifierExpression();
268 else if (element
instanceof PsiMethodCallExpression
) {
269 final PsiReferenceExpression methodExpression
= ((PsiMethodCallExpression
)element
).getMethodExpression();
270 qualifier
= methodExpression
.getQualifierExpression();
271 @NonNls final String referenceName
= methodExpression
.getReferenceName();
272 if (referenceName
== null) return Collections
.emptyList();
274 boolean allowOuterClasses
= false;
275 if (qualifier
!= null) {
276 PsiType type
= qualifier
.getType();
277 if (type
instanceof PsiClassType
) {
278 psiClass
= ((PsiClassType
)type
).resolve();
281 if (qualifier
instanceof PsiJavaCodeReferenceElement
) {
282 final PsiElement resolved
= ((PsiJavaCodeReferenceElement
)qualifier
).resolve();
283 if (resolved
instanceof PsiClass
) {
284 if (psiClass
== null) psiClass
= (PsiClass
)resolved
;
287 } else if (psiClass
== null) {
288 psiClass
= PsiTreeUtil
.getParentOfType(element
, PsiClass
.class);
289 allowOuterClasses
= true;
292 if (psiClass
instanceof PsiTypeParameter
) {
293 PsiClass
[] supers
= psiClass
.getSupers();
294 List
<PsiClass
> filtered
= new ArrayList
<PsiClass
>();
295 for (PsiClass aSuper
: supers
) {
296 if (!aSuper
.getManager().isInProject(aSuper
)) continue;
297 if (!(aSuper
instanceof PsiTypeParameter
)) filtered
.add(aSuper
);
302 if (psiClass
== null || !psiClass
.getManager().isInProject(psiClass
)) {
303 return Collections
.emptyList();
306 if (!allowOuterClasses
||
307 !isAllowOuterTargetClass() ||
308 ApplicationManager
.getApplication().isUnitTestMode())
309 return Collections
.singletonList(psiClass
);
311 List
<PsiClass
> result
= new ArrayList
<PsiClass
>();
313 while (psiClass
!= null) {
314 result
.add(psiClass
);
315 if (psiClass
.hasModifierProperty(PsiModifier
.STATIC
)) break;
316 psiClass
= PsiTreeUtil
.getParentOfType(psiClass
, PsiClass
.class);
322 protected static void startTemplate (@NotNull Editor editor
, final Template template
, @NotNull final Project project
) {
323 startTemplate(editor
, template
, project
, null);
326 protected static void startTemplate (@NotNull final Editor editor
, final Template template
, @NotNull final Project project
, final TemplateEditingListener listener
) {
327 Runnable runnable
= new Runnable() {
329 if (project
.isDisposed() || editor
.isDisposed()) return;
330 TemplateManager
.getInstance(project
).startTemplate(editor
, template
, listener
);
333 if (ApplicationManager
.getApplication().isUnitTestMode()) {
337 ApplicationManager
.getApplication().invokeLater(runnable
);