From 528aa74bfdead82f6bd13bb56c8157e4f7e93dc0 Mon Sep 17 00:00:00 2001 From: Danila Ponomarenko Date: Mon, 18 Jun 2012 18:06:10 +0400 Subject: [PATCH] IDEA-87401 Partially --- ...reateConstructorParameterFromFieldFix.java | 5 +- .../impl/AssignFieldFromParameterAction.java | 26 +- ...reateAssignFieldsFromParametersAction.java | 308 ++++++++++++ .../impl/CreateFieldFromParameterAction.java | 465 ++---------------- .../impl/CreateFieldFromParameterDialog.java | 80 +-- .../impl/FieldFromParameterUtils.java | 259 ++++++++++ .../src/messages/CodeInsightBundle.properties | 5 +- resources/src/META-INF/IdeaPlugin.xml | 4 + 8 files changed, 673 insertions(+), 479 deletions(-) create mode 100644 java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateAssignFieldsFromParametersAction.java create mode 100644 java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java index 07d7a31be90d..01f6a428e305 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateConstructorParameterFromFieldFix.java @@ -25,6 +25,7 @@ import com.intellij.codeInsight.generation.PsiMethodMember; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.intention.impl.AssignFieldFromParameterAction; import com.intellij.codeInsight.intention.impl.CreateFieldFromParameterAction; +import com.intellij.codeInsight.intention.impl.FieldFromParameterUtils; import com.intellij.ide.util.MemberChooser; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.Editor; @@ -385,12 +386,12 @@ public class CreateConstructorParameterFromFieldFix implements IntentionAction { } if (o1 instanceof PsiField && o2 instanceof PsiParameter) { - final PsiField field = CreateFieldFromParameterAction.getParameterAssignedToField((PsiParameter)o2); + final PsiField field = FieldFromParameterUtils.getParameterAssignedToField((PsiParameter)o2); if (field == null) return 1; return o1.getTextOffset() - field.getTextOffset(); } if (o1 instanceof PsiParameter && o2 instanceof PsiField) { - final PsiField field = CreateFieldFromParameterAction.getParameterAssignedToField((PsiParameter)o1); + final PsiField field = FieldFromParameterUtils.getParameterAssignedToField((PsiParameter)o1); if (field == null) return -1; return field.getTextOffset() - o2.getTextOffset(); } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java index 9bcd9c8706d4..85b51af88bac 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/AssignFieldFromParameterAction.java @@ -36,21 +36,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class AssignFieldFromParameterAction extends BaseIntentionAction { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.AssignFieldFromParameterAction"); - - @Nullable - private static PsiType getType(final PsiParameter myParameter) { - if (myParameter == null) return null; - PsiType type = myParameter.getType(); - if (type instanceof PsiEllipsisType) type = ((PsiEllipsisType)type).toArrayType(); - return type; - } + private static final Logger LOG = Logger.getInstance(AssignFieldFromParameterAction.class); @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - PsiParameter myParameter = CreateFieldFromParameterAction.findParameterAtCursor(file, editor); - final PsiType type = getType(myParameter); - PsiClass targetClass = myParameter == null ? null : PsiTreeUtil.getParentOfType(myParameter, PsiClass.class); + final PsiParameter myParameter = FieldFromParameterUtils.findParameterAtCursor(file, editor); + final PsiType type = FieldFromParameterUtils.getType(myParameter); + final PsiClass targetClass = PsiTreeUtil.getParentOfType(myParameter, PsiClass.class); if (myParameter == null || !myParameter.isValid() || !myParameter.getManager().isInProject(myParameter) @@ -60,10 +52,10 @@ public class AssignFieldFromParameterAction extends BaseIntentionAction { || !type.isValid() || targetClass == null || targetClass.isInterface() - || CreateFieldFromParameterAction.getParameterAssignedToField(myParameter) != null) { + || FieldFromParameterUtils.getParameterAssignedToField(myParameter) != null) { return false; } - PsiField field = findFieldToAssign(myParameter); + final PsiField field = findFieldToAssign(myParameter); if (field == null) return false; if (!field.getLanguage().isKindOf(JavaLanguage.INSTANCE)) return false; setText(CodeInsightBundle.message("intention.assign.field.from.parameter.text", field.getName())); @@ -79,7 +71,7 @@ public class AssignFieldFromParameterAction extends BaseIntentionAction { @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) { - PsiParameter myParameter = CreateFieldFromParameterAction.findParameterAtCursor(file, editor); + PsiParameter myParameter = FieldFromParameterUtils.findParameterAtCursor(file, editor); if (!CodeInsightUtilBase.prepareFileForWrite(myParameter.getContainingFile())) return; IdeDocumentHistory.getInstance(project).includeCurrentPlaceAsChangePlace(); @@ -115,7 +107,7 @@ public class AssignFieldFromParameterAction extends BaseIntentionAction { PsiStatement assignmentStmt = factory.createStatementFromText(stmtText, methodBody); assignmentStmt = (PsiStatement)CodeStyleManager.getInstance(project).reformat(assignmentStmt); PsiStatement[] statements = methodBody.getStatements(); - int i = CreateFieldFromParameterAction.findFieldAssignmentAnchor(statements, null, targetClass, parameter); + int i = FieldFromParameterUtils.findFieldAssignmentAnchor(statements, null, targetClass, parameter); PsiElement inserted; if (i == statements.length) { inserted = methodBody.add(assignmentStmt); @@ -138,7 +130,7 @@ public class AssignFieldFromParameterAction extends BaseIntentionAction { final boolean isMethodStatic = method.hasModifierProperty(PsiModifier.STATIC); VariableKind kind = isMethodStatic ? VariableKind.STATIC_FIELD : VariableKind.FIELD; - SuggestedNameInfo suggestedNameInfo = styleManager.suggestVariableName(kind, propertyName, null, getType(myParameter)); + SuggestedNameInfo suggestedNameInfo = styleManager.suggestVariableName(kind, propertyName, null, FieldFromParameterUtils.getSubstitutedType(myParameter)); final String fieldName = suggestedNameInfo.names[0]; diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateAssignFieldsFromParametersAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateAssignFieldsFromParametersAction.java new file mode 100644 index 000000000000..d0363eb66eca --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateAssignFieldsFromParametersAction.java @@ -0,0 +1,308 @@ +/* +* Copyright 2000-2012 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. +*/ +package com.intellij.codeInsight.intention.impl; + +import com.intellij.codeInsight.CodeInsightBundle; +import com.intellij.codeInsight.CodeInsightUtilBase; +import com.intellij.codeInsight.generation.ClassMember; +import com.intellij.codeInsight.generation.MemberChooserObject; +import com.intellij.codeInsight.generation.PsiMethodMember; +import com.intellij.ide.util.MemberChooser; +import com.intellij.lang.java.JavaLanguage; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import com.intellij.openapi.util.Key; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.codeStyle.SuggestedNameInfo; +import com.intellij.psi.codeStyle.VariableKind; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.ui.SimpleColoredComponent; +import com.intellij.ui.SimpleTextAttributes; +import com.intellij.ui.speedSearch.SpeedSearchUtil; +import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ConcurrentWeakHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.swing.*; +import java.util.*; + +/** + * @author Danila Ponomarenko + */ +public class CreateAssignFieldsFromParametersAction extends BaseIntentionAction { + private static final Logger LOG = Logger.getInstance(CreateFieldFromParameterAction.class); + private static final Key, Boolean>> PARAMS = Key.create("FIELDS_FROM_PARAMS"); + + private static final Object LOCK = new Object(); + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + PsiParameter psiParameter = FieldFromParameterUtils.findParameterAtCursor(file, editor); + PsiMethod method = findMethod(psiParameter, editor, file); + if (method == null) return false; + + final List parameters = getAvailableParameters(method); + + synchronized (LOCK) { + final Collection> params = getUnboundedParams(method); + params.clear(); + for (PsiParameter parameter : parameters) { + params.add(SmartPointerManager.getInstance(project).createSmartPsiElementPointer(parameter)); + } + if (params.size() <= 1) return false; + if (psiParameter == null) { + psiParameter = params.iterator().next().getElement(); + LOG.assertTrue(psiParameter != null); + } + + setText(CodeInsightBundle.message("intention.create.assign.fields.from.parameters.text", method.isConstructor() ? "Constructor" : "Method")); + } + return isAvailable(psiParameter); + } + + @Nullable + private static PsiMethod findMethod(@Nullable PsiParameter parameter, @NotNull Editor editor, @NotNull PsiFile file) { + if (parameter == null) { + final PsiElement elementAt = file.findElementAt(editor.getCaretModel().getOffset()); + if (elementAt instanceof PsiIdentifier) { + final PsiElement parent = elementAt.getParent(); + if (parent instanceof PsiMethod) { + return (PsiMethod)parent; + } + } + } + else { + final PsiElement declarationScope = parameter.getDeclarationScope(); + if (declarationScope instanceof PsiMethod) { + return (PsiMethod)declarationScope; + } + } + + return null; + } + + @NotNull + private static List getAvailableParameters(@NotNull PsiMethod method) { + final List parameters = new ArrayList(); + for (PsiParameter parameter : method.getParameterList().getParameters()) { + if (isAvailable(parameter)) { + parameters.add(parameter); + } + } + return parameters; + } + + private static boolean isAvailable(PsiParameter psiParameter) { + final PsiType type = FieldFromParameterUtils.getSubstitutedType(psiParameter); + PsiClass targetClass = PsiTreeUtil.getParentOfType(psiParameter, PsiClass.class); + return psiParameter.isValid() + && psiParameter.getLanguage().isKindOf(JavaLanguage.INSTANCE) + && psiParameter.getDeclarationScope() instanceof PsiMethod + && ((PsiMethod)psiParameter.getDeclarationScope()).getBody() != null + && psiParameter.getManager().isInProject(psiParameter) + && type != null + && type.isValid() + && FieldFromParameterUtils.getParameterAssignedToField(psiParameter) == null + && targetClass != null + && !targetClass.isInterface() + ; + } + + @NotNull + private static Collection> getUnboundedParams(PsiMethod psiMethod) { + Map, Boolean> params = psiMethod.getUserData(PARAMS); + if (params == null) psiMethod.putUserData(PARAMS, params = new ConcurrentWeakHashMap, Boolean>(1)); + final Map, Boolean> finalParams = params; + return new AbstractCollection>() { + @Override + public boolean add(SmartPsiElementPointer psiVariable) { + return finalParams.put(psiVariable, Boolean.TRUE) == null; + } + + @Override + public Iterator> iterator() { + return finalParams.keySet().iterator(); + } + + @Override + public int size() { + return finalParams.size(); + } + + @Override + public void clear() { + finalParams.clear(); + } + }; + } + + @Override + @NotNull + public String getFamilyName() { + return CodeInsightBundle.message("intention.create.assign.fields.from.parameters.family"); + } + + @Override + public void invoke(@NotNull Project project, Editor editor, PsiFile file) { + invoke(project, editor, file, !ApplicationManager.getApplication().isUnitTestMode()); + } + + private static void invoke(final Project project, Editor editor, PsiFile file, boolean isInteractive) { + PsiParameter myParameter = FieldFromParameterUtils.findParameterAtCursor(file, editor); + if (!CodeInsightUtilBase.prepareFileForWrite(file)) return; + final PsiMethod method = myParameter != null ? (PsiMethod)myParameter.getDeclarationScope() : PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PsiMethod.class); + LOG.assertTrue(method != null); + final Collection> unboundedParams; + synchronized (LOCK) { + unboundedParams = getUnboundedParams(method); + if (unboundedParams.isEmpty()) return; + if (myParameter == null) { + myParameter = unboundedParams.iterator().next().getElement(); + } + } + if (unboundedParams.size() > 1 && isInteractive) { + ClassMember[] members = new ClassMember[unboundedParams.size()]; + ClassMember selection = null; + int i = 0; + for (SmartPsiElementPointer pointer : unboundedParams) { + final PsiParameter parameter = pointer.getElement(); + final ParameterClassMember classMember = new ParameterClassMember(parameter); + members[i++] = classMember; + if (parameter == myParameter) { + selection = classMember; + } + } + final PsiParameterList parameterList = method.getParameterList(); + Arrays.sort(members, new Comparator() { + @Override + public int compare(ClassMember o1, ClassMember o2) { + return parameterList.getParameterIndex(((ParameterClassMember)o1).getParameter()) - + parameterList.getParameterIndex(((ParameterClassMember)o2).getParameter()); + } + }); + + final MemberChooser chooser = new MemberChooser(members, false, true, project); + if (selection != null) { + chooser.selectElements(new ClassMember[]{selection}); + } + chooser.setTitle("Choose " + (method.isConstructor() ? "Constructor" : "Method") + " Parameters"); + chooser.setCopyJavadocVisible(false); + chooser.show(); + if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return; + final List selectedElements = chooser.getSelectedElements(); + if (selectedElements == null) return; + + final HashSet usedNames = new HashSet(); + for (ClassMember selectedElement : selectedElements) { + processParameter(project, ((ParameterClassMember)selectedElement).getParameter(), usedNames); + } + } + else { + processParameter(project, myParameter); + } + synchronized (LOCK) { + unboundedParams.clear(); + } + } + + private static void processParameter(final Project project, + final PsiParameter myParameter) { + processParameter(project, myParameter, new HashSet()); + } + + private static void processParameter(final Project project, + final PsiParameter myParameter, + final Set usedNames) { + IdeDocumentHistory.getInstance(project).includeCurrentPlaceAsChangePlace(); + final PsiType type = FieldFromParameterUtils.getSubstitutedType(myParameter); + final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project); + final String parameterName = myParameter.getName(); + String propertyName = styleManager.variableNameToPropertyName(parameterName, VariableKind.PARAMETER); + + final PsiClass targetClass = PsiTreeUtil.getParentOfType(myParameter, PsiClass.class); + final PsiMethod method = (PsiMethod)myParameter.getDeclarationScope(); + + final boolean isMethodStatic = method.hasModifierProperty(PsiModifier.STATIC); + + VariableKind kind = isMethodStatic ? VariableKind.STATIC_FIELD : VariableKind.FIELD; + SuggestedNameInfo suggestedNameInfo = styleManager.suggestVariableName(kind, propertyName, null, type); + String[] names = suggestedNameInfo.names; + + final boolean isFinal = !isMethodStatic && method.isConstructor(); + final String fieldName = usedNames.add(names[0]) ? names[0] : JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(names[0], myParameter, true); + + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + try { + FieldFromParameterUtils.createFieldAndAddAssignment( + project, + targetClass, + method, + myParameter, + type, + fieldName, + isMethodStatic, + isFinal); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + }); + } + + @Override + public boolean startInWriteAction() { + return false; + } + + private static class ParameterClassMember implements ClassMember { + private PsiParameter myParameter; + + private ParameterClassMember(PsiParameter parameter) { + myParameter = parameter; + } + + @Override + public MemberChooserObject getParentNodeDelegate() { + return new PsiMethodMember((PsiMethod)myParameter.getDeclarationScope()); + } + + @Override + public void renderTreeNode(SimpleColoredComponent component, JTree tree) { + SpeedSearchUtil.appendFragmentsForSpeedSearch(tree, getText(), SimpleTextAttributes.REGULAR_ATTRIBUTES, false, component); + component.setIcon(myParameter.getIcon(0)); + } + + @Override + public String getText() { + return myParameter.getName(); + } + + public PsiParameter getParameter() { + return myParameter; + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateFieldFromParameterAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateFieldFromParameterAction.java index 20ee44b6ee3a..1dbcd23cbda1 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateFieldFromParameterAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateFieldFromParameterAction.java @@ -17,212 +17,53 @@ package com.intellij.codeInsight.intention.impl; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.CodeInsightUtilBase; -import com.intellij.codeInsight.NullableNotNullManager; -import com.intellij.codeInsight.generation.ClassMember; -import com.intellij.codeInsight.generation.MemberChooserObject; -import com.intellij.codeInsight.generation.PsiMethodMember; -import com.intellij.codeInsight.intention.IntentionAction; -import com.intellij.ide.util.MemberChooser; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.DialogWrapper; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Pair; -import com.intellij.openapi.util.Ref; -import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; -import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.codeStyle.SuggestedNameInfo; import com.intellij.psi.codeStyle.VariableKind; -import com.intellij.psi.search.LocalSearchScope; -import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiUtil; -import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.refactoring.util.RefactoringUtil; -import com.intellij.ui.SimpleColoredComponent; -import com.intellij.ui.SimpleTextAttributes; -import com.intellij.ui.speedSearch.SpeedSearchUtil; import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; -import com.intellij.util.containers.ConcurrentWeakHashMap; import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import javax.swing.*; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; -public class CreateFieldFromParameterAction implements IntentionAction { - private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.intention.impl.CreateFieldFromParameterAction"); - private static final Key, Boolean>> PARAMS = Key.create("FIELDS_FROM_PARAMS"); - - private static final Object LOCK = new Object(); - - private String myName = ""; - private boolean myConstructor = false; - - @Nullable - private static PsiType[] getTypes(final PsiParameter parameter) { - if (parameter == null) return null; - PsiType type = parameter.getType(); - if (type instanceof PsiEllipsisType) type = ((PsiEllipsisType)type).toArrayType(); - if (type instanceof PsiArrayType) return new PsiType[]{type}; - final PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(type); - final PsiClass psiClass = result.getElement(); - if (psiClass == null) return new PsiType[] {type}; - final HashSet usedTypeParameters = new HashSet(); - RefactoringUtil.collectTypeParameters(usedTypeParameters, parameter); - for (Iterator iterator = usedTypeParameters.iterator(); iterator.hasNext();) { - PsiTypeParameter usedTypeParameter = iterator.next(); - if (parameter.getDeclarationScope() != usedTypeParameter.getOwner()) { - iterator.remove(); - } - } - final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(parameter.getProject()); - PsiSubstitutor subst = PsiSubstitutor.EMPTY; - for (PsiTypeParameter usedTypeParameter : usedTypeParameters) { - subst = subst.put(usedTypeParameter, TypeConversionUtil.typeParameterErasure(usedTypeParameter)); - } - PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; - final Map typeMap = result.getSubstitutor().getSubstitutionMap(); - for (PsiTypeParameter typeParameter : typeMap.keySet()) { - final PsiType psiType = typeMap.get(typeParameter); - substitutor = substitutor.put(typeParameter, psiType != null ? subst.substitute(psiType) : null); - } - return new PsiType[]{psiClass instanceof PsiTypeParameter ? subst.substitute((PsiTypeParameter)psiClass) : elementFactory.createType(psiClass, substitutor)}; - } - - @Override - @NotNull - public String getText() { - if (myName == null) { - return CodeInsightBundle.message("intention.create.fields.from.parameters.text", myConstructor ? "Constructor" : "Method") ; - } - return CodeInsightBundle.message("intention.create.field.from.parameter.text", myName); - } +public class CreateFieldFromParameterAction extends BaseIntentionAction { + private static final Logger LOG = Logger.getInstance(CreateFieldFromParameterAction.class); @Override public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { - PsiParameter psiParameter = findParameterAtCursor(file, editor); - PsiMethod method = null; - if (psiParameter == null) { - final PsiElement elementAt = file.findElementAt(editor.getCaretModel().getOffset()); - if (elementAt instanceof PsiIdentifier) { - final PsiElement parent = elementAt.getParent(); - if (parent instanceof PsiMethod) { - method = (PsiMethod)parent; - } - } - } else { - final PsiElement declarationScope = psiParameter.getDeclarationScope(); - if (declarationScope instanceof PsiMethod) { - method = (PsiMethod)declarationScope; - } + final PsiParameter parameter = FieldFromParameterUtils.findParameterAtCursor(file, editor); + if (parameter == null || !isAvailable(parameter)) { + return false; } - if (method == null) return false; + setText(CodeInsightBundle.message("intention.create.field.from.parameter.text", parameter.getName())); - final List parameters = new ArrayList(); - for (PsiParameter parameter : method.getParameterList().getParameters()) { - if (isAvailable(parameter)) { - parameters.add(parameter); - } - } - - synchronized (LOCK) { - final Collection> params = getUnboundedParams(method); - params.clear(); - for (PsiParameter parameter : parameters) { - params.add(SmartPointerManager.getInstance(project).createSmartPsiElementPointer(parameter)); - } - if (params.isEmpty()) return false; - if (psiParameter == null) { - psiParameter = params.iterator().next().getElement(); - LOG.assertTrue(psiParameter != null); - } - myName = params.size() > 1 && !ApplicationManager.getApplication().isUnitTestMode() ? null : psiParameter.getName(); - myConstructor = method.isConstructor(); - } - return isAvailable(psiParameter); + return true; } private static boolean isAvailable(PsiParameter psiParameter) { - final PsiType[] types = getTypes(psiParameter); - PsiClass targetClass = PsiTreeUtil.getParentOfType(psiParameter, PsiClass.class); + final PsiType type = FieldFromParameterUtils.getSubstitutedType(psiParameter); + final PsiClass targetClass = PsiTreeUtil.getParentOfType(psiParameter, PsiClass.class); return psiParameter.isValid() && psiParameter.getLanguage().isKindOf(JavaLanguage.INSTANCE) && psiParameter.getDeclarationScope() instanceof PsiMethod && ((PsiMethod)psiParameter.getDeclarationScope()).getBody() != null && psiParameter.getManager().isInProject(psiParameter) - && types != null - && types[0].isValid() - && getParameterAssignedToField(psiParameter) == null + && type != null + && type.isValid() + && FieldFromParameterUtils.getParameterAssignedToField(psiParameter) == null && targetClass != null - && !targetClass.isInterface() - ; - } - - @NotNull - private static Collection> getUnboundedParams(PsiMethod psiMethod) { - Map, Boolean> params = psiMethod.getUserData(PARAMS); - if (params == null) psiMethod.putUserData(PARAMS, params = new ConcurrentWeakHashMap, Boolean>(1)); - final Map, Boolean> finalParams = params; - return new AbstractCollection>() { - @Override - public boolean add(SmartPsiElementPointer psiVariable) { - return finalParams.put(psiVariable, Boolean.TRUE) == null; - } - - @Override - public Iterator> iterator() { - return finalParams.keySet().iterator(); - } - - @Override - public int size() { - return finalParams.size(); - } - - @Override - public void clear() { - finalParams.clear(); - } - }; - } - - @Nullable - public static PsiField getParameterAssignedToField(final PsiParameter parameter) { - for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), false)) { - if (!(reference instanceof PsiReferenceExpression)) continue; - final PsiReferenceExpression expression = (PsiReferenceExpression)reference; - if (!(expression.getParent() instanceof PsiAssignmentExpression)) continue; - final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression.getParent(); - if (assignmentExpression.getRExpression() != expression) continue; - final PsiExpression lExpression = assignmentExpression.getLExpression(); - if (!(lExpression instanceof PsiReferenceExpression)) continue; - final PsiElement element = ((PsiReferenceExpression)lExpression).resolve(); - if (element instanceof PsiField) return (PsiField)element; - } - return null; - } - - @Nullable - static PsiParameter findParameterAtCursor(final PsiFile file, final Editor editor) { - final int offset = editor.getCaretModel().getOffset(); - final PsiParameterList parameterList = PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiParameterList.class, false); - if (parameterList == null) return null; - final PsiParameter[] parameters = parameterList.getParameters(); - for (PsiParameter parameter : parameters) { - final TextRange range = parameter.getTextRange(); - if (range.getStartOffset() <= offset && offset <= range.getEndOffset()) return parameter; - } - return null; + && !targetClass.isInterface(); } @Override @@ -233,97 +74,35 @@ public class CreateFieldFromParameterAction implements IntentionAction { @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) { - invoke(project, editor, file, !ApplicationManager.getApplication().isUnitTestMode()); - } + final PsiParameter myParameter = FieldFromParameterUtils.findParameterAtCursor(file, editor); + if (myParameter == null || !CodeInsightUtilBase.prepareFileForWrite(file)) return; - private static void invoke(final Project project, Editor editor, PsiFile file, boolean isInteractive) { - PsiParameter myParameter = findParameterAtCursor(file, editor); - if (!CodeInsightUtilBase.prepareFileForWrite(file)) return; - final PsiMethod method = myParameter != null ? (PsiMethod)myParameter.getDeclarationScope() : PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PsiMethod.class); - LOG.assertTrue(method != null); - final Collection> unboundedParams; - synchronized (LOCK) { - unboundedParams = getUnboundedParams(method); - if (unboundedParams.isEmpty()) return; - if (myParameter == null) { - myParameter = unboundedParams.iterator().next().getElement(); - } - } - if (unboundedParams.size() > 1 && !ApplicationManager.getApplication().isUnitTestMode()) { - ClassMember[] members = new ClassMember[unboundedParams.size()]; - ClassMember selection = null; - int i = 0; - for (SmartPsiElementPointer pointer : unboundedParams) { - final PsiParameter parameter = pointer.getElement(); - final ParameterClassMember classMember = new ParameterClassMember(parameter); - members[i++] = classMember; - if (parameter == myParameter) { - selection = classMember; - } - } - final PsiParameterList parameterList = method.getParameterList(); - Arrays.sort(members, new Comparator() { - @Override - public int compare(ClassMember o1, ClassMember o2) { - return parameterList.getParameterIndex(((ParameterClassMember)o1).getParameter()) - - parameterList.getParameterIndex(((ParameterClassMember)o2).getParameter()); - } - }); - - final MemberChooser chooser = new MemberChooser(members, false, true, project); - if (selection != null) { - chooser.selectElements(new ClassMember[] {selection}); - } - chooser.setTitle("Choose " + (method.isConstructor() ? "Constructor" : "Method") + " Parameters to Generate Fields"); - chooser.setCopyJavadocVisible(false); - chooser.show(); - if (chooser.getExitCode() != DialogWrapper.OK_EXIT_CODE) return; - final List selectedElements = chooser.getSelectedElements(); - if (selectedElements == null) return; - if (selectedElements.size() == 1) { - processParameter(project, ((ParameterClassMember)selectedElements.get(0)).getParameter(), isInteractive); - } else { - //do not ask for names in batch - final HashSet usedNames = new HashSet(); - for (ClassMember selectedElement : selectedElements) { - processParameter(project, ((ParameterClassMember)selectedElement).getParameter(), false, usedNames); - } - } - } - else { - processParameter(project, myParameter, isInteractive); - } - synchronized (LOCK) { - unboundedParams.clear(); - } - } - - private static void processParameter(final Project project, - final PsiParameter myParameter, - boolean isInteractive) { - processParameter(project, myParameter, isInteractive, new HashSet()); - } - - private static void processParameter(final Project project, - final PsiParameter myParameter, - boolean isInteractive, - final Set usedNames) { IdeDocumentHistory.getInstance(project).includeCurrentPlaceAsChangePlace(); - final PsiType[] types = getTypes(myParameter); + try { + processParameter(project, myParameter, !ApplicationManager.getApplication().isUnitTestMode()); + } + catch (IncorrectOperationException e) { + LOG.error(e); + } + } + + private static void processParameter(final @NotNull Project project, + final @NotNull PsiParameter myParameter, + final boolean isInteractive) { + final PsiType type = FieldFromParameterUtils.getSubstitutedType(myParameter); final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project); final String parameterName = myParameter.getName(); String propertyName = styleManager.variableNameToPropertyName(parameterName, VariableKind.PARAMETER); String fieldNameToCalc; boolean isFinalToCalc; - PsiType type; final PsiClass targetClass = PsiTreeUtil.getParentOfType(myParameter, PsiClass.class); final PsiMethod method = (PsiMethod)myParameter.getDeclarationScope(); final boolean isMethodStatic = method.hasModifierProperty(PsiModifier.STATIC); VariableKind kind = isMethodStatic ? VariableKind.STATIC_FIELD : VariableKind.FIELD; - SuggestedNameInfo suggestedNameInfo = styleManager.suggestVariableName(kind, propertyName, null, types[0]); + SuggestedNameInfo suggestedNameInfo = styleManager.suggestVariableName(kind, propertyName, null, type); String[] names = suggestedNameInfo.names; if (isInteractive) { @@ -338,16 +117,16 @@ public class CreateFieldFromParameterAction implements IntentionAction { } names = ArrayUtil.toStringArray(namesList); - boolean myBeFinal = method.isConstructor(); - CreateFieldFromParameterDialog dialog = new CreateFieldFromParameterDialog( + final CreateFieldFromParameterDialog dialog = new CreateFieldFromParameterDialog( project, names, - targetClass, myBeFinal, types); + targetClass, + method.isConstructor(), + type + ); dialog.show(); if (!dialog.isOK()) return; - type = dialog.getType(); - if (type == null) return; fieldNameToCalc = dialog.getEnteredName(); isFinalToCalc = dialog.isDeclareFinal(); @@ -355,94 +134,24 @@ public class CreateFieldFromParameterAction implements IntentionAction { } else { isFinalToCalc = !isMethodStatic && method.isConstructor(); - if (usedNames.add(names[0])) { - fieldNameToCalc = names[0]; - } - else { - fieldNameToCalc = JavaCodeStyleManager.getInstance(project).suggestUniqueVariableName(names[0], myParameter, true); - } - type= types[0]; + fieldNameToCalc = names[0]; } final boolean isFinal = isFinalToCalc; final String fieldName = fieldNameToCalc; - final PsiType fieldType = type; ApplicationManager.getApplication().runWriteAction(new Runnable() { @Override public void run() { try { - PsiManager psiManager = PsiManager.getInstance(project); - PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory(); - - PsiField field = factory.createField(fieldName, fieldType); - PsiModifierList modifierList = field.getModifierList(); - modifierList.setModifierProperty(PsiModifier.STATIC, isMethodStatic); - modifierList.setModifierProperty(PsiModifier.FINAL, isFinal); - - final NullableNotNullManager manager = NullableNotNullManager.getInstance(project); - final String nullable = manager.getNullable(myParameter); - if (nullable != null) { - modifierList.addAfter(factory.createAnnotationFromText("@" + nullable, field), null); - } else if (isFinal) { - final String notNull = manager.getNotNull(myParameter); - if (notNull != null) { - modifierList.addAfter(factory.createAnnotationFromText("@" + notNull, field), null); - } - } - - PsiCodeBlock methodBody = method.getBody(); - if (methodBody == null) return; - PsiStatement[] statements = methodBody.getStatements(); - - Ref> anchorRef = new Ref>(); - int i = findFieldAssignmentAnchor(statements, anchorRef, targetClass, myParameter); - Pair fieldAnchor = anchorRef.get(); - - String stmtText = fieldName + " = " + parameterName + ";"; - if (fieldName.equals(parameterName)) { - @NonNls String prefix = isMethodStatic ? targetClass.getName() == null ? "" : targetClass.getName() + "." : "this."; - stmtText = prefix + stmtText; - } - - PsiStatement assignmentStmt = factory.createStatementFromText(stmtText, methodBody); - assignmentStmt = (PsiStatement)CodeStyleManager.getInstance(project).reformat(assignmentStmt); - - if (i == statements.length) { - methodBody.add(assignmentStmt); - } - else { - methodBody.addAfter(assignmentStmt, i > 0 ? statements[i - 1] : null); - } - - if (fieldAnchor != null) { - PsiVariable psiVariable = fieldAnchor.getFirst(); - psiVariable.normalizeDeclaration(); - } - - boolean found = false; - final PsiField[] fields = targetClass.getFields(); - for (PsiField f : fields) { - if (f.getName().equals(field.getName())) { - found = true; - break; - } - } - - if (!found) { - if (fieldAnchor != null) { - Boolean insertBefore = fieldAnchor.getSecond(); - PsiField inField = fieldAnchor.getFirst(); - if (insertBefore.booleanValue()) { - targetClass.addBefore(field, inField); - } - else { - targetClass.addAfter(field, inField); - } - } - else { - targetClass.add(field); - } - } + FieldFromParameterUtils.createFieldAndAddAssignment( + project, + targetClass, + method, + myParameter, + type, + fieldName, + isMethodStatic, + isFinal); } catch (IncorrectOperationException e) { LOG.error(e); @@ -451,90 +160,8 @@ public class CreateFieldFromParameterAction implements IntentionAction { }); } - static int findFieldAssignmentAnchor(final PsiStatement[] statements, @Nullable final Ref> anchorRef, - final PsiClass targetClass, final PsiParameter myParameter) { - int i = 0; - for (; i < statements.length; i++) { - PsiStatement psiStatement = statements[i]; - - if (psiStatement instanceof PsiExpressionStatement) { - PsiExpressionStatement expressionStatement = (PsiExpressionStatement)psiStatement; - PsiExpression expression = expressionStatement.getExpression(); - - if (expression instanceof PsiMethodCallExpression) { - PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; - @NonNls String text = methodCallExpression.getMethodExpression().getText(); - - if (text.equals("super") || text.equals("this")) { - continue; - } - } - else if (expression instanceof PsiAssignmentExpression) { - PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression; - PsiExpression lExpression = assignmentExpression.getLExpression(); - PsiExpression rExpression = assignmentExpression.getRExpression(); - - if (!(lExpression instanceof PsiReferenceExpression)) break; - if (!(rExpression instanceof PsiReferenceExpression)) break; - - PsiReferenceExpression lReference = (PsiReferenceExpression)lExpression; - PsiReferenceExpression rReference = (PsiReferenceExpression)rExpression; - - PsiElement lElement = lReference.resolve(); - PsiElement rElement = rReference.resolve(); - - if (!(lElement instanceof PsiField) || ((PsiField)lElement).getContainingClass() != targetClass) break; - if (!(rElement instanceof PsiParameter)) break; - - if (myParameter.getTextRange().getStartOffset() < rElement.getTextRange().getStartOffset()) { - if (anchorRef != null) { - anchorRef.set(Pair.create((PsiField)lElement, Boolean.TRUE)); - } - break; - } - - if (anchorRef != null) { - anchorRef.set(Pair.create((PsiField)lElement, Boolean.FALSE)); - } - continue; - } - } - - break; - } - return i; - } - @Override public boolean startInWriteAction() { return false; } - - private static class ParameterClassMember implements ClassMember { - private PsiParameter myParameter; - - private ParameterClassMember(PsiParameter parameter) { - myParameter = parameter; - } - - @Override - public MemberChooserObject getParentNodeDelegate() { - return new PsiMethodMember((PsiMethod)myParameter.getDeclarationScope()); - } - - @Override - public void renderTreeNode(SimpleColoredComponent component, JTree tree) { - SpeedSearchUtil.appendFragmentsForSpeedSearch(tree, getText(), SimpleTextAttributes.REGULAR_ATTRIBUTES, false, component); - component.setIcon(myParameter.getIcon(0)); - } - - @Override - public String getText() { - return myParameter.getName(); - } - - public PsiParameter getParameter() { - return myParameter; - } - } } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateFieldFromParameterDialog.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateFieldFromParameterDialog.java index 7465ed54930c..5952bbfd1762 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateFieldFromParameterDialog.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateFieldFromParameterDialog.java @@ -66,7 +66,7 @@ class CreateFieldFromParameterDialog extends DialogWrapper { @Override protected void doOKAction() { if (myCbFinal.isEnabled()) { - PropertiesComponent.getInstance().setValue(PROPERTY_NAME, ""+myCbFinal.isSelected()); + PropertiesComponent.getInstance().setValue(PROPERTY_NAME, "" + myCbFinal.isSelected()); } final PsiField[] fields = myTargetClass.getFields(); @@ -97,11 +97,11 @@ class CreateFieldFromParameterDialog extends DialogWrapper { public String getEnteredName() { if (myNameField instanceof JComboBox) { - JComboBox combobox = (JComboBox) myNameField; - return (String) combobox.getEditor().getItem(); + JComboBox combobox = (JComboBox)myNameField; + return (String)combobox.getEditor().getItem(); } else { - return ((JTextField) myNameField).getText(); + return ((JTextField)myNameField).getText(); } } @@ -123,46 +123,46 @@ class CreateFieldFromParameterDialog extends DialogWrapper { combobox.setMaximumRowCount(8); combobox.registerKeyboardAction( - new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - if (combobox.isPopupVisible()) { - combobox.setPopupVisible(false); - } - else { - doCancelAction(); - } + new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + if (combobox.isPopupVisible()) { + combobox.setPopupVisible(false); } - }, - KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), - JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT + else { + doCancelAction(); + } + } + }, + KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), + JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); combobox.addItemListener( - new ItemListener() { - @Override - public void itemStateChanged(ItemEvent e) { - updateOkStatus(); - } + new ItemListener() { + @Override + public void itemStateChanged(ItemEvent e) { + updateOkStatus(); } + } ); combobox.getEditor().getEditorComponent().addKeyListener( - new KeyAdapter() { - @Override - public void keyPressed(KeyEvent e) { - updateOkStatus(); - } - - @Override - public void keyReleased(KeyEvent e) { - updateOkStatus(); - } - - @Override - public void keyTyped(KeyEvent e) { - updateOkStatus(); - } + new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + updateOkStatus(); } + + @Override + public void keyReleased(KeyEvent e) { + updateOkStatus(); + } + + @Override + public void keyTyped(KeyEvent e) { + updateOkStatus(); + } + } ); } else { @@ -203,7 +203,8 @@ class CreateFieldFromParameterDialog extends DialogWrapper { if (myTypes.length > 1) { myTypeSelector = new TypeSelector(myProject); myTypeSelector.setTypes(myTypes); - } else { + } + else { myTypeSelector = new TypeSelector(myTypes[0], myProject); } panel.add(myTypeSelector.getComponent(), gbConstraints); @@ -240,7 +241,8 @@ class CreateFieldFromParameterDialog extends DialogWrapper { myCbFinal = new JCheckBox(CodeInsightBundle.message("dialog.create.field.from.parameter.declare.final.checkbox")); if (myFieldMayBeFinal) { myCbFinal.setSelected(PropertiesComponent.getInstance().isTrueValue(PROPERTY_NAME)); - } else { + } + else { myCbFinal.setSelected(false); myCbFinal.setEnabled(false); } @@ -264,7 +266,7 @@ class CreateFieldFromParameterDialog extends DialogWrapper { myNameField.requestFocusInWindow(); } else { - ((JComboBox) myNameField).getEditor().getEditorComponent().requestFocusInWindow(); + ((JComboBox)myNameField).getEditor().getEditorComponent().requestFocusInWindow(); } } diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java new file mode 100644 index 000000000000..007308d7adba --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/FieldFromParameterUtils.java @@ -0,0 +1,259 @@ +/* + * Copyright 2000-2012 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. + */ +package com.intellij.codeInsight.intention.impl; + +import com.intellij.codeInsight.NullableNotNullManager; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.search.LocalSearchScope; +import com.intellij.psi.search.searches.ReferencesSearch; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + +/** + * @author Danila Ponomarenko + */ +public final class FieldFromParameterUtils { + @Nullable + public static PsiParameter findParameterAtCursor(@NotNull PsiFile file, @NotNull Editor editor) { + final int offset = editor.getCaretModel().getOffset(); + final PsiParameterList parameterList = PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiParameterList.class, false); + if (parameterList == null) return null; + final PsiParameter[] parameters = parameterList.getParameters(); + for (PsiParameter parameter : parameters) { + final TextRange range = parameter.getTextRange(); + if (range.getStartOffset() <= offset && offset <= range.getEndOffset()) return parameter; + } + return null; + } + + @Nullable + public static PsiType getType(@Nullable PsiParameter myParameter) { + if (myParameter == null) return null; + PsiType type = myParameter.getType(); + return type instanceof PsiEllipsisType ? ((PsiEllipsisType)type).toArrayType() : type; + } + + @Nullable + public static PsiType getSubstitutedType(@Nullable PsiParameter parameter) { + if (parameter == null) return null; + + final PsiType type = getType(parameter); + + if (type instanceof PsiArrayType) { + return type; + } + + final PsiClassType.ClassResolveResult result = PsiUtil.resolveGenericsClassInType(type); + final PsiClass psiClass = result.getElement(); + if (psiClass == null) return type; + final HashSet usedTypeParameters = new HashSet(); + RefactoringUtil.collectTypeParameters(usedTypeParameters, parameter); + for (Iterator iterator = usedTypeParameters.iterator(); iterator.hasNext(); ) { + PsiTypeParameter usedTypeParameter = iterator.next(); + if (parameter.getDeclarationScope() != usedTypeParameter.getOwner()) { + iterator.remove(); + } + } + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(parameter.getProject()); + PsiSubstitutor subst = PsiSubstitutor.EMPTY; + for (PsiTypeParameter usedTypeParameter : usedTypeParameters) { + subst = subst.put(usedTypeParameter, TypeConversionUtil.typeParameterErasure(usedTypeParameter)); + } + PsiSubstitutor substitutor = PsiSubstitutor.EMPTY; + final Map typeMap = result.getSubstitutor().getSubstitutionMap(); + for (PsiTypeParameter typeParameter : typeMap.keySet()) { + final PsiType psiType = typeMap.get(typeParameter); + substitutor = substitutor.put(typeParameter, psiType != null ? subst.substitute(psiType) : null); + } + return psiClass instanceof PsiTypeParameter ? subst.substitute((PsiTypeParameter)psiClass) : elementFactory.createType(psiClass, substitutor); + } + + @Nullable + public static PsiField getParameterAssignedToField(final PsiParameter parameter) { + for (PsiReference reference : ReferencesSearch.search(parameter, new LocalSearchScope(parameter.getDeclarationScope()), false)) { + if (!(reference instanceof PsiReferenceExpression)) continue; + final PsiReferenceExpression expression = (PsiReferenceExpression)reference; + if (!(expression.getParent() instanceof PsiAssignmentExpression)) continue; + final PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression.getParent(); + if (assignmentExpression.getRExpression() != expression) continue; + final PsiExpression lExpression = assignmentExpression.getLExpression(); + if (!(lExpression instanceof PsiReferenceExpression)) continue; + final PsiElement element = ((PsiReferenceExpression)lExpression).resolve(); + if (element instanceof PsiField) return (PsiField)element; + } + return null; + } + + public static int findFieldAssignmentAnchor(final PsiStatement[] statements, + final @Nullable Ref> anchorRef, + final PsiClass targetClass, + final PsiParameter myParameter) { + int i = 0; + for (; i < statements.length; i++) { + PsiStatement psiStatement = statements[i]; + + if (psiStatement instanceof PsiExpressionStatement) { + PsiExpressionStatement expressionStatement = (PsiExpressionStatement)psiStatement; + PsiExpression expression = expressionStatement.getExpression(); + + if (expression instanceof PsiMethodCallExpression) { + PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)expression; + @NonNls String text = methodCallExpression.getMethodExpression().getText(); + + if (text.equals("super") || text.equals("this")) { + continue; + } + } + else if (expression instanceof PsiAssignmentExpression) { + PsiAssignmentExpression assignmentExpression = (PsiAssignmentExpression)expression; + PsiExpression lExpression = assignmentExpression.getLExpression(); + PsiExpression rExpression = assignmentExpression.getRExpression(); + + if (!(lExpression instanceof PsiReferenceExpression)) break; + if (!(rExpression instanceof PsiReferenceExpression)) break; + + PsiReferenceExpression lReference = (PsiReferenceExpression)lExpression; + PsiReferenceExpression rReference = (PsiReferenceExpression)rExpression; + + PsiElement lElement = lReference.resolve(); + PsiElement rElement = rReference.resolve(); + + if (!(lElement instanceof PsiField) || ((PsiField)lElement).getContainingClass() != targetClass) break; + if (!(rElement instanceof PsiParameter)) break; + + if (myParameter.getTextRange().getStartOffset() < rElement.getTextRange().getStartOffset()) { + if (anchorRef != null) { + anchorRef.set(Pair.create((PsiField)lElement, Boolean.TRUE)); + } + break; + } + + if (anchorRef != null) { + anchorRef.set(Pair.create((PsiField)lElement, Boolean.FALSE)); + } + continue; + } + } + + break; + } + return i; + } + + public static void createFieldAndAddAssignment(final @NotNull Project project, + final @NotNull PsiClass targetClass, + final @NotNull PsiMethod method, + final @NotNull PsiParameter parameter, + final @NotNull PsiType fieldType, final @NotNull String fieldName, + final boolean isStatic, final boolean isFinal) throws IncorrectOperationException { + + PsiManager psiManager = PsiManager.getInstance(project); + PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory(); + + PsiField field = factory.createField(fieldName, fieldType); + PsiModifierList modifierList = field.getModifierList(); + + if (modifierList == null) return; + modifierList.setModifierProperty(PsiModifier.STATIC, isStatic); + modifierList.setModifierProperty(PsiModifier.FINAL, isFinal); + + final NullableNotNullManager manager = NullableNotNullManager.getInstance(project); + final String nullable = manager.getNullable(parameter); + if (nullable != null) { + modifierList.addAfter(factory.createAnnotationFromText("@" + nullable, field), null); + } + else if (isFinal) { + final String notNull = manager.getNotNull(parameter); + if (notNull != null) { + modifierList.addAfter(factory.createAnnotationFromText("@" + notNull, field), null); + } + } + + PsiCodeBlock methodBody = method.getBody(); + if (methodBody == null) return; + PsiStatement[] statements = methodBody.getStatements(); + + Ref> anchorRef = new Ref>(); + int i = findFieldAssignmentAnchor(statements, anchorRef, targetClass, parameter); + Pair fieldAnchor = anchorRef.get(); + + String stmtText = fieldName + " = " + parameter.getName() + ";"; + if (fieldName.equals(parameter.getName())) { + @NonNls String prefix = isStatic ? targetClass.getName() == null ? "" : targetClass.getName() + "." : "this."; + stmtText = prefix + stmtText; + } + + PsiStatement assignmentStmt = factory.createStatementFromText(stmtText, methodBody); + assignmentStmt = (PsiStatement)CodeStyleManager.getInstance(project).reformat(assignmentStmt); + + if (i == statements.length) { + methodBody.add(assignmentStmt); + } + else { + methodBody.addAfter(assignmentStmt, i > 0 ? statements[i - 1] : null); + } + + if (fieldAnchor != null) { + PsiVariable psiVariable = fieldAnchor.getFirst(); + psiVariable.normalizeDeclaration(); + } + + boolean found = false; + final PsiField[] fields = targetClass.getFields(); + for (PsiField f : fields) { + if (f.getName().equals(field.getName())) { + found = true; + break; + } + } + + if (!found) { + if (fieldAnchor != null) { + Boolean insertBefore = fieldAnchor.getSecond(); + PsiField inField = fieldAnchor.getFirst(); + if (insertBefore.booleanValue()) { + targetClass.addBefore(field, inField); + } + else { + targetClass.addAfter(field, inField); + } + } + else { + targetClass.add(field); + } + } + } + + private FieldFromParameterUtils() { + } +} diff --git a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties index 355e2c4179e8..93c564167c51 100644 --- a/platform/platform-resources-en/src/messages/CodeInsightBundle.properties +++ b/platform/platform-resources-en/src/messages/CodeInsightBundle.properties @@ -190,9 +190,10 @@ intention.error.cannot.create.class.message=Cannot Create Class ''{0}'' intention.error.cannot.create.class.title=Failed to Create Class intention.assign.field.from.parameter.text=Assign Parameter to Field ''{0}'' intention.assign.field.from.parameter.family=Assign Parameter to Field -intention.create.field.from.parameter.text=Create Field For Parameter ''{0}'' -intention.create.fields.from.parameters.text=Create Fields For {0} Parameters +intention.create.field.from.parameter.text=Create Field for Parameter ''{0}'' intention.create.field.from.parameter.family=Create Field for Parameter +intention.create.assign.fields.from.parameters.text=Create/Assign Fields for {0} Parameters +intention.create.assign.fields.from.parameters.family=Create/Assign Fields for Parameters intention.implement.abstract.method.searching.for.descendants.progress=Searching For Descendants... intention.implement.abstract.method.error.no.classes.message=There are no classes found where this method can be implemented intention.implement.abstract.method.error.no.classes.title=No Classes Found diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index f4bbd5083e5a..54571f9fba4b 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -587,6 +587,10 @@ com.intellij.codeInsight.intention.impl.AssignFieldFromParameterAction Declaration + + com.intellij.codeInsight.intention.impl.CreateAssignFieldsFromParametersAction + Declaration + com.intellij.codeInsight.daemon.impl.quickfix.CreateLocalVarFromInstanceofAction Declaration