-
-
Notifications
You must be signed in to change notification settings - Fork 343
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature: Add filtering helpers (ParameterReferenceFunction, Parameter…
…ScopeFunction) (#1136) * test - common code shared by all variable based reference functions * feature: add ParameterReferenceFunction, ParameterScopeFunction * test: ParameterReferenceFunction, rameterScopeFunction
- Loading branch information
1 parent
e9bcf09
commit a40f86b
Showing
4 changed files
with
495 additions
and
0 deletions.
There are no files selected for viewing
49 changes: 49 additions & 0 deletions
49
src/main/java/spoon/reflect/visitor/filter/ParameterReferenceFunction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
/** | ||
* 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.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 | ||
* and returns all {@link CtParameterReference}s, which refers this input. | ||
* <br> | ||
* Usage:<br> | ||
* <pre> {@code | ||
* CtParameter param = ...; | ||
* param | ||
* .map(new ParameterReferenceFunction()) | ||
* .forEach((CtParameterReference ref)->...process references...); | ||
* } | ||
* </pre> | ||
*/ | ||
public class ParameterReferenceFunction implements CtConsumableFunction<CtParameter<?>> { | ||
|
||
public ParameterReferenceFunction() { | ||
} | ||
|
||
@Override | ||
public void apply(CtParameter<?> parameter, CtConsumer<Object> outputConsumer) { | ||
parameter | ||
.map(new ParameterScopeFunction()) | ||
.select(new DirectReferenceFilter<CtParameterReference<?>>(parameter.getReference())) | ||
.forEach(outputConsumer); | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
src/main/java/spoon/reflect/visitor/filter/ParameterScopeFunction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
/** | ||
* 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.reflect.declaration.CtExecutable; | ||
import spoon.reflect.declaration.CtParameter; | ||
import spoon.reflect.visitor.chain.CtConsumableFunction; | ||
import spoon.reflect.visitor.chain.CtConsumer; | ||
|
||
/** | ||
* This Query expects a {@link CtParameter} as input | ||
* and returns all CtElements, | ||
* which are in visibility scope of that parameter. | ||
* In other words, it returns all elements, | ||
* which might be reference to that parameter. | ||
* <br> | ||
* It can be used to search for variable declarations or | ||
* variable references which might be in name conflict with input parameter. | ||
* <br> | ||
* Usage:<br> | ||
* <pre> {@code | ||
* CtParameter param = ...; | ||
* param.map(new ParameterScopeFunction()).forEach(...process result...); | ||
* } | ||
* </pre> | ||
*/ | ||
public class ParameterScopeFunction implements CtConsumableFunction<CtParameter<?>> { | ||
|
||
public ParameterScopeFunction() { | ||
} | ||
|
||
@Override | ||
public void apply(CtParameter<?> parameter, CtConsumer<Object> outputConsumer) { | ||
CtExecutable<?> exec = parameter.getParent(CtExecutable.class); | ||
if (exec == null) { | ||
//cannot search for parameter references of parameter which has no executable | ||
return; | ||
} | ||
exec.filterChildren(null).forEach(outputConsumer); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
package spoon.test.query_function; | ||
|
||
import org.junit.Before; | ||
import org.junit.Test; | ||
|
||
import spoon.Launcher; | ||
import spoon.reflect.code.CtAbstractInvocation; | ||
import spoon.reflect.code.CtBinaryOperator; | ||
import spoon.reflect.code.CtCatchVariable; | ||
import spoon.reflect.code.CtExpression; | ||
import spoon.reflect.code.CtLiteral; | ||
import spoon.reflect.cu.SourcePosition; | ||
import spoon.reflect.cu.position.NoSourcePosition; | ||
import spoon.reflect.declaration.CtClass; | ||
import spoon.reflect.declaration.CtElement; | ||
import spoon.reflect.declaration.CtExecutable; | ||
import spoon.reflect.declaration.CtParameter; | ||
import spoon.reflect.declaration.CtType; | ||
import spoon.reflect.declaration.CtVariable; | ||
import spoon.reflect.factory.Factory; | ||
import spoon.reflect.reference.CtCatchVariableReference; | ||
import spoon.reflect.reference.CtExecutableReference; | ||
import spoon.reflect.reference.CtVariableReference; | ||
import spoon.reflect.visitor.chain.CtConsumableFunction; | ||
import spoon.reflect.visitor.filter.ParameterReferenceFunction; | ||
import spoon.test.query_function.testclasses.packageA.ClassA; | ||
|
||
import java.util.HashMap; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
import static org.junit.Assert.assertEquals; | ||
import static org.junit.Assert.assertTrue; | ||
import static org.junit.Assert.fail; | ||
|
||
public class QueryTest { | ||
|
||
static int countOfModelClasses = 5; | ||
|
||
Factory factory; | ||
CtClass<?> classA; | ||
CtClass<?> classA_privateChild; | ||
CtClass<?> classA_protectedChild; | ||
CtClass<?> classA_publicChild; | ||
CtClass<?> classA_packProtChild; | ||
|
||
@Before | ||
public void setup() throws Exception { | ||
final Launcher launcher = new Launcher(); | ||
launcher.setArgs(new String[] {"--output-type", "nooutput","--level","info" }); | ||
launcher.getEnvironment().setCommentEnabled(true); | ||
launcher.addInputResource("./src/test/java/spoon/test/query_function/testclasses"); | ||
launcher.run(); | ||
factory = launcher.getFactory(); | ||
classA = factory.Class().get(ClassA.class); | ||
classA_privateChild = factory.Class().get(ClassA.class.getName()+"$PrivateChildA"); | ||
classA_protectedChild = factory.Class().get(ClassA.class.getName()+"$ProtectedChildA"); | ||
classA_publicChild = factory.Class().get(ClassA.class.getName()+"$PublicChildA"); | ||
classA_packProtChild = factory.Class().get(ClassA.class.getName()+"$PackageProtectedChildA"); | ||
} | ||
|
||
@Test | ||
public void testCheckModelConsistency() { | ||
//this constructor creates all nested classes, creates all fields and calls all methods and checks that each field occurrence has assigned correct literal | ||
new ClassA(); | ||
|
||
//1) search for all variable declarations with name "field" | ||
//2) check that each of them is using different identification value | ||
class Context { | ||
int classCount = 0; | ||
Map<Integer, CtElement> unique = new HashMap<>(); | ||
} | ||
Context context = new Context(); | ||
|
||
factory.Package().getRootPackage().filterChildren((CtElement e)->{ | ||
if (e instanceof CtType) { | ||
context.classCount++; | ||
} | ||
if (e instanceof CtVariable) { | ||
CtVariable<?> var = (CtVariable<?>) e; | ||
if("field".equals(var.getSimpleName())==false) { | ||
return false; | ||
} | ||
//check only these variables whose name is "field" | ||
Integer val = getLiteralValue(var); | ||
CtElement ambiquous = context.unique.put(val, var); | ||
if(ambiquous!=null) { | ||
fail("Two variables ["+ambiquous.toString()+","+var.toString()+"] has same value"); | ||
} | ||
} | ||
return false; | ||
}).list(); | ||
assertEquals(countOfModelClasses, context.classCount); | ||
} | ||
|
||
@Test | ||
public void testParameterReferenceFunction() throws Exception { | ||
//visits all the CtParameter 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((CtParameter<?> param)->{ | ||
if(param.getSimpleName().equals("field")) { | ||
int value = getLiteralValue(param); | ||
checkVariableAccess(param, value, new ParameterReferenceFunction()); | ||
} | ||
return false; | ||
}).list(); | ||
} | ||
|
||
private void checkVariableAccess(CtVariable<?> var, int value, CtConsumableFunction<?> query) { | ||
class Context { | ||
int classCount = 0; | ||
int realCount = 0; | ||
int expectedCount = 0; | ||
Set<String> unique = new HashSet<>(); | ||
} | ||
Context context = new Context(); | ||
//use FieldReferenceQuery to found all occurences of the field | ||
var.map(query).forEach((CtVariableReference<?> fr)->{ | ||
//check that all the found field references has same right hand expression | ||
assertEquals(value, getVariableReferenceValue(fr)); | ||
context.realCount++; | ||
}); | ||
//use filterChildren to scan all field references and count the number of field references which has same value => expectedCount | ||
factory.Package().getRootPackage().filterChildren((CtElement e)->{ | ||
if (e instanceof CtType) { | ||
context.classCount++; | ||
} | ||
if (e instanceof CtVariableReference) { | ||
CtVariableReference<?> fr = (CtVariableReference<?>) e; | ||
if("field".equals(fr.getSimpleName())==false) { | ||
return false; | ||
} | ||
int refValue = getVariableReferenceValue(fr); | ||
if(refValue<0) { | ||
fail(); | ||
} | ||
if(refValue==value) { | ||
context.expectedCount++; | ||
} | ||
} | ||
return false; | ||
}).list(); | ||
//check that we are counting the correct set of classes. Update this number when you change the model | ||
//check that both scans found same number of references | ||
assertEquals("field="+value, context.expectedCount, context.realCount); | ||
System.out.println("field="+value+" found "+context.realCount+" referenes"); | ||
} | ||
|
||
private int getVariableReferenceValue(CtVariableReference<?> fr) { | ||
CtBinaryOperator binOp = fr.getParent(CtBinaryOperator.class); | ||
if(binOp==null) { | ||
if (fr instanceof CtCatchVariableReference) { | ||
return getCommentValue(fr); | ||
} | ||
return -1; | ||
} | ||
return getLiteralValue(binOp.getRightHandOperand()); | ||
} | ||
|
||
private Integer getLiteralValue(CtVariable<?> var) { | ||
CtExpression<?> exp = var.getDefaultExpression(); | ||
if(exp!=null) { | ||
return getLiteralValue(exp); | ||
} | ||
if (var instanceof CtParameter) { | ||
CtParameter param = (CtParameter) var; | ||
CtExecutable<?> l_exec = param.getParent(CtExecutable.class); | ||
int l_argIdx = l_exec.getParameters().indexOf(param); | ||
assertTrue(l_argIdx>=0); | ||
CtExecutableReference<?> l_execRef = l_exec.getReference(); | ||
List<CtAbstractInvocation<?>> list = l_exec.getFactory().Package().getRootPackage().filterChildren((CtAbstractInvocation inv)->{ | ||
return inv.getExecutable().equals(l_execRef); | ||
}).list(); | ||
assertEquals(1, list.size()); | ||
CtAbstractInvocation inv = list.get(0); | ||
return getLiteralValue((CtExpression<?>)inv.getArguments().get(l_argIdx)); | ||
} | ||
if(var instanceof CtCatchVariable) { | ||
return getCommentValue(var); | ||
} | ||
throw new AssertionError("Unexpected variable "+var.toString()); | ||
} | ||
|
||
private int getCommentValue(CtElement e) { | ||
while(true) { | ||
if(e==null) { | ||
return -1; | ||
} | ||
if(e.getComments().isEmpty()==false) { | ||
break; | ||
} | ||
e = e.getParent(); | ||
} | ||
if(e.getComments().size()==1) { | ||
String l_c = e.getComments().get(0).getContent(); | ||
return Integer.parseInt(l_c); | ||
} | ||
return -1; | ||
} | ||
|
||
private Integer getLiteralValue(CtExpression<?> exp) { | ||
return ((CtLiteral<Integer>) exp).getValue(); | ||
} | ||
|
||
private SourcePosition getPosition(CtElement e) { | ||
SourcePosition sp = e.getPosition(); | ||
while(sp instanceof NoSourcePosition) { | ||
e = e.getParent(); | ||
if(e==null) { | ||
break; | ||
} | ||
sp = e.getPosition(); | ||
} | ||
return sp; | ||
} | ||
|
||
} |
Oops, something went wrong.