| /* |
| * Copyright 2000-2009 JetBrains s.r.o. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /* |
| * User: anna |
| * Date: 06-May-2008 |
| */ |
| package com.intellij.refactoring.extractMethodObject; |
| |
| import com.intellij.codeInsight.NullableNotNullManager; |
| import com.intellij.codeInsight.generation.GenerateMembersUtil; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.diagnostic.Logger; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.ui.DialogWrapper; |
| import com.intellij.openapi.util.Comparing; |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.codeStyle.CodeStyleManager; |
| import com.intellij.psi.codeStyle.CodeStyleSettingsManager; |
| import com.intellij.psi.codeStyle.JavaCodeStyleManager; |
| import com.intellij.psi.codeStyle.VariableKind; |
| import com.intellij.psi.controlFlow.ControlFlowUtil; |
| import com.intellij.psi.impl.source.PsiImmediateClassType; |
| import com.intellij.psi.search.GlobalSearchScope; |
| import com.intellij.psi.search.LocalSearchScope; |
| import com.intellij.psi.search.SearchScope; |
| import com.intellij.psi.search.searches.ReferencesSearch; |
| import com.intellij.psi.util.PropertyUtil; |
| import com.intellij.psi.util.PsiTreeUtil; |
| import com.intellij.psi.util.PsiUtil; |
| import com.intellij.psi.util.PsiUtilCore; |
| import com.intellij.refactoring.BaseRefactoringProcessor; |
| import com.intellij.refactoring.HelpID; |
| import com.intellij.refactoring.changeSignature.ChangeSignatureProcessor; |
| import com.intellij.refactoring.changeSignature.ParameterInfoImpl; |
| import com.intellij.refactoring.classMembers.MemberInfoBase; |
| import com.intellij.refactoring.extractMethod.AbstractExtractDialog; |
| import com.intellij.refactoring.extractMethod.ExtractMethodProcessor; |
| import com.intellij.refactoring.ui.MemberSelectionPanel; |
| import com.intellij.refactoring.util.RefactoringUtil; |
| import com.intellij.refactoring.util.classMembers.MemberInfo; |
| import com.intellij.refactoring.util.duplicates.Match; |
| import com.intellij.testFramework.LightVirtualFile; |
| import com.intellij.usageView.UsageInfo; |
| import com.intellij.usageView.UsageViewDescriptor; |
| import com.intellij.usageView.UsageViewUtil; |
| import com.intellij.util.ArrayUtil; |
| import com.intellij.util.Function; |
| import com.intellij.util.IncorrectOperationException; |
| import com.intellij.util.VisibilityUtil; |
| import org.jetbrains.annotations.NonNls; |
| import org.jetbrains.annotations.NotNull; |
| |
| import javax.swing.*; |
| import java.util.*; |
| |
| public class ExtractMethodObjectProcessor extends BaseRefactoringProcessor { |
| private static final Logger LOG = Logger.getInstance("#" + ExtractMethodObjectProcessor.class.getName()); |
| @NonNls public static final String REFACTORING_NAME = "Extract Method Object"; |
| |
| private final PsiElementFactory myElementFactory; |
| |
| protected final MyExtractMethodProcessor myExtractProcessor; |
| private boolean myCreateInnerClass = true; |
| private String myInnerClassName; |
| |
| private boolean myMultipleExitPoints; |
| private PsiField[] myOutputFields; |
| |
| private PsiMethod myInnerMethod; |
| private boolean myMadeStatic = false; |
| private final Set<MethodToMoveUsageInfo> myUsages = new LinkedHashSet<MethodToMoveUsageInfo>(); |
| private PsiClass myInnerClass; |
| private boolean myChangeReturnType; |
| private Runnable myCopyMethodToInner; |
| |
| public ExtractMethodObjectProcessor(Project project, Editor editor, PsiElement[] elements, final String innerClassName) { |
| super(project); |
| myInnerClassName = innerClassName; |
| myExtractProcessor = new MyExtractMethodProcessor(project, editor, elements, null, REFACTORING_NAME, innerClassName, HelpID.EXTRACT_METHOD_OBJECT); |
| myElementFactory = JavaPsiFacade.getInstance(project).getElementFactory(); |
| } |
| |
| @NotNull |
| protected UsageViewDescriptor createUsageViewDescriptor(final UsageInfo[] usages) { |
| return new ExtractMethodObjectViewDescriptor(getMethod()); |
| } |
| |
| @NotNull |
| protected UsageInfo[] findUsages() { |
| final ArrayList<UsageInfo> result = new ArrayList<UsageInfo>(); |
| final PsiClass containingClass = getMethod().getContainingClass(); |
| final SearchScope scope = PsiUtilCore.getVirtualFile(containingClass) == null |
| ? new LocalSearchScope(containingClass) |
| : GlobalSearchScope.projectScope(myProject); |
| PsiReference[] refs = |
| ReferencesSearch.search(getMethod(), scope, false).toArray(PsiReference.EMPTY_ARRAY); |
| for (PsiReference ref : refs) { |
| final PsiElement element = ref.getElement(); |
| if (element != null && element.isValid()) { |
| result.add(new UsageInfo(element)); |
| } |
| } |
| if (isCreateInnerClass()) { |
| final Set<PsiMethod> usedMethods = new LinkedHashSet<PsiMethod>(); |
| getMethod().accept(new JavaRecursiveElementWalkingVisitor() { |
| @Override |
| public void visitMethodCallExpression(PsiMethodCallExpression expression) { |
| super.visitMethodCallExpression(expression); |
| final PsiMethod method = expression.resolveMethod(); |
| if (method != null) { |
| usedMethods.add(method); |
| } |
| } |
| }); |
| |
| |
| for (PsiMethod usedMethod : usedMethods) { |
| if (usedMethod.getModifierList().hasModifierProperty(PsiModifier.PRIVATE)) { |
| PsiMethod toMove = usedMethod; |
| for (PsiReference reference : ReferencesSearch.search(usedMethod)) { |
| if (!PsiTreeUtil.isAncestor(getMethod(), reference.getElement(), false)) { |
| toMove = null; |
| break; |
| } |
| } |
| if (toMove != null) { |
| myUsages.add(new MethodToMoveUsageInfo(toMove)); |
| } |
| } |
| } |
| } |
| UsageInfo[] usageInfos = result.toArray(new UsageInfo[result.size()]); |
| return UsageViewUtil.removeDuplicatedUsages(usageInfos); |
| } |
| |
| public void performRefactoring(final UsageInfo[] usages) { |
| try { |
| if (isCreateInnerClass()) { |
| myInnerClass = (PsiClass)getMethod().getContainingClass().add(myElementFactory.createClass(getInnerClassName())); |
| final boolean isStatic = copyMethodModifiers() && notHasGeneratedFields(); |
| for (UsageInfo usage : usages) { |
| final PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(usage.getElement(), PsiMethodCallExpression.class); |
| if (methodCallExpression != null) { |
| replaceMethodCallExpression(inferTypeArguments(methodCallExpression), methodCallExpression); |
| } |
| } |
| |
| if (myExtractProcessor.generatesConditionalExit()) { |
| myInnerClass.add(myElementFactory.createField("myResult", PsiPrimitiveType.BOOLEAN)); |
| myInnerClass.add(myElementFactory.createMethodFromText("boolean is(){return myResult;}", myInnerClass)); |
| } |
| |
| final PsiParameter[] parameters = getMethod().getParameterList().getParameters(); |
| if (parameters.length > 0) { |
| createInnerClassConstructor(parameters); |
| } else if (isStatic) { |
| final PsiMethod copy = (PsiMethod)getMethod().copy(); |
| copy.setName("invoke"); |
| myInnerClass.add(copy); |
| if (myMultipleExitPoints) { |
| addOutputVariableFieldsWithGetters(); |
| } |
| return; |
| } |
| if (myMultipleExitPoints) { |
| addOutputVariableFieldsWithGetters(); |
| } |
| myCopyMethodToInner = new Runnable() { |
| public void run() { |
| copyMethodWithoutParameters(); |
| copyMethodTypeParameters(); |
| } |
| }; |
| } else { |
| for (UsageInfo usage : usages) { |
| final PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(usage.getElement(), PsiMethodCallExpression.class); |
| if (methodCallExpression != null) { |
| methodCallExpression.replace(processMethodDeclaration( methodCallExpression.getArgumentList())); |
| } |
| } |
| } |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| |
| void moveUsedMethodsToInner() { |
| if (!myUsages.isEmpty()) { |
| if (ApplicationManager.getApplication().isUnitTestMode()) { |
| for (MethodToMoveUsageInfo usage : myUsages) { |
| final PsiMember member = (PsiMember)usage.getElement(); |
| LOG.assertTrue(member != null); |
| myInnerClass.add(member.copy()); |
| member.delete(); |
| } |
| return; |
| } |
| final List<MemberInfo> memberInfos = new ArrayList<MemberInfo>(); |
| for (MethodToMoveUsageInfo usage : myUsages) { |
| memberInfos.add(new MemberInfo((PsiMethod)usage.getElement())); |
| } |
| |
| final MemberSelectionPanel panel = new MemberSelectionPanel("&Methods to move to the extracted class", memberInfos, null); |
| DialogWrapper dlg = new DialogWrapper(myProject, false) { |
| { |
| init(); |
| setTitle("Move Methods Used in Extracted Block Only"); |
| } |
| |
| |
| @Override |
| protected JComponent createCenterPanel() { |
| return panel; |
| } |
| }; |
| dlg.show(); |
| if (dlg.isOK()) { |
| ApplicationManager.getApplication().runWriteAction(new Runnable() { |
| public void run() { |
| for (MemberInfoBase<PsiMember> memberInfo : panel.getTable().getSelectedMemberInfos()) { |
| if (memberInfo.isChecked()) { |
| myInnerClass.add(memberInfo.getMember().copy()); |
| memberInfo.getMember().delete(); |
| } |
| } |
| } |
| }); |
| } |
| } |
| } |
| |
| private void addOutputVariableFieldsWithGetters() throws IncorrectOperationException { |
| final Map<String, String> var2FieldNames = new HashMap<String, String>(); |
| final PsiVariable[] outputVariables = myExtractProcessor.getOutputVariables(); |
| for (int i = 0; i < outputVariables.length; i++) { |
| final PsiVariable var = outputVariables[i]; |
| final PsiField outputField = myOutputFields[i]; |
| final String name = getPureName(var); |
| LOG.assertTrue(name != null); |
| final PsiField field; |
| if (outputField != null) { |
| var2FieldNames.put(var.getName(), outputField.getName()); |
| myInnerClass.add(outputField); |
| field = outputField; |
| } else { |
| field = PropertyUtil.findPropertyField(myInnerClass, name, false); |
| } |
| LOG.assertTrue(field != null, "i:" + i + "; output variables: " + Arrays.toString(outputVariables) + "; parameters: " + Arrays.toString(getMethod().getParameterList().getParameters()) + "; output field: " + outputField ); |
| myInnerClass.add(GenerateMembersUtil.generateGetterPrototype(field)); |
| } |
| |
| final PsiCodeBlock body = getMethod().getBody(); |
| LOG.assertTrue(body != null); |
| final LinkedHashSet<PsiLocalVariable> vars = new LinkedHashSet<PsiLocalVariable>(); |
| final Map<PsiElement, PsiElement> replacementMap = new LinkedHashMap<PsiElement, PsiElement>(); |
| final List<PsiReturnStatement> returnStatements = new ArrayList<PsiReturnStatement>(); |
| body.accept(new JavaRecursiveElementWalkingVisitor() { |
| @Override |
| public void visitReturnStatement(PsiReturnStatement statement) { |
| returnStatements.add(statement); |
| } |
| }); |
| if (myExtractProcessor.generatesConditionalExit()) { |
| for (int i = 0; i < returnStatements.size() - 1; i++) { |
| final PsiReturnStatement condition = returnStatements.get(i); |
| final PsiElement container = condition.getParent(); |
| final PsiStatement resultStmt = myElementFactory.createStatementFromText("myResult = true;", container); |
| if (!RefactoringUtil.isLoopOrIf(container)) { |
| container.addBefore(resultStmt, condition); |
| } else { |
| RefactoringUtil.putStatementInLoopBody(resultStmt, container, condition); |
| } |
| } |
| |
| LOG.assertTrue(!returnStatements.isEmpty()); |
| final PsiReturnStatement returnStatement = returnStatements.get(returnStatements.size() - 1); |
| final PsiElement container = returnStatement.getParent(); |
| final PsiStatement resultStmt = myElementFactory.createStatementFromText("myResult = false;", container); |
| if (!RefactoringUtil.isLoopOrIf(container)) { |
| container.addBefore(resultStmt, returnStatement); |
| } else { |
| RefactoringUtil.putStatementInLoopBody(resultStmt, container, returnStatement); |
| } |
| } |
| body.accept(new JavaRecursiveElementWalkingVisitor() { |
| @Override |
| public void visitReturnStatement(final PsiReturnStatement statement) { |
| super.visitReturnStatement(statement); |
| try { |
| replacementMap.put(statement, myElementFactory.createStatementFromText("return this;", statement)); |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| |
| @Override |
| public void visitClass(PsiClass aClass) { |
| } |
| |
| @Override |
| public void visitDeclarationStatement(final PsiDeclarationStatement statement) { |
| super.visitDeclarationStatement(statement); |
| final PsiElement[] declaredElements = statement.getDeclaredElements();//todo |
| for (PsiElement declaredElement : declaredElements) { |
| if (declaredElement instanceof PsiVariable) { |
| for (PsiVariable variable : outputVariables) { |
| PsiVariable var = (PsiVariable)declaredElement; |
| if (Comparing.strEqual(var.getName(), variable.getName())) { |
| final PsiExpression initializer = var.getInitializer(); |
| if (initializer == null) { |
| replacementMap.put(statement, null); |
| } |
| else { |
| replacementMap.put(var, var); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void visitReferenceExpression(final PsiReferenceExpression expression) { |
| super.visitReferenceExpression(expression); |
| final PsiElement resolved = expression.resolve(); |
| if (resolved instanceof PsiLocalVariable) { |
| final String var = ((PsiLocalVariable)resolved).getName(); |
| for (PsiVariable variable : outputVariables) { |
| if (Comparing.strEqual(variable.getName(), var)) { |
| vars.add((PsiLocalVariable)resolved); |
| break; |
| } |
| } |
| } |
| } |
| }); |
| |
| for (PsiLocalVariable var : vars) { |
| final String fieldName = var2FieldNames.get(var.getName()); |
| for (PsiReference reference : ReferencesSearch.search(var)) { |
| reference.handleElementRename(fieldName); |
| } |
| } |
| |
| for (PsiElement statement : replacementMap.keySet()) { |
| final PsiElement replacement = replacementMap.get(statement); |
| if (replacement != null) { |
| if (statement instanceof PsiLocalVariable) { |
| final PsiLocalVariable variable = (PsiLocalVariable)statement; |
| variable.normalizeDeclaration(); |
| final PsiExpression initializer = variable.getInitializer(); |
| LOG.assertTrue(initializer != null); |
| final PsiStatement assignmentStatement = myElementFactory.createStatementFromText(var2FieldNames.get(variable.getName()) + " = " + initializer.getText() + ";", statement); |
| final PsiDeclarationStatement declaration = PsiTreeUtil.getParentOfType(statement, PsiDeclarationStatement.class); |
| LOG.assertTrue(declaration != null); |
| declaration.replace(assignmentStatement); |
| } else { |
| if (statement instanceof PsiReturnStatement) { |
| final PsiExpression returnValue = ((PsiReturnStatement)statement).getReturnValue(); |
| if (!(returnValue instanceof PsiReferenceExpression || returnValue == null || returnValue instanceof PsiLiteralExpression)) { |
| statement.getParent() |
| .addBefore(myElementFactory.createStatementFromText(returnValue.getText() + ";", returnValue), statement); |
| } |
| } |
| statement.replace(replacement); |
| } |
| } else { |
| statement.delete(); |
| } |
| } |
| |
| myChangeReturnType = true; |
| } |
| |
| void runChangeSignature() { |
| if (myCopyMethodToInner != null) { |
| myCopyMethodToInner.run(); |
| } |
| if (myChangeReturnType) { |
| final PsiTypeElement typeElement = ((PsiLocalVariable)((PsiDeclarationStatement)JavaPsiFacade.getElementFactory(myProject) |
| .createStatementFromText(myInnerClassName + " l =null;", myInnerClass)).getDeclaredElements()[0]).getTypeElement(); |
| final PsiTypeElement innerMethodReturnTypeElement = myInnerMethod.getReturnTypeElement(); |
| LOG.assertTrue(innerMethodReturnTypeElement != null); |
| innerMethodReturnTypeElement.replace(typeElement); |
| } |
| } |
| |
| private String getPureName(PsiVariable var) { |
| final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(myProject); |
| return styleManager.variableNameToPropertyName(var.getName(), styleManager.getVariableKind(var)); |
| } |
| |
| public PsiExpression processMethodDeclaration( PsiExpressionList expressionList) throws IncorrectOperationException { |
| if (isCreateInnerClass()) { |
| final String typeArguments = getMethod().hasTypeParameters() ? "<" + StringUtil.join(Arrays.asList(getMethod().getTypeParameters()), |
| new Function<PsiTypeParameter, String>() { |
| public String fun(final PsiTypeParameter typeParameter) { |
| final String typeParameterName = |
| typeParameter.getName(); |
| LOG.assertTrue(typeParameterName != null); |
| return typeParameterName; |
| } |
| }, ", ") + ">" : ""; |
| final PsiMethodCallExpression methodCallExpression = |
| (PsiMethodCallExpression)myElementFactory.createExpressionFromText("invoke" + expressionList.getText(), null); |
| return replaceMethodCallExpression(typeArguments, methodCallExpression); |
| } |
| else { |
| final String paramsDeclaration = getMethod().getParameterList().getText(); |
| final PsiType returnType = getMethod().getReturnType(); |
| LOG.assertTrue(returnType != null); |
| |
| final PsiCodeBlock methodBody = getMethod().getBody(); |
| LOG.assertTrue(methodBody != null); |
| return myElementFactory.createExpressionFromText("new Object(){ \n" + |
| "private " + |
| returnType.getPresentableText() + |
| " " + myInnerClassName + |
| paramsDeclaration + |
| methodBody.getText() + |
| "}." + myInnerClassName + |
| expressionList.getText(), null); |
| } |
| } |
| |
| |
| private PsiMethodCallExpression replaceMethodCallExpression(final String inferredTypeArguments, |
| final PsiMethodCallExpression methodCallExpression) |
| throws IncorrectOperationException { |
| @NonNls final String staticqualifier = |
| getMethod().hasModifierProperty(PsiModifier.STATIC) && notHasGeneratedFields() ? getInnerClassName() : null; |
| @NonNls String newReplacement; |
| final PsiReferenceExpression methodExpression = methodCallExpression.getMethodExpression(); |
| final PsiExpressionList argumentList = methodCallExpression.getArgumentList(); |
| if (staticqualifier != null) { |
| newReplacement = argumentList.getExpressions().length > 0 |
| ? "new " + staticqualifier + inferredTypeArguments + argumentList.getText() + "." |
| : staticqualifier + "."; |
| } else { |
| final PsiExpression qualifierExpression = methodExpression.getQualifierExpression(); |
| final String qualifier = qualifierExpression != null ? qualifierExpression.getText() + "." : ""; |
| newReplacement = qualifier + "new " + getInnerClassName() + inferredTypeArguments + argumentList.getText()+ "."; |
| } |
| return (PsiMethodCallExpression)methodCallExpression.replace(myElementFactory.createExpressionFromText(newReplacement + "invoke()", null)); |
| } |
| |
| @NotNull |
| private String inferTypeArguments(final PsiMethodCallExpression methodCallExpression) { |
| final PsiReferenceParameterList list = methodCallExpression.getMethodExpression().getParameterList(); |
| |
| if (list != null && list.getTypeArguments().length > 0) { |
| return list.getText(); |
| } |
| final PsiTypeParameter[] methodTypeParameters = getMethod().getTypeParameters(); |
| if (methodTypeParameters.length > 0) { |
| List<String> typeSignature = new ArrayList<String>(); |
| final PsiSubstitutor substitutor = methodCallExpression.resolveMethodGenerics().getSubstitutor(); |
| for (final PsiTypeParameter typeParameter : methodTypeParameters) { |
| final PsiType type = substitutor.substitute(typeParameter); |
| if (type == null || PsiType.NULL.equals(type)) { |
| return ""; |
| } |
| typeSignature.add(type.getPresentableText()); |
| } |
| return "<" + StringUtil.join(typeSignature, ", ") + ">"; |
| |
| } |
| return ""; |
| } |
| |
| protected String getCommandName() { |
| return REFACTORING_NAME; |
| } |
| |
| |
| private boolean copyMethodModifiers() throws IncorrectOperationException { |
| final PsiModifierList methodModifierList = getMethod().getModifierList(); |
| |
| final PsiModifierList innerClassModifierList = myInnerClass.getModifierList(); |
| LOG.assertTrue(innerClassModifierList != null); |
| innerClassModifierList.setModifierProperty(VisibilityUtil.getVisibilityModifier(methodModifierList), true); |
| final boolean isStatic = methodModifierList.hasModifierProperty(PsiModifier.STATIC); |
| innerClassModifierList.setModifierProperty(PsiModifier.STATIC, isStatic); |
| return isStatic; |
| } |
| |
| private void copyMethodTypeParameters() throws IncorrectOperationException { |
| final PsiTypeParameterList typeParameterList = myInnerClass.getTypeParameterList(); |
| LOG.assertTrue(typeParameterList != null); |
| |
| for (PsiTypeParameter parameter : getMethod().getTypeParameters()) { |
| typeParameterList.add(parameter); |
| } |
| } |
| |
| private void copyMethodWithoutParameters() throws IncorrectOperationException { |
| final PsiMethod newMethod = myElementFactory.createMethod("invoke", getMethod().getReturnType()); |
| newMethod.getThrowsList().replace(getMethod().getThrowsList()); |
| |
| final PsiCodeBlock replacedMethodBody = newMethod.getBody(); |
| LOG.assertTrue(replacedMethodBody != null); |
| final PsiCodeBlock methodBody = getMethod().getBody(); |
| LOG.assertTrue(methodBody != null); |
| replacedMethodBody.replace(methodBody); |
| PsiUtil.setModifierProperty(newMethod, PsiModifier.STATIC, myInnerClass.hasModifierProperty(PsiModifier.STATIC) && notHasGeneratedFields()); |
| myInnerMethod = (PsiMethod)myInnerClass.add(newMethod); |
| } |
| |
| private boolean notHasGeneratedFields() { |
| return !myMultipleExitPoints && getMethod().getParameterList().getParametersCount() == 0; |
| } |
| |
| private void createInnerClassConstructor(final PsiParameter[] parameters) throws IncorrectOperationException { |
| final PsiMethod constructor = myElementFactory.createConstructor(); |
| final PsiParameterList parameterList = constructor.getParameterList(); |
| for (PsiParameter parameter : parameters) { |
| final PsiModifierList parameterModifierList = parameter.getModifierList(); |
| LOG.assertTrue(parameterModifierList != null); |
| final String parameterName = parameter.getName(); |
| LOG.assertTrue(parameterName != null); |
| PsiParameter parm = myElementFactory.createParameter(parameterName, parameter.getType()); |
| if (CodeStyleSettingsManager.getSettings(myProject).GENERATE_FINAL_PARAMETERS) { |
| final PsiModifierList modifierList = parm.getModifierList(); |
| LOG.assertTrue(modifierList != null); |
| modifierList.setModifierProperty(PsiModifier.FINAL, true); |
| } |
| parameterList.add(parm); |
| |
| final PsiField field = createField(parm, constructor, parameterModifierList.hasModifierProperty(PsiModifier.FINAL)); |
| for (PsiReference reference : ReferencesSearch.search(parameter)) { |
| reference.handleElementRename(field.getName()); |
| } |
| } |
| myInnerClass.add(constructor); |
| } |
| |
| private PsiField createField(PsiParameter parameter, PsiMethod constructor, boolean isFinal) { |
| final String parameterName = parameter.getName(); |
| PsiType type = parameter.getType(); |
| if (type instanceof PsiEllipsisType) type = ((PsiEllipsisType)type).toArrayType(); |
| try { |
| final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(getMethod().getProject()); |
| final String fieldName = styleManager.suggestVariableName(VariableKind.FIELD, styleManager.variableNameToPropertyName(parameterName, VariableKind.PARAMETER), null, type).names[0]; |
| PsiField field = myElementFactory.createField(fieldName, type); |
| |
| final PsiModifierList modifierList = field.getModifierList(); |
| LOG.assertTrue(modifierList != null); |
| final NullableNotNullManager manager = NullableNotNullManager.getInstance(myProject); |
| if (manager.isNullable(parameter, false)) { |
| modifierList.addAfter(myElementFactory.createAnnotationFromText("@" + manager.getDefaultNullable(), field), null); |
| } |
| modifierList.setModifierProperty(PsiModifier.FINAL, isFinal); |
| |
| final PsiCodeBlock methodBody = constructor.getBody(); |
| |
| LOG.assertTrue(methodBody != null); |
| |
| @NonNls final String stmtText; |
| if (Comparing.strEqual(parameterName, fieldName)) { |
| stmtText = "this." + fieldName + " = " + parameterName + ";"; |
| } else { |
| stmtText = fieldName + " = " + parameterName + ";"; |
| } |
| PsiStatement assignmentStmt = myElementFactory.createStatementFromText(stmtText, methodBody); |
| assignmentStmt = (PsiStatement)CodeStyleManager.getInstance(constructor.getProject()).reformat(assignmentStmt); |
| methodBody.add(assignmentStmt); |
| |
| field = (PsiField)myInnerClass.add(field); |
| return field; |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| return null; |
| } |
| |
| protected void changeInstanceAccess(final Project project) |
| throws IncorrectOperationException { |
| if (myMadeStatic) { |
| PsiReference[] refs = |
| ReferencesSearch.search(myInnerMethod, GlobalSearchScope.projectScope(project), false).toArray(PsiReference.EMPTY_ARRAY); |
| for (PsiReference ref : refs) { |
| final PsiElement element = ref.getElement(); |
| final PsiMethodCallExpression callExpression = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class); |
| if (callExpression != null) { |
| replaceMethodCallExpression(inferTypeArguments(callExpression), callExpression); |
| } |
| } |
| } |
| } |
| |
| public PsiMethod getMethod() { |
| return myExtractProcessor.getExtractedMethod(); |
| } |
| |
| public String getInnerClassName() { |
| return myInnerClassName; |
| } |
| |
| public void setCreateInnerClass(final boolean createInnerClass) { |
| myCreateInnerClass = createInnerClass; |
| } |
| |
| public boolean isCreateInnerClass() { |
| return myCreateInnerClass; |
| } |
| |
| |
| public MyExtractMethodProcessor getExtractProcessor() { |
| return myExtractProcessor; |
| } |
| |
| protected AbstractExtractDialog createExtractMethodObjectDialog(final MyExtractMethodProcessor processor) { |
| return new ExtractMethodObjectDialog(myProject, processor.getTargetClass(), processor.getInputVariables(), processor.getReturnType(), |
| processor.getTypeParameterList(), |
| processor.getThrownExceptions(), processor.isStatic(), processor.isCanBeStatic(), |
| processor.getElements(), myMultipleExitPoints){ |
| @Override |
| protected boolean isUsedAfter(PsiVariable variable) { |
| return ArrayUtil.find(processor.getOutputVariables(), variable) != -1; |
| } |
| }; |
| } |
| |
| public PsiClass getInnerClass() { |
| return myInnerClass; |
| } |
| |
| public class MyExtractMethodProcessor extends ExtractMethodProcessor { |
| |
| public MyExtractMethodProcessor(Project project, |
| Editor editor, |
| PsiElement[] elements, |
| PsiType forcedReturnType, |
| String refactoringName, |
| String initialMethodName, |
| String helpId) { |
| super(project, editor, elements, forcedReturnType, refactoringName, initialMethodName, helpId); |
| |
| } |
| |
| @Override |
| protected void apply(final AbstractExtractDialog dialog) { |
| super.apply(dialog); |
| myCreateInnerClass = !(dialog instanceof ExtractMethodObjectDialog) || ((ExtractMethodObjectDialog)dialog).createInnerClass(); |
| myInnerClassName = myCreateInnerClass ? StringUtil.capitalize(dialog.getChosenMethodName()) : dialog.getChosenMethodName(); |
| } |
| |
| @Override |
| protected AbstractExtractDialog createExtractMethodDialog(final boolean direct) { |
| return createExtractMethodObjectDialog(this); |
| } |
| |
| @Override |
| protected boolean checkOutputVariablesCount() { |
| myMultipleExitPoints = super.checkOutputVariablesCount(); |
| myOutputFields = new PsiField[myOutputVariables.length]; |
| for (int i = 0; i < myOutputVariables.length; i++) { |
| PsiVariable variable = myOutputVariables[i]; |
| if (!myInputVariables.contains(variable)) { //one field creation |
| final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(myProject); |
| final String fieldName = |
| styleManager.suggestVariableName(VariableKind.FIELD, getPureName(variable), null, variable.getType()).names[0]; |
| try { |
| myOutputFields[i] = myElementFactory.createField(fieldName, variable.getType()); |
| } |
| catch (IncorrectOperationException e) { |
| LOG.error(e); |
| } |
| } |
| } |
| return !myCreateInnerClass && myMultipleExitPoints; |
| } |
| |
| @Override |
| public PsiElement processMatch(final Match match) throws IncorrectOperationException { |
| final boolean makeStatic = myInnerMethod != null && |
| RefactoringUtil.isInStaticContext(match.getMatchStart(), getExtractedMethod().getContainingClass()) && |
| !myInnerMethod.getContainingClass().hasModifierProperty(PsiModifier.STATIC); |
| final PsiElement element = super.processMatch(match); |
| if (makeStatic) { |
| myMadeStatic = true; |
| final PsiModifierList modifierList = myInnerMethod.getContainingClass().getModifierList(); |
| LOG.assertTrue(modifierList != null); |
| modifierList.setModifierProperty(PsiModifier.STATIC, true); |
| PsiUtil.setModifierProperty(myInnerMethod, PsiModifier.STATIC, true); |
| } |
| PsiMethodCallExpression methodCallExpression = null; |
| if (element instanceof PsiMethodCallExpression) { |
| methodCallExpression = (PsiMethodCallExpression)element; |
| } |
| else if (element instanceof PsiExpressionStatement) { |
| final PsiExpression expression = ((PsiExpressionStatement)element).getExpression(); |
| if (expression instanceof PsiMethodCallExpression) { |
| methodCallExpression = (PsiMethodCallExpression)expression; |
| } |
| else if (expression instanceof PsiAssignmentExpression) { |
| final PsiExpression psiExpression = ((PsiAssignmentExpression)expression).getRExpression(); |
| if (psiExpression instanceof PsiMethodCallExpression) { |
| methodCallExpression = (PsiMethodCallExpression)psiExpression; |
| } |
| } |
| } else if (element instanceof PsiDeclarationStatement) { |
| final PsiElement[] declaredElements = ((PsiDeclarationStatement)element).getDeclaredElements(); |
| for (PsiElement declaredElement : declaredElements) { |
| if (declaredElement instanceof PsiLocalVariable) { |
| final PsiExpression initializer = ((PsiLocalVariable)declaredElement).getInitializer(); |
| if (initializer instanceof PsiMethodCallExpression) { |
| methodCallExpression = (PsiMethodCallExpression)initializer; |
| break; |
| } |
| } |
| } |
| } |
| if (methodCallExpression == null) return element; |
| |
| PsiExpression expression = processMethodDeclaration(methodCallExpression.getArgumentList()); |
| |
| return methodCallExpression.replace(expression); |
| } |
| |
| public PsiVariable[] getOutputVariables() { |
| return myOutputVariables; |
| } |
| |
| @Override |
| protected boolean isNeedToChangeCallContext() { |
| return false; |
| } |
| |
| @Override |
| protected void declareNecessaryVariablesAfterCall(final PsiVariable outputVariable) throws IncorrectOperationException { |
| if (myMultipleExitPoints) { |
| final String object = JavaCodeStyleManager.getInstance(myProject).suggestUniqueVariableName(StringUtil.decapitalize(myInnerClassName), outputVariable, true); |
| final PsiStatement methodCallStatement = PsiTreeUtil.getParentOfType(getMethodCall(), PsiStatement.class); |
| LOG.assertTrue(methodCallStatement != null); |
| final PsiStatement declarationStatement = myElementFactory |
| .createStatementFromText(myInnerClassName + " " + object + " = " + getMethodCall().getText() + ";", myInnerMethod); |
| if (methodCallStatement instanceof PsiIfStatement) { |
| methodCallStatement.getParent().addBefore(declarationStatement, methodCallStatement); |
| final PsiExpression conditionExpression = ((PsiIfStatement)methodCallStatement).getCondition(); |
| setMethodCall((PsiMethodCallExpression)conditionExpression.replace(myElementFactory.createExpressionFromText(object + ".is()", myInnerMethod))); |
| } else if (myElements[0] instanceof PsiExpression){ |
| methodCallStatement.getParent().addBefore(declarationStatement, methodCallStatement); |
| } else { |
| final PsiDeclarationStatement replace = (PsiDeclarationStatement)methodCallStatement.replace(declarationStatement); |
| setMethodCall((PsiMethodCallExpression)((PsiLocalVariable)replace.getDeclaredElements()[0]).getInitializer()); |
| } |
| |
| final List<PsiVariable> usedVariables = myControlFlowWrapper.getUsedVariables(); |
| Collection<ControlFlowUtil.VariableInfo> reassigned = myControlFlowWrapper.getInitializedTwice(); |
| for (PsiVariable variable : usedVariables) { |
| String name = variable.getName(); |
| LOG.assertTrue(name != null); |
| PsiStatement st = null; |
| final String pureName = getPureName(variable); |
| final int varIdxInOutput = ArrayUtil.find(myOutputVariables, variable); |
| final String getterName = varIdxInOutput > -1 && myOutputFields[varIdxInOutput] != null ? PropertyUtil.suggestGetterName( |
| myOutputFields[varIdxInOutput]) : PropertyUtil.suggestGetterName(pureName, variable.getType()); |
| if (isDeclaredInside(variable)) { |
| st = myElementFactory.createStatementFromText( |
| variable.getType().getCanonicalText() + " " + name + " = " + object + "." + getterName + "();", |
| myInnerMethod); |
| if (reassigned.contains(new ControlFlowUtil.VariableInfo(variable, null))) { |
| final PsiElement[] psiElements = ((PsiDeclarationStatement)st).getDeclaredElements(); |
| assert psiElements.length > 0; |
| PsiVariable var = (PsiVariable)psiElements[0]; |
| PsiUtil.setModifierProperty(var, PsiModifier.FINAL, false); |
| } |
| } |
| else { |
| if (varIdxInOutput != -1) { |
| st = myElementFactory.createStatementFromText(name + " = " + object + "." + getterName + "();", myInnerMethod); |
| } |
| } |
| if (st != null) { |
| addToMethodCallLocation(st); |
| } |
| } |
| if (myElements[0] instanceof PsiAssignmentExpression) { |
| getMethodCall().getParent().replace(((PsiAssignmentExpression)getMethodCall().getParent()).getLExpression()); |
| } else if (myElements[0] instanceof PsiPostfixExpression || myElements[0] instanceof PsiPrefixExpression) { |
| getMethodCall().getParent().replace(((PsiBinaryExpression)getMethodCall().getParent()).getLOperand()); |
| } |
| |
| rebindExitStatement(object); |
| } |
| else { |
| super.declareNecessaryVariablesAfterCall(outputVariable); |
| } |
| } |
| |
| private void rebindExitStatement(final String objectName) { |
| final PsiStatement exitStatementCopy = myExtractProcessor.myFirstExitStatementCopy; |
| if (exitStatementCopy != null) { |
| myExtractProcessor.getDuplicates().clear(); |
| final Map<String, PsiVariable> outVarsNames = new HashMap<String, PsiVariable>(); |
| for (PsiVariable variable : myOutputVariables) { |
| outVarsNames.put(variable.getName(), variable); |
| } |
| final Map<PsiElement, PsiElement> replaceMap = new HashMap<PsiElement, PsiElement>(); |
| exitStatementCopy.accept(new JavaRecursiveElementWalkingVisitor() { |
| @Override |
| public void visitReferenceExpression(PsiReferenceExpression expression) { |
| super.visitReferenceExpression(expression); |
| if (expression.resolve() == null) { |
| final PsiVariable variable = outVarsNames.get(expression.getReferenceName()); |
| if (variable != null) { |
| final String call2Getter = objectName + "." + PropertyUtil.suggestGetterName(getPureName(variable), variable.getType()) + "()"; |
| final PsiExpression callToGetter = myElementFactory.createExpressionFromText(call2Getter, variable); |
| replaceMap.put(expression, callToGetter); |
| } |
| } |
| } |
| }); |
| for (PsiElement element : replaceMap.keySet()) { |
| if (element.isValid()) { |
| element.replace(replaceMap.get(element)); |
| } |
| } |
| } |
| } |
| |
| public boolean generatesConditionalExit() { |
| return myGenerateConditionalExit; |
| } |
| } |
| } |