Skip to content

Commit

Permalink
feature: search for CtVariableReference in defined scope
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky committed Mar 11, 2017
1 parent b715c39 commit da240cd
Show file tree
Hide file tree
Showing 9 changed files with 276 additions and 70 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* Copyright (C) 2006-2017 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.reflect.visitor.filter;

import spoon.SpoonException;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.reference.CtVariableReference;
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 mapping function expects a {@link CtVariable} as input
* and returns all {@link CtVariableReference}s, which refers this input.
* It is used to implement {@link LocalVariableReferenceFunction}, {@link ParameterReferenceFunction}, {@link CatchVariableReferenceFunction}
*/
abstract class AbstractVariableReferenceFunction implements CtConsumableFunction<CtElement> {

final CtVariable<?> targetVariable;
final Class<?> variableClass;
final Class<?> variableReferenceClass;

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

/**
* This constructor allows to define target variable - the one for which this function will search for.
* In such case the input of mapping function represents the searching scope
* @param variable - the parameter declaration which is searched in scope of input element
*/
protected AbstractVariableReferenceFunction(Class<?> variableClass, Class<?> variableReferenceClass, CtVariable<?> variable) {
this.variableClass = variableClass;
this.variableReferenceClass = variableReferenceClass;
this.targetVariable = variable;
}

@Override
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();
CtQuery scopeQuery;
if (scope == variable) {
//we are starting search from local variable declaration
scopeQuery = createScopeQuery(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) {
//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
//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;
}
}
return false;
}
})
.forEach(outputConsumer);
}

protected 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;
}
}

protected abstract CtQuery createScopeQuery(CtElement scope, Context context);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
package spoon.reflect.visitor.filter;

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

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

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

public CatchVariableReferenceFunction(CtCatchVariable<?> catchVariable) {
super(CtCatchVariable.class, CtCatchVariableReference.class, catchVariable);
}

@Override
public void apply(CtCatchVariable<?> localVariable, CtConsumer<Object> outputConsumer) {
localVariable
.map(new CatchVariableScopeFunction())
.select(new DirectReferenceFilter<CtCatchVariableReference<?>>(localVariable.getReference()))
.forEach(outputConsumer);
protected CtQuery createScopeQuery(CtElement scope, Context context) {
return scope.map(new CatchVariableScopeFunction(context));
}
}
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 @@ -18,11 +18,8 @@

import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtType;
import spoon.reflect.reference.CtLocalVariableReference;
import spoon.reflect.visitor.Filter;
import spoon.reflect.visitor.chain.CtConsumableFunction;
import spoon.reflect.visitor.chain.CtConsumer;
import spoon.reflect.visitor.chain.CtQuery;

/**
* This Query expects a {@link CtLocalVariable} as input
Expand All @@ -37,41 +34,24 @@
* }
* </pre>
*/
public class LocalVariableReferenceFunction implements CtConsumableFunction<CtLocalVariable<?>> {
public class LocalVariableReferenceFunction extends AbstractVariableReferenceFunction {

public LocalVariableReferenceFunction() {
super(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) {
super(CtLocalVariable.class, CtLocalVariableReference.class, localVariable);
}

@Override
public void apply(final CtLocalVariable<?> localVariable, CtConsumer<Object> outputConsumer) {
final String simpleName = localVariable.getSimpleName();
class Context {
boolean hasLocalType = false;
}
final Context context = new Context();
localVariable
.map(new LocalVariableScopeFunction())
.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) {
//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();
}
return true;
}
}
return false;
}
})
.forEach(outputConsumer);
protected CtQuery createScopeQuery(CtElement scope, Context context) {
return scope.map(new LocalVariableScopeFunction(context));
}
}
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);
}
}
Loading

0 comments on commit da240cd

Please sign in to comment.