/* * Copyright 2013 Guidewire Software, Inc. */ package gw.plugin.ij.refactor.intoduceField; import com.intellij.codeInsight.ExceptionUtil; import com.intellij.codeInsight.highlighting.HighlightManager; import com.intellij.openapi.actionSystem.DataContext; 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.TextRange; import com.intellij.openapi.wm.WindowManager; import com.intellij.psi.PsiCallExpression; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiClassType; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiExpression; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiJavaCodeReferenceElement; import com.intellij.psi.PsiLocalVariable; import com.intellij.psi.PsiMember; import com.intellij.psi.PsiMethod; import com.intellij.psi.PsiModifier; import com.intellij.psi.PsiParameter; import com.intellij.psi.PsiPrimitiveType; import com.intellij.psi.PsiReference; import com.intellij.psi.PsiReferenceExpression; import com.intellij.psi.PsiType; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer; import com.intellij.refactoring.introduceField.InplaceIntroduceConstantPopup; import com.intellij.refactoring.ui.TypeSelectorManagerImpl; import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.RefactoringUtil; import com.intellij.refactoring.util.classMembers.ClassMemberReferencesVisitor; import com.intellij.refactoring.util.occurrences.ExpressionOccurrenceManager; import com.intellij.refactoring.util.occurrences.OccurrenceManager; import gw.plugin.ij.lang.psi.impl.GosuClassFileImpl; import gw.plugin.ij.refactor.GosuRefactoringUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; public class GosuIntroduceConstantHandler extends GosuBaseExpressionToFieldHandler { public static final String REFACTORING_NAME = RefactoringBundle.message("introduce.constant.title"); protected InplaceIntroduceConstantPopup myInplaceIntroduceConstantPopup; public GosuIntroduceConstantHandler() { super(true); } protected String getHelpID() { return HelpID.INTRODUCE_CONSTANT; } public void invoke(Project project, PsiExpression[] expressions) { for (PsiExpression expression : expressions) { final PsiFile file = expression.getContainingFile(); if (!CommonRefactoringUtil.checkReadOnlyStatus(project, file)) { return; } } PsiDocumentManager.getInstance(project).commitAllDocuments(); super.invoke(project, expressions, null); } 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, getHelpID(), project, getElementProcessor(project, editor)); } protected boolean invokeImpl(final Project project, final PsiLocalVariable localVariable, final Editor editor) { // final PsiElement parent = localVariable.getParent(); // if (!(parent instanceof PsiDeclarationStatement)) { // String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("error.wrong.caret.position.local.or.expression.name")); // CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, getHelpID()); // return false; // } final GosuLocalToFieldHandler localToFieldHandler = new GosuLocalToFieldHandler(project, true) { @Override protected Settings showRefactoringDialog(PsiClass aClass, PsiLocalVariable local, PsiExpression[] occurences, boolean isStatic) { return GosuIntroduceConstantHandler.this.showRefactoringDialog(project, editor, aClass, local.getInitializer(), local.getType(), occurences, local, null, true); } }; return localToFieldHandler.convertLocalToField(localVariable, editor); } protected Settings showRefactoringDialog(Project project, final Editor editor, PsiClass parentClass, PsiExpression expr, PsiType type, PsiExpression[] occurrences, PsiElement anchorElement, PsiElement anchorElementIfAll, boolean isLocalVariable) { final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(expr != null ? expr : anchorElement, PsiMethod.class); 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 replaceAllOccurrences = true; final AbstractInplaceIntroducer activeIntroducer = AbstractInplaceIntroducer.getActiveIntroducer(editor); if (activeIntroducer != null) { if (!(activeIntroducer instanceof InplaceIntroduceConstantPopup) || !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(); replaceAllOccurrences = activeIntroducer.isReplaceAllOccurrences(); type = ((InplaceIntroduceConstantPopup) activeIntroducer).getType(); } for (PsiExpression occurrence : occurrences) { if (RefactoringUtil.isAssignmentLHS(occurrence)) { String message = RefactoringBundle.getCannotRefactorMessage("Selected expression is used for write"); CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, getHelpID()); highlightError(project, editor, occurrence); return null; } } if (localVariable == null) { //For gosu we will accept any expression regadless is it can be constant or not // if (GosuRefactoringUtil.isCompileTimeConstant(expr)) { // String message = // RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("selected.expression.cannot.be.a.constant.initializer")); // CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, getHelpID()); // highlightError(project, editor, errorElement); // return null; // } } else { final PsiExpression initializer = localVariable.getInitializer(); if (initializer == null) { String message = RefactoringBundle .getCannotRefactorMessage(RefactoringBundle.message("variable.does.not.have.an.initializer", localVariable.getName())); CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, getHelpID()); return null; } //For gosu we will accept any expression regadless is it can be constant or not // if (!GosuRefactoringUtil.isCompileTimeConstant(initializer)) { // String message = RefactoringBundle.getCannotRefactorMessage( // RefactoringBundle.message("initializer.for.variable.cannot.be.a.constant.initializer", localVariable.getName())); // CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, getHelpID()); // highlightError(project, editor, errorElement); // return null; // } } final TypeSelectorManagerImpl typeSelectorManager = new TypeSelectorManagerImpl(project, type, containingMethod, expr, occurrences); if (editor != null && false/*editor.getSettings().isVariableInplaceRenameEnabled()*/ && (expr == null || expr.isPhysical()) && activeIntroducer == null) { myInplaceIntroduceConstantPopup = new InplaceIntroduceConstantPopup(project, editor, parentClass, expr, localVariable, occurrences, typeSelectorManager, anchorElement, anchorElementIfAll, expr != null ? createOccurrenceManager(expr, parentClass) : null); if (myInplaceIntroduceConstantPopup.startInplaceIntroduceTemplate()) { return null; } } final GosuIntroduceConstantDialog dialog = new GosuIntroduceConstantDialog(project, parentClass, expr, localVariable, localVariable != null, occurrences, getParentClass(), typeSelectorManager, enteredName); dialog.setReplaceAllOccurrences(replaceAllOccurrences); dialog.show(); if (!dialog.isOK()) { if (occurrences.length > 1) { WindowManager.getInstance().getStatusBar(project).setInfo(RefactoringBundle.message("press.escape.to.remove.the.highlighting")); } return null; } return new Settings(dialog.getEnteredName(), expr, occurrences, dialog.isReplaceAllOccurrences(), true, true, InitializationPlace.IN_FIELD_DECLARATION, dialog.getFieldVisibility(), localVariable, dialog.getSelectedType(), dialog.isDeleteVariable(), dialog.getDestinationClass(), dialog.isAnnotateAsNonNls(), dialog.introduceEnumConstant()); } private static void highlightError(Project project, Editor editor, PsiElement errorElement) { if (editor != null) { final TextAttributes attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES); final TextRange textRange = errorElement.getTextRange(); HighlightManager.getInstance(project).addRangeHighlight(editor, textRange.getStartOffset(), textRange.getEndOffset(), attributes, true, new ArrayList<RangeHighlighter>()); } } protected String getRefactoringName() { return REFACTORING_NAME; } @Override public AbstractInplaceIntroducer getInplaceIntroducer() { return myInplaceIntroduceConstantPopup; } @Nullable private PsiElement isStaticFinalInitializer(PsiExpression expr) { PsiClass parentClass = expr != null ? getParentClass(expr) : null; if (parentClass == null) { return null; } IsStaticFinalInitializerExpression visitor = new IsStaticFinalInitializerExpression(parentClass, expr); expr.accept(visitor); return visitor.getElementReference(); } protected OccurrenceManager createOccurrenceManager(final PsiExpression selectedExpr, final PsiClass parentClass) { return new ExpressionOccurrenceManager(selectedExpr, parentClass, null) { @Override public PsiElement getAnchorStatementForAllInScope(PsiElement scope) { return GosuRefactoringUtil.getAnchorElementForMultipleExpressions(getOccurrences(), scope); } }; } private static class IsStaticFinalInitializerExpression extends ClassMemberReferencesVisitor { private PsiElement myElementReference = null; private final PsiExpression myInitializer; public IsStaticFinalInitializerExpression(PsiClass aClass, PsiExpression initializer) { super(aClass); myInitializer = initializer; } @Override public void visitReferenceExpression(PsiReferenceExpression expression) { final PsiElement psiElement = expression.resolve(); if ((psiElement instanceof PsiLocalVariable || psiElement instanceof PsiParameter) && !PsiTreeUtil.isAncestor(myInitializer, psiElement, false)) { myElementReference = expression; } else { super.visitReferenceExpression(expression); } } @Override public void visitCallExpression(PsiCallExpression callExpression) { super.visitCallExpression(callExpression); final List<PsiClassType> checkedExceptions = ExceptionUtil.getThrownCheckedExceptions(new PsiElement[]{callExpression}); if (!checkedExceptions.isEmpty()) { myElementReference = callExpression; } } protected void visitClassMemberReferenceElement(PsiMember classMember, PsiJavaCodeReferenceElement classMemberReference) { if (!classMember.hasModifierProperty(PsiModifier.STATIC)) { myElementReference = classMemberReference; } } @Override public void visitElement(PsiElement element) { if (myElementReference != null) { return; } super.visitElement(element); } @Nullable public PsiElement getElementReference() { return myElementReference; } } public PsiClass getParentClass(@NotNull PsiExpression initializerExpression) { final PsiType type = initializerExpression.getType(); if (type != null && GosuRefactoringUtil.isCompileTimeConstant(initializerExpression)) { if (type instanceof PsiPrimitiveType || PsiType.getJavaLangString(initializerExpression.getManager(), initializerExpression.getResolveScope()).equals(type)) { return super.getParentClass(initializerExpression); } } PsiElement parent = initializerExpression.getUserData(GosuElementToWorkOn.PARENT); if (parent == null) { parent = initializerExpression; } PsiClass aClass = PsiTreeUtil.getParentOfType(parent, PsiClass.class); while (aClass != null) { if (aClass.hasModifierProperty(PsiModifier.STATIC)) { return aClass; } if (aClass.getParent() instanceof GosuClassFileImpl) { return aClass; } aClass = PsiTreeUtil.getParentOfType(aClass, PsiClass.class); } return null; } protected boolean validClass(PsiClass parentClass, Editor editor) { return true; } }