Skip to content

Commit

Permalink
feature: add helper for query variables (VariableReferenceFunction, V…
Browse files Browse the repository at this point in the history
…ariableScopeFunction) (#1114)

* feature: add VariableReferenceFunction

* test VariableReferenceFunction

* fix comments of VariableReferenceFunction

* feature: VariableScopeFunction

* test VariableScopeFunction

* add missing license header
  • Loading branch information
pvojtechovsky authored and surli committed Feb 1, 2017
1 parent e514e17 commit 8c35c98
Show file tree
Hide file tree
Showing 3 changed files with 237 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* 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.code.CtCatchVariable;
import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.reference.CtVariableReference;
import spoon.reflect.visitor.CtScanner;
import spoon.reflect.visitor.chain.CtConsumableFunction;
import spoon.reflect.visitor.chain.CtConsumer;

/**
* The mapping function, accepting {@link CtVariable}
* <ul>
* <li>CtLocalVariable - local variable declared in body
* <li>CtField - member field of an type
* <li>CtParameter - method parameter
* <li>CtCatchVariable - try - catch variable
* </ul>
* and returning all the {@link CtVariableReference}, which refers this variable
*/
public class VariableReferenceFunction implements CtConsumableFunction<CtVariable<?>> {

protected final Visitor visitor = new Visitor();
protected CtConsumer<Object> outputConsumer;

@Override
public void apply(CtVariable<?> variable, CtConsumer<Object> outputConsumer) {
this.outputConsumer = outputConsumer;
variable.accept(visitor);
}

private static final FieldReferenceFunction fieldReferenceFunction = new FieldReferenceFunction();
private static final LocalVariableReferenceFunction localVariableReferenceFunction = new LocalVariableReferenceFunction();
private static final ParameterReferenceFunction parameterReferenceFunction = new ParameterReferenceFunction();
private static final CatchVariableReferenceFunction catchVariableReferenceFunction = new CatchVariableReferenceFunction();

protected class Visitor extends CtScanner {
@Override
protected void enter(CtElement e) {
throw new SpoonException("Unsupported variable of type " + e.getClass().getName());
}
/**
* calls outputConsumer for each reference of the field
*/
@Override
public <T> void visitCtField(CtField<T> field) {
fieldReferenceFunction.apply(field, outputConsumer);
}

/**
* calls outputConsumer for each reference of the local variable
*/
@Override
public <T> void visitCtLocalVariable(CtLocalVariable<T> localVariable) {
localVariableReferenceFunction.apply(localVariable, outputConsumer);
}

/**
* calls outputConsumer for each reference of the parameter
*/
@Override
public <T> void visitCtParameter(CtParameter<T> parameter) {
parameterReferenceFunction.apply(parameter, outputConsumer);
}

/**
* calls outputConsumer for each reference of the catch variable
*/
@Override
public <T> void visitCtCatchVariable(CtCatchVariable<T> catchVariable) {
catchVariableReferenceFunction.apply(catchVariable, outputConsumer);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* 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.code.CtCatchVariable;
import spoon.reflect.code.CtLocalVariable;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtField;
import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.visitor.CtScanner;
import spoon.reflect.visitor.chain.CtConsumableFunction;
import spoon.reflect.visitor.chain.CtConsumer;

/**
* The mapping function, accepting {@link CtVariable}
* <ul>
* <li>CtLocalVariable - local variable declared in body
* <li>CtField - member field of an type
* <li>CtParameter - method parameter
* <li>CtCatchVariable - try - catch variable
* </ul>
* and returning all the CtElements, which exists in visibility scope of this variable.
*/
public class VariableScopeFunction implements CtConsumableFunction<CtVariable<?>> {

protected final Visitor visitor = new Visitor();
protected CtConsumer<Object> outputConsumer;

@Override
public void apply(CtVariable<?> variable, CtConsumer<Object> outputConsumer) {
this.outputConsumer = outputConsumer;
variable.accept(visitor);
}

private static final FieldScopeFunction fieldScopeFunction = new FieldScopeFunction();
private static final LocalVariableScopeFunction localVariableScopeFunction = new LocalVariableScopeFunction();
private static final ParameterScopeFunction parameterScopeFunction = new ParameterScopeFunction();
private static final CatchVariableScopeFunction catchVariableScopeFunction = new CatchVariableScopeFunction();

protected class Visitor extends CtScanner {
@Override
protected void enter(CtElement e) {
throw new SpoonException("Unsupported variable of type " + e.getClass().getName());
}
/**
* calls outputConsumer for each reference of the field
*/
@Override
public <T> void visitCtField(CtField<T> field) {
fieldScopeFunction.apply(field, outputConsumer);
}

/**
* calls outputConsumer for each reference of the local variable
*/
@Override
public <T> void visitCtLocalVariable(CtLocalVariable<T> localVariable) {
localVariableScopeFunction.apply(localVariable, outputConsumer);
}

/**
* calls outputConsumer for each reference of the parameter
*/
@Override
public <T> void visitCtParameter(CtParameter<T> parameter) {
parameterScopeFunction.apply(parameter, outputConsumer);
}

/**
* calls outputConsumer for each reference of the catch variable
*/
@Override
public <T> void visitCtCatchVariable(CtCatchVariable<T> catchVariable) {
catchVariableScopeFunction.apply(catchVariable, outputConsumer);
}
}
}
48 changes: 48 additions & 0 deletions src/test/java/spoon/test/query_function/QueryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,15 @@
import spoon.reflect.reference.CtVariableReference;
import spoon.reflect.visitor.chain.CtConsumableFunction;
import spoon.reflect.visitor.filter.CatchVariableReferenceFunction;
import spoon.reflect.visitor.filter.CatchVariableScopeFunction;
import spoon.reflect.visitor.filter.FieldReferenceFunction;
import spoon.reflect.visitor.filter.FieldScopeFunction;
import spoon.reflect.visitor.filter.LocalVariableReferenceFunction;
import spoon.reflect.visitor.filter.LocalVariableScopeFunction;
import spoon.reflect.visitor.filter.ParameterReferenceFunction;
import spoon.reflect.visitor.filter.ParameterScopeFunction;
import spoon.reflect.visitor.filter.VariableReferenceFunction;
import spoon.reflect.visitor.filter.VariableScopeFunction;
import spoon.test.query_function.testclasses.ClassC;
import spoon.test.query_function.testclasses.packageA.ClassA;
import spoon.test.query_function.testclasses.packageA.ClassB;
Expand All @@ -39,6 +45,7 @@
import java.util.Set;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

Expand Down Expand Up @@ -169,6 +176,47 @@ public void testFieldReferenceFunction() throws Exception {
}).list();
}

@Test
public void testVariableReferenceFunction() throws Exception {
//visits all the CtVariable elements whose name is "field" and search for all their references
//The test detects whether found references are correct by these two checks:
//1) the each found reference is on the left side of binary operator and on the right side there is unique reference identification number. Like: (field == 7)
//2) the model is searched for all variable references which has same identification number and counts them
//Then it checks that counted number of references and found number of references is same
factory.Package().getRootPackage().filterChildren((CtVariable<?> var)->{
if(var.getSimpleName().equals("field")) {
int value = getLiteralValue(var);
checkVariableAccess(var, value, new VariableReferenceFunction());
}
return false;
}).list();
}

@Test
public void testVariableScopeFunction() throws Exception {
//visits all the CtVariable elements whose name is "field" and search for all elements in their scopes
//Comparing with the result found by basic functions
List list = factory.Package().getRootPackage().filterChildren((CtVariable<?> var)->{
if(var.getSimpleName().equals("field")) {
CtElement[] real = var.map(new VariableScopeFunction()).list().toArray(new CtElement[0]);
if(var instanceof CtLocalVariable) {
assertArrayEquals(var.map(new LocalVariableScopeFunction()).list().toArray(new CtElement[0]), real);
} else if(var instanceof CtField) {
assertArrayEquals(var.map(new FieldScopeFunction()).list().toArray(new CtElement[0]), real);
} else if(var instanceof CtParameter) {
assertArrayEquals(var.map(new ParameterScopeFunction()).list().toArray(new CtElement[0]), real);
} else if(var instanceof CtCatchVariable) {
assertArrayEquals(var.map(new CatchVariableScopeFunction()).list().toArray(new CtElement[0]), real);
} else {
fail("Unexpected variable of type "+var.getClass().getName());
}
return true;
}
return false;
}).list();
assertTrue(list.size()>0);
}

private void checkVariableAccess(CtVariable<?> var, int value, CtConsumableFunction<?> query) {
class Context {
int classCount = 0;
Expand Down

0 comments on commit 8c35c98

Please sign in to comment.