update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / codeInsight / generation / GenerateDelegateHandler.java
blob28a77deef7eb5e7d2c4721ab29c3c1c5f4831f37
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.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;
41 import java.util.Map;
42 import java.util.Set;
44 /**
45 * @author mike
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)) {
52 return;
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() {
64 public void run() {
65 try {
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();
78 assert block != null;
79 final PsiElement first = block.getFirstBodyElement();
80 assert first != null;
81 editor.getCaretModel().moveToOffset(first.getTextRange().getStartOffset());
82 editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
83 editor.getSelectionModel().removeSelection();
86 catch (IncorrectOperationException e) {
87 LOG.error(e);
90 });
93 public boolean startInWriteAction() {
94 return false;
97 private static PsiGenerationInfo<PsiMethod> generateDelegatePrototype(PsiMethodMember methodCandidate, PsiElement target) throws IncorrectOperationException {
98 PsiMethod method = GenerateMembersUtil.substituteGenericMethod(methodCandidate.getElement(), methodCandidate.getSubstitutor());
99 clearMethod(method);
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.");
120 break;
124 call.append(name);
125 call.append(".");
127 else if (target instanceof PsiMethod) {
128 PsiMethod m = (PsiMethod)target;
129 modifierList = m.getModifierList();
130 call.append(m.getName());
131 call.append("().");
134 call.append(method.getName());
135 call.append("(");
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());
142 call.append(");");
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);
172 else {
173 method.add(codeBlock);
176 final PsiDocComment docComment = method.getDocComment();
177 if (docComment != null) {
178 docComment.delete();
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();
189 @Nullable
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);
240 chooser.show();
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()]);
247 else {
248 result = new PsiMethodMember[] {methodInstances.get(0)};
251 return result;
254 public static boolean isApplicable(PsiFile file, Editor editor) {
255 ClassMember[] targetElements = getTargetElements(file, editor);
256 return targetElements != null && targetElements.length > 0;
259 @Nullable
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);
268 chooser.show();
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();
276 else {
277 target = targetElements[0].getElement();
279 return target;
282 @Nullable
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()]);