From 8c35c987c7c7e87ddcc42167026d76f7b404f71d Mon Sep 17 00:00:00 2001 From: Pavel Vojtechovsky Date: Wed, 1 Feb 2017 16:12:30 +0100 Subject: [PATCH] feature: add helper for query variables (VariableReferenceFunction, VariableScopeFunction) (#1114) * feature: add VariableReferenceFunction * test VariableReferenceFunction * fix comments of VariableReferenceFunction * feature: VariableScopeFunction * test VariableScopeFunction * add missing license header --- .../filter/VariableReferenceFunction.java | 95 +++++++++++++++++++ .../visitor/filter/VariableScopeFunction.java | 94 ++++++++++++++++++ .../spoon/test/query_function/QueryTest.java | 48 ++++++++++ 3 files changed, 237 insertions(+) create mode 100644 src/main/java/spoon/reflect/visitor/filter/VariableReferenceFunction.java create mode 100644 src/main/java/spoon/reflect/visitor/filter/VariableScopeFunction.java diff --git a/src/main/java/spoon/reflect/visitor/filter/VariableReferenceFunction.java b/src/main/java/spoon/reflect/visitor/filter/VariableReferenceFunction.java new file mode 100644 index 00000000000..374793c05f2 --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/filter/VariableReferenceFunction.java @@ -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} + * + * and returning all the {@link CtVariableReference}, which refers this variable + */ +public class VariableReferenceFunction implements CtConsumableFunction> { + + protected final Visitor visitor = new Visitor(); + protected CtConsumer outputConsumer; + + @Override + public void apply(CtVariable variable, CtConsumer 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 void visitCtField(CtField field) { + fieldReferenceFunction.apply(field, outputConsumer); + } + + /** + * calls outputConsumer for each reference of the local variable + */ + @Override + public void visitCtLocalVariable(CtLocalVariable localVariable) { + localVariableReferenceFunction.apply(localVariable, outputConsumer); + } + + /** + * calls outputConsumer for each reference of the parameter + */ + @Override + public void visitCtParameter(CtParameter parameter) { + parameterReferenceFunction.apply(parameter, outputConsumer); + } + + /** + * calls outputConsumer for each reference of the catch variable + */ + @Override + public void visitCtCatchVariable(CtCatchVariable catchVariable) { + catchVariableReferenceFunction.apply(catchVariable, outputConsumer); + } + } +} diff --git a/src/main/java/spoon/reflect/visitor/filter/VariableScopeFunction.java b/src/main/java/spoon/reflect/visitor/filter/VariableScopeFunction.java new file mode 100644 index 00000000000..5e276477153 --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/filter/VariableScopeFunction.java @@ -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} + *
    + *
  • CtLocalVariable - local variable declared in body + *
  • CtField - member field of an type + *
  • CtParameter - method parameter + *
  • CtCatchVariable - try - catch variable + *
+ * and returning all the CtElements, which exists in visibility scope of this variable. + */ +public class VariableScopeFunction implements CtConsumableFunction> { + + protected final Visitor visitor = new Visitor(); + protected CtConsumer outputConsumer; + + @Override + public void apply(CtVariable variable, CtConsumer 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 void visitCtField(CtField field) { + fieldScopeFunction.apply(field, outputConsumer); + } + + /** + * calls outputConsumer for each reference of the local variable + */ + @Override + public void visitCtLocalVariable(CtLocalVariable localVariable) { + localVariableScopeFunction.apply(localVariable, outputConsumer); + } + + /** + * calls outputConsumer for each reference of the parameter + */ + @Override + public void visitCtParameter(CtParameter parameter) { + parameterScopeFunction.apply(parameter, outputConsumer); + } + + /** + * calls outputConsumer for each reference of the catch variable + */ + @Override + public void visitCtCatchVariable(CtCatchVariable catchVariable) { + catchVariableScopeFunction.apply(catchVariable, outputConsumer); + } + } +} diff --git a/src/test/java/spoon/test/query_function/QueryTest.java b/src/test/java/spoon/test/query_function/QueryTest.java index dec2487ba75..fa0a0ebd6fe 100644 --- a/src/test/java/spoon/test/query_function/QueryTest.java +++ b/src/test/java/spoon/test/query_function/QueryTest.java @@ -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; @@ -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; @@ -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;