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
.refactoring
.introduceParameter
;
18 import com
.intellij
.codeInsight
.ChangeContextUtil
;
19 import com
.intellij
.lang
.Language
;
20 import com
.intellij
.lang
.java
.JavaLanguage
;
21 import com
.intellij
.openapi
.diagnostic
.Logger
;
22 import com
.intellij
.psi
.*;
23 import com
.intellij
.psi
.codeStyle
.CodeStyleManager
;
24 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
25 import com
.intellij
.psi
.javadoc
.PsiDocTag
;
26 import com
.intellij
.psi
.util
.PsiTreeUtil
;
27 import com
.intellij
.psi
.util
.PsiUtil
;
28 import com
.intellij
.refactoring
.util
.FieldConflictsResolver
;
29 import com
.intellij
.refactoring
.util
.RefactoringUtil
;
30 import com
.intellij
.refactoring
.util
.javadoc
.MethodJavaDocHelper
;
31 import com
.intellij
.refactoring
.util
.usageInfo
.DefaultConstructorImplicitUsageInfo
;
32 import com
.intellij
.usageView
.UsageInfo
;
33 import com
.intellij
.util
.IncorrectOperationException
;
34 import com
.intellij
.util
.VisibilityUtil
;
35 import com
.intellij
.util
.containers
.MultiMap
;
36 import gnu
.trove
.TIntArrayList
;
37 import gnu
.trove
.TIntProcedure
;
38 import org
.jetbrains
.annotations
.Nullable
;
41 * @author Maxim.Medvedev
43 public class JavaIntroduceParameterMethodUsagesProcessor
implements IntroduceParameterMethodUsagesProcessor
{
44 private static final Logger LOG
=
45 Logger
.getInstance("#com.intellij.refactoring.introduceParameter.JavaIntroduceParameterMethodUsagesProcessor");
46 private static final JavaLanguage myLanguage
= Language
.findInstance(JavaLanguage
.class);
48 private static boolean isJavaUsage(UsageInfo usage
) {
49 PsiElement e
= usage
.getElement();
50 return e
!= null && e
.getLanguage().is(myLanguage
);
53 public boolean isMethodUsage(UsageInfo usage
) {
54 return RefactoringUtil
.isMethodUsage(usage
.getElement()) && isJavaUsage(usage
);
57 public boolean processChangeMethodUsage(IntroduceParameterData data
, UsageInfo usage
, UsageInfo
[] usages
) throws IncorrectOperationException
{
58 if (!isMethodUsage(usage
)) return true;
59 final PsiElement ref
= usage
.getElement();
60 PsiCall callExpression
= RefactoringUtil
.getCallExpressionByMethodReference(ref
);
61 PsiExpressionList argList
= RefactoringUtil
.getArgumentListByMethodReference(ref
);
62 PsiExpression
[] oldArgs
= argList
.getExpressions();
64 final PsiExpression anchor
;
65 if (!data
.getMethodToSearchFor().isVarArgs()) {
66 anchor
= getLast(oldArgs
);
69 final PsiParameter
[] parameters
= data
.getMethodToSearchFor().getParameterList().getParameters();
70 if (parameters
.length
> oldArgs
.length
) {
71 anchor
= getLast(oldArgs
);
74 LOG
.assertTrue(parameters
.length
> 0);
75 final int lastNonVararg
= parameters
.length
- 2;
76 anchor
= lastNonVararg
>= 0 ? oldArgs
[lastNonVararg
] : null;
80 //if we insert parameter in method usage which is contained in method in which we insert this parameter too, we must insert parameter name instead of its initializer
81 PsiMethod method
= PsiTreeUtil
.getParentOfType(argList
, PsiMethod
.class);
82 if (method
!= null && isMethodInUsages(data
, method
, usages
)) {
84 .addAfter(JavaPsiFacade
.getElementFactory(data
.getProject()).createExpressionFromText(data
.getParameterName(), argList
), anchor
);
87 ChangeContextUtil
.encodeContextInfo(data
.getParameterInitializer(), true);
88 PsiExpression newArg
= (PsiExpression
)argList
.addAfter(data
.getParameterInitializer(), anchor
);
89 ChangeContextUtil
.decodeContextInfo(newArg
, null, null);
90 ChangeContextUtil
.clearContextInfo(data
.getParameterInitializer());
91 // here comes some postprocessing...
92 new OldReferenceResolver(callExpression
, newArg
, data
.getMethodToReplaceIn(), data
.getReplaceFieldsWithGetters(),
93 data
.getParameterInitializer()).resolve();
97 removeParametersFromCall(callExpression
.getArgumentList(), data
.getParametersToRemove());
101 private static boolean isMethodInUsages(IntroduceParameterData data
, PsiMethod method
, UsageInfo
[] usages
) {
102 PsiManager manager
= PsiManager
.getInstance(data
.getProject());
103 for (UsageInfo info
: usages
) {
104 if (!(info
instanceof DefaultConstructorImplicitUsageInfo
) && manager
.areElementsEquivalent(info
.getElement(), method
)) {
111 private static void removeParametersFromCall(final PsiExpressionList argList
, TIntArrayList parametersToRemove
) {
112 final PsiExpression
[] exprs
= argList
.getExpressions();
113 parametersToRemove
.forEachDescending(new TIntProcedure() {
114 public boolean execute(final int paramNum
) {
116 exprs
[paramNum
].delete();
118 catch (IncorrectOperationException e
) {
127 private static PsiExpression
getLast(PsiExpression
[] oldArgs
) {
128 PsiExpression anchor
;
129 if (oldArgs
.length
> 0) {
130 anchor
= oldArgs
[oldArgs
.length
- 1];
139 public void findConflicts(IntroduceParameterData data
, UsageInfo
[] usages
, MultiMap
<PsiElement
, String
> conflicts
) {
142 public boolean processChangeMethodSignature(IntroduceParameterData data
, UsageInfo usage
, UsageInfo
[] usages
) throws IncorrectOperationException
{
143 if (!(usage
.getElement() instanceof PsiMethod
) || !isJavaUsage(usage
)) return true;
144 PsiMethod method
= (PsiMethod
)usage
.getElement();
146 final FieldConflictsResolver fieldConflictsResolver
= new FieldConflictsResolver(data
.getParameterName(), method
.getBody());
147 final MethodJavaDocHelper javaDocHelper
= new MethodJavaDocHelper(method
);
148 PsiElementFactory factory
= JavaPsiFacade
.getInstance(data
.getProject()).getElementFactory();
150 final PsiParameter
[] parameters
= method
.getParameterList().getParameters();
151 data
.getParametersToRemove().forEachDescending(new TIntProcedure() {
152 public boolean execute(final int paramNum
) {
154 PsiParameter param
= parameters
[paramNum
];
155 PsiDocTag tag
= javaDocHelper
.getTagForParameter(param
);
161 catch (IncorrectOperationException e
) {
168 PsiParameter parameter
= factory
.createParameter(data
.getParameterName(), data
.getForcedType());
169 PsiUtil
.setModifierProperty(parameter
, PsiModifier
.FINAL
, data
.isDeclareFinal());
170 final PsiParameter anchorParameter
= getAnchorParameter(method
);
171 final PsiParameterList parameterList
= method
.getParameterList();
172 parameter
= (PsiParameter
)parameterList
.addAfter(parameter
, anchorParameter
);
173 JavaCodeStyleManager
.getInstance(data
.getProject()).shortenClassReferences(parameter
);
174 final PsiDocTag tagForAnchorParameter
= javaDocHelper
.getTagForParameter(anchorParameter
);
175 javaDocHelper
.addParameterAfter(data
.getParameterName(), tagForAnchorParameter
);
177 fieldConflictsResolver
.fix();
183 private static PsiParameter
getAnchorParameter(PsiMethod methodToReplaceIn
) {
184 PsiParameterList parameterList
= methodToReplaceIn
.getParameterList();
185 final PsiParameter anchorParameter
;
186 final PsiParameter
[] parameters
= parameterList
.getParameters();
187 final int length
= parameters
.length
;
188 if (!methodToReplaceIn
.isVarArgs()) {
189 anchorParameter
= length
> 0 ? parameters
[length
- 1] : null;
192 LOG
.assertTrue(length
> 0);
193 LOG
.assertTrue(parameters
[length
- 1].isVarArgs());
194 anchorParameter
= length
> 1 ? parameters
[length
- 2] : null;
196 return anchorParameter
;
199 public boolean processAddDefaultConstructor(IntroduceParameterData data
, UsageInfo usage
, UsageInfo
[] usages
) {
200 if (!(usage
.getElement() instanceof PsiClass
) || !isJavaUsage(usage
)) return true;
201 PsiClass aClass
= (PsiClass
)usage
.getElement();
202 if (!(aClass
instanceof PsiAnonymousClass
)) {
203 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(data
.getProject()).getElementFactory();
204 PsiMethod constructor
= factory
.createMethodFromText(aClass
.getName() + "(){}", aClass
);
205 constructor
= (PsiMethod
)CodeStyleManager
.getInstance(data
.getProject()).reformat(constructor
);
206 constructor
= (PsiMethod
)aClass
.add(constructor
);
207 PsiUtil
.setModifierProperty(constructor
, VisibilityUtil
.getVisibilityModifier(aClass
.getModifierList()), true);
208 processAddSuperCall(data
, new UsageInfo(constructor
), usages
);
216 public boolean processAddSuperCall(IntroduceParameterData data
, UsageInfo usage
, UsageInfo
[] usages
) throws IncorrectOperationException
{
217 if (!(usage
.getElement() instanceof PsiMethod
) || !isJavaUsage(usage
)) return true;
218 PsiMethod constructor
= (PsiMethod
)usage
.getElement();
220 if (!constructor
.isConstructor()) return true;
222 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(data
.getProject()).getElementFactory();
223 PsiExpressionStatement superCall
= (PsiExpressionStatement
)factory
.createStatementFromText("super();", constructor
);
224 superCall
= (PsiExpressionStatement
)CodeStyleManager
.getInstance(data
.getProject()).reformat(superCall
);
225 PsiCodeBlock body
= constructor
.getBody();
226 final PsiStatement
[] statements
= body
.getStatements();
227 if (statements
.length
> 0) {
228 superCall
= (PsiExpressionStatement
)body
.addBefore(superCall
, statements
[0]);
231 superCall
= (PsiExpressionStatement
)body
.add(superCall
);
233 processChangeMethodUsage(data
, new ExternalUsageInfo(((PsiMethodCallExpression
)superCall
.getExpression()).getMethodExpression()), usages
);