blob: 46d90d2a3050bbbaadbfdd4d884ff5cfc0dc4b3b [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.variable;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.util.Pass;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.codeInspection.utils.ControlFlowUtils;
import org.jetbrains.plugins.groovy.lang.psi.GrControlFlowOwner;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
import org.jetbrains.plugins.groovy.lang.psi.api.formatter.GrControlStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrStatement;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariableDeclaration;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrClosableBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.params.GrParameter;
import org.jetbrains.plugins.groovy.lang.psi.api.util.GrStatementOwner;
import org.jetbrains.plugins.groovy.refactoring.GrRefactoringError;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceContext;
import org.jetbrains.plugins.groovy.refactoring.introduce.GrIntroduceHandlerBase;
import org.jetbrains.plugins.groovy.refactoring.introduce.StringPartInfo;
/**
* Created by Max Medvedev on 10/29/13
*/
public class GrIntroduceVariableHandler extends GrIntroduceHandlerBase<GroovyIntroduceVariableSettings, GrControlFlowOwner> {
public static final String DUMMY_NAME = "________________xxx_________________";
protected static final String REFACTORING_NAME = GroovyRefactoringBundle.message("introduce.variable.title");
private RangeMarker myPosition = null;
@NotNull
@Override
protected GrControlFlowOwner[] findPossibleScopes(GrExpression selectedExpr,
GrVariable variable,
StringPartInfo stringPartInfo,
Editor editor) {
// Get container element
final GrControlFlowOwner scope = ControlFlowUtils.findControlFlowOwner(stringPartInfo != null ? stringPartInfo.getLiteral() : selectedExpr);
if (scope == null) {
throw new GrRefactoringError(
GroovyRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME));
}
if (!GroovyRefactoringUtil.isAppropriateContainerForIntroduceVariable(scope)) {
throw new GrRefactoringError(
GroovyRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context", REFACTORING_NAME));
}
return new GrControlFlowOwner[]{scope};
}
@Override
protected void checkExpression(@NotNull GrExpression selectedExpr) {
// Cannot perform refactoring in parameter default values
PsiElement parent = selectedExpr.getParent();
while (!(parent == null || parent instanceof GroovyFileBase || parent instanceof GrParameter)) {
parent = parent.getParent();
}
if (checkInFieldInitializer(selectedExpr)) {
throw new GrRefactoringError(GroovyRefactoringBundle.message("refactoring.is.not.supported.in.the.current.context"));
}
if (parent instanceof GrParameter) {
throw new GrRefactoringError(GroovyRefactoringBundle.message("refactoring.is.not.supported.in.method.parameters"));
}
}
@Override
protected void checkVariable(@NotNull GrVariable variable) throws GrRefactoringError {
throw new GrRefactoringError(null);
}
@Override
protected void checkStringLiteral(@NotNull StringPartInfo info) throws GrRefactoringError {
//todo
}
@Override
protected void checkOccurrences(@NotNull PsiElement[] occurrences) {
//nothing to do
}
private static boolean checkInFieldInitializer(@NotNull GrExpression expr) {
PsiElement parent = expr.getParent();
if (parent instanceof GrClosableBlock) {
return false;
}
if (parent instanceof GrField && expr == ((GrField)parent).getInitializerGroovy()) {
return true;
}
if (parent instanceof GrExpression) {
return checkInFieldInitializer(((GrExpression)parent));
}
return false;
}
/**
* Inserts new variable declarations and replaces occurrences
*/
@Override
public GrVariable runRefactoring(@NotNull final GrIntroduceContext context, @NotNull final GroovyIntroduceVariableSettings settings) {
// Generating variable declaration
GrVariable insertedVar = processExpression(context, settings);
moveOffsetToPositionMarker(context.getEditor());
return insertedVar;
}
private void moveOffsetToPositionMarker(Editor editor) {
if (editor != null && getPositionMarker() != null) {
editor.getSelectionModel().removeSelection();
editor.getCaretModel().moveToOffset(getPositionMarker().getEndOffset());
}
}
@Override
protected GrInplaceVariableIntroducer getIntroducer(@NotNull GrIntroduceContext context, OccurrencesChooser.ReplaceChoice choice) {
final Ref<GrIntroduceContext> contextRef = Ref.create(context);
if (context.getStringPart() != null) {
extractStringPart(contextRef);
}
context = contextRef.get();
final GrStatement anchor = findAnchor(context, choice == OccurrencesChooser.ReplaceChoice.ALL);
if (anchor.getParent() instanceof GrControlStatement) {
addBraces(anchor, contextRef);
}
return new GrInplaceVariableIntroducer(getRefactoringName(), choice, contextRef.get()) {
@Override
protected GrVariable runRefactoring(GrIntroduceContext context, GroovyIntroduceVariableSettings settings, boolean processUsages) {
if (processUsages) {
return processExpression(context, settings);
}
else {
return addVariable(context, settings);
}
}
@Override
protected void performPostIntroduceTasks() {
super.performPostIntroduceTasks();
moveOffsetToPositionMarker(contextRef.get().getEditor());
}
};
}
private static GrVariable addVariable(@NotNull GrIntroduceContext context, @NotNull GroovyIntroduceVariableSettings settings) {
GrStatement anchor = findAnchor(context, settings.replaceAllOccurrences());
PsiElement parent = anchor.getParent();
assert parent instanceof GrStatementOwner;
GrVariableDeclaration generated = generateDeclaration(context, settings);
GrStatement declaration = ((GrStatementOwner)parent).addStatementBefore(generated, anchor);
declaration = (GrStatement)JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(declaration);
return ((GrVariableDeclaration)declaration).getVariables()[0];
}
@Override
protected void showScopeChooser(GrControlFlowOwner[] scopes, Pass<GrControlFlowOwner> callback, Editor editor) {
//todo do nothing right now
}
@NotNull
private static GrVariableDeclaration generateDeclaration(@NotNull GrIntroduceContext context,
@NotNull GroovyIntroduceVariableSettings settings) {
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(context.getProject());
final String[] modifiers = settings.isDeclareFinal() ? new String[]{PsiModifier.FINAL} : null;
final GrVariableDeclaration declaration =
factory.createVariableDeclaration(modifiers, "foo", settings.getSelectedType(), settings.getName());
generateInitializer(context, declaration.getVariables()[0]);
return declaration;
}
@NotNull
private GrVariable processExpression(@NotNull GrIntroduceContext context,
@NotNull GroovyIntroduceVariableSettings settings) {
GrVariableDeclaration varDecl = generateDeclaration(context, settings);
if (context.getStringPart() != null) {
final GrExpression ref = context.getStringPart().replaceLiteralWithConcatenation(DUMMY_NAME);
return doProcessExpression(context, settings, varDecl, new PsiElement[]{ref}, ref, true);
}
else {
final GrExpression expression = context.getExpression();
assert expression != null;
return doProcessExpression(context, settings, varDecl, context.getOccurrences(), expression, true);
}
}
private GrVariable doProcessExpression(@NotNull final GrIntroduceContext context,
@NotNull GroovyIntroduceVariableSettings settings,
@NotNull GrVariableDeclaration varDecl,
@NotNull PsiElement[] elements,
@NotNull GrExpression expression, boolean processUsages) {
return new GrIntroduceLocalVariableProcessor(context, settings, elements, expression, processUsages) {
@Override
protected void refreshPositionMarker(PsiElement e) {
GrIntroduceVariableHandler.this.refreshPositionMarker(context.getEditor().getDocument().createRangeMarker(e.getTextRange()));
}
}.processExpression(varDecl);
}
@NotNull
private static GrExpression generateInitializer(@NotNull GrIntroduceContext context,
@NotNull GrVariable variable) {
final GrExpression initializer = context.getStringPart() != null
? context.getStringPart().createLiteralFromSelected()
: context.getExpression();
final GrExpression dummyInitializer = variable.getInitializerGroovy();
assert dummyInitializer != null;
return dummyInitializer.replaceWithExpression(initializer, true);
}
void refreshPositionMarker(RangeMarker marker) {
myPosition = marker;
}
private RangeMarker getPositionMarker() {
return myPosition;
}
@NotNull
@Override
protected String getRefactoringName() {
return REFACTORING_NAME;
}
@NotNull
@Override
protected String getHelpID() {
return HelpID.INTRODUCE_VARIABLE;
}
@Override
@NotNull
protected GroovyIntroduceVariableDialog getDialog(@NotNull GrIntroduceContext context) {
final GroovyVariableValidator validator = new GroovyVariableValidator(context);
return new GroovyIntroduceVariableDialog(context, validator);
}
}