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
;
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 final PopupChooserBuilder builder
= new PopupChooserBuilder(list
);
119 renderer
.installSpeedSearch(builder
);
121 Runnable runnable
= new Runnable() {
123 int index
= list
.getSelectedIndex();
124 if (index
< 0) return;
125 final PsiClass aClass
= (PsiClass
) list
.getSelectedValue();
126 CommandProcessor
.getInstance().executeCommand(project
, new Runnable() {
128 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
130 doInvoke(project
, aClass
);
140 setTitle(QuickFixBundle
.message("target.class.chooser.title")).
141 setItemChoosenCallback(runnable
).
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());
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
;
166 return PsiModifier
.PUBLIC
;
170 protected static boolean shouldCreateStaticMember(PsiReferenceExpression ref
, PsiClass targetClass
) {
171 if (targetClass
.isInterface()) {
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) {
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
;
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);
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();
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() {
252 //Should return only valid inproject classes
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();
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
);
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);
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() {
330 if (project
.isDisposed() || editor
.isDisposed()) return;
331 TemplateManager
.getInstance(project
).startTemplate(editor
, template
, listener
);
334 if (ApplicationManager
.getApplication().isUnitTestMode()) {
338 ApplicationManager
.getApplication().invokeLater(runnable
);