blob: abcf02e3db38561c37d1f8945ebe9066a07e9b45 [file] [log] [blame]
/*
* Copyright 2000-2014 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.
*/
package org.jetbrains.plugins.groovy.refactoring.introduce;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Computable;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.refactoring.introduce.inplace.AbstractInplaceIntroducer;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import com.intellij.util.Function;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyFileType;
import org.jetbrains.plugins.groovy.lang.lexer.GroovyTokenTypes;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.*;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.path.GrMethodCallExpression;
import org.jetbrains.plugins.groovy.lang.psi.impl.PsiImplUtil;
import java.util.List;
/**
* Created by Max Medvedev on 10/28/13
*/
public abstract class GrAbstractInplaceIntroducer<Settings extends GrIntroduceSettings> extends AbstractInplaceIntroducer<GrVariable, PsiElement> {
private SmartTypePointer myTypePointer;
private final OccurrencesChooser.ReplaceChoice myReplaceChoice;
private RangeMarker myVarMarker;
private final PsiFile myFile;
private final GrIntroduceContext myContext;
public GrAbstractInplaceIntroducer(String title,
OccurrencesChooser.ReplaceChoice replaceChoice,
GrIntroduceContext context) {
super(context.getProject(), context.getEditor(), context.getExpression(), context.getVar(), context.getOccurrences(), title, GroovyFileType.GROOVY_FILE_TYPE);
myReplaceChoice = replaceChoice;
myContext = context;
myFile = context.getPlace().getContainingFile();
}
public GrIntroduceContext getContext() {
return myContext;
}
@Override
public void setReplaceAllOccurrences(boolean allOccurrences) {
throw new IncorrectOperationException("don't invoke this method");
}
@Override
public GrExpression restoreExpression(PsiFile containingFile, GrVariable variable, RangeMarker marker, String exprText) {
if (exprText == null) return null;
if (variable == null || !variable.isValid()) return null;
final PsiElement refVariableElement = containingFile.findElementAt(marker.getStartOffset());
final PsiElement refVariableElementParent = refVariableElement != null ? refVariableElement.getParent() : null;
GrExpression expression =
refVariableElementParent instanceof GrNewExpression && refVariableElement.getNode().getElementType() == GroovyTokenTypes.kNEW
? (GrNewExpression)refVariableElementParent
: refVariableElementParent instanceof GrParenthesizedExpression ? ((GrParenthesizedExpression)refVariableElementParent).getOperand()
: PsiTreeUtil.getParentOfType(refVariableElement, GrReferenceExpression.class);
if (expression instanceof GrReferenceExpression && !(expression.getParent() instanceof GrMethodCall)) {
final String referenceName = ((GrReferenceExpression)expression).getReferenceName();
if (((GrReferenceExpression)expression).resolve() == variable ||
Comparing.strEqual(variable.getName(), referenceName) ||
Comparing.strEqual(exprText, referenceName)) {
return (GrExpression)expression
.replace(GroovyPsiElementFactory.getInstance(myProject).createExpressionFromText(exprText, variable));
}
}
if (expression == null) {
expression = PsiTreeUtil.getParentOfType(refVariableElement, GrExpression.class);
}
while (expression instanceof GrReferenceExpression || expression instanceof GrCall) {
final PsiElement parent = expression.getParent();
if (parent instanceof GrMethodCallExpression) {
if (parent.getText().equals(exprText)) return (GrExpression)parent;
}
if (parent instanceof GrExpression) {
expression = (GrExpression)parent;
if (expression.getText().equals(exprText)) {
return expression;
}
}
else if (expression instanceof GrReferenceExpression){
return null;
} else {
break;
}
}
if (expression != null && expression.isValid() && expression.getText().equals(exprText)) {
return expression;
}
if (refVariableElementParent instanceof GrExpression && refVariableElementParent.getText().equals(exprText)) {
return (GrExpression)refVariableElementParent;
}
return null;
}
@Override
protected void updateTitle(@Nullable GrVariable variable, String value) {
if (variable == null) {
super.updateTitle(variable, value);
}
else {
final String variableText = variable.getParent().getText();
final PsiElement identifier = variable.getNameIdentifierGroovy();
final int startOffsetInParent = identifier.getStartOffsetInParent() + variable.getStartOffsetInParent();
setPreviewText(
variableText.substring(0, startOffsetInParent) + value + variableText.substring(startOffsetInParent + identifier.getTextLength()));
revalidate();
}
}
@Override
protected void updateTitle(@Nullable GrVariable variable) {
if (variable == null) return;
setPreviewText(variable.getParent().getText());
revalidate();
}
@Nullable
@Override
protected PsiElement getNameIdentifier() {
return ((GrVariable)myElementToRename).getNameIdentifierGroovy();
}
@Override
protected GrVariable getVariable() {
if (myVarMarker == null) return null;
int offset = myVarMarker.getStartOffset();
PsiElement at = myFile.findElementAt(offset);
GrVariable var = PsiTreeUtil.getParentOfType(at, GrVariable.class);
return var;
}
@Override
protected void moveOffsetAfter(boolean success) {
super.moveOffsetAfter(success);
}
@Override
protected void performIntroduce() {
runRefactoring(new IntroduceContextAdapter(), getSettings(), true);
}
@NotNull
protected PsiElement[] restoreOccurrences() {
List<PsiElement> result = ContainerUtil.map(getOccurrenceMarkers(), new Function<RangeMarker, PsiElement>() {
@Override
public PsiElement fun(RangeMarker marker) {
return PsiImplUtil.findElementInRange(myFile, marker.getStartOffset(), marker.getEndOffset(), GrExpression.class);
}
});
return PsiUtilCore.toPsiElementArray(result);
}
@Nullable
@Override
protected GrVariable createFieldToStartTemplateOn(boolean replaceAll, String[] names) {
final Settings settings = getInitialSettingsForInplace(myContext, myReplaceChoice, names);
if (settings == null) return null;
SmartPsiElementPointer<GrVariable> pointer = ApplicationManager.getApplication().runWriteAction(new Computable<SmartPsiElementPointer<GrVariable>>() {
@Override
public SmartPsiElementPointer<GrVariable> compute() {
GrVariable var = runRefactoring(myContext, settings, false);
return var != null ? SmartPointerManager.getInstance(myProject).createSmartPsiElementPointer(var) : null;
}
});
if (pointer != null) {
GrVariable var = pointer.getElement();
if (var != null) {
myVarMarker = myContext.getEditor().getDocument().createRangeMarker(var.getTextRange());
}
return var;
}
else {
return null;
}
}
protected abstract GrVariable runRefactoring(GrIntroduceContext context, Settings settings, boolean processUsages);
@Nullable
protected abstract Settings getInitialSettingsForInplace(@NotNull GrIntroduceContext context,
@NotNull OccurrencesChooser.ReplaceChoice choice,
String[] names);
@Override
public boolean isReplaceAllOccurrences() {
return myReplaceChoice != OccurrencesChooser.ReplaceChoice.NO;
}
protected abstract Settings getSettings();
@Override
protected void restoreState(GrVariable psiField) {
PsiType declaredType = psiField.getDeclaredType();
myTypePointer = declaredType != null ? SmartTypePointerManager.getInstance(myProject).createSmartTypePointer(declaredType) : null;
super.restoreState(psiField);
}
@Nullable
protected PsiType getSelectedType() {
return myTypePointer != null ? myTypePointer.getType() : null;
}
private class IntroduceContextAdapter implements GrIntroduceContext {
@NotNull
@Override
public Project getProject() {
return myProject;
}
@Override
public Editor getEditor() {
return myEditor;
}
@Nullable
@Override
public GrExpression getExpression() {
return (GrExpression)getExpr();
}
@Nullable
@Override
public GrVariable getVar() {
return getLocalVariable();
}
@Nullable
@Override
public StringPartInfo getStringPart() {
return null;
}
@NotNull
@Override
public PsiElement[] getOccurrences() {
return restoreOccurrences();
}
@Override
public PsiElement getScope() {
return myScope;
}
@NotNull
@Override
public PsiElement getPlace() {
GrExpression expression = getExpression();
return expression != null ? expression : getLocalVariable();
}
}
}