IDEA-52183: If groovy library added to a project 'Method getMetaClass is not implemen...
[fedora-idea.git] / plugins / groovy / src / org / jetbrains / plugins / groovy / overrideImplement / GroovyOverrideImplementUtil.java
blob01e28b64a4b360e2ac989e958db9e9b5db900d96
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 org.jetbrains.plugins.groovy.overrideImplement;
18 import com.intellij.codeInsight.generation.OverrideImplementUtil;
19 import com.intellij.codeInsight.generation.PsiMethodMember;
20 import com.intellij.ide.fileTemplates.FileTemplate;
21 import com.intellij.ide.fileTemplates.FileTemplateManager;
22 import com.intellij.ide.fileTemplates.JavaTemplateUtil;
23 import com.intellij.ide.util.MemberChooser;
24 import com.intellij.openapi.application.ApplicationManager;
25 import com.intellij.openapi.diagnostic.Logger;
26 import com.intellij.openapi.editor.Editor;
27 import com.intellij.openapi.editor.ScrollType;
28 import com.intellij.openapi.project.Project;
29 import com.intellij.openapi.util.Condition;
30 import com.intellij.psi.*;
31 import com.intellij.psi.impl.compiled.ClsParameterImpl;
32 import com.intellij.psi.infos.CandidateInfo;
33 import com.intellij.psi.util.PsiTypesUtil;
34 import com.intellij.util.IncorrectOperationException;
35 import com.intellij.util.containers.ContainerUtil;
36 import com.intellij.util.text.CharArrayUtil;
37 import org.jetbrains.annotations.NonNls;
38 import org.jetbrains.annotations.NotNull;
39 import org.jetbrains.plugins.groovy.GroovyBundle;
40 import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
41 import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
42 import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrTopLevelDefintion;
43 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrCodeBlock;
44 import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
45 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
46 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinitionBody;
47 import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
48 import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
50 import java.io.IOException;
51 import java.util.ArrayList;
52 import java.util.Collection;
53 import java.util.List;
54 import java.util.Properties;
56 /**
57 * User: Dmitry.Krasilschikov
58 * Date: 14.09.2007
60 public class GroovyOverrideImplementUtil {
61 private static final Logger LOG = Logger.getInstance("org.jetbrains.plugins.groovy.overrideImplement.GroovyOverrideImplementUtil");
63 private GroovyOverrideImplementUtil() {
66 public static void invokeOverrideImplement(final Project project, final Editor editor, final PsiFile file, boolean isImplement) {
67 final int offset = editor.getCaretModel().getOffset();
69 PsiElement parent = file.findElementAt(offset);
70 if (parent == null) return;
72 while (!(parent instanceof GrTypeDefinition)) {
73 parent = parent.getParent();
74 if (parent == null) return;
77 final GrTypeDefinition aClass = (GrTypeDefinition) parent;
79 if (isImplement && aClass.isInterface()) return;
81 Collection<CandidateInfo> candidates = OverrideImplementUtil.getMethodsToOverrideImplement(aClass, isImplement);
82 if (candidates.isEmpty()) return;
84 List<PsiMethodMember> classMembers = new ArrayList<PsiMethodMember>();
85 for (CandidateInfo candidate : candidates) {
86 final PsiMethod method = (PsiMethod)candidate.getElement();
87 assert method != null;
88 final PsiClass containingClass = method.getContainingClass();
89 if (containingClass != null && !GrTypeDefinition.DEFAULT_BASE_CLASS_NAME.equals(containingClass.getQualifiedName())) {
90 classMembers.add(new PsiMethodMember(candidate));
95 MemberChooser<PsiMethodMember> chooser = new MemberChooser<PsiMethodMember>(classMembers.toArray(new PsiMethodMember[classMembers.size()]), false, true, project);
96 chooser.setTitle(isImplement ? GroovyBundle.message("select.methods.to.implement") : GroovyBundle.message("select.methods.to.override"));
97 chooser.show();
99 final List<PsiMethodMember> selectedElements = chooser.getSelectedElements();
100 if (selectedElements == null || selectedElements.size() == 0) return;
102 for (PsiMethodMember methodMember : selectedElements) {
103 final PsiMethod method = methodMember.getElement();
104 final PsiSubstitutor substitutor = methodMember.getSubstitutor();
106 final boolean isAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT);
108 // assert isAbstract == isImplement;
109 String templName = isAbstract ? JavaTemplateUtil.TEMPLATE_IMPLEMENTED_METHOD_BODY : JavaTemplateUtil.TEMPLATE_OVERRIDDEN_METHOD_BODY;
111 final FileTemplate template = FileTemplateManager.getInstance().getCodeTemplate(templName);
112 final GrMethod result = createOverrideImplementMethodSignature(project, method, substitutor, aClass);
114 ApplicationManager.getApplication().runWriteAction(new Runnable() {
115 public void run() {
116 try {
117 PsiModifierList modifierList = result.getModifierList();
118 modifierList.setModifierProperty(PsiModifier.ABSTRACT, false);
119 modifierList.setModifierProperty(PsiModifier.NATIVE, false);
121 setupOverridingMethodBody(project, method, result, template, substitutor);
123 final GrTypeDefinitionBody classBody = aClass.getBody();
124 final PsiMethod[] methods = aClass.getMethods();
126 PsiElement anchor = null;
128 final int caretPosition = editor.getCaretModel().getOffset();
129 final PsiElement thisCaretPsiElement = file.findElementAt(caretPosition);
131 final GrTopLevelDefintion previousTopLevelElement = PsiUtil.findPreviousTopLevelElementByThisElement(thisCaretPsiElement);
133 if (thisCaretPsiElement != null && thisCaretPsiElement.getParent() instanceof GrTypeDefinitionBody) {
134 if (GroovyTokenTypes.mLCURLY.equals(thisCaretPsiElement.getNode().getElementType())) {
135 anchor = thisCaretPsiElement.getNextSibling();
136 } else if (GroovyTokenTypes.mRCURLY.equals(thisCaretPsiElement.getNode().getElementType())) {
137 anchor = thisCaretPsiElement.getPrevSibling();
138 } else {
139 anchor = thisCaretPsiElement;
142 } else if (previousTopLevelElement != null && previousTopLevelElement instanceof GrMethod) {
143 final PsiElement nextElement = previousTopLevelElement.getNextSibling();
144 if (nextElement != null) {
145 anchor = nextElement;
147 } else if (methods.length != 0) {
148 final PsiMethod lastMethod = methods[methods.length - 1];
149 if (lastMethod != null) {
150 final PsiElement nextSibling = lastMethod.getNextSibling();
151 if (nextSibling != null) {
152 anchor = nextSibling;
156 } else {
157 final PsiElement firstChild = classBody.getFirstChild();
158 assert firstChild != null;
159 final PsiElement nextElement = firstChild.getNextSibling();
160 assert nextElement != null;
162 anchor = nextElement;
165 final GrMethod addedMethod = aClass.addMemberDeclaration(result, anchor);
167 PsiUtil.shortenReferences(addedMethod);
168 //[GenerateMembersUtil.positionCaret in unsuitable because method.getBody() returns null, it is necessary use method.getBlock() instead.
169 //but it is impossible in common case]
170 // GenerateMembersUtil.positionCaret(editor, result, true);
171 positionCaret(editor, addedMethod);
172 } catch (IncorrectOperationException e) {
173 throw new RuntimeException(e);
181 private static void positionCaret(Editor editor, GrMethod result) {
182 final GrOpenBlock body = result.getBlock();
183 if (body == null) return;
185 final PsiElement lBrace = body.getLBrace();
187 assert lBrace != null;
188 PsiElement l = lBrace.getNextSibling();
189 assert l != null;
191 final PsiElement rBrace = body.getRBrace();
193 assert rBrace != null;
194 PsiElement r = rBrace.getPrevSibling();
195 assert r != null;
197 LOG.assertTrue(!PsiDocumentManager.getInstance(result.getProject()).isUncommited(editor.getDocument()));
198 String text = editor.getDocument().getText();
200 int start = l.getTextRange().getStartOffset();
201 start = CharArrayUtil.shiftForward(text, start, "\n\t ");
202 int end = r.getTextRange().getEndOffset();
203 end = CharArrayUtil.shiftBackward(text, end - 1, "\n\t ") + 1;
205 editor.getCaretModel().moveToOffset(Math.min(start, end));
206 editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
207 if (start < end) {
208 //Not an empty body
209 editor.getSelectionModel().setSelection(start, end);
213 private static boolean writeMethodModifiers(StringBuffer text, PsiModifierList modifierList, String[] modifiers) {
214 boolean wasAddedModifiers = false;
215 for (String modifierType : modifiers) {
216 if (modifierList.hasModifierProperty(modifierType) && modifierType != PsiModifier.PUBLIC) {
217 text.append(modifierType);
218 text.append(" ");
219 wasAddedModifiers = true;
222 return wasAddedModifiers;
226 private static final String[] GROOVY_MODIFIERS = new String[]{
227 PsiModifier.PUBLIC,
228 PsiModifier.PROTECTED,
229 PsiModifier.PRIVATE,
230 PsiModifier.STATIC,
231 PsiModifier.ABSTRACT,
232 PsiModifier.FINAL,
233 PsiModifier.SYNCHRONIZED,
237 private static GrMethod createOverrideImplementMethodSignature(Project project, PsiMethod method, PsiSubstitutor substitutor, PsiClass aClass) {
238 StringBuffer buffer = new StringBuffer();
239 if (!writeMethodModifiers(buffer, method.getModifierList(), GROOVY_MODIFIERS)) {
240 buffer.append("def ");
243 final PsiType returnType = substitutor.substitute(method.getReturnType());
245 if (method.isConstructor()) {
246 buffer.append(aClass.getName());
248 } else {
249 if (returnType != null) {
250 buffer.append(returnType.getCanonicalText());
251 buffer.append(" ");
254 buffer.append(method.getName());
256 buffer.append(" ");
258 buffer.append("(");
259 final PsiParameter[] parameters = method.getParameterList().getParameters();
260 for (int i = 0; i < parameters.length; i++) {
261 if (i > 0) buffer.append(", ");
262 PsiParameter parameter = parameters[i];
263 final PsiType parameterType = substitutor.substitute(parameter.getType());
264 buffer.append(parameterType.getCanonicalText());
265 buffer.append(" ");
266 final String paramName = parameter.getName();
267 if (paramName != null) {
268 buffer.append(paramName);
269 } else if (parameter instanceof ClsParameterImpl) {
270 final ClsParameterImpl clsParameter = (ClsParameterImpl) parameter;
271 buffer.append(((PsiParameter) clsParameter.getMirror()).getName());
275 buffer.append(")");
276 buffer.append(" ");
278 buffer.append("{");
279 buffer.append("}");
281 return (GrMethod) GroovyPsiElementFactory.getInstance(project).createTopElementFromText(buffer.toString());
284 private static void setupOverridingMethodBody(Project project,
285 PsiMethod method,
286 GrMethod resultMethod,
287 FileTemplate template,
288 PsiSubstitutor substitutor) {
289 final PsiType returnType = substitutor.substitute(method.getReturnType());
291 String returnTypeText = "";
292 if (returnType != null) {
293 returnTypeText = returnType.getPresentableText();
295 Properties properties = new Properties();
297 properties.setProperty(FileTemplate.ATTRIBUTE_RETURN_TYPE, returnTypeText);
298 properties.setProperty(FileTemplate.ATTRIBUTE_DEFAULT_RETURN_VALUE, PsiTypesUtil.getDefaultValueOfType(returnType));
299 properties.setProperty(FileTemplate.ATTRIBUTE_CALL_SUPER, callSuper(method, resultMethod));
300 JavaTemplateUtil.setClassAndMethodNameProperties(properties, method.getContainingClass(), resultMethod);
302 try {
303 String bodyText = template.getText(properties);
304 final GrCodeBlock newBody = GroovyPsiElementFactory.getInstance(project).createMethodBodyFromText("\n" + bodyText + "\n");
306 resultMethod.setBlock(newBody);
308 catch (IOException e) {
309 LOG.error(e);
313 @NotNull
314 private static String callSuper(PsiMethod superMethod, PsiMethod overriding) {
315 @NonNls StringBuilder buffer = new StringBuilder();
316 if (!superMethod.isConstructor() && superMethod.getReturnType() != PsiType.VOID) {
317 buffer.append("return ");
319 buffer.append("super");
320 PsiParameter[] parms = overriding.getParameterList().getParameters();
321 if (!superMethod.isConstructor()) {
322 buffer.append(".");
323 buffer.append(superMethod.getName());
325 buffer.append("(");
326 for (int i = 0; i < parms.length; i++) {
327 String name = parms[i].getName();
328 if (i > 0) buffer.append(",");
329 buffer.append(name);
331 buffer.append(")");
332 return buffer.toString();
335 public static Collection<CandidateInfo> getMethodsToImplement(PsiClass typeDefinition) {
336 Collection<CandidateInfo> methodsToImplement = OverrideImplementUtil.getMethodsToOverrideImplement(typeDefinition, true);
337 methodsToImplement = ContainerUtil.findAll(methodsToImplement, new Condition<CandidateInfo>() {
338 public boolean value(CandidateInfo candidateInfo) {
339 //noinspection ConstantConditions
340 return !GrTypeDefinition.DEFAULT_BASE_CLASS_NAME
341 .equals(((PsiMethod)candidateInfo.getElement()).getContainingClass().getQualifiedName());
344 return methodsToImplement;