Skip to content

Commit

Permalink
feature ChangeLocalVariableName refactoring + helpers
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky committed Jan 17, 2017
1 parent 76d275d commit aa5badd
Show file tree
Hide file tree
Showing 11 changed files with 479 additions and 112 deletions.
89 changes: 89 additions & 0 deletions src/main/java/spoon/refactoring/AbstractRenameRefactor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package spoon.refactoring;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import spoon.SpoonException;
import spoon.reflect.declaration.CtNamedElement;
import spoon.reflect.reference.CtReference;
import spoon.reflect.visitor.chain.CtConsumer;

public abstract class AbstractRenameRefactor<T extends CtNamedElement> implements Refactor {
public static final Pattern javaIdentifierRE = Pattern.compile("\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*");

protected T target;
protected String newName;
protected Pattern newNameValidationRE;

protected AbstractRenameRefactor(Pattern newNameValidationRE) {
this.newNameValidationRE = newNameValidationRE;
}

@Override
public void refactor() {
List<Issue> issues = getIssues();
if (issues.isEmpty() == false) {
throw new SpoonException("Refactoring cannot be processed. There are issues: " + issues.toString());
}
refactorNoCheck();
}

protected void refactorNoCheck() {
forEachReference(new CtConsumer<CtReference>() {
@Override
public void accept(CtReference t) {
t.setSimpleName(AbstractRenameRefactor.this.newName);
}
});
target.setSimpleName(newName);
}

protected abstract void forEachReference(CtConsumer<CtReference> consumer);

@Override
public List<Issue> getIssues() {
List<Issue> issues = new ArrayList<>();
detectIssues(issues);
return issues;
}

protected void detectIssues(List<Issue> issues) {
checkNewNameIsValid(issues);
detectNameConflicts(issues);
}

/**
* checks whether {@link #newName} is valid java identifier
* @param issues
*/
protected void checkNewNameIsValid(List<Issue> issues) {
}

protected void detectNameConflicts(List<Issue> issues) {
}


protected boolean isJavaIdentifier(String name) {
return javaIdentifierRE.matcher(name).matches();
}

public T getTarget() {
return target;
}

public void setTarget(T target) {
this.target = target;
}

public String getNewName() {
return newName;
}

public void setNewName(String newName) {
if (newNameValidationRE != null && newNameValidationRE.matcher(newName).matches() == false) {
throw new SpoonException("New name \"" + newName + "\" is not valid name");
}
this.newName = newName;
}
}
68 changes: 68 additions & 0 deletions src/main/java/spoon/refactoring/ChangeLocalVariableName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package spoon.refactoring;

import java.util.List;
import java.util.regex.Pattern;

import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.code.CtStatementList;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.reference.CtReference;
import spoon.reflect.visitor.Filter;
import spoon.reflect.visitor.chain.CtConsumer;
import spoon.reflect.visitor.chain.CtQuery;
import spoon.reflect.visitor.query.VariableReferenceQuery;
import spoon.reflect.visitor.query.VariableScopeQuery;

public class ChangeLocalVariableName extends AbstractRenameRefactor<CtLocalVariable<?>> {

public static Pattern validVariableNameRE = javaIdentifierRE;

public ChangeLocalVariableName() {
super(validVariableNameRE);
}

@Override
protected void forEachReference(CtConsumer<CtReference> consumer) {
getTarget().map(new VariableReferenceQuery()).forEach(consumer);
}

@Override
protected void detectNameConflicts(List<Issue> issues) {
//search for conflicts in scope of parent statement list
CtStatementList l_scope = getTarget().getParent(CtStatementList.class);
//the helper query which searches in scope of input variable for declaration of target variable
CtQuery scopeQuery = target.getFactory().createQuery().map(new VariableScopeQuery().setFilter(new Filter<CtElement>() {
public boolean matches(CtElement element) {
return element == target;
};
}));
//search for all variable declarations with newName - potential conflict
l_scope.filterChildren(new Filter<CtLocalVariable<?>>() {
@Override
public boolean matches(CtLocalVariable<?> var) {
if (newName.equals(var.getSimpleName())) {
/*
* visit all elements in scope of input variable
* and match them if they are target variable
*/
return scopeQuery.setInput(var).list().size() > 0;
}
return false;
}
})
//called for each variable declaration which is in conflict with newName
.forEach(new CtConsumer<CtLocalVariable<?>>() {
@Override
public void accept(CtLocalVariable<?> conflictVar) {
Issue issue = createNameConflictIssue(conflictVar);
if (issue != null) {
issues.add(issue);
}
}
});
}

protected Issue createNameConflictIssue(CtLocalVariable<?> conflictVar) {
return new IssueImpl("Local variable with name " + conflictVar.getSimpleName() + " already exists.");
}
}
21 changes: 21 additions & 0 deletions src/main/java/spoon/refactoring/Issue.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* Copyright (C) 2006-2016 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.refactoring;

public interface Issue {
String getMessage();
}
20 changes: 20 additions & 0 deletions src/main/java/spoon/refactoring/IssueImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package spoon.refactoring;

public class IssueImpl implements Issue {

private String message;

public IssueImpl(String message) {
this.message = message;
}

@Override
public String getMessage() {
return message;
}

@Override
public String toString() {
return getMessage();
}
}
27 changes: 27 additions & 0 deletions src/main/java/spoon/refactoring/Refactor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (C) 2006-2016 INRIA and contributors
* Spoon - http://spoon.gforge.inria.fr/
*
* This software is governed by the CeCILL-C License under French law and
* abiding by the rules of distribution of free software. You can use, modify
* and/or redistribute the software under the terms of the CeCILL-C license as
* circulated by CEA, CNRS and INRIA at http://www.cecill.info.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
*
* The fact that you are presently reading this means that you have had
* knowledge of the CeCILL-C license and that you accept its terms.
*/
package spoon.refactoring;

import java.util.List;

/**
*
*/
public interface Refactor {
List<Issue> getIssues();
void refactor();
}
7 changes: 3 additions & 4 deletions src/main/java/spoon/refactoring/Refactoring.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@

import spoon.SpoonException;
import spoon.reflect.code.CtBlock;
import spoon.reflect.code.CtCatchVariable;
import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtExecutable;
Expand Down Expand Up @@ -59,7 +58,7 @@ public boolean matches(CtTypeReference<?> reference) {
}
});
}

/**
* Changes the simple name of the `variable` to `name`
* @param variable - the to be change variable
Expand All @@ -71,11 +70,11 @@ public static void changeVariableName(CtVariable<?> variable, String name) {

protected static class Visitor extends CtScanner {
String name;

public Visitor(String name) {
this.name = name;
}

@Override
protected void enter(CtElement e) {
throw new SpoonException("Renaming of " + e.getClass().getName() + " is not supported.");
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/spoon/reflect/visitor/chain/CtQueryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,12 @@ private String getName() {
}

private boolean isFailOnCCE() {
return getStep().isFailOnCCE();
AbstractStep step = getStep();
if (step == null) {
//it is final consumer. Never throw CCE on final forEach consumer
return false;
}
return step.isFailOnCCE();
}

private AbstractStep getStep() {
Expand Down
Loading

0 comments on commit aa5badd

Please sign in to comment.