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
.generation
;
18 import com
.intellij
.codeInsight
.CodeInsightActionHandler
;
19 import com
.intellij
.codeInsight
.CodeInsightBundle
;
20 import com
.intellij
.codeInsight
.intention
.AddAnnotationFix
;
21 import com
.intellij
.ide
.util
.MemberChooser
;
22 import com
.intellij
.openapi
.application
.ApplicationManager
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.editor
.Editor
;
25 import com
.intellij
.openapi
.editor
.ScrollType
;
26 import com
.intellij
.openapi
.fileEditor
.FileDocumentManager
;
27 import com
.intellij
.openapi
.project
.Project
;
28 import com
.intellij
.psi
.*;
29 import com
.intellij
.psi
.codeStyle
.CodeStyleManager
;
30 import com
.intellij
.psi
.javadoc
.PsiDocComment
;
31 import com
.intellij
.psi
.util
.*;
32 import com
.intellij
.util
.IncorrectOperationException
;
33 import com
.intellij
.util
.containers
.HashMap
;
34 import com
.intellij
.util
.containers
.HashSet
;
35 import org
.jetbrains
.annotations
.NonNls
;
36 import org
.jetbrains
.annotations
.NotNull
;
37 import org
.jetbrains
.annotations
.Nullable
;
39 import java
.util
.ArrayList
;
40 import java
.util
.List
;
47 public class GenerateDelegateHandler
implements CodeInsightActionHandler
{
48 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.generation.GenerateDelegateHandler");
50 public void invoke(@NotNull final Project project
, @NotNull final Editor editor
, @NotNull final PsiFile file
) {
51 if (!FileDocumentManager
.getInstance().requestWriting(editor
.getDocument(), project
)) {
54 PsiDocumentManager
.getInstance(project
).commitAllDocuments();
56 final PsiElement target
= chooseTarget(file
, editor
, project
);
57 if (target
== null) return;
59 final PsiMethodMember
[] candidates
= chooseMethods(target
, file
, editor
, project
);
60 if (candidates
== null || candidates
.length
== 0) return;
63 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
66 int offset
= editor
.getCaretModel().getOffset();
68 List
<PsiGenerationInfo
<PsiMethod
>> prototypes
= new ArrayList
<PsiGenerationInfo
<PsiMethod
>>(candidates
.length
);
69 for (PsiMethodMember candidate
: candidates
) {
70 prototypes
.add(generateDelegatePrototype(candidate
, target
));
73 List
<PsiGenerationInfo
<PsiMethod
>> results
= GenerateMembersUtil
.insertMembersAtOffset(file
, offset
, prototypes
);
75 if (!results
.isEmpty()) {
76 PsiMethod firstMethod
= results
.get(0).getPsiMember();
77 final PsiCodeBlock block
= firstMethod
.getBody();
79 final PsiElement first
= block
.getFirstBodyElement();
81 editor
.getCaretModel().moveToOffset(first
.getTextRange().getStartOffset());
82 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
83 editor
.getSelectionModel().removeSelection();
86 catch (IncorrectOperationException e
) {
93 public boolean startInWriteAction() {
97 private static PsiGenerationInfo
<PsiMethod
> generateDelegatePrototype(PsiMethodMember methodCandidate
, PsiElement target
) throws IncorrectOperationException
{
98 PsiMethod method
= GenerateMembersUtil
.substituteGenericMethod(methodCandidate
.getElement(), methodCandidate
.getSubstitutor());
101 clearModifiers(method
);
103 @NonNls StringBuffer call
= new StringBuffer();
105 PsiModifierList modifierList
= null;
107 if (method
.getReturnType() != PsiType
.VOID
) {
108 call
.append("return ");
111 if (target
instanceof PsiField
) {
112 PsiField field
= (PsiField
)target
;
113 modifierList
= field
.getModifierList();
114 final String name
= field
.getName();
116 final PsiParameter
[] parameters
= method
.getParameterList().getParameters();
117 for (PsiParameter parameter
: parameters
) {
118 if (name
.equals(parameter
.getName())) {
119 call
.append("this.");
127 else if (target
instanceof PsiMethod
) {
128 PsiMethod m
= (PsiMethod
)target
;
129 modifierList
= m
.getModifierList();
130 call
.append(m
.getName());
134 call
.append(method
.getName());
136 final PsiParameter
[] parameters
= method
.getParameterList().getParameters();
137 for (int j
= 0; j
< parameters
.length
; j
++) {
138 PsiParameter parameter
= parameters
[j
];
139 if (j
> 0) call
.append(",");
140 call
.append(parameter
.getName());
144 final PsiManager psiManager
= method
.getManager();
145 PsiStatement stmt
= JavaPsiFacade
.getInstance(psiManager
.getProject()).getElementFactory().createStatementFromText(call
.toString(), method
);
146 stmt
= (PsiStatement
)CodeStyleManager
.getInstance(psiManager
.getProject()).reformat(stmt
);
147 method
.getBody().add(stmt
);
149 if (modifierList
!= null && modifierList
.hasModifierProperty(PsiModifier
.STATIC
)) {
150 PsiUtil
.setModifierProperty(method
, PsiModifier
.STATIC
, true);
153 PsiUtil
.setModifierProperty(method
, PsiModifier
.PUBLIC
, true);
155 final Project project
= method
.getProject();
156 for (PsiAnnotation annotation
: methodCandidate
.getElement().getModifierList().getAnnotations()) {
157 final AddAnnotationFix fix
= new AddAnnotationFix(annotation
.getQualifiedName(), method
);
158 if (fix
.isAvailable(project
, null, method
.getContainingFile())) {
159 fix
.invoke(project
, null, method
.getContainingFile());
163 return new PsiGenerationInfo
<PsiMethod
>(method
);
166 private static void clearMethod(PsiMethod method
) throws IncorrectOperationException
{
167 LOG
.assertTrue(!method
.isPhysical());
168 PsiCodeBlock codeBlock
= JavaPsiFacade
.getInstance(method
.getProject()).getElementFactory().createCodeBlock();
169 if (method
.getBody() != null) {
170 method
.getBody().replace(codeBlock
);
173 method
.add(codeBlock
);
176 final PsiDocComment docComment
= method
.getDocComment();
177 if (docComment
!= null) {
182 private static void clearModifiers(PsiMethod method
) throws IncorrectOperationException
{
183 final PsiElement
[] children
= method
.getModifierList().getChildren();
184 for (PsiElement child
: children
) {
185 if (child
instanceof PsiKeyword
) child
.delete();
190 private static PsiMethodMember
[] chooseMethods(PsiElement target
, PsiFile file
, Editor editor
, Project project
) {
191 PsiClassType
.ClassResolveResult resolveResult
= null;
193 if (target
instanceof PsiField
) {
194 resolveResult
= PsiUtil
.resolveGenericsClassInType(((PsiField
)target
).getType());
196 else if (target
instanceof PsiMethod
) {
197 resolveResult
= PsiUtil
.resolveGenericsClassInType(((PsiMethod
)target
).getReturnType());
200 if (resolveResult
== null || resolveResult
.getElement() == null) return null;
201 PsiClass targetClass
= resolveResult
.getElement();
202 PsiSubstitutor substitutor
= resolveResult
.getSubstitutor();
204 int offset
= editor
.getCaretModel().getOffset();
205 PsiElement element
= file
.findElementAt(offset
);
206 if (element
== null) return null;
207 PsiClass aClass
= PsiTreeUtil
.getParentOfType(element
, PsiClass
.class);
208 if (aClass
== null) return null;
210 List
<PsiMethodMember
> methodInstances
= new ArrayList
<PsiMethodMember
>();
212 final PsiMethod
[] allMethods
= targetClass
.getAllMethods();
213 final Set
<MethodSignature
> signatures
= new HashSet
<MethodSignature
>();
214 Map
<PsiClass
, PsiSubstitutor
> superSubstitutors
= new HashMap
<PsiClass
, PsiSubstitutor
>();
215 JavaPsiFacade facade
= JavaPsiFacade
.getInstance(target
.getProject());
216 for (PsiMethod method
: allMethods
) {
217 final PsiClass superClass
= method
.getContainingClass();
218 if (CommonClassNames
.JAVA_LANG_OBJECT
.equals(superClass
.getQualifiedName())) continue;
219 if (method
.isConstructor()) continue;
220 PsiSubstitutor superSubstitutor
= superSubstitutors
.get(superClass
);
221 if (superSubstitutor
== null) {
222 superSubstitutor
= TypeConversionUtil
.getSuperClassSubstitutor(superClass
, targetClass
, substitutor
);
223 superSubstitutors
.put(superClass
, superSubstitutor
);
225 PsiSubstitutor methodSubstitutor
= GenerateMembersUtil
.correctSubstitutor(method
, superSubstitutor
);
226 MethodSignature signature
= method
.getSignature(methodSubstitutor
);
227 if (!signatures
.contains(signature
)) {
228 signatures
.add(signature
);
229 if (facade
.getResolveHelper().isAccessible(method
, target
, aClass
)) {
230 methodInstances
.add(new PsiMethodMember(method
, methodSubstitutor
));
235 PsiMethodMember
[] result
;
236 if (!ApplicationManager
.getApplication().isUnitTestMode()) {
237 MemberChooser
<PsiElementClassMember
> chooser
= new MemberChooser
<PsiElementClassMember
>(methodInstances
.toArray(new PsiMethodMember
[methodInstances
.size()]), false, true, project
);
238 chooser
.setTitle(CodeInsightBundle
.message("generate.delegate.method.chooser.title"));
239 chooser
.setCopyJavadocVisible(false);
242 if (chooser
.getExitCode() != MemberChooser
.OK_EXIT_CODE
) return null;
244 final List
<PsiElementClassMember
> list
= chooser
.getSelectedElements();
245 result
= list
.toArray(new PsiMethodMember
[list
.size()]);
248 result
= new PsiMethodMember
[] {methodInstances
.get(0)};
254 public static boolean isApplicable(PsiFile file
, Editor editor
) {
255 ClassMember
[] targetElements
= getTargetElements(file
, editor
);
256 return targetElements
!= null && targetElements
.length
> 0;
260 private static PsiElement
chooseTarget(PsiFile file
, Editor editor
, Project project
) {
261 PsiElement target
= null;
262 final PsiElementClassMember
[] targetElements
= getTargetElements(file
, editor
);
263 if (targetElements
== null || targetElements
.length
== 0) return null;
264 if (!ApplicationManager
.getApplication().isUnitTestMode()) {
265 MemberChooser
<PsiElementClassMember
> chooser
= new MemberChooser
<PsiElementClassMember
>(targetElements
, false, false, project
);
266 chooser
.setTitle(CodeInsightBundle
.message("generate.delegate.target.chooser.title"));
267 chooser
.setCopyJavadocVisible(false);
270 if (chooser
.getExitCode() != MemberChooser
.OK_EXIT_CODE
) return null;
272 final List
<PsiElementClassMember
> selectedElements
= chooser
.getSelectedElements();
274 if (selectedElements
!= null && selectedElements
.size() > 0) target
= selectedElements
.get(0).getElement();
277 target
= targetElements
[0].getElement();
283 private static PsiElementClassMember
[] getTargetElements(PsiFile file
, Editor editor
) {
284 int offset
= editor
.getCaretModel().getOffset();
285 PsiElement element
= file
.findElementAt(offset
);
286 if (element
== null) return null;
287 PsiClass aClass
= PsiTreeUtil
.getParentOfType(element
, PsiClass
.class);
288 if (aClass
== null) return null;
290 List
<PsiElementClassMember
> result
= new ArrayList
<PsiElementClassMember
>();
292 final PsiField
[] fields
= aClass
.getAllFields();
293 PsiResolveHelper helper
= JavaPsiFacade
.getInstance(aClass
.getProject()).getResolveHelper();
294 for (PsiField field
: fields
) {
295 final PsiType type
= field
.getType();
296 if (helper
.isAccessible(field
, aClass
, aClass
) && type
instanceof PsiClassType
) {
297 result
.add(new PsiFieldMember(field
));
301 final PsiMethod
[] methods
= aClass
.getAllMethods();
302 for (PsiMethod method
: methods
) {
303 if (CommonClassNames
.JAVA_LANG_OBJECT
.equals(method
.getContainingClass().getQualifiedName())) continue;
304 final PsiType returnType
= method
.getReturnType();
305 if (returnType
!= null && PropertyUtil
.isSimplePropertyGetter(method
) && helper
.isAccessible(method
, aClass
, aClass
) &&
306 returnType
instanceof PsiClassType
) {
307 result
.add(new PsiMethodMember(method
));
311 return result
.toArray(new PsiElementClassMember
[result
.size()]);