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");
+ }
+ }
+}
+