blob: 75473959ddccbedb39dca6282808075c3838a92d [file] [log] [blame]
/*
* 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.
*/
/*
* Created by IntelliJ IDEA.
* User: dsl
* Date: 29.05.2002
* Time: 13:05:34
* To change template for new class use
* Code Style | Class Templates options (Tools | IDE Options).
*/
package com.intellij.refactoring.introduceField;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.codeInsight.TestFrameworks;
import com.intellij.codeInsight.daemon.impl.quickfix.AnonymousTargetClassPreselectionUtil;
import com.intellij.codeInsight.highlighting.HighlightManager;
import com.intellij.codeInsight.lookup.LookupManager;
import com.intellij.codeInsight.navigation.NavigationUtil;
import com.intellij.ide.util.DirectoryChooserUtil;
import com.intellij.ide.util.PackageUtil;
import com.intellij.ide.util.PsiClassListCellRenderer;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.Result;
import com.intellij.openapi.command.WriteCommandAction;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.colors.EditorColors;
import com.intellij.openapi.editor.colors.EditorColorsManager;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.WindowManager;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiElementProcessor;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.refactoring.IntroduceHandlerBase;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer;
import com.intellij.refactoring.introduceVariable.IntroduceVariableBase;
import com.intellij.refactoring.rename.RenameJavaVariableProcessor;
import com.intellij.refactoring.util.*;
import com.intellij.refactoring.util.occurrences.OccurrenceManager;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
import com.intellij.psi.util.FileTypeUtils;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
public abstract class BaseExpressionToFieldHandler extends IntroduceHandlerBase {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.introduceField.BaseExpressionToFieldHandler");
public enum InitializationPlace {
IN_CURRENT_METHOD,
IN_FIELD_DECLARATION,
IN_CONSTRUCTOR,
IN_SETUP_METHOD
}
private final boolean myIsConstant;
private PsiClass myParentClass;
protected BaseExpressionToFieldHandler(boolean isConstant) {
myIsConstant = isConstant;
}
protected boolean invokeImpl(final Project project, @NotNull final PsiExpression selectedExpr, final Editor editor) {
final PsiElement element = getPhysicalElement(selectedExpr);
final PsiFile file = element.getContainingFile();
LOG.assertTrue(file != null, "expr.getContainingFile() == null");
if (LOG.isDebugEnabled()) {
LOG.debug("expression:" + selectedExpr);
}
final PsiType tempType = getTypeByExpression(selectedExpr);
if (tempType == null || LambdaUtil.notInferredType(tempType)) {
String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("unknown.expression.type"));
CommonRefactoringUtil.showErrorHint(project, editor, message, getRefactoringName(), getHelpID());
return false;
}
if (PsiType.VOID.equals(tempType)) {
String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("selected.expression.has.void.type"));
CommonRefactoringUtil.showErrorHint(project, editor, message, getRefactoringName(), getHelpID());
return false;
}
myParentClass = getParentClass(selectedExpr);
final List<PsiClass> classes = new ArrayList<PsiClass>();
PsiClass aClass = myParentClass;
while (aClass != null) {
classes.add(aClass);
final PsiField psiField = ConvertToFieldRunnable.checkForwardRefs(selectedExpr, aClass);
if (psiField != null && psiField.getParent() == aClass) break;
aClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class, true);
}
final AbstractInplaceIntroducer activeIntroducer = AbstractInplaceIntroducer.getActiveIntroducer(editor);
final boolean shouldSuggestDialog = activeIntroducer instanceof InplaceIntroduceConstantPopup &&
activeIntroducer.startsOnTheSameElement(selectedExpr, null);
if (classes.size() == 1 || editor == null || ApplicationManager.getApplication().isUnitTestMode() || shouldSuggestDialog) {
return !convertExpressionToField(selectedExpr, editor, file, project, tempType);
}
else {
PsiClass selection = AnonymousTargetClassPreselectionUtil.getPreselection(classes, myParentClass);
NavigationUtil.getPsiElementPopup(classes.toArray(new PsiClass[classes.size()]), new PsiClassListCellRenderer(),
"Choose class to introduce " + (myIsConstant ? "constant" : "field"),
new PsiElementProcessor<PsiClass>() {
@Override
public boolean execute(@NotNull PsiClass aClass) {
AnonymousTargetClassPreselectionUtil.rememberSelection(aClass, myParentClass);
myParentClass = aClass;
convertExpressionToField(selectedExpr, editor, file, project, tempType);
return false;
}
}, selection).showInBestPositionFor(editor);
}
return true;
}
private boolean convertExpressionToField(PsiExpression selectedExpr,
@Nullable Editor editor,
PsiFile file,
final Project project,
PsiType tempType) {
if (myParentClass == null) {
if (FileTypeUtils.isInServerPageFile(file)) {
CommonRefactoringUtil.showErrorHint(project, editor, RefactoringBundle.message("error.not.supported.for.jsp", getRefactoringName()),
getRefactoringName(), getHelpID());
return true;
}
else {
LOG.assertTrue(false);
return true;
}
}
if (!validClass(myParentClass, editor)) {
return true;
}
if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) return true;
final OccurrenceManager occurrenceManager = createOccurrenceManager(selectedExpr, myParentClass);
final PsiExpression[] occurrences = occurrenceManager.getOccurrences();
final PsiElement anchorStatementIfAll = occurrenceManager.getAnchorStatementForAll();
List<RangeHighlighter> highlighters = null;
if (editor != null) {
highlighters = RefactoringUtil.highlightAllOccurrences(project, occurrences, editor);
}
PsiElement tempAnchorElement = RefactoringUtil.getParentExpressionAnchorElement(selectedExpr);
if (!Comparing.strEqual(IntroduceConstantHandler.REFACTORING_NAME, getRefactoringName()) &&
IntroduceVariableBase.checkAnchorBeforeThisOrSuper(project, editor, tempAnchorElement, getRefactoringName(), getHelpID()))
return true;
final Settings settings =
showRefactoringDialog(project, editor, myParentClass, selectedExpr, tempType,
occurrences, tempAnchorElement, anchorStatementIfAll);
if (settings == null) return true;
if (settings.getForcedType() != null) {
tempType = settings.getForcedType();
}
final PsiType type = tempType;
if (editor != null) {
HighlightManager highlightManager = HighlightManager.getInstance(project);
for (RangeHighlighter highlighter : highlighters) {
highlightManager.removeSegmentHighlighter(editor, highlighter);
}
}
final Runnable runnable =
new ConvertToFieldRunnable(settings.getSelectedExpr(), settings, type, settings.getOccurrences(), occurrenceManager,
anchorStatementIfAll, tempAnchorElement, editor,
myParentClass);
new WriteCommandAction(project, getRefactoringName()){
@Override
protected void run(Result result) throws Throwable {
runnable.run();
}
}.execute();
return false;
}
public static void setModifiers(PsiField field, Settings settings) {
if (!settings.isIntroduceEnumConstant()) {
if (settings.isDeclareStatic()) {
PsiUtil.setModifierProperty(field, PsiModifier.STATIC, true);
}
if (settings.isDeclareFinal()) {
PsiUtil.setModifierProperty(field, PsiModifier.FINAL, true);
}
if (settings.isAnnotateAsNonNls()) {
PsiAnnotation annotation = JavaPsiFacade.getInstance(field.getProject()).getElementFactory()
.createAnnotationFromText("@" + AnnotationUtil.NON_NLS, field);
final PsiModifierList modifierList = field.getModifierList();
LOG.assertTrue(modifierList != null);
modifierList.addAfter(annotation, null);
}
}
JavaCodeStyleManager.getInstance(field.getProject()).shortenClassReferences(field);
}
public static PsiElement getPhysicalElement(final PsiExpression selectedExpr) {
PsiElement element = selectedExpr.getUserData(ElementToWorkOn.PARENT);
if (element == null) element = selectedExpr;
return element;
}
private static TextAttributes highlightAttributes() {
return EditorColorsManager.getInstance().getGlobalScheme().getAttributes(
EditorColors.SEARCH_RESULT_ATTRIBUTES
);
}
protected abstract OccurrenceManager createOccurrenceManager(PsiExpression selectedExpr, PsiClass parentClass);
protected final PsiClass getParentClass() {
return myParentClass;
}
protected abstract boolean validClass(PsiClass parentClass, Editor editor);
private static PsiElement getNormalizedAnchor(PsiElement anchorElement) {
PsiElement child = anchorElement;
while (child != null) {
PsiElement prev = child.getPrevSibling();
if (RefactoringUtil.isExpressionAnchorElement(prev)) break;
if (prev instanceof PsiJavaToken && ((PsiJavaToken)prev).getTokenType() == JavaTokenType.LBRACE) break;
child = prev;
}
child = PsiTreeUtil.skipSiblingsForward(child, PsiWhiteSpace.class, PsiComment.class);
PsiElement anchor;
if (child != null) {
anchor = child;
}
else {
anchor = anchorElement;
}
return anchor;
}
protected abstract String getHelpID();
protected abstract Settings showRefactoringDialog(Project project, Editor editor, PsiClass parentClass, PsiExpression expr,
PsiType type, PsiExpression[] occurrences, PsiElement anchorElement,
PsiElement anchorElementIfAll);
private static PsiType getTypeByExpression(PsiExpression expr) {
return RefactoringUtil.getTypeByExpressionWithExpectedType(expr);
}
public PsiClass getParentClass(@NotNull PsiExpression initializerExpression) {
PsiElement element = initializerExpression.getUserData(ElementToWorkOn.PARENT);
if (element == null) element = initializerExpression.getParent();
PsiElement parent = element;
while (parent != null) {
if (parent instanceof PsiClass && !(parent instanceof PsiAnonymousClass && myIsConstant)) {
return (PsiClass)parent;
}
parent = parent.getParent();
}
return null;
}
public static PsiMethod getEnclosingConstructor(PsiClass parentClass, PsiElement element) {
if (element == null) return null;
final PsiMethod[] constructors = parentClass.getConstructors();
for (PsiMethod constructor : constructors) {
if (PsiTreeUtil.isAncestor(constructor, element, false)) {
if (PsiTreeUtil.getParentOfType(element, PsiClass.class) != parentClass) return null;
return constructor;
}
}
return null;
}
private static void addInitializationToSetUp(final PsiExpression initializer,
final PsiField field,
final PsiExpression[] occurrences,
final boolean replaceAll,
final PsiClass parentClass) throws IncorrectOperationException {
final PsiMethod setupMethod = TestFrameworks.getInstance().findOrCreateSetUpMethod(parentClass);
assert setupMethod != null;
PsiElement anchor = null;
if (PsiTreeUtil.isAncestor(setupMethod, initializer, true)) {
anchor = replaceAll
? RefactoringUtil.getAnchorElementForMultipleExpressions(occurrences, setupMethod)
: PsiTreeUtil.getParentOfType(initializer, PsiStatement.class);
}
final PsiExpressionStatement expressionStatement =
(PsiExpressionStatement)JavaPsiFacade.getInstance(parentClass.getProject()).getElementFactory()
.createStatementFromText(field.getName() + "= expr;", null);
PsiAssignmentExpression expr = (PsiAssignmentExpression)expressionStatement.getExpression();
final PsiExpression rExpression = expr.getRExpression();
LOG.assertTrue(rExpression != null);
rExpression.replace(initializer);
final PsiCodeBlock body = setupMethod.getBody();
assert body != null;
body.addBefore(expressionStatement, anchor);
}
private static void addInitializationToConstructors(PsiExpression initializerExpression,
PsiField field,
PsiMethod enclosingConstructor, final PsiClass parentClass) {
try {
PsiClass aClass = field.getContainingClass();
PsiMethod[] constructors = aClass.getConstructors();
boolean added = false;
for (PsiMethod constructor : constructors) {
if (constructor == enclosingConstructor) continue;
PsiCodeBlock body = constructor.getBody();
if (body == null) continue;
PsiStatement[] statements = body.getStatements();
if (statements.length > 0) {
PsiStatement first = statements[0];
if (first instanceof PsiExpressionStatement) {
PsiExpression expression = ((PsiExpressionStatement)first).getExpression();
if (expression instanceof PsiMethodCallExpression) {
@NonNls String text = ((PsiMethodCallExpression)expression).getMethodExpression().getText();
if ("this".equals(text)) {
continue;
}
}
}
}
PsiStatement assignment = createAssignment(field, initializerExpression, body.getLastChild(), parentClass);
assignment = (PsiStatement) body.add(assignment);
ChangeContextUtil.decodeContextInfo(assignment, field.getContainingClass(),
RefactoringChangeUtil.createThisExpression(field.getManager(), null));
added = true;
}
if (!added && enclosingConstructor == null) {
PsiElementFactory factory = JavaPsiFacade.getInstance(field.getProject()).getElementFactory();
PsiMethod constructor = (PsiMethod)aClass.add(factory.createConstructor());
final PsiCodeBlock body = constructor.getBody();
PsiStatement assignment = createAssignment(field, initializerExpression, body.getLastChild(), parentClass);
assignment = (PsiStatement) body.add(assignment);
ChangeContextUtil.decodeContextInfo(assignment, field.getContainingClass(),
RefactoringChangeUtil.createThisExpression(field.getManager(), null));
}
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
private static PsiField createField(String fieldName,
PsiType type,
PsiExpression initializerExpr,
boolean includeInitializer, final PsiClass parentClass) {
@NonNls StringBuilder pattern = new StringBuilder();
pattern.append("private int ");
pattern.append(fieldName);
if (includeInitializer) {
pattern.append("=0");
}
pattern.append(";");
PsiManager psiManager = parentClass.getManager();
PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
try {
PsiField field = factory.createFieldFromText(pattern.toString(), null);
final PsiTypeElement typeElement = factory.createTypeElement(type);
field.getTypeElement().replace(typeElement);
field = (PsiField)CodeStyleManager.getInstance(psiManager.getProject()).reformat(field);
if (includeInitializer) {
field.getInitializer().replace(initializerExpr);
}
return field;
}
catch (IncorrectOperationException e) {
LOG.error(e);
return null;
}
}
private static PsiStatement createAssignment(PsiField field,
PsiExpression initializerExpr,
PsiElement context,
final PsiClass parentClass) {
try {
@NonNls String pattern = "x=0;";
PsiManager psiManager = parentClass.getManager();
PsiElementFactory factory = JavaPsiFacade.getInstance(psiManager.getProject()).getElementFactory();
PsiExpressionStatement statement = (PsiExpressionStatement)factory.createStatementFromText(pattern, null);
statement = (PsiExpressionStatement)CodeStyleManager.getInstance(psiManager.getProject()).reformat(statement);
PsiAssignmentExpression expr = (PsiAssignmentExpression)statement.getExpression();
final PsiExpression rExpression = expr.getRExpression();
LOG.assertTrue(rExpression != null);
rExpression.replace(initializerExpr);
final PsiReferenceExpression fieldReference = RenameJavaVariableProcessor.createMemberReference(field, context);
expr.getLExpression().replace(fieldReference);
return statement;
}
catch (IncorrectOperationException e) {
LOG.error(e);
return null;
}
}
protected abstract boolean accept(ElementToWorkOn elementToWorkOn);
protected ElementToWorkOn.ElementsProcessor<ElementToWorkOn> getElementProcessor(final Project project, final Editor editor) {
return new ElementToWorkOn.ElementsProcessor<ElementToWorkOn>() {
@Override
public boolean accept(ElementToWorkOn el) {
return BaseExpressionToFieldHandler.this.accept(el);
}
@Override
public void pass(final ElementToWorkOn elementToWorkOn) {
if (elementToWorkOn == null) return;
final boolean hasRunTemplate = LookupManager.getActiveLookup(editor) == null;
if (elementToWorkOn.getExpression() == null) {
final PsiLocalVariable localVariable = elementToWorkOn.getLocalVariable();
final boolean result = invokeImpl(project, localVariable, editor) && hasRunTemplate;
if (result) {
editor.getSelectionModel().removeSelection();
}
}
else {
if (invokeImpl(project, elementToWorkOn.getExpression(), editor) && hasRunTemplate) {
editor.getSelectionModel().removeSelection();
}
}
}
};
}
protected abstract String getRefactoringName();
public static class Settings {
private final String myFieldName;
private final PsiType myForcedType;
private final boolean myReplaceAll;
private final boolean myDeclareStatic;
private final boolean myDeclareFinal;
private final InitializationPlace myInitializerPlace;
private final String myVisibility;
private final boolean myDeleteLocalVariable;
private final TargetDestination myTargetClass;
private final boolean myAnnotateAsNonNls;
private final boolean myIntroduceEnumConstant;
private final PsiExpression mySelectedExpr;
private PsiExpression[] myOccurrences;
public PsiLocalVariable getLocalVariable() {
return myLocalVariable;
}
public boolean isDeleteLocalVariable() {
return myDeleteLocalVariable;
}
private final PsiLocalVariable myLocalVariable;
public String getFieldName() {
return myFieldName;
}
public boolean isDeclareStatic() {
return myDeclareStatic;
}
public boolean isDeclareFinal() {
return myDeclareFinal;
}
public InitializationPlace getInitializerPlace() {
return myInitializerPlace;
}
public String getFieldVisibility() {
return myVisibility;
}
@Nullable
public PsiClass getDestinationClass() {
return myTargetClass != null ? myTargetClass.getTargetClass() : null;
}
public PsiType getForcedType() {
return myForcedType;
}
public boolean isReplaceAll() {
return myReplaceAll;
}
public boolean isAnnotateAsNonNls() {
return myAnnotateAsNonNls;
}
public boolean isIntroduceEnumConstant() {
return myIntroduceEnumConstant;
}
public Settings(String fieldName,
PsiExpression selectedExpr,
PsiExpression[] occurrences,
boolean replaceAll,
boolean declareStatic, boolean declareFinal,
InitializationPlace initializerPlace, String visibility, PsiLocalVariable localVariableToRemove, PsiType forcedType,
boolean deleteLocalVariable,
TargetDestination targetDestination,
final boolean annotateAsNonNls,
final boolean introduceEnumConstant) {
myFieldName = fieldName;
myOccurrences = occurrences;
mySelectedExpr = selectedExpr;
myReplaceAll = replaceAll;
myDeclareStatic = declareStatic;
myDeclareFinal = declareFinal;
myInitializerPlace = initializerPlace;
myVisibility = visibility;
myLocalVariable = localVariableToRemove;
myDeleteLocalVariable = deleteLocalVariable;
myForcedType = forcedType;
myTargetClass = targetDestination;
myAnnotateAsNonNls = annotateAsNonNls;
myIntroduceEnumConstant = introduceEnumConstant;
}
public Settings(String fieldName,
PsiExpression selectedExpression,
PsiExpression[] occurrences,
boolean replaceAll,
boolean declareStatic, boolean declareFinal,
InitializationPlace initializerPlace, String visibility, PsiLocalVariable localVariableToRemove, PsiType forcedType,
boolean deleteLocalVariable,
PsiClass targetClass,
final boolean annotateAsNonNls,
final boolean introduceEnumConstant) {
this(fieldName, selectedExpression, occurrences, replaceAll, declareStatic, declareFinal, initializerPlace, visibility, localVariableToRemove, forcedType, deleteLocalVariable, new TargetDestination(targetClass), annotateAsNonNls, introduceEnumConstant);
}
public PsiExpression getSelectedExpr() {
return mySelectedExpr;
}
public PsiExpression[] getOccurrences() {
return myOccurrences;
}
public void setOccurrences(PsiExpression[] occurrences) {
myOccurrences = occurrences;
}
}
public static class TargetDestination {
private final String myQualifiedName;
private final Project myProject;
private PsiClass myParentClass;
private PsiClass myTargetClass;
public TargetDestination(String qualifiedName, PsiClass parentClass) {
myQualifiedName = qualifiedName;
myParentClass = parentClass;
myProject = parentClass.getProject();
}
public TargetDestination(@NotNull PsiClass targetClass) {
myTargetClass = targetClass;
myQualifiedName = targetClass.getQualifiedName();
myProject = targetClass.getProject();
}
@Nullable
public PsiClass getTargetClass() {
if (myTargetClass != null) return myTargetClass;
final String packageName = StringUtil.getPackageName(myQualifiedName);
final String shortName = StringUtil.getShortName(myQualifiedName);
if (Comparing.strEqual(myParentClass.getQualifiedName(), packageName)) {
myTargetClass = (PsiClass)myParentClass.add(JavaPsiFacade.getElementFactory(myProject).createClass(shortName));
return myTargetClass;
}
PsiPackage psiPackage = JavaPsiFacade.getInstance(myProject).findPackage(packageName);
final PsiDirectory psiDirectory;
if (psiPackage != null) {
final PsiDirectory[] directories = psiPackage.getDirectories(GlobalSearchScope.allScope(myProject));
psiDirectory = directories.length > 1 ? DirectoryChooserUtil.chooseDirectory(directories, null, myProject, new HashMap<PsiDirectory, String>()) : directories[0];
} else {
psiDirectory = PackageUtil.findOrCreateDirectoryForPackage(myProject, packageName, myParentClass.getContainingFile().getContainingDirectory(), false);
}
myTargetClass = psiDirectory != null ? JavaDirectoryService.getInstance().createClass(psiDirectory, shortName) : null;
return myTargetClass;
}
}
public static class ConvertToFieldRunnable implements Runnable {
private PsiExpression mySelectedExpr;
private final Settings mySettings;
private final PsiElement myAnchorElement;
private final Project myProject;
private final String myFieldName;
private final PsiType myType;
private final PsiExpression[] myOccurrences;
private final boolean myReplaceAll;
private final OccurrenceManager myOccurrenceManager;
private final PsiElement myAnchorStatementIfAll;
private final PsiElement myAnchorElementIfOne;
private final Boolean myOutOfCodeBlockExtraction;
private final PsiElement myElement;
private boolean myDeleteSelf;
private final Editor myEditor;
private final PsiClass myParentClass;
private PsiField myField;
public ConvertToFieldRunnable(PsiExpression selectedExpr,
Settings settings,
PsiType type,
PsiExpression[] occurrences,
OccurrenceManager occurrenceManager,
PsiElement anchorStatementIfAll,
PsiElement anchorElementIfOne,
Editor editor,
PsiClass parentClass) {
mySelectedExpr = selectedExpr;
mySettings = settings;
myAnchorElement = settings.isReplaceAll() ? anchorStatementIfAll : anchorElementIfOne;
myProject = selectedExpr.getProject();
myFieldName = settings.getFieldName();
myType = type;
myOccurrences = occurrences;
myReplaceAll = settings.isReplaceAll();
myOccurrenceManager = occurrenceManager;
myAnchorStatementIfAll = anchorStatementIfAll;
myAnchorElementIfOne = anchorElementIfOne;
myOutOfCodeBlockExtraction = selectedExpr.getUserData(ElementToWorkOn.OUT_OF_CODE_BLOCK);
myDeleteSelf = myOutOfCodeBlockExtraction != null;
myElement = getPhysicalElement(selectedExpr);
if (myElement.getParent() instanceof PsiExpressionStatement && getNormalizedAnchor(myAnchorElement).equals(myAnchorElement) && selectedExpr.isPhysical()) {
PsiStatement statement = (PsiStatement)myElement.getParent();
if (statement.getParent() instanceof PsiCodeBlock) {
myDeleteSelf = true;
}
}
myEditor = editor;
myParentClass = parentClass;
}
public void run() {
try {
InitializationPlace initializerPlace = mySettings.getInitializerPlace();
final PsiLocalVariable localVariable = mySettings.getLocalVariable();
final boolean deleteLocalVariable = mySettings.isDeleteLocalVariable();
@Nullable PsiExpression initializer = null;
if (localVariable != null) {
initializer = localVariable.getInitializer();
}
else if (!(mySelectedExpr instanceof PsiReferenceExpression && ((PsiReferenceExpression)mySelectedExpr).resolve() == null)){
initializer = mySelectedExpr;
}
initializer = IntroduceVariableBase.replaceExplicitWithDiamondWhenApplicable(initializer, myType);
final PsiMethod enclosingConstructor = getEnclosingConstructor(myParentClass, myAnchorElement);
PsiClass destClass = mySettings.getDestinationClass() == null ? myParentClass : mySettings.getDestinationClass();
if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, destClass.getContainingFile())) return;
ChangeContextUtil.encodeContextInfo(destClass, true);
myField = mySettings.isIntroduceEnumConstant() ? EnumConstantsUtil.createEnumConstant(destClass, myFieldName, initializer) :
createField(myFieldName, myType, initializer, initializerPlace == InitializationPlace.IN_FIELD_DECLARATION && initializer != null,
myParentClass);
setModifiers(myField, mySettings);
PsiElement finalAnchorElement = null;
if (destClass == myParentClass) {
for (finalAnchorElement = myAnchorElement;
finalAnchorElement != null && finalAnchorElement.getParent() != destClass;
finalAnchorElement = finalAnchorElement.getParent()) {
}
}
PsiMember anchorMember = finalAnchorElement instanceof PsiMember ? (PsiMember)finalAnchorElement : null;
if (anchorMember instanceof PsiEnumConstant && destClass == anchorMember.getContainingClass()) {
final String initialName = "Constants";
String constantsClassName = initialName;
PsiClass innerClass = destClass.findInnerClassByName(constantsClassName, true);
if (innerClass == null || !isConstantsClass(innerClass)) {
int i = 1;
while (destClass.findInnerClassByName(constantsClassName, true) != null) {
constantsClassName = initialName + i++;
}
PsiClass psiClass = JavaPsiFacade.getElementFactory(myProject).createClass(constantsClassName);
PsiUtil.setModifierProperty(psiClass, PsiModifier.PRIVATE, true);
PsiUtil.setModifierProperty(psiClass, PsiModifier.STATIC, true);
destClass = (PsiClass)destClass.add(psiClass);
} else {
destClass = innerClass;
}
anchorMember = null;
}
myField = appendField(initializer, initializerPlace, destClass, myParentClass, myField, anchorMember);
if (!mySettings.isIntroduceEnumConstant()) {
VisibilityUtil.fixVisibility(myOccurrences, myField, mySettings.getFieldVisibility());
}
PsiStatement assignStatement = null;
PsiElement anchorElementHere = null;
if (initializerPlace == InitializationPlace.IN_CURRENT_METHOD && initializer != null ||
initializerPlace == InitializationPlace.IN_CONSTRUCTOR && enclosingConstructor != null && initializer != null) {
if (myReplaceAll) {
if (enclosingConstructor != null) {
final PsiElement anchorInConstructor = RefactoringUtil.getAnchorElementForMultipleExpressions(mySettings.myOccurrences,
enclosingConstructor);
anchorElementHere = anchorInConstructor != null ? anchorInConstructor : myAnchorStatementIfAll;
}
else {
anchorElementHere = myAnchorStatementIfAll;
}
}
else {
anchorElementHere = myAnchorElementIfOne;
}
assignStatement = createAssignment(myField, initializer, anchorElementHere, myParentClass);
if (anchorElementHere != null && !RefactoringUtil.isLoopOrIf(anchorElementHere.getParent())) {
anchorElementHere.getParent().addBefore(assignStatement, getNormalizedAnchor(anchorElementHere));
}
}
if (initializerPlace == InitializationPlace.IN_CONSTRUCTOR && initializer != null) {
addInitializationToConstructors(initializer, myField, enclosingConstructor, myParentClass);
}
if (initializerPlace == InitializationPlace.IN_SETUP_METHOD && initializer != null) {
addInitializationToSetUp(initializer, myField, myOccurrences, myReplaceAll, myParentClass);
}
if (mySelectedExpr.getParent() instanceof PsiParenthesizedExpression) {
mySelectedExpr = (PsiExpression)mySelectedExpr.getParent();
}
if (myOutOfCodeBlockExtraction != null) {
final int endOffset = mySelectedExpr.getUserData(ElementToWorkOn.TEXT_RANGE).getEndOffset();
PsiElement endElement = myElement.getContainingFile().findElementAt(endOffset);
while (true) {
final PsiElement parent = endElement.getParent();
if (parent instanceof PsiClass) break;
endElement = parent;
}
PsiElement last = PsiTreeUtil.skipSiblingsBackward(endElement, PsiWhiteSpace.class);
if (last.getTextRange().getStartOffset() < myElement.getTextRange().getStartOffset()) {
last = myElement;
}
myElement.getParent().deleteChildRange(myElement, last);
}
else if (myDeleteSelf) {
myElement.getParent().delete();
}
if (myReplaceAll) {
List<PsiElement> array = new ArrayList<PsiElement>();
for (PsiExpression occurrence : myOccurrences) {
if (occurrence instanceof PsiExpression) {
occurrence = RefactoringUtil.outermostParenthesizedExpression(occurrence);
}
if (myDeleteSelf && occurrence.equals(mySelectedExpr)) continue;
final PsiElement replaced = RefactoringUtil.replaceOccurenceWithFieldRef(occurrence, myField, destClass);
if (replaced != null) {
array.add(replaced);
}
}
if (myEditor != null) {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
PsiElement[] exprsToHighlight = PsiUtilCore.toPsiElementArray(array);
HighlightManager highlightManager = HighlightManager.getInstance(myProject);
highlightManager.addOccurrenceHighlights(myEditor, exprsToHighlight, highlightAttributes(), true, null);
WindowManager
.getInstance().getStatusBar(myProject).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting"));
}
}
}
else {
if (!myDeleteSelf) {
mySelectedExpr = RefactoringUtil.outermostParenthesizedExpression(mySelectedExpr);
RefactoringUtil.replaceOccurenceWithFieldRef(mySelectedExpr, myField, destClass);
}
}
if (anchorElementHere != null && RefactoringUtil.isLoopOrIf(anchorElementHere.getParent())) {
RefactoringUtil.putStatementInLoopBody(assignStatement, anchorElementHere.getParent(), anchorElementHere);
}
if (localVariable != null) {
if (deleteLocalVariable) {
localVariable.normalizeDeclaration();
localVariable.getParent().delete();
}
}
ChangeContextUtil.decodeContextInfo(destClass, destClass, null);
}
catch (IncorrectOperationException e) {
LOG.error(e);
}
}
static boolean isConstantsClass(PsiClass innerClass) {
if (innerClass.getMethods().length != 0) return false;
for (PsiField field : innerClass.getFields()) {
if (!field.hasModifierProperty(PsiModifier.STATIC) || !field.hasModifierProperty(PsiModifier.FINAL)) return false;
}
return true;
}
static PsiField appendField(final PsiExpression initializer,
InitializationPlace initializerPlace, final PsiClass destClass,
final PsiClass parentClass,
final PsiField psiField,
final PsiMember anchorMember) {
return appendField(destClass, psiField, anchorMember, initializerPlace == InitializationPlace.IN_FIELD_DECLARATION
? checkForwardRefs(initializer, parentClass) : null);
}
public static PsiField appendField(final PsiClass destClass,
final PsiField psiField,
final PsiElement anchorMember,
final PsiField forwardReference) {
final PsiClass parentClass = PsiTreeUtil.getParentOfType(anchorMember, PsiClass.class);
if (anchorMember instanceof PsiField &&
anchorMember.getParent() == parentClass &&
destClass == parentClass &&
((PsiField)anchorMember).hasModifierProperty(PsiModifier.STATIC) == psiField.hasModifierProperty(PsiModifier.STATIC)) {
return (PsiField)destClass.addBefore(psiField, anchorMember);
}
else if (anchorMember instanceof PsiClassInitializer &&
anchorMember.getParent() == parentClass &&
destClass == parentClass) {
PsiField field = (PsiField)destClass.addBefore(psiField, anchorMember);
destClass.addBefore(CodeEditUtil.createLineFeed(field.getManager()), anchorMember);
return field;
}
else {
if (forwardReference != null ) {
return forwardReference.getParent() == destClass ?
(PsiField)destClass.addAfter(psiField, forwardReference) :
(PsiField)forwardReference.getParent().addAfter(psiField, forwardReference);
} else {
return (PsiField)destClass.add(psiField);
}
}
}
@Nullable
private static PsiField checkForwardRefs(@Nullable final PsiExpression initializer, final PsiClass parentClass) {
if (initializer == null) return null;
final PsiField[] refConstantFields = new PsiField[1];
initializer.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
final PsiElement resolve = expression.resolve();
if (resolve instanceof PsiField &&
PsiTreeUtil.isAncestor(parentClass, resolve, false) && ((PsiField)resolve).hasInitializer() &&
!PsiTreeUtil.isAncestor(initializer, resolve, false)) {
if (refConstantFields[0] == null || refConstantFields[0].getTextOffset() < resolve.getTextOffset()) {
refConstantFields[0] = (PsiField)resolve;
}
}
}
});
return refConstantFields[0];
}
public PsiField getField() {
return myField;
}
}
}