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
.NotNull
;
39 import org
.jetbrains
.annotations
.Nullable
;
42 * @author Maxim.Medvedev
44 public class JavaIntroduceParameterMethodUsagesProcessor
implements IntroduceParameterMethodUsagesProcessor
{
45 private static final Logger LOG
=
46 Logger
.getInstance("#com.intellij.refactoring.introduceParameter.JavaIntroduceParameterMethodUsagesProcessor");
47 private static final JavaLanguage myLanguage
= Language
.findInstance(JavaLanguage
.class);
49 private static boolean isJavaUsage(UsageInfo usage
) {
50 PsiElement e
= usage
.getElement();
51 return e
!= null && e
.getLanguage().is(myLanguage
);
54 public boolean isMethodUsage(UsageInfo usage
) {
55 return RefactoringUtil
.isMethodUsage(usage
.getElement()) && isJavaUsage(usage
);
58 public boolean processChangeMethodUsage(IntroduceParameterData data
, UsageInfo usage
, UsageInfo
[] usages
) throws IncorrectOperationException
{
59 if (!isMethodUsage(usage
)) return true;
60 final PsiElement ref
= usage
.getElement();
61 PsiCall callExpression
= RefactoringUtil
.getCallExpressionByMethodReference(ref
);
62 PsiExpressionList argList
= RefactoringUtil
.getArgumentListByMethodReference(ref
);
63 PsiExpression
[] oldArgs
= argList
.getExpressions();
65 final PsiExpression anchor
;
66 if (!data
.getMethodToSearchFor().isVarArgs()) {
67 anchor
= getLast(oldArgs
);
70 final PsiParameter
[] parameters
= data
.getMethodToSearchFor().getParameterList().getParameters();
71 if (parameters
.length
> oldArgs
.length
) {
72 anchor
= getLast(oldArgs
);
75 LOG
.assertTrue(parameters
.length
> 0);
76 final int lastNonVararg
= parameters
.length
- 2;
77 anchor
= lastNonVararg
>= 0 ? oldArgs
[lastNonVararg
] : null;
81 //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
82 PsiMethod method
= PsiTreeUtil
.getParentOfType(argList
, PsiMethod
.class);
83 if (method
!= null && isMethodInUsages(data
, method
, usages
)) {
85 .addAfter(JavaPsiFacade
.getElementFactory(data
.getProject()).createExpressionFromText(data
.getParameterName(), argList
), anchor
);
88 ChangeContextUtil
.encodeContextInfo(data
.getParameterInitializer(), true);
89 PsiExpression newArg
= (PsiExpression
)argList
.addAfter(data
.getParameterInitializer(), anchor
);
90 ChangeContextUtil
.decodeContextInfo(newArg
, null, null);
91 ChangeContextUtil
.clearContextInfo(data
.getParameterInitializer());
92 // here comes some postprocessing...
93 new OldReferenceResolver(callExpression
, newArg
, data
.getMethodToReplaceIn(), data
.getReplaceFieldsWithGetters(),
94 data
.getParameterInitializer()).resolve();
98 final PsiExpressionList argumentList
= callExpression
.getArgumentList();
99 LOG
.assertTrue(argumentList
!= null, callExpression
.getText());
100 removeParametersFromCall(argumentList
, data
.getParametersToRemove());
104 private static boolean isMethodInUsages(IntroduceParameterData data
, PsiMethod method
, UsageInfo
[] usages
) {
105 PsiManager manager
= PsiManager
.getInstance(data
.getProject());
106 for (UsageInfo info
: usages
) {
107 if (!(info
instanceof DefaultConstructorImplicitUsageInfo
) && manager
.areElementsEquivalent(info
.getElement(), method
)) {
114 private static void removeParametersFromCall(@NotNull final PsiExpressionList argList
, TIntArrayList parametersToRemove
) {
115 final PsiExpression
[] exprs
= argList
.getExpressions();
116 parametersToRemove
.forEachDescending(new TIntProcedure() {
117 public boolean execute(final int paramNum
) {
119 exprs
[paramNum
].delete();
121 catch (IncorrectOperationException e
) {
130 private static PsiExpression
getLast(PsiExpression
[] oldArgs
) {
131 PsiExpression anchor
;
132 if (oldArgs
.length
> 0) {
133 anchor
= oldArgs
[oldArgs
.length
- 1];
142 public void findConflicts(IntroduceParameterData data
, UsageInfo
[] usages
, MultiMap
<PsiElement
, String
> conflicts
) {
145 public boolean processChangeMethodSignature(IntroduceParameterData data
, UsageInfo usage
, UsageInfo
[] usages
) throws IncorrectOperationException
{
146 if (!(usage
.getElement() instanceof PsiMethod
) || !isJavaUsage(usage
)) return true;
147 PsiMethod method
= (PsiMethod
)usage
.getElement();
149 final FieldConflictsResolver fieldConflictsResolver
= new FieldConflictsResolver(data
.getParameterName(), method
.getBody());
150 final MethodJavaDocHelper javaDocHelper
= new MethodJavaDocHelper(method
);
151 PsiElementFactory factory
= JavaPsiFacade
.getInstance(data
.getProject()).getElementFactory();
153 final PsiParameter
[] parameters
= method
.getParameterList().getParameters();
154 data
.getParametersToRemove().forEachDescending(new TIntProcedure() {
155 public boolean execute(final int paramNum
) {
157 PsiParameter param
= parameters
[paramNum
];
158 PsiDocTag tag
= javaDocHelper
.getTagForParameter(param
);
164 catch (IncorrectOperationException e
) {
171 PsiParameter parameter
= factory
.createParameter(data
.getParameterName(), data
.getForcedType());
172 PsiUtil
.setModifierProperty(parameter
, PsiModifier
.FINAL
, data
.isDeclareFinal());
173 final PsiParameter anchorParameter
= getAnchorParameter(method
);
174 final PsiParameterList parameterList
= method
.getParameterList();
175 parameter
= (PsiParameter
)parameterList
.addAfter(parameter
, anchorParameter
);
176 JavaCodeStyleManager
.getInstance(data
.getProject()).shortenClassReferences(parameter
);
177 final PsiDocTag tagForAnchorParameter
= javaDocHelper
.getTagForParameter(anchorParameter
);
178 javaDocHelper
.addParameterAfter(data
.getParameterName(), tagForAnchorParameter
);
180 fieldConflictsResolver
.fix();
186 private static PsiParameter
getAnchorParameter(PsiMethod methodToReplaceIn
) {
187 PsiParameterList parameterList
= methodToReplaceIn
.getParameterList();
188 final PsiParameter anchorParameter
;
189 final PsiParameter
[] parameters
= parameterList
.getParameters();
190 final int length
= parameters
.length
;
191 if (!methodToReplaceIn
.isVarArgs()) {
192 anchorParameter
= length
> 0 ? parameters
[length
- 1] : null;
195 LOG
.assertTrue(length
> 0);
196 LOG
.assertTrue(parameters
[length
- 1].isVarArgs());
197 anchorParameter
= length
> 1 ? parameters
[length
- 2] : null;
199 return anchorParameter
;
202 public boolean processAddDefaultConstructor(IntroduceParameterData data
, UsageInfo usage
, UsageInfo
[] usages
) {
203 if (!(usage
.getElement() instanceof PsiClass
) || !isJavaUsage(usage
)) return true;
204 PsiClass aClass
= (PsiClass
)usage
.getElement();
205 if (!(aClass
instanceof PsiAnonymousClass
)) {
206 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(data
.getProject()).getElementFactory();
207 PsiMethod constructor
= factory
.createMethodFromText(aClass
.getName() + "(){}", aClass
);
208 constructor
= (PsiMethod
)CodeStyleManager
.getInstance(data
.getProject()).reformat(constructor
);
209 constructor
= (PsiMethod
)aClass
.add(constructor
);
210 PsiUtil
.setModifierProperty(constructor
, VisibilityUtil
.getVisibilityModifier(aClass
.getModifierList()), true);
211 processAddSuperCall(data
, new UsageInfo(constructor
), usages
);
219 public boolean processAddSuperCall(IntroduceParameterData data
, UsageInfo usage
, UsageInfo
[] usages
) throws IncorrectOperationException
{
220 if (!(usage
.getElement() instanceof PsiMethod
) || !isJavaUsage(usage
)) return true;
221 PsiMethod constructor
= (PsiMethod
)usage
.getElement();
223 if (!constructor
.isConstructor()) return true;
225 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(data
.getProject()).getElementFactory();
226 PsiExpressionStatement superCall
= (PsiExpressionStatement
)factory
.createStatementFromText("super();", constructor
);
227 superCall
= (PsiExpressionStatement
)CodeStyleManager
.getInstance(data
.getProject()).reformat(superCall
);
228 PsiCodeBlock body
= constructor
.getBody();
229 final PsiStatement
[] statements
= body
.getStatements();
230 if (statements
.length
> 0) {
231 superCall
= (PsiExpressionStatement
)body
.addBefore(superCall
, statements
[0]);
234 superCall
= (PsiExpressionStatement
)body
.add(superCall
);
236 processChangeMethodUsage(data
, new ExternalUsageInfo(((PsiMethodCallExpression
)superCall
.getExpression()).getMethodExpression()), usages
);