| /* |
| * 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()); |
| } |
| |
| } |