diff --git a/doc/filter.md b/doc/filter.md index 79465949787..3a81ca9434d 100644 --- a/doc/filter.md +++ b/doc/filter.md @@ -2,7 +2,7 @@ title: Filter tags: [quering] keywords: quering, query, filter, ast, elements -last_updated: October 5, 2015 +last_updated: May 27, 2016 --- Spoon aims at giving developers a way to query code elements in @@ -33,6 +33,7 @@ Filter class | Description `CompositeFilter` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/visitor/filter/CompositeFilter.html)) | defines a composite filter, which can compose several filters together by using `FilteringOperator` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/visitor/filter/FilteringOperator.html)). `OverridingMethodFilter` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/visitor/filter/OverridingMethodFilter.html)) | get all overriding methods from the method given. `OverriddenMethodFilter` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/visitor/filter/OverriddenMethodFilter.html)) | get all overridden methods from the method given. +`LineFilter` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/visitor/filter/LineFilter.html)) | get all elements that can be considered as line of code (e.g. directly contained in a block, or a then statement). See below a code example about the usage of these filters. Three filters of them are used. The first returns all AST nodes of type `CtAssignment` ([javadoc](http://spoon.gforge.inria.fr/mvnsites/spoon-core/apidocs/spoon/reflect/code/CtAssignment.html)). diff --git a/src/main/java/spoon/reflect/visitor/filter/LineFilter.java b/src/main/java/spoon/reflect/visitor/filter/LineFilter.java new file mode 100644 index 00000000000..e5b01851b26 --- /dev/null +++ b/src/main/java/spoon/reflect/visitor/filter/LineFilter.java @@ -0,0 +1,72 @@ +/** + * Copyright (C) 2006-2015 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.code.CtBlock; +import spoon.reflect.code.CtIf; +import spoon.reflect.code.CtLoop; +import spoon.reflect.code.CtStatement; +import spoon.reflect.code.CtStatementList; +import spoon.reflect.declaration.CtElement; +import spoon.reflect.declaration.ParentNotInitializedException; + +/** + * This filter matches all elements that can be considered as line of code (e.g. directly contained in a block, or a then statement). This discards CtStatement that are not used as statement (such as a method call used as RHS). + *
+ * // lines of a method
+ * List<CtStatement> lines = method.getElements(new LineFilter());
+ * // find the parent that is used as a statement (in a block or in a branch)
+ * CtStatement parentStatement = element.getParent(new LineFilter());
+ * 
+ */ +public class LineFilter extends TypeFilter { + + /** + * Creates the filter. + */ + public LineFilter() { + super(CtStatement.class); + } + + @Override + public boolean matches(CtStatement element) { + if (!super.matches(element)) { + return false; + } + if (element instanceof CtBlock) { + return false; + } + CtElement parent; + try { + parent = element.getParent(); + } catch (ParentNotInitializedException e) { + return false; + } + if (parent instanceof CtStatementList) { + return true; + } + if (parent instanceof CtIf) { + CtIf anIf = (CtIf) parent; + return element.equals(anIf.getThenStatement()) || element.equals(anIf.getElseStatement()); + } + if (parent instanceof CtLoop) { + CtLoop loop = (CtLoop) parent; + return loop.getBody().equals(element); + } + return false; + } +} diff --git a/src/test/java/spoon/test/filters/FilterTest.java b/src/test/java/spoon/test/filters/FilterTest.java index 916d8f5786c..d52f5e46a88 100644 --- a/src/test/java/spoon/test/filters/FilterTest.java +++ b/src/test/java/spoon/test/filters/FilterTest.java @@ -3,12 +3,15 @@ import org.junit.Before; import org.junit.Test; import spoon.Launcher; -import spoon.compiler.SpoonResourceHelper; import spoon.reflect.code.CtCFlowBreak; import spoon.reflect.code.CtExpression; import spoon.reflect.code.CtFieldAccess; +import spoon.reflect.code.CtIf; import spoon.reflect.code.CtInvocation; +import spoon.reflect.code.CtLoop; import spoon.reflect.code.CtNewClass; +import spoon.reflect.code.CtStatement; +import spoon.reflect.code.CtSwitch; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtElement; import spoon.reflect.declaration.CtExecutable; @@ -27,6 +30,7 @@ import spoon.reflect.visitor.filter.FieldAccessFilter; import spoon.reflect.visitor.filter.FilteringOperator; import spoon.reflect.visitor.filter.InvocationFilter; +import spoon.reflect.visitor.filter.LineFilter; import spoon.reflect.visitor.filter.NameFilter; import spoon.reflect.visitor.filter.OverriddenMethodFilter; import spoon.reflect.visitor.filter.OverridingMethodFilter; @@ -41,6 +45,7 @@ import spoon.test.filters.testclasses.SubTostada; import spoon.test.filters.testclasses.Tacos; import spoon.test.filters.testclasses.Tostada; +import spoon.testing.utils.ModelUtils; import java.util.ArrayList; import java.util.List; @@ -58,9 +63,7 @@ public class FilterTest { @Before public void setup() throws Exception { - Launcher spoon = new Launcher(); - factory = spoon.createFactory(); - spoon.createCompiler(factory, SpoonResourceHelper.resources("./src/test/java/spoon/test/filters/Foo.java")).build(); + factory = ModelUtils.build(Foo.class); } @Test @@ -79,6 +82,46 @@ public void testReturnOrThrowFilter() throws Exception { assertEquals(2, expressions.size()); } + @Test + public void testLineFilter() throws Exception { + CtType foo = ModelUtils.buildClass(FooLine.class); + CtMethod method = foo.getMethod("simple"); + List expressions = method.getElements(new LineFilter()); + assertEquals(3, expressions.size()); + assertNull(expressions.get(0).getParent(new LineFilter())); + + method = foo.getMethod("loopBlock"); + expressions = method.getElements(new LineFilter()); + assertEquals(2, expressions.size()); + assertNull(expressions.get(0).getParent(new LineFilter())); + assertTrue(expressions.get(1).getParent(new LineFilter()) instanceof CtLoop); + + method = foo.getMethod("loopNoBlock"); + expressions = method.getElements(new LineFilter()); + assertEquals(2, expressions.size()); + assertNull(expressions.get(0).getParent(new LineFilter())); + assertTrue(expressions.get(1).getParent(new LineFilter()) instanceof CtLoop); + + method = foo.getMethod("ifBlock"); + expressions = method.getElements(new LineFilter()); + assertEquals(2, expressions.size()); + assertNull(expressions.get(0).getParent(new LineFilter())); + assertTrue(expressions.get(1).getParent(new LineFilter()) instanceof CtIf); + + method = foo.getMethod("ifNoBlock"); + expressions = method.getElements(new LineFilter()); + assertEquals(2, expressions.size()); + assertNull(expressions.get(0).getParent(new LineFilter())); + assertTrue(expressions.get(1).getParent(new LineFilter()) instanceof CtIf); + + method = foo.getMethod("switchBlock"); + expressions = method.getElements(new LineFilter()); + assertEquals(3, expressions.size()); + assertNull(expressions.get(0).getParent(new LineFilter())); + assertTrue(expressions.get(1).getParent(new LineFilter()) instanceof CtSwitch); + assertTrue(expressions.get(2).getParent(new LineFilter()) instanceof CtSwitch); + } + @Test public void testFieldAccessFilter() throws Exception { diff --git a/src/test/java/spoon/test/filters/FooLine.java b/src/test/java/spoon/test/filters/FooLine.java new file mode 100644 index 00000000000..a1fff842ed4 --- /dev/null +++ b/src/test/java/spoon/test/filters/FooLine.java @@ -0,0 +1,41 @@ +package spoon.test.filters; + +class FooLine { + void simple() { + int x = 3; + int z = 0; + System.out.println(z); + } + + void loopBlock() { + for(int i = 0; i < 10; i++) { + System.out.println(i); + } + } + + void loopNoBlock() { + for(int i = 0; i < 10; i++) + System.out.println(i); + } + + void ifBlock() { + if (3 < 4) { + System.out.println("if"); + } + } + + void ifNoBlock() { + if (3 < 4) + System.out.println("if"); + } + + void switchBlock() { + switch ("test") { + case "test": + break; + default: + System.out.println("switch"); + } + } +} +