/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.refactor.intoduceField;
import com.intellij.codeInsight.TargetElementUtilBase;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.Pass;
import com.intellij.psi.PsiDeclarationStatement;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiExpression;
import com.intellij.psi.PsiExpressionStatement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.PsiLocalVariable;
import com.intellij.psi.PsiReference;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.refactoring.IntroduceTargetChooser;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import gw.plugin.ij.refactor.GosuCodeInsightUtil;
import gw.plugin.ij.refactor.GosuRefactoringUtil;
import gw.plugin.ij.refactor.GosuRenderFuction;
import gw.plugin.ij.refactor.introduceVariable.GosuIntroduceVariableBase;
import java.util.List;
/**
* @author dsl
*/
public class GosuElementToWorkOn {
public static final Key<PsiElement> PARENT = Key.create("PARENT");
private final PsiExpression myExpression;
private final PsiLocalVariable myLocalVariable;
public static final Key<String> PREFIX = Key.create("prefix");
public static final Key<String> SUFFIX = Key.create("suffix");
public static final Key<RangeMarker> TEXT_RANGE = Key.create("range");
public static final Key<Boolean> OUT_OF_CODE_BLOCK = Key.create("out_of_code_block");
private GosuElementToWorkOn(PsiLocalVariable localVariable, PsiExpression expr) {
myLocalVariable = localVariable;
myExpression = expr;
}
public PsiExpression getExpression() {
return myExpression;
}
public PsiLocalVariable getLocalVariable() {
return myLocalVariable;
}
public boolean isInvokedOnDeclaration() {
return myExpression == null;
}
public static void processElementToWorkOn(final Editor editor, final PsiFile file, final String refactoringName, final String helpId, final Project project, final Pass<GosuElementToWorkOn> processor) {
PsiLocalVariable localVar = null;
PsiExpression expr = null;
if (!editor.getSelectionModel().hasSelection()) {
PsiElement element = TargetElementUtilBase.findTargetElement(editor, TargetElementUtilBase
.ELEMENT_NAME_ACCEPTED | TargetElementUtilBase
.REFERENCED_ELEMENT_ACCEPTED | TargetElementUtilBase
.LOOKUP_ITEM_ACCEPTED);
if (element instanceof PsiLocalVariable) {
localVar = (PsiLocalVariable) element;
PsiElement elementAt = file.findElementAt(editor.getCaretModel().getOffset());
if (elementAt instanceof PsiIdentifier && GosuRefactoringUtil.isPsiReferenceExpression(elementAt.getParent())) {
expr = (PsiExpression) elementAt.getParent();
} else {
final PsiReference reference = TargetElementUtilBase.findReference(editor);
if (reference != null) {
final PsiElement refElement = reference.getElement();
if (GosuRefactoringUtil.isPsiReferenceExpression(refElement)) {
expr = (PsiExpression) refElement;
}
}
}
} else {
final PsiLocalVariable variable = PsiTreeUtil.getParentOfType(file.findElementAt(editor.getCaretModel().getOffset()), PsiLocalVariable.class);
final int offset = editor.getCaretModel().getOffset();
final PsiElement[] statementsInRange = GosuIntroduceVariableBase.findStatementsAtOffset(editor, file, offset);
if (statementsInRange.length == 1 && (PsiUtilCore.hasErrorElementChild(statementsInRange[0]) || !GosuRefactoringUtil.isStatementOrExpressionstatement(statementsInRange[0]))) {
editor.getSelectionModel().selectLineAtCaret();
final GosuElementToWorkOn elementToWorkOn = getElementToWorkOn(editor, file, refactoringName, helpId, project, localVar, expr);
if (elementToWorkOn == null || elementToWorkOn.getLocalVariable() == null && elementToWorkOn.getExpression() == null) {
editor.getSelectionModel().removeSelection();
}
}
if (!editor.getSelectionModel().hasSelection()) {
final List<PsiExpression> expressions = GosuIntroduceVariableBase.collectExpressions(file, editor, offset, statementsInRange);
if (expressions.isEmpty()) {
editor.getSelectionModel().selectLineAtCaret();
} else if (expressions.size() == 1) {
expr = expressions.get(0);
} else {
IntroduceTargetChooser.showChooser(editor, expressions, new Pass<PsiExpression>() {
@Override
public void pass(final PsiExpression selectedValue) {
PsiLocalVariable var = null; //replace var if selected expression == var initializer
if (variable != null && variable.getInitializer() == selectedValue) {
var = variable;
}
processor.pass(getElementToWorkOn(editor, file, refactoringName, helpId, project, var, selectedValue));
}
}, new GosuRenderFuction());
return;
}
}
}
}
processor.pass(getElementToWorkOn(editor, file, refactoringName, helpId, project, localVar, expr));
}
private static GosuElementToWorkOn getElementToWorkOn(final Editor editor, final PsiFile file,
final String refactoringName,
final String helpId,
final Project project, PsiLocalVariable localVar, PsiExpression expr) {
int startOffset = 0;
int endOffset = 0;
if (localVar == null && expr == null) {
startOffset = editor.getSelectionModel().getSelectionStart();
endOffset = editor.getSelectionModel().getSelectionEnd();
expr = GosuCodeInsightUtil.findExpressionInRange(file, startOffset, endOffset);
if (expr == null) {
PsiIdentifier ident = GosuCodeInsightUtil.findElementInRange(file, startOffset, endOffset, PsiIdentifier.class);
if (ident != null) {
localVar = PsiTreeUtil.getParentOfType(ident, PsiLocalVariable.class);
}
}
}
if (expr == null && localVar == null) {
PsiElement[] statements = GosuCodeInsightUtil.findStatementsInRange(file, startOffset, endOffset);
if (statements.length == 1 && statements[0] instanceof PsiExpressionStatement) {
expr = ((PsiExpressionStatement) statements[0]).getExpression();
} else if (statements.length == 1 && statements[0] instanceof PsiDeclarationStatement) {
PsiDeclarationStatement decl = (PsiDeclarationStatement) statements[0];
PsiElement[] declaredElements = decl.getDeclaredElements();
if (declaredElements.length == 1 && declaredElements[0] instanceof PsiLocalVariable) {
localVar = (PsiLocalVariable) declaredElements[0];
}
}
}
if (localVar == null && expr == null) {
expr = GosuIntroduceVariableBase.getSelectedExpression(project, file, startOffset, endOffset);
}
if (localVar == null) {
if (expr != null) {
final String errorMessage = GosuIntroduceVariableBase.getErrorMessage(expr);
if (errorMessage != null) {
CommonRefactoringUtil.showErrorHint(project, editor, errorMessage, refactoringName, helpId);
return null;
}
}
if (expr == null) {
String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("error.wrong.caret.position.local.or.expression.name"));
CommonRefactoringUtil.showErrorHint(project, editor, message, refactoringName, helpId);
return null;
}
}
return new GosuElementToWorkOn(localVar, expr);
}
}