update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / introduceParameter / JavaIntroduceParameterMethodUsagesProcessor.java
blobffa0f7720947ca74d40384137a7b19e5999ee082
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 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;
40 /**
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);
68 else {
69 final PsiParameter[] parameters = data.getMethodToSearchFor().getParameterList().getParameters();
70 if (parameters.length > oldArgs.length) {
71 anchor = getLast(oldArgs);
73 else {
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)) {
83 argList
84 .addAfter(JavaPsiFacade.getElementFactory(data.getProject()).createExpressionFromText(data.getParameterName(), argList), anchor);
86 else {
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());
98 return false;
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)) {
105 return true;
108 return false;
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) {
115 try {
116 exprs[paramNum].delete();
118 catch (IncorrectOperationException e) {
119 LOG.error(e);
121 return true;
126 @Nullable
127 private static PsiExpression getLast(PsiExpression[] oldArgs) {
128 PsiExpression anchor;
129 if (oldArgs.length > 0) {
130 anchor = oldArgs[oldArgs.length - 1];
132 else {
133 anchor = null;
135 return anchor;
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) {
153 try {
154 PsiParameter param = parameters[paramNum];
155 PsiDocTag tag = javaDocHelper.getTagForParameter(param);
156 if (tag != null) {
157 tag.delete();
159 param.delete();
161 catch (IncorrectOperationException e) {
162 LOG.error(e);
164 return true;
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();
179 return false;
182 @Nullable
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;
191 else {
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);
210 else {
211 return true;
213 return false;
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]);
230 else {
231 superCall = (PsiExpressionStatement)body.add(superCall);
233 processChangeMethodUsage(data, new ExternalUsageInfo(((PsiMethodCallExpression)superCall.getExpression()).getMethodExpression()), usages);
234 return false;