Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

review 1: feature: search for CtVariableReference in defined scope #1193

Merged
merged 1 commit into from
Mar 17, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

import spoon.reflect.code.CtCatchVariable;
import spoon.reflect.reference.CtCatchVariableReference;
import spoon.reflect.visitor.chain.CtConsumableFunction;
import spoon.reflect.visitor.chain.CtConsumer;

/**
* This Query expects a {@link CtCatchVariable} as input
Expand All @@ -34,16 +32,13 @@
* }
* </pre>
*/
public class CatchVariableReferenceFunction implements CtConsumableFunction<CtCatchVariable<?>> {
public class CatchVariableReferenceFunction extends LocalVariableReferenceFunction {

public CatchVariableReferenceFunction() {
super(CtCatchVariable.class, CtCatchVariableReference.class);
}

@Override
public void apply(CtCatchVariable<?> localVariable, CtConsumer<Object> outputConsumer) {
localVariable
.map(new CatchVariableScopeFunction())
.select(new DirectReferenceFilter<CtCatchVariableReference<?>>(localVariable.getReference()))
.forEach(outputConsumer);
public CatchVariableReferenceFunction(CtCatchVariable<?> catchVariable) {
super(CtCatchVariable.class, CtCatchVariableReference.class, catchVariable);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import spoon.reflect.code.CtCatchVariable;
import spoon.reflect.visitor.chain.CtConsumableFunction;
import spoon.reflect.visitor.chain.CtConsumer;
import spoon.reflect.visitor.chain.CtScannerListener;

/**
* This Query expects a {@link CtCatchVariable} as input
Expand All @@ -39,12 +40,20 @@
* </pre>
*/
public class CatchVariableScopeFunction implements CtConsumableFunction<CtCatchVariable<?>> {
private final CtScannerListener listener;

public CatchVariableScopeFunction() {
this.listener = null;
}
public CatchVariableScopeFunction(CtScannerListener queryListener) {
this.listener = queryListener;
}

@Override
public void apply(CtCatchVariable<?> catchVariable, CtConsumer<Object> outputConsumer) {
catchVariable.getParent(CtCatch.class).getBody().filterChildren(null).forEach(outputConsumer);
catchVariable
.getParent(CtCatch.class).getBody()
.map(new CtScannerFunction().setListener(this.listener))
.forEach(outputConsumer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/
package spoon.reflect.visitor.filter;

import spoon.SpoonException;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtField;
import spoon.reflect.reference.CtFieldReference;
import spoon.reflect.visitor.chain.CtConsumableFunction;
Expand All @@ -34,14 +36,32 @@
* }
* </pre>
*/
public class FieldReferenceFunction implements CtConsumableFunction<CtField<?>> {
public class FieldReferenceFunction implements CtConsumableFunction<CtElement> {
private final CtField<?> field;

public FieldReferenceFunction() {
this.field = null;
}

public FieldReferenceFunction(CtField<?> field) {
this.field = field;
}

@Override
public void apply(CtField<?> field, CtConsumer<Object> outputConsumer) {
field.getFactory().getModel().getRootPackage()
public void apply(CtElement fieldOrScope, CtConsumer<Object> outputConsumer) {
CtElement scope;
CtField<?> field = this.field;
if (field == null) {
if (fieldOrScope instanceof CtField) {
field = (CtField<?>) fieldOrScope;
} else {
throw new SpoonException("The input of FieldReferenceFunction must be a CtField but is " + fieldOrScope.getClass().getSimpleName());
}
scope = field.getFactory().getModel().getRootPackage();
} else {
scope = fieldOrScope;
}
scope
.filterChildren(new DirectReferenceFilter<CtFieldReference<?>>(field.getReference()))
.forEach(outputConsumer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,25 @@
*/
package spoon.reflect.visitor.filter;

import spoon.SpoonException;
import spoon.reflect.code.CtCatchVariable;
import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.reference.CtLocalVariableReference;
import spoon.reflect.reference.CtVariableReference;
import spoon.reflect.visitor.CtAbstractVisitor;
import spoon.reflect.visitor.Filter;
import spoon.reflect.visitor.chain.CtConsumableFunction;
import spoon.reflect.visitor.chain.CtConsumer;
import spoon.reflect.visitor.chain.CtQuery;
import spoon.reflect.visitor.chain.CtScannerListener;
import spoon.reflect.visitor.chain.ScanningMode;

/**
* This Query expects a {@link CtLocalVariable} as input
* This mapping function expects a {@link CtLocalVariable} as input
* and returns all {@link CtLocalVariableReference}s, which refers this input.
* <br>
* Usage:<br>
Expand All @@ -37,35 +46,93 @@
* }
* </pre>
*/
public class LocalVariableReferenceFunction implements CtConsumableFunction<CtLocalVariable<?>> {
public class LocalVariableReferenceFunction implements CtConsumableFunction<CtElement> {
final CtVariable<?> targetVariable;
final Class<?> variableClass;
final Class<?> variableReferenceClass;

public LocalVariableReferenceFunction() {
this(CtLocalVariable.class, CtLocalVariableReference.class);
}

/**
* This constructor allows to define input local variable - the one for which this function will search for.
* In such case the input of mapping function represents the scope
* where this local variable is searched for.
* @param localVariable - the local variable declaration which is searched in scope of input element of this mapping function.
*/
public LocalVariableReferenceFunction(CtLocalVariable<?> localVariable) {
this(CtLocalVariable.class, CtLocalVariableReference.class, localVariable);
}

LocalVariableReferenceFunction(Class<?> variableClass, Class<?> variableReferenceClass) {
this.variableClass = variableClass;
this.variableReferenceClass = variableReferenceClass;
this.targetVariable = null;
}

LocalVariableReferenceFunction(Class<?> variableClass, Class<?> variableReferenceClass, CtVariable<?> variable) {
this.variableClass = variableClass;
this.variableReferenceClass = variableReferenceClass;
this.targetVariable = variable;
}

@Override
public void apply(final CtLocalVariable<?> localVariable, CtConsumer<Object> outputConsumer) {
final String simpleName = localVariable.getSimpleName();
class Context {
boolean hasLocalType = false;
public void apply(final CtElement scope, CtConsumer<Object> outputConsumer) {
CtVariable<?> var = targetVariable;
if (var == null) {
if (variableClass.isInstance(scope)) {
var = (CtVariable<?>) scope;
} else {
throw new SpoonException("The input of " + getClass().getSimpleName() + " must be a " + variableClass.getSimpleName() + " but is " + scope.getClass().getSimpleName());
}
}
final CtVariable<?> variable = var;
final String simpleName = variable.getSimpleName();
//the context which knows whether we are scanning in scope of local type or not
final Context context = new Context();
localVariable
.map(new LocalVariableScopeFunction())
.select(new Filter<CtElement>() {
CtQuery scopeQuery;
if (scope == variable) {
//we are starting search from local variable declaration
scopeQuery = createScopeQuery(variable, scope, context);
} else {
//we are starting search later, somewhere deep in scope of variable declaration
final CtElement variableParent = variable.getParent();
/*
* search in parents of searching scope for the variableParent
* 1) to check that scope is a child of variableParent
* 2) to detect if there is an local class between variable declaration and scope
*/
if (scope.map(new ParentFunction()).select(new Filter<CtElement>() {
@Override
public boolean matches(CtElement element) {
if (element instanceof CtType) {
context.hasLocalType = true;
} else if (element instanceof CtLocalVariableReference<?>) {
CtLocalVariableReference<?> localVarRef = (CtLocalVariableReference<?>) element;
if (simpleName.equals(localVarRef.getSimpleName())) {
//we have found a variable reference in visibility scope of localVariable
if (context.hasLocalType) {
//detected that the search scope is in local class declared in visibility scope of variable
context.nrTypes++;
}
return variableParent == element;
}
}).first() == null) {
//the scope is not under children of localVariable
throw new SpoonException("Cannot search for references of variable in wrong scope.");
}
//search in all children of the scope element
scopeQuery = scope.map(new CtScannerFunction().setListener(context));
}
scopeQuery.select(new Filter<CtElement>() {
@Override
public boolean matches(CtElement element) {
if (variableReferenceClass.isInstance(element)) {
CtVariableReference<?> varRef = (CtVariableReference<?>) element;
if (simpleName.equals(varRef.getSimpleName())) {
//we have found a variable reference of required type in visibility scope of targetVariable
if (context.hasLocalType()) {
//there exists a local type in visibility scope of this variable declaration
//the variable declarations in scope of this local class may shadow input localVariable
//so finally check that there is no other localVariable, which shadows the input localVariable
return localVariable == localVarRef.getDeclaration();
//another variable declarations in scope of this local class may shadow input localVariable
//so finally check that found variable reference is really a reference to target variable
return variable == varRef.getDeclaration();
}
//else we can be sure that found reference is reference to variable
return true;
}
}
Expand All @@ -74,4 +141,60 @@ public boolean matches(CtElement element) {
})
.forEach(outputConsumer);
}

private static class Context implements CtScannerListener {
int nrTypes = 0;

@Override
public ScanningMode enter(CtElement element) {
if (element instanceof CtType) {
nrTypes++;
}
return ScanningMode.NORMAL;
}

@Override
public void exit(CtElement element) {
if (element instanceof CtType) {
nrTypes--;
}
}
boolean hasLocalType() {
return nrTypes > 0;
}
}

private static final class QueryCreator extends CtAbstractVisitor {
CtElement scope;
CtScannerListener listener;
CtQuery query;

QueryCreator(CtElement scope, CtScannerListener listener) {
super();
this.scope = scope;
this.listener = listener;
}

@Override
public <T> void visitCtLocalVariable(CtLocalVariable<T> localVariable) {
query = scope.map(new LocalVariableScopeFunction(listener));
}
@Override
public <T> void visitCtCatchVariable(CtCatchVariable<T> catchVariable) {
query = scope.map(new CatchVariableScopeFunction(listener));
}
@Override
public <T> void visitCtParameter(CtParameter<T> parameter) {
query = scope.map(new ParameterScopeFunction(listener));
}
}

private CtQuery createScopeQuery(CtVariable<?> variable, CtElement scope, Context context) {
QueryCreator qc = new QueryCreator(scope, context);
variable.accept(qc);
if (qc.query == null) {
throw new SpoonException("Unexpected type of variable: " + variable.getClass().getName());
}
return qc.query;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@
package spoon.reflect.visitor.filter;

import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.visitor.Filter;
import spoon.reflect.visitor.chain.CtConsumableFunction;
import spoon.reflect.visitor.chain.CtConsumer;
import spoon.reflect.visitor.chain.CtScannerListener;

/**
* This Query expects a {@link CtLocalVariable} as input
Expand All @@ -38,12 +41,28 @@
* </pre>
*/
public class LocalVariableScopeFunction implements CtConsumableFunction<CtLocalVariable<?>> {
private final CtScannerListener listener;

public LocalVariableScopeFunction() {
this.listener = null;
}

public LocalVariableScopeFunction(CtScannerListener queryListener) {
this.listener = queryListener;
}

@Override
public void apply(CtLocalVariable<?> localVariable, CtConsumer<Object> outputConsumer) {
localVariable.map(new SiblingsFunction().mode(SiblingsFunction.Mode.NEXT).includingSelf(true)).filterChildren(null).forEach(outputConsumer);
public void apply(final CtLocalVariable<?> localVariable, CtConsumer<Object> outputConsumer) {
localVariable
.map(new SiblingsFunction().mode(SiblingsFunction.Mode.NEXT).includingSelf(true))
.map(new CtScannerFunction().setListener(this.listener))
.select(new Filter<CtElement>() {
@Override
public boolean matches(CtElement element) {
//ignore localVariable itself
return localVariable != element;
}
})
.forEach(outputConsumer);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@

import spoon.reflect.declaration.CtParameter;
import spoon.reflect.reference.CtParameterReference;
import spoon.reflect.visitor.chain.CtConsumableFunction;
import spoon.reflect.visitor.chain.CtConsumer;

/**
* This Query expects a {@link CtParameter} as input
Expand All @@ -34,16 +32,18 @@
* }
* </pre>
*/
public class ParameterReferenceFunction implements CtConsumableFunction<CtParameter<?>> {
public class ParameterReferenceFunction extends LocalVariableReferenceFunction {

public ParameterReferenceFunction() {
super(CtParameter.class, CtParameterReference.class);
}

@Override
public void apply(CtParameter<?> parameter, CtConsumer<Object> outputConsumer) {
parameter
.map(new ParameterScopeFunction())
.select(new DirectReferenceFilter<CtParameterReference<?>>(parameter.getReference()))
.forEach(outputConsumer);
/**
* This constructor allows to define target parameter - the one for which this function will search for.
* In such case the input of mapping function represents the searching scope
* @param parameter - the parameter declaration which is searched in scope of input element
*/
public ParameterReferenceFunction(CtParameter<?> parameter) {
super(CtParameter.class, CtParameterReference.class, parameter);
}
}
Loading