28d8ea1543d92f8e1135c36eabcdd640e93dca58
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / inline / InlineParameterExpressionProcessor.java
blob28d8ea1543d92f8e1135c36eabcdd640e93dca58
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.inline;
18 import com.intellij.codeInsight.ExceptionUtil;
19 import com.intellij.codeInspection.sameParameterValue.SameParameterValueInspection;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.util.Key;
22 import com.intellij.openapi.util.Ref;
23 import com.intellij.psi.*;
24 import com.intellij.psi.controlFlow.DefUseUtil;
25 import com.intellij.psi.search.searches.ReferencesSearch;
26 import com.intellij.psi.util.PsiTreeUtil;
27 import com.intellij.psi.util.PsiUtil;
28 import com.intellij.refactoring.BaseRefactoringProcessor;
29 import com.intellij.refactoring.util.InlineUtil;
30 import com.intellij.refactoring.util.RefactoringUIUtil;
31 import com.intellij.refactoring.util.RefactoringUtil;
32 import com.intellij.usageView.UsageInfo;
33 import com.intellij.usageView.UsageViewDescriptor;
34 import com.intellij.usageView.UsageViewUtil;
35 import com.intellij.util.containers.MultiMap;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
39 import java.util.*;
41 /**
42 * @author yole
44 public class InlineParameterExpressionProcessor extends BaseRefactoringProcessor {
45 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.inline.InlineParameterExpressionProcessor");
46 public static final Key<Boolean> CREATE_LOCAL_FOR_TESTS = Key.create("CREATE_INLINE_PARAMETER_LOCAL_FOR_TESTS");
48 private final PsiCallExpression myMethodCall;
49 private final PsiMethod myMethod;
50 private final PsiParameter myParameter;
51 private PsiExpression myInitializer;
52 private final boolean mySameClass;
53 private final PsiMethod myCallingMethod;
54 private boolean myCreateLocal;
56 public InlineParameterExpressionProcessor(final PsiCallExpression methodCall,
57 final PsiMethod method,
58 final PsiParameter parameter,
59 final PsiExpression initializer,
60 boolean createLocal) {
61 super(method.getProject());
62 myMethodCall = methodCall;
63 myMethod = method;
64 myParameter = parameter;
65 myInitializer = initializer;
66 myCreateLocal = createLocal;
68 PsiClass callingClass = PsiTreeUtil.getParentOfType(methodCall, PsiClass.class);
69 mySameClass = (callingClass == myMethod.getContainingClass());
70 myCallingMethod = PsiTreeUtil.getParentOfType(myMethodCall, PsiMethod.class);
73 @Override
74 protected String getCommandName() {
75 return InlineParameterHandler.REFACTORING_NAME;
78 @Override
79 protected void refreshElements(PsiElement[] elements) {
82 @Override
83 protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
84 return new InlineViewDescriptor(myParameter);
87 @NotNull
88 @Override
89 protected UsageInfo[] findUsages() {
91 int parameterIndex = myMethod.getParameterList().getParameterIndex(myParameter);
92 final Map<PsiVariable, PsiElement> localToParamRef = new HashMap<PsiVariable, PsiElement>();
93 final PsiExpression[] arguments = myMethodCall.getArgumentList().getExpressions();
94 for (int i = 0; i < arguments.length; i++) {
95 if (i != parameterIndex && arguments[i] instanceof PsiReferenceExpression) {
96 final PsiReferenceExpression referenceExpression = (PsiReferenceExpression)arguments[i];
97 final PsiElement element = referenceExpression.resolve();
98 if (element instanceof PsiLocalVariable || element instanceof PsiParameter) {
99 final PsiParameter param = myMethod.getParameterList().getParameters()[i];
100 final PsiExpression paramRef =
101 JavaPsiFacade.getInstance(myMethod.getProject()).getElementFactory().createExpressionFromText(param.getName(), myMethod);
102 localToParamRef.put((PsiVariable)element, paramRef);
107 final List<UsageInfo> result = new ArrayList<UsageInfo>();
108 myInitializer.accept(new JavaRecursiveElementVisitor() {
109 @Override
110 public void visitReferenceExpression(final PsiReferenceExpression expression) {
111 super.visitReferenceExpression(expression);
112 final PsiElement element = expression.resolve();
113 if (element instanceof PsiLocalVariable) {
114 final PsiLocalVariable localVariable = (PsiLocalVariable)element;
115 final PsiElement[] elements = DefUseUtil.getDefs(myCallingMethod.getBody(), localVariable, expression);
116 if (elements.length == 1) {
117 PsiExpression localInitializer = null;
118 if (elements[0] instanceof PsiLocalVariable) {
119 localInitializer = ((PsiLocalVariable)elements[0]).getInitializer();
121 else if (elements[0] instanceof PsiAssignmentExpression) {
122 localInitializer = ((PsiAssignmentExpression)elements[0]).getRExpression();
124 else if (elements[0] instanceof PsiReferenceExpression) {
125 final PsiReferenceExpression refElement = (PsiReferenceExpression)elements[0];
126 final PsiElement parent = refElement.getParent();
127 if (parent instanceof PsiAssignmentExpression && ((PsiAssignmentExpression)parent).getLExpression() == refElement) {
128 localInitializer = ((PsiAssignmentExpression)parent).getRExpression();
131 if (localInitializer != null) {
132 final PsiElement replacement;
133 if (localToParamRef.containsKey(localVariable)) {
134 replacement = localToParamRef.get(localVariable);
136 else {
137 replacement = replaceArgs(localToParamRef, localInitializer.copy());
139 result.add(new LocalReplacementUsageInfo(expression, replacement));
146 if (!myCreateLocal) {
147 for (PsiReference ref : ReferencesSearch.search(myParameter).findAll()) {
148 result.add(new UsageInfo(ref));
152 final UsageInfo[] usageInfos = result.toArray(new UsageInfo[result.size()]);
153 return UsageViewUtil.removeDuplicatedUsages(usageInfos);
156 private static PsiElement replaceArgs(final Map<PsiVariable, PsiElement> elementsToReplace, PsiElement expression) {
157 final Map<PsiElement, PsiElement> replacements = new HashMap<PsiElement, PsiElement>();
158 expression.accept(new JavaRecursiveElementVisitor() {
159 @Override
160 public void visitReferenceExpression(PsiReferenceExpression referenceExpression) {
161 super.visitReferenceExpression(referenceExpression);
162 final PsiElement resolved = referenceExpression.resolve();
163 if (resolved instanceof PsiVariable) {
164 final PsiVariable variable = (PsiVariable)resolved;
165 final PsiElement replacement = elementsToReplace.get(variable);
166 if (replacement != null) {
167 replacements.put(referenceExpression, replacement);
172 return RefactoringUtil.replaceElementsWithMap(expression, replacements);
175 @Override
176 protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
177 final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
178 myInitializer.accept(new JavaRecursiveElementWalkingVisitor() {
179 @Override
180 public void visitReferenceExpression(final PsiReferenceExpression expression) {
181 super.visitReferenceExpression(expression);
182 final PsiElement element = expression.resolve();
183 if (element instanceof PsiMember && !((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) {
184 if (myMethod.hasModifierProperty(PsiModifier.STATIC)) {
185 conflicts.putValue(expression, "Parameter initializer depends on " + RefactoringUIUtil.getDescription(element, false) + " which is not available inside the static method");
188 if (element instanceof PsiMethod || element instanceof PsiField) {
189 if (!mySameClass && !((PsiModifierListOwner)element).hasModifierProperty(PsiModifier.STATIC)) {
190 conflicts.putValue(expression, "Parameter initializer depend on non static member from some other class");
191 } else if (!PsiUtil.isAccessible((PsiMember)element, myMethod, null)) {
192 conflicts.putValue(expression, "Parameter initializer depends on value which is not available inside method");
194 } else if (element instanceof PsiParameter) {
195 conflicts.putValue(expression, "Parameter initializer depends on callers parameter");
199 @Override
200 public void visitThisExpression(PsiThisExpression thisExpression) {
201 super.visitThisExpression(thisExpression);
202 final PsiJavaCodeReferenceElement qualifier = thisExpression.getQualifier();
203 PsiElement containingClass;
204 if (qualifier != null) {
205 containingClass = qualifier.resolve();
207 else {
208 containingClass = PsiTreeUtil.getParentOfType(myMethodCall, PsiClass.class);
210 final PsiClass methodContainingClass = myMethod.getContainingClass();
211 LOG.assertTrue(methodContainingClass != null);
212 if (!PsiTreeUtil.isAncestor(containingClass, methodContainingClass, false)) {
213 conflicts.putValue(thisExpression,
214 "Parameter initializer depends on this which is not available inside the method and cannot be inlined");
215 } else if (myMethod.hasModifierProperty(PsiModifier.STATIC)) {
216 conflicts.putValue(thisExpression, "Parameter initializer depends on this which is not available inside the static method");
220 @Override
221 public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
222 super.visitReferenceElement(reference);
223 if (myMethod.hasModifierProperty(PsiModifier.STATIC)) {
224 final PsiElement resolved = reference.resolve();
225 if (resolved instanceof PsiClass && !((PsiClass)resolved).hasModifierProperty(PsiModifier.STATIC)) {
226 conflicts.putValue(reference, "Parameter initializer depends on non static class which is not available inside static method");
231 @Override
232 public void visitNewExpression(PsiNewExpression expression) {
233 super.visitNewExpression(expression);
234 final PsiJavaCodeReferenceElement reference = expression.getClassOrAnonymousClassReference();
235 if (reference != null) {
236 final PsiElement resolved = reference.resolve();
237 if (resolved instanceof PsiClass) {
238 final PsiClass refClass = (PsiClass)resolved;
239 final String classUnavailableMessage = "Parameter initializer depends on " +
240 RefactoringUIUtil.getDescription(refClass, true) +
241 " which is not available inside method and cannot be inlined";
242 if (!PsiUtil.isAccessible(refClass, myMethod, null)) {
243 conflicts.putValue(expression, classUnavailableMessage);
245 else {
246 final PsiClass methodContainingClass = myMethod.getContainingClass();
247 LOG.assertTrue(methodContainingClass != null);
248 if (!PsiTreeUtil.isAncestor(myMethod, refClass, false)) {
249 PsiElement parent = refClass;
250 while ((parent = parent.getParent()) instanceof PsiClass) {
251 if (!PsiUtil.isAccessible((PsiClass)parent, myMethod, null)) {
252 break;
255 if (!(parent instanceof PsiFile)) {
256 conflicts.putValue(expression, classUnavailableMessage);
265 final UsageInfo[] usages = refUsages.get();
266 final Set<PsiVariable> vars = new HashSet<PsiVariable>();
267 for (UsageInfo usageInfo : usages) {
268 if (usageInfo instanceof LocalReplacementUsageInfo) {
269 final PsiVariable var = ((LocalReplacementUsageInfo)usageInfo).getVariable();
270 if (var != null) {
271 vars.add(var);
275 for (PsiVariable var : vars) {
276 for (PsiReference ref : ReferencesSearch.search(var)) {
277 final PsiElement element = ref.getElement();
278 if (element instanceof PsiExpression && isAccessedForWriting((PsiExpression)element)) {
279 conflicts.putValue(element, "Parameter initializer depends on value which is not available inside method and cannot be inlined");
280 break;
284 return showConflicts(conflicts);
287 private static boolean isAccessedForWriting (PsiExpression expr) {
288 while (expr.getParent() instanceof PsiArrayAccessExpression) {
289 expr = (PsiExpression)expr.getParent();
291 return PsiUtil.isAccessedForWriting(expr);
294 @Override
295 protected void performRefactoring(UsageInfo[] usages) {
296 final List<PsiClassType> thrownExceptions = ExceptionUtil.getThrownCheckedExceptions(new PsiElement[]{myInitializer});
297 final Set<PsiVariable> varsUsedInInitializer = new HashSet<PsiVariable>();
298 final Set<PsiJavaCodeReferenceElement> paramRefsToInline = new HashSet<PsiJavaCodeReferenceElement>();
299 final Map<PsiElement, PsiElement> replacements = new HashMap<PsiElement, PsiElement>();
300 for (UsageInfo usage : usages) {
301 if (usage instanceof LocalReplacementUsageInfo) {
302 final LocalReplacementUsageInfo replacementUsageInfo = (LocalReplacementUsageInfo)usage;
303 final PsiElement element = replacementUsageInfo.getElement();
304 final PsiElement replacement = replacementUsageInfo.getReplacement();
305 if (element != null && replacement != null) {
306 replacements.put(element, replacement);
308 varsUsedInInitializer.add(replacementUsageInfo.getVariable());
310 else {
311 LOG.assertTrue(!myCreateLocal);
312 paramRefsToInline.add((PsiJavaCodeReferenceElement)usage.getElement());
315 myInitializer = (PsiExpression)RefactoringUtil.replaceElementsWithMap(myInitializer, replacements);
317 if (myCreateLocal) {
318 final PsiElementFactory factory = JavaPsiFacade.getInstance(myMethod.getProject()).getElementFactory();
319 PsiDeclarationStatement localDeclaration =
320 factory.createVariableDeclarationStatement(myParameter.getName(), myParameter.getType(), myInitializer);
321 final PsiLocalVariable declaredVar = (PsiLocalVariable)localDeclaration.getDeclaredElements()[0];
322 PsiUtil.setModifierProperty(declaredVar, PsiModifier.FINAL, myParameter.hasModifierProperty(PsiModifier.FINAL));
323 final PsiExpression localVarInitializer =
324 InlineUtil.inlineVariable(myParameter, myInitializer, (PsiReferenceExpression)factory.createExpressionFromText(myParameter.getName(), myMethod));
325 final PsiExpression initializer = declaredVar.getInitializer();
326 LOG.assertTrue(initializer != null);
327 initializer.replace(localVarInitializer);
328 final PsiCodeBlock body = myMethod.getBody();
329 if (body != null) {
330 body.addAfter(localDeclaration, body.getLBrace());
332 } else {
333 for (PsiJavaCodeReferenceElement paramRef : paramRefsToInline) {
334 InlineUtil.inlineVariable(myParameter, myInitializer, paramRef);
338 //delete var if it becomes unused
339 for (PsiVariable variable : varsUsedInInitializer) {
340 if (variable != null && variable.isValid()) {
341 if (ReferencesSearch.search(variable).findFirst() == null) {
342 variable.delete();
347 SameParameterValueInspection.InlineParameterValueFix.removeParameter(myMethod, myParameter);
349 if (!thrownExceptions.isEmpty()) {
350 for (PsiClassType exception : thrownExceptions) {
351 PsiClass exceptionClass = exception.resolve();
352 if (exceptionClass != null) {
353 PsiUtil.addException(myMethod, exceptionClass);
359 private static class LocalReplacementUsageInfo extends UsageInfo {
360 private final PsiElement myReplacement;
361 private final PsiVariable myVariable;
363 public LocalReplacementUsageInfo(@NotNull PsiReference element, @NotNull PsiElement replacement) {
364 super(element);
365 final PsiElement resolved = element.resolve();
366 myVariable = resolved instanceof PsiVariable ? (PsiVariable)resolved : null;
367 myReplacement = replacement;
370 @Nullable
371 public PsiElement getReplacement() {
372 return myReplacement.isValid() ? myReplacement : null;
375 @Nullable
376 public PsiVariable getVariable() {
377 return myVariable != null && myVariable.isValid() ? myVariable : null;