blob: 1bb6a97cdb1cad75afff5406d8483da8bd65b9e1 [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.project.Project;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.plugins.groovy.lang.psi.GroovyFileBase;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.GrVariable;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.expressions.GrExpression;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrTypeDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringUtil;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
/**
* @author Maxim.Medvedev
*/
public class GrIntroduceValidatorEngine implements GrIntroduceHandlerBase.Validator {
private final GrIntroduceContext myContext;
private final ConflictReporter myReporter;
public GrIntroduceValidatorEngine(GrIntroduceContext context, ConflictReporter reporter) {
myContext = context;
myReporter = reporter;
}
@Override
public boolean isOK(GrIntroduceDialog dialog) {
final GrIntroduceSettings settings = dialog.getSettings();
if (settings == null) return false;
String varName = settings.getName();
boolean allOccurrences = settings.replaceAllOccurrences();
final MultiMap<PsiElement, String> conflicts = isOKImpl(varName, allOccurrences);
return conflicts.size() <= 0 || reportConflicts(conflicts, getProject());
}
private static boolean reportConflicts(final MultiMap<PsiElement, String> conflicts, final Project project) {
ConflictsDialog conflictsDialog = new ConflictsDialog(project, conflicts);
conflictsDialog.show();
return conflictsDialog.isOK();
}
private MultiMap<PsiElement, String> isOKImpl(String varName, boolean replaceAllOccurrences) {
PsiElement firstOccurrence = getFirstOccurrence(replaceAllOccurrences);
final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
assert varName != null;
final int offset = firstOccurrence.getTextRange().getStartOffset();
validateOccurrencesDown(myContext.getScope(), conflicts, varName, offset);
if (!(myContext.getScope() instanceof GroovyFileBase)) {
validateVariableOccurrencesUp(myContext.getScope(), conflicts, varName, offset);
}
if (replaceAllOccurrences) {
for (PsiElement element : myContext.getOccurrences()) {
if (element == firstOccurrence) continue;
validateVariableOccurrencesUp(element, conflicts, varName, element.getTextRange().getStartOffset());
}
}
return conflicts;
}
private PsiElement getFirstOccurrence(boolean replaceAllOccurrences) {
if (replaceAllOccurrences) {
if (myContext.getOccurrences().length > 0) {
GroovyRefactoringUtil.sortOccurrences(myContext.getOccurrences());
return myContext.getOccurrences()[0];
}
else {
return myContext.getPlace();
}
}
else {
final GrExpression expression = myContext.getExpression();
return expression != null ? expression : myContext.getStringPart().getLiteral();
}
}
/**
* Use for validator tests
*/
public String isOKTest(String varName, boolean allOccurences) {
MultiMap<PsiElement, String> list = isOKImpl(varName, allOccurences);
String result = "";
final String[] strings = ArrayUtil.toStringArray((Collection<String>)list.values());
Arrays.sort(strings, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
for (String s : strings) {
result = result + s.replaceAll("<b><code>", "").replaceAll("</code></b>", "") + "\n";
}
if (!list.isEmpty()) {
result = result.substring(0, result.length() - 1);
}
if (result.isEmpty()) {
result = "ok";
}
return result;
}
/**
* @param startElement Container to start checking conflicts from
* @param conflicts Conflict accumulator
* @param varName Variable name
* @param startOffset
*/
private void validateOccurrencesDown(@NotNull PsiElement startElement,
@NotNull MultiMap<PsiElement, String> conflicts,
@NotNull String varName,
double startOffset) {
PsiElement child = startElement.getFirstChild();
while (child != null) {
// Do not check defined classes, methods, closures and blocks before
if (child instanceof GrTypeDefinition ||
child instanceof GrMethod ||
GroovyRefactoringUtil.isAppropriateContainerForIntroduceVariable(child) &&
child.getTextRange().getEndOffset() < startOffset) {
myReporter.check(child, conflicts, varName);
child = child.getNextSibling();
continue;
}
if (child instanceof GrVariable) {
myReporter.check(child, conflicts, varName);
validateOccurrencesDown(child, conflicts, varName, startOffset);
}
else {
validateOccurrencesDown(child, conflicts, varName, startOffset);
}
child = child.getNextSibling();
}
}
private void validateVariableOccurrencesUp(PsiElement startElement,
MultiMap<PsiElement, String> conflicts,
@NotNull String varName,
double startOffset) {
PsiElement prevSibling = startElement.getPrevSibling();
while (prevSibling != null) {
if (!(GroovyRefactoringUtil.isAppropriateContainerForIntroduceVariable(prevSibling) &&
prevSibling.getTextRange().getEndOffset() < startOffset)) {
validateOccurrencesDown(prevSibling, conflicts, varName, startOffset);
}
prevSibling = prevSibling.getPrevSibling();
}
PsiElement parent = startElement.getParent();
// Do not check context out of method, type definition and directories
if (parent == null ||
parent instanceof GrMethod ||
parent instanceof GrTypeDefinition ||
parent instanceof GroovyFileBase ||
parent instanceof PsiDirectory) {
return;
}
validateVariableOccurrencesUp(parent, conflicts, varName, startOffset);
}
/**
* Validates name to be suggested in context
*/
@Override
public String validateName(String name, boolean increaseNumber) {
String result = name;
if (!isOKImpl(name, true).isEmpty() && !increaseNumber || name.isEmpty()) {
return "";
}
int i = 1;
while (!isOKImpl(result, true).isEmpty()) {
result = name + i;
i++;
}
return result;
}
@Override
public Project getProject() {
return myContext.getProject();
}
}