blob: 297f3fa7770afecb7ed371d6c916c9edd9ec61c6 [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.ui.Messages;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.refactoring.HelpID;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.refactoring.util.RefactoringMessageUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.VisibilityUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.groovy.GroovyLanguage;
import org.jetbrains.plugins.groovy.codeStyle.GrReferenceAdjuster;
import org.jetbrains.plugins.groovy.lang.psi.GroovyPsiElementFactory;
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.GrVariableDeclaration;
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.lang.psi.api.statements.typedef.GrEnumTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrEnumConstantList;
import org.jetbrains.plugins.groovy.lang.psi.impl.synthetic.GroovyScriptClass;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
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 java.util.ArrayList;
/**
* @author Max Medvedev
*/
public class GrIntroduceConstantProcessor {
private final GrIntroduceContext context;
private final GrIntroduceConstantSettings settings;
public GrIntroduceConstantProcessor(GrIntroduceContext context, GrIntroduceConstantSettings settings) {
this.context = context;
this.settings = settings;
}
@Nullable
public GrField run() {
final PsiClass targetClass = settings.getTargetClass();
if (targetClass == null) return null;
if (checkErrors(targetClass)) {
return null;
}
final GrVariableDeclaration declaration = addDeclaration(targetClass);
final GrField field = (GrField)declaration.getVariables()[0];
GrVariable localVar = GrIntroduceHandlerBase.resolveLocalVar(context);
if (localVar != null) {
assert localVar.getInitializerGroovy() != null : "initializer should exist: " + localVar.getText();
GrIntroduceHandlerBase.deleteLocalVar(localVar);
if (settings.replaceAllOccurrences()) {
processOccurrences(field);
}
else {
replaceOccurrence(field, localVar.getInitializerGroovy(), isEscalateVisibility());
}
}
else if (context.getStringPart() != null) {
final GrExpression ref = context.getStringPart().replaceLiteralWithConcatenation(field.getName());
final PsiElement element = replaceOccurrence(field, ref, isEscalateVisibility());
updateCaretPosition(element);
}
else if (context.getExpression() != null) {
if (settings.replaceAllOccurrences()) {
processOccurrences(field);
}
else {
replaceOccurrence(field, context.getExpression(), isEscalateVisibility());
}
}
return field;
}
private void processOccurrences(GrField field) {
final PsiElement[] occurrences = context.getOccurrences();
GroovyRefactoringUtil.sortOccurrences(occurrences);
for (PsiElement occurrence : occurrences) {
replaceOccurrence(field, occurrence, isEscalateVisibility());
}
}
private void updateCaretPosition(PsiElement element) {
context.getEditor().getCaretModel().moveToOffset(element.getTextRange().getEndOffset());
context.getEditor().getSelectionModel().removeSelection();
}
protected GrVariableDeclaration addDeclaration(PsiClass targetClass) {
GrVariableDeclaration declaration = createField(targetClass);
final GrVariableDeclaration added;
if (targetClass instanceof GrEnumTypeDefinition) {
final GrEnumConstantList enumConstants = ((GrEnumTypeDefinition)targetClass).getEnumConstantList();
added = (GrVariableDeclaration)targetClass.addAfter(declaration, enumConstants);
}
else {
added = ((GrVariableDeclaration)targetClass.add(declaration));
}
JavaCodeStyleManager.getInstance(added.getProject()).shortenClassReferences(added);
return added;
}
protected boolean checkErrors(@NotNull PsiClass targetClass) {
String fieldName = settings.getName();
String errorString = check(targetClass, fieldName);
if (errorString != null) {
String message = RefactoringBundle.getCannotRefactorMessage(errorString);
CommonRefactoringUtil
.showErrorMessage(GrIntroduceConstantHandler.REFACTORING_NAME, message, HelpID.INTRODUCE_CONSTANT, context.getProject());
return true;
}
PsiField oldField = targetClass.findFieldByName(fieldName, true);
if (oldField != null) {
String message = RefactoringBundle.message("field.exists", fieldName, oldField.getContainingClass().getQualifiedName());
int answer = Messages
.showYesNoDialog(context.getProject(), message, GrIntroduceConstantHandler.REFACTORING_NAME, Messages.getWarningIcon());
if (answer != Messages.YES) {
return true;
}
}
return false;
}
@Nullable
private String check(@NotNull PsiClass targetClass, @Nullable final String fieldName) {
if (!GroovyLanguage.INSTANCE.equals(targetClass.getLanguage())) {
return GroovyRefactoringBundle.message("class.language.is.not.groovy");
}
if (fieldName == null || fieldName.isEmpty()) {
return RefactoringBundle.message("no.field.name.specified");
}
else if (!PsiNameHelper.getInstance(context.getProject()).isIdentifier(fieldName)) {
return RefactoringMessageUtil.getIncorrectIdentifierMessage(fieldName);
}
if (targetClass instanceof GroovyScriptClass) {
return GroovyRefactoringBundle.message("target.class.must.not.be.script");
}
return null;
}
private PsiElement replaceOccurrence(@NotNull GrField field, @NotNull PsiElement occurrence, boolean escalateVisibility) {
boolean isOriginal = occurrence == context.getExpression();
final GrReferenceExpression newExpr = createRefExpression(field, occurrence);
final PsiElement replaced = occurrence instanceof GrExpression
? ((GrExpression)occurrence).replaceWithExpression(newExpr, false)
: occurrence.replace(newExpr);
if (escalateVisibility) {
PsiUtil.escalateVisibility(field, replaced);
}
if (replaced instanceof GrReferenceExpression) {
GrReferenceAdjuster.shortenReference((GrReferenceExpression)replaced);
}
if (isOriginal) {
updateCaretPosition(replaced);
}
return replaced;
}
@NotNull
private static GrReferenceExpression createRefExpression(@NotNull GrField field, @NotNull PsiElement place) {
final PsiClass containingClass = field.getContainingClass();
assert containingClass != null;
final String qname = containingClass.getQualifiedName();
final String fieldName = field.getName();
final String refText = qname != null && !qname.equals(fieldName) ? qname + "." + fieldName : fieldName;
return GroovyPsiElementFactory.getInstance(place.getProject()).createReferenceExpressionFromText(refText, place);
}
@NotNull
private GrVariableDeclaration createField(PsiClass targetClass) {
final String name = settings.getName();
final PsiType type = settings.getSelectedType();
String[] modifiers = collectModifiers(targetClass);
final GroovyPsiElementFactory factory = GroovyPsiElementFactory.getInstance(context.getProject());
return factory.createFieldDeclaration(modifiers, name, getInitializer(), type);
}
@NotNull
protected GrExpression getInitializer() {
GrVariable var = GrIntroduceHandlerBase.resolveLocalVar(context);
GrExpression expression = context.getExpression();
if (var != null) {
return var.getInitializerGroovy();
}
else if (expression != null) {
return expression;
}
else {
return context.getStringPart().createLiteralFromSelected();
}
}
@NotNull
private String[] collectModifiers(PsiClass targetClass) {
String modifier = isEscalateVisibility() ? PsiModifier.PRIVATE : settings.getVisibilityModifier();
ArrayList<String> modifiers = new ArrayList<String>();
if (modifier!= null && !PsiModifier.PACKAGE_LOCAL.equals(modifier)) {
modifiers.add(modifier);
}
if (!targetClass.isInterface()) {
modifiers.add(PsiModifier.STATIC);
modifiers.add(PsiModifier.FINAL);
}
return ArrayUtil.toStringArray(modifiers);
}
private boolean isEscalateVisibility() {
return VisibilityUtil.ESCALATE_VISIBILITY.equals(settings.getVisibilityModifier());
}
}