/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.refactor.intoduceField; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.wm.WindowManager; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiDeclarationStatement; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiLocalVariable; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifierListOwner; import com.intellij.psi.PsiReference; import com.intellij.psi.PsiStatement; import com.intellij.psi.PsiType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer; import com.intellij.refactoring.introduceParameter.AbstractJavaInplaceIntroducer; import com.intellij.refactoring.ui.TypeSelectorManagerImpl; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.occurrences.ExpressionOccurrenceManager; import com.intellij.refactoring.util.occurrences.NotInSuperCallOccurrenceFilter; import com.intellij.refactoring.util.occurrences.NotInThisCallFilter; import com.intellij.refactoring.util.occurrences.OccurrenceFilter; import com.intellij.refactoring.util.occurrences.OccurrenceManager; import gw.plugin.ij.lang.psi.api.statements.IGosuVariable; import gw.plugin.ij.lang.psi.impl.statements.GosuForEachStatementImpl; import gw.plugin.ij.refactor.GosuRefactoringUtil; import org.jetbrains.annotations.NotNull; import java.util.List; public class GosuIntroduceFieldHandler extends GosuBaseExpressionToFieldHandler { public static final String REFACTORING_NAME = RefactoringBundle.message("introduce.field.title"); private static final MyOccurrenceFilter MY_OCCURRENCE_FILTER = new MyOccurrenceFilter(); private GosuInplaceIntroduceFieldPopup myGosuInplaceIntroduceFieldPopup; public GosuIntroduceFieldHandler() { super(false); } protected String getRefactoringName() { return REFACTORING_NAME; } protected boolean validClass(PsiClass parentClass, Editor editor) { if (parentClass.isInterface()) { String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("cannot.introduce.field.in.interface")); CommonRefactoringUtil.showErrorHint(parentClass.getProject(), editor, message, REFACTORING_NAME, getHelpID()); return false; } else if (!GosuRefactoringUtil.isValidToIntroduceField(parentClass, editor, REFACTORING_NAME, getHelpID())) { return false; } else { return true; } } protected String getHelpID() { return HelpID.INTRODUCE_FIELD; } public void invoke(@NotNull final Project project, final Editor editor, PsiFile file, DataContext dataContext) { if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) { return; } PsiDocumentManager.getInstance(project).commitAllDocuments(); GosuElementToWorkOn.processElementToWorkOn(editor, file, REFACTORING_NAME, HelpID.INTRODUCE_FIELD, project, getElementProcessor(project, editor)); } protected Settings showRefactoringDialog(Project project, Editor editor, PsiClass parentClass, PsiExpression expr, PsiType type, PsiExpression[] occurrences, PsiElement anchorElement, PsiElement anchorElementIfAll, boolean isLocalVariable) { final AbstractInplaceIntroducer activeIntroducer = AbstractInplaceIntroducer.getActiveIntroducer(editor); PsiLocalVariable localVariable = null; if (isLocalVariable && anchorElement instanceof PsiLocalVariable) { localVariable = (PsiLocalVariable) anchorElement; } else if (GosuRefactoringUtil.isPsiReferenceExpression(expr)) { PsiElement ref = ((PsiReference) expr).resolve(); if (ref instanceof PsiLocalVariable) { localVariable = (PsiLocalVariable) ref; } } String enteredName = null; boolean replaceAll = false; if (activeIntroducer != null) { if (!(activeIntroducer instanceof GosuInplaceIntroduceFieldPopup) || !activeIntroducer.startsOnTheSameElement(expr, localVariable)) { AbstractInplaceIntroducer.unableToStartWarning(project, editor); return null; } activeIntroducer.stopIntroduce(editor); expr = (PsiExpression) activeIntroducer.getExpr(); localVariable = (PsiLocalVariable) activeIntroducer.getLocalVariable(); occurrences = (PsiExpression[]) activeIntroducer.getOccurrences(); enteredName = activeIntroducer.getInputName(); replaceAll = activeIntroducer.isReplaceAllOccurrences(); type = ((AbstractJavaInplaceIntroducer) activeIntroducer).getType(); GosuIntroduceFieldDialog.ourLastInitializerPlace = ((GosuInplaceIntroduceFieldPopup) activeIntroducer).getInitializerPlace(); } final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(expr != null ? expr : anchorElement, PsiMethod.class); final PsiModifierListOwner staticParentElement = PsiUtil.getEnclosingStaticElement(getElement(expr, anchorElement), parentClass); boolean declareStatic = staticParentElement != null; boolean isInSuperOrThis = false; if (!declareStatic) { for (int i = 0; !declareStatic && i < occurrences.length; i++) { PsiExpression occurrence = occurrences[i]; isInSuperOrThis = isInSuperOrThis(occurrence); declareStatic = isInSuperOrThis; } } int occurrencesNumber = occurrences.length; final boolean currentMethodConstructor = containingMethod != null && containingMethod.isConstructor(); final boolean allowInitInMethod = (!currentMethodConstructor || !isInSuperOrThis) && (anchorElement instanceof PsiLocalVariable || GosuRefactoringUtil.isStatement(anchorElement)); final boolean allowInitInMethodIfAll = (!currentMethodConstructor || !isInSuperOrThis) && GosuRefactoringUtil.isStatement(anchorElementIfAll); if (editor != null && false/*editor.getSettings().isVariableInplaceRenameEnabled()*/ && (expr == null || expr.isPhysical()) && activeIntroducer == null) { myGosuInplaceIntroduceFieldPopup = new GosuInplaceIntroduceFieldPopup(localVariable, parentClass, declareStatic, currentMethodConstructor, occurrences, expr, new TypeSelectorManagerImpl(project, type, containingMethod, expr, occurrences), editor, allowInitInMethod, allowInitInMethodIfAll, anchorElement, anchorElementIfAll, expr != null ? createOccurrenceManager(expr, parentClass) : null, project); if (myGosuInplaceIntroduceFieldPopup.startInplaceIntroduceTemplate()) { return null; } } GosuIntroduceFieldDialog dialog = new GosuIntroduceFieldDialog( project, parentClass, expr, localVariable, currentMethodConstructor, localVariable != null, declareStatic, occurrences, allowInitInMethod, allowInitInMethodIfAll, new TypeSelectorManagerImpl(project, type, containingMethod, expr, occurrences), enteredName ); dialog.setReplaceAllOccurrences(replaceAll); dialog.show(); if (!dialog.isOK()) { if (occurrencesNumber > 1) { WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting")); } return null; } if (!dialog.isDeleteVariable()) { localVariable = null; } return new Settings(dialog.getEnteredName(), expr, occurrences, dialog.isReplaceAllOccurrences(), declareStatic, dialog.isDeclareFinal(), dialog.getInitializerPlace(), dialog.getFieldVisibility(), localVariable, dialog.getFieldType(), localVariable != null, (TargetDestination) null, false, false); } private static PsiElement getElement(PsiExpression expr, PsiElement anchorElement) { PsiElement element = null; if (expr != null) { element = expr.getUserData(GosuElementToWorkOn.PARENT); if (element == null) { element = expr; } } if (element == null) { element = anchorElement; } return element; } @Override public AbstractInplaceIntroducer getInplaceIntroducer() { return myGosuInplaceIntroduceFieldPopup; } private static boolean isInSuperOrThis(PsiExpression occurrence) { return !NotInSuperCallOccurrenceFilter.INSTANCE.isOK(occurrence) || !NotInThisCallFilter.INSTANCE.isOK(occurrence); } protected OccurrenceManager createOccurrenceManager(final PsiExpression selectedExpr, final PsiClass parentClass) { final OccurrenceFilter occurrenceFilter = isInSuperOrThis(selectedExpr) ? null : MY_OCCURRENCE_FILTER; return new ExpressionOccurrenceManager(selectedExpr, parentClass, occurrenceFilter, true) { @Override public PsiElement getAnchorStatementForAllInScope(PsiElement scope) { return GosuRefactoringUtil.getAnchorElementForMultipleExpressions(getOccurrences(), scope); } }; } protected boolean invokeImpl(final Project project, PsiLocalVariable localVariable, final Editor editor) { final PsiElement parent = localVariable.getParent(); if (!(parent instanceof PsiDeclarationStatement || localVariable instanceof IGosuVariable)) { String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("error.wrong.caret.position.local.or.expression.name")); CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, getHelpID()); return false; } else if (parent instanceof GosuForEachStatementImpl) { return false; } if (!GosuRefactoringUtil.isValidToIntroduceField(localVariable, editor, REFACTORING_NAME, getHelpID())) { return false; } GosuLocalToFieldHandler localToFieldHandler = new GosuLocalToFieldHandler(project, false) { @Override protected Settings showRefactoringDialog(PsiClass aClass, PsiLocalVariable local, PsiExpression[] occurences, boolean isStatic) { final PsiStatement statement = PsiTreeUtil.getParentOfType(local, PsiStatement.class); return GosuIntroduceFieldHandler.this.showRefactoringDialog(project, editor, aClass, local.getInitializer(), local.getType(), occurences, local, statement, true); } @Override protected int getChosenClassIndex(List<PsiClass> classes) { return GosuIntroduceFieldHandler.this.getChosenClassIndex(classes); } }; return localToFieldHandler.convertLocalToField(localVariable, editor); } protected int getChosenClassIndex(List<PsiClass> classes) { return classes.size() - 1; } private static class MyOccurrenceFilter implements OccurrenceFilter { public boolean isOK(PsiExpression occurrence) { return !isInSuperOrThis(occurrence); } } }