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