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
;
57 * User: Dmitry.Krasilschikov
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"));
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() {
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();
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
;
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();
191 final PsiElement rBrace
= body
.getRBrace();
193 assert rBrace
!= null;
194 PsiElement r
= rBrace
.getPrevSibling();
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
);
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
);
219 wasAddedModifiers
= true;
222 return wasAddedModifiers
;
226 private static final String
[] GROOVY_MODIFIERS
= new String
[]{
228 PsiModifier
.PROTECTED
,
231 PsiModifier
.ABSTRACT
,
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());
249 if (returnType
!= null) {
250 buffer
.append(returnType
.getCanonicalText());
254 buffer
.append(method
.getName());
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());
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());
281 return (GrMethod
) GroovyPsiElementFactory
.getInstance(project
).createTopElementFromText(buffer
.toString());
284 private static void setupOverridingMethodBody(Project project
,
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
);
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
) {
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()) {
323 buffer
.append(superMethod
.getName());
326 for (int i
= 0; i
< parms
.length
; i
++) {
327 String name
= parms
[i
].getName();
328 if (i
> 0) 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
;