| /* |
| * 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; |
| } |
| } |
| } |