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
.lang
.ASTNode
;
19 import com
.intellij
.lang
.StdLanguages
;
20 import com
.intellij
.openapi
.diagnostic
.Logger
;
21 import com
.intellij
.openapi
.editor
.Editor
;
22 import com
.intellij
.openapi
.editor
.ScrollType
;
23 import com
.intellij
.openapi
.project
.Project
;
24 import com
.intellij
.openapi
.util
.Pair
;
25 import com
.intellij
.psi
.*;
26 import com
.intellij
.psi
.codeStyle
.CodeStyleSettingsManager
;
27 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
28 import com
.intellij
.psi
.codeStyle
.VariableKind
;
29 import com
.intellij
.psi
.javadoc
.PsiDocComment
;
30 import com
.intellij
.psi
.util
.PsiUtil
;
31 import com
.intellij
.psi
.util
.TypeConversionUtil
;
32 import com
.intellij
.refactoring
.util
.RefactoringConflictsUtil
;
33 import com
.intellij
.util
.VisibilityUtil
;
34 import com
.intellij
.util
.IncorrectOperationException
;
35 import com
.intellij
.util
.containers
.HashMap
;
36 import org
.jetbrains
.annotations
.NonNls
;
37 import org
.jetbrains
.annotations
.NotNull
;
38 import org
.jetbrains
.annotations
.Nullable
;
40 import java
.util
.Collections
;
41 import java
.util
.List
;
44 public class GenerateMembersUtil
{
45 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.generation.GenerateMembersUtil");
47 private GenerateMembersUtil() {
51 public static <T
extends GenerationInfo
> List
<T
> insertMembersAtOffset(PsiFile file
, int offset
, @NotNull List
<T
> memberPrototypes
) throws IncorrectOperationException
{
52 if (memberPrototypes
.isEmpty()) return memberPrototypes
;
53 final PsiElement leaf
= file
.findElementAt(offset
);
54 if (leaf
== null) return Collections
.emptyList();
56 PsiClass aClass
= findClassAtOffset(file
, leaf
);
57 if (aClass
== null) return Collections
.emptyList();
58 PsiElement anchor
= memberPrototypes
.get(0).findInsertionAnchor(aClass
, leaf
);
60 if (anchor
instanceof PsiWhiteSpace
) {
61 final ASTNode spaceNode
= anchor
.getNode();
62 anchor
= anchor
.getNextSibling();
64 assert spaceNode
!= null;
65 if (spaceNode
.getStartOffset() <= offset
&& spaceNode
.getStartOffset() + spaceNode
.getTextLength() >= offset
) {
66 final ASTNode singleNewLineWhitespace
= JavaPsiFacade
.getInstance(file
.getProject()).getElementFactory().createWhiteSpaceFromText(spaceNode
.getText().substring(0, offset
- spaceNode
.getStartOffset())).getNode();
67 spaceNode
.getTreeParent().replaceChild(spaceNode
, singleNewLineWhitespace
); // See http://jetbrains.net/jira/browse/IDEADEV-12837
71 // Q: shouldn't it be somewhere in PSI?
72 PsiElement element
= anchor
;
74 if (element
== null) break;
75 if (element
instanceof PsiField
|| element
instanceof PsiMethod
|| element
instanceof PsiClassInitializer
) break;
76 element
= element
.getNextSibling();
78 if (element
instanceof PsiField
) {
79 PsiField field
= (PsiField
) element
;
80 PsiTypeElement typeElement
= field
.getTypeElement();
81 if (typeElement
!= null && !field
.equals(typeElement
.getParent())) {
82 field
.normalizeDeclaration();
87 return insertMembersBeforeAnchor(aClass
, anchor
, memberPrototypes
);
91 public static <T
extends GenerationInfo
> List
<T
> insertMembersBeforeAnchor(PsiClass aClass
, PsiElement anchor
, @NotNull List
<T
> memberPrototypes
) throws IncorrectOperationException
{
92 boolean before
= true;
93 for (T memberPrototype
: memberPrototypes
) {
94 memberPrototype
.insert(aClass
, anchor
, before
);
96 anchor
= memberPrototype
.getPsiMember();
98 return memberPrototypes
;
101 public static void positionCaret(@NotNull Editor editor
, @NotNull PsiElement firstMember
, boolean toEditMethodBody
) {
102 LOG
.assertTrue(firstMember
.isValid());
104 if (toEditMethodBody
) {
105 PsiMethod method
= (PsiMethod
) firstMember
;
106 PsiCodeBlock body
= method
.getBody();
108 PsiElement l
= body
.getFirstBodyElement();
109 while (l
instanceof PsiWhiteSpace
) l
= l
.getNextSibling();
110 if (l
== null) l
= body
;
111 PsiElement r
= body
.getLastBodyElement();
112 while (r
instanceof PsiWhiteSpace
) r
= r
.getPrevSibling();
113 if (r
== null) r
= body
;
115 int start
= l
.getTextRange().getStartOffset();
116 int end
= r
.getTextRange().getEndOffset();
118 editor
.getCaretModel().moveToOffset(Math
.min(start
, end
));
119 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
122 editor
.getSelectionModel().setSelection(start
, end
);
129 if (firstMember
instanceof PsiMethod
) {
130 PsiMethod method
= (PsiMethod
) firstMember
;
131 PsiCodeBlock body
= method
.getBody();
133 offset
= method
.getTextRange().getStartOffset();
136 offset
= body
.getLBrace().getTextRange().getEndOffset();
140 offset
= firstMember
.getTextRange().getStartOffset();
143 editor
.getCaretModel().moveToOffset(offset
);
144 editor
.getScrollingModel().scrollToCaret(ScrollType
.RELATIVE
);
145 editor
.getSelectionModel().removeSelection();
148 public static PsiElement
insert(PsiClass aClass
, PsiMember member
, PsiElement anchor
, boolean before
) throws IncorrectOperationException
{
149 if (member
instanceof PsiMethod
) {
150 if (!aClass
.isInterface()) {
151 final PsiParameter
[] parameters
= ((PsiMethod
)member
).getParameterList().getParameters();
152 final boolean generateFinals
= CodeStyleSettingsManager
.getSettings(aClass
.getProject()).GENERATE_FINAL_PARAMETERS
;
153 for (final PsiParameter parameter
: parameters
) {
154 final PsiModifierList modifierList
= parameter
.getModifierList();
155 assert modifierList
!= null;
156 modifierList
.setModifierProperty(PsiModifier
.FINAL
, generateFinals
);
161 if (anchor
!= null) {
162 return before ? aClass
.addBefore(member
, anchor
) : aClass
.addAfter(member
, anchor
);
165 return aClass
.add(member
);
170 private static PsiClass
findClassAtOffset(PsiFile file
, PsiElement leaf
) {
171 PsiElement element
= leaf
;
172 while (element
!= null && !(element
instanceof PsiFile
)) {
173 if (element
instanceof PsiClass
&& !(element
instanceof PsiTypeParameter
)) {
174 final PsiClass psiClass
= (PsiClass
)element
;
175 if (psiClass
.isEnum()) {
176 PsiElement lastChild
= null;
177 for (PsiElement child
: psiClass
.getChildren()) {
178 if (child
instanceof PsiJavaToken
&& ";".equals(child
.getText())) {
182 else if (child
instanceof PsiJavaToken
&& ",".equals(child
.getText()) || child
instanceof PsiEnumConstant
) {
186 if (lastChild
!= null) {
187 int adjustedOffset
= lastChild
.getTextRange().getEndOffset();
188 if (leaf
.getTextRange().getEndOffset() <= adjustedOffset
) return findClassAtOffset(file
, file
.findElementAt(adjustedOffset
));
193 element
= element
.getParent();
198 public static PsiMethod
substituteGenericMethod(PsiMethod method
, PsiSubstitutor substitutor
) {
199 Project project
= method
.getProject();
200 PsiElementFactory factory
= JavaPsiFacade
.getInstance(method
.getProject()).getElementFactory();
202 PsiTypeParameter
[] typeParams
= method
.getTypeParameters();
204 PsiType returnType
= method
.getReturnType();
207 if (method
.isConstructor()) {
208 newMethod
= factory
.createConstructor();
209 newMethod
.getNameIdentifier().replace(factory
.createIdentifier(method
.getName()));
212 newMethod
= factory
.createMethod(method
.getName(), substituteType(substitutor
, returnType
));
215 RefactoringConflictsUtil
.setVisibility(newMethod
.getModifierList(), VisibilityUtil
.getVisibilityModifier(method
.getModifierList()));
217 PsiElement navigationElement
= method
.getNavigationElement();
218 PsiDocComment docComment
= ((PsiDocCommentOwner
)navigationElement
).getDocComment();
219 if (docComment
!= null) {
220 newMethod
.addAfter(docComment
, null);
223 PsiParameter
[] parameters
= method
.getParameterList().getParameters();
224 JavaCodeStyleManager codeStyleManager
= JavaCodeStyleManager
.getInstance(project
);
225 Map
<PsiType
,Pair
<String
,Integer
>> m
= new HashMap
<PsiType
, Pair
<String
,Integer
>>();
226 for (int i
= 0; i
< parameters
.length
; i
++) {
227 PsiParameter parameter
= parameters
[i
];
228 final PsiType parameterType
= parameter
.getType();
229 PsiType substituted
= substituteType(substitutor
, parameterType
);
230 @NonNls String paramName
= parameter
.getName();
231 final String
[] baseSuggestions
= codeStyleManager
.suggestVariableName(VariableKind
.PARAMETER
, null, null, parameterType
).names
;
232 boolean isBaseNameGenerated
= false;
233 for (String s
: baseSuggestions
) {
234 if (s
.equals(paramName
)) {
235 isBaseNameGenerated
= true;
240 if (paramName
== null || isBaseNameGenerated
&& !substituted
.equals(parameterType
)) {
241 Pair
<String
, Integer
> pair
= m
.get(substituted
);
243 paramName
= pair
.first
+ pair
.second
;
244 m
.put(substituted
, Pair
.create(pair
.first
, pair
.second
.intValue() + 1));
247 String
[] names
= codeStyleManager
.suggestVariableName(VariableKind
.PARAMETER
, null, null, substituted
).names
;
248 if (names
.length
> 0) {
249 paramName
= names
[0];
250 } else paramName
= "p" + i
;
252 m
.put(substituted
, new Pair
<String
, Integer
>(paramName
, 1));
256 if (paramName
== null) paramName
= "p" + i
;
258 PsiParameter newParameter
= factory
.createParameter(paramName
, substituted
);
259 if (parameter
.getLanguage() == StdLanguages
.JAVA
) {
260 newParameter
.getModifierList().replace(parameter
.getModifierList());
262 newMethod
.getParameterList().add(newParameter
);
265 for (PsiTypeParameter typeParam
: typeParams
) {
266 if (substitutor
.substitute(typeParam
) != null) newMethod
.getTypeParameterList().add(typeParam
);
269 PsiClassType
[] thrownTypes
= method
.getThrowsList().getReferencedTypes();
270 for (PsiClassType thrownType
: thrownTypes
) {
271 newMethod
.getThrowsList().add(factory
.createReferenceElementByType((PsiClassType
)substituteType(substitutor
, thrownType
)));
275 catch (IncorrectOperationException e
) {
281 private static PsiType
substituteType(final PsiSubstitutor substitutor
, final PsiType type
) {
282 final PsiType psiType
= substitutor
.substitute(type
);
283 if (psiType
!= null) return psiType
;
284 return TypeConversionUtil
.erasure(type
);
287 public static PsiSubstitutor
correctSubstitutor(PsiMethod method
, PsiSubstitutor substitutor
) {
288 PsiClass hisClass
= method
.getContainingClass();
289 PsiTypeParameter
[] typeParameters
= method
.getTypeParameters();
290 if (typeParameters
.length
> 0) {
291 if (PsiUtil
.isRawSubstitutor(hisClass
, substitutor
)) {
292 substitutor
= JavaPsiFacade
.getInstance(method
.getProject()).getElementFactory().createRawSubstitutor(substitutor
, typeParameters
);
298 public static boolean isChildInRange(PsiElement child
, PsiElement first
, PsiElement last
) {
299 if (child
.equals(first
)) return true;
301 if (child
.equals(first
)) return false; // before first
302 if (child
.equals(last
)) return true;
303 child
= child
.getNextSibling();
304 if (child
== null) return false;