blob: f7320b93561dbdf30a457f450bff78c58dc9348d [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.constant;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.introduce.inplace.OccurrencesChooser;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.lang.psi.GroovyRecursiveElementVisitor;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrField;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
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.expressions.GrReferenceExpression;
import org.jetbrains.plugins.groovy.refactoring.GrRefactoringError;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle;
import org.jetbrains.plugins.groovy.refactoring.introduce.*;
import java.util.*;
/**
* @author Maxim.Medvedev
*/
public class GrIntroduceConstantHandler extends GrIntroduceFieldHandlerBase<GrIntroduceConstantSettings> {
public static final String REFACTORING_NAME = "Introduce Constant";
@NotNull
@Override
protected String getRefactoringName() {
return REFACTORING_NAME;
}
@NotNull
@Override
protected String getHelpID() {
return HelpID.INTRODUCE_CONSTANT;
}
@Override
protected void checkExpression(@NotNull GrExpression selectedExpr) {
GrVariable variable = GrIntroduceHandlerBase.resolveLocalVar(selectedExpr);
if (variable != null) {
checkVariable(variable);
}
else {
selectedExpr.accept(new ConstantChecker(selectedExpr, selectedExpr));
}
}
@Override
protected void checkVariable(@NotNull GrVariable variable) throws GrRefactoringError {
final GrExpression initializer = variable.getInitializerGroovy();
if (initializer == null) {
throw new GrRefactoringError(RefactoringBundle.message("variable.does.not.have.an.initializer", variable.getName()));
}
checkExpression(initializer);
}
@Override
protected void checkStringLiteral(@NotNull StringPartInfo info) throws GrRefactoringError {
//todo
}
@Override
protected void checkOccurrences(@NotNull PsiElement[] occurrences) {
if (hasLhs(occurrences)) {
throw new GrRefactoringError(GroovyRefactoringBundle.message("selected.variable.is.used.for.write"));
}
}
@Nullable
public static PsiClass findContainingClass(GrIntroduceContext context) {
return (PsiClass)context.getScope();
}
@NotNull
@Override
protected GrIntroduceDialog<GrIntroduceConstantSettings> getDialog(@NotNull GrIntroduceContext context) {
return new GrIntroduceConstantDialog(context, findContainingClass(context));
}
@Override
public GrField runRefactoring(@NotNull GrIntroduceContext context, @NotNull GrIntroduceConstantSettings settings) {
return new GrIntroduceConstantProcessor(context, settings).run();
}
@Override
protected GrAbstractInplaceIntroducer<GrIntroduceConstantSettings> getIntroducer(@NotNull GrIntroduceContext context, @NotNull OccurrencesChooser.ReplaceChoice choice) {
final Ref<GrIntroduceContext> contextRef = Ref.create(context);
if (context.getStringPart() != null) {
extractStringPart(contextRef);
}
return new GrInplaceConstantIntroducer(contextRef.get(), choice);
}
@NotNull
@Override
protected Map<OccurrencesChooser.ReplaceChoice, List<Object>> getOccurrenceOptions(@NotNull GrIntroduceContext context) {
HashMap<OccurrencesChooser.ReplaceChoice, List<Object>> map = ContainerUtil.newLinkedHashMap();
GrVariable localVar = resolveLocalVar(context);
if (localVar != null) {
map.put(OccurrencesChooser.ReplaceChoice.ALL, Arrays.<Object>asList(context.getOccurrences()));
return map;
}
if (context.getExpression() != null) {
map.put(OccurrencesChooser.ReplaceChoice.NO, Collections.<Object>singletonList(context.getExpression()));
}
else if (context.getStringPart() != null) {
map.put(OccurrencesChooser.ReplaceChoice.NO, Collections.<Object>singletonList(context.getStringPart()));
}
PsiElement[] occurrences = context.getOccurrences();
if (occurrences.length > 1) {
map.put(OccurrencesChooser.ReplaceChoice.ALL, Arrays.<Object>asList(occurrences));
}
return map;
}
private static class ConstantChecker extends GroovyRecursiveElementVisitor {
private final PsiElement scope;
private final GrExpression expr;
@Override
public void visitReferenceExpression(GrReferenceExpression referenceExpression) {
final PsiElement resolved = referenceExpression.resolve();
if (resolved instanceof PsiVariable) {
if (!isStaticFinalField((PsiVariable)resolved)) {
if (expr instanceof GrClosableBlock) {
if (!PsiTreeUtil.isContextAncestor(scope, resolved, true)) {
throw new GrRefactoringError(GroovyRefactoringBundle.message("closure.uses.external.variables"));
}
}
else {
throw new GrRefactoringError(RefactoringBundle.message("selected.expression.cannot.be.a.constant.initializer"));
}
}
}
else if (resolved instanceof PsiMethod && ((PsiMethod)resolved).getContainingClass() != null) {
final GrExpression qualifier = referenceExpression.getQualifierExpression();
if (qualifier == null ||
(qualifier instanceof GrReferenceExpression && ((GrReferenceExpression)qualifier).resolve() instanceof PsiClass)) {
if (!((PsiMethod)resolved).hasModifierProperty(PsiModifier.STATIC)) {
throw new GrRefactoringError(RefactoringBundle.message("selected.expression.cannot.be.a.constant.initializer"));
}
}
}
}
private static boolean isStaticFinalField(PsiVariable var) {
return var instanceof PsiField && var.hasModifierProperty(PsiModifier.FINAL) && var.hasModifierProperty(PsiModifier.STATIC);
}
@Override
public void visitClosure(GrClosableBlock closure) {
if (closure == expr) {
super.visitClosure(closure);
}
else {
closure.accept(new ConstantChecker(closure, scope));
}
}
private ConstantChecker(GrExpression expr, PsiElement expressionScope) {
scope = expressionScope;
this.expr = expr;
}
}
}