From 8833c26c4f0717c8b889cee8cbd3edc74a5d30aa Mon Sep 17 00:00:00 2001
From: Pavel Vojtechovsky
Date: Sun, 8 Jan 2017 13:34:47 +0100
Subject: [PATCH] add query factory and use it
---
.../java/spoon/reflect/factory/Factory.java | 12 +++++
.../spoon/reflect/factory/FactoryImpl.java | 23 +++++++++
.../spoon/reflect/factory/QueryFactory.java | 51 +++++++++++++++++++
.../support/SerializationModelStreamer.java | 11 ++--
.../reflect/declaration/CtElementImpl.java | 7 ++-
.../java/spoon/test/filters/FilterTest.java | 12 ++---
6 files changed, 102 insertions(+), 14 deletions(-)
create mode 100644 src/main/java/spoon/reflect/factory/QueryFactory.java
diff --git a/src/main/java/spoon/reflect/factory/Factory.java b/src/main/java/spoon/reflect/factory/Factory.java
index ee78d295402..53acddf414d 100644
--- a/src/main/java/spoon/reflect/factory/Factory.java
+++ b/src/main/java/spoon/reflect/factory/Factory.java
@@ -104,6 +104,7 @@
import spoon.reflect.reference.CtUnboundVariableReference;
import spoon.reflect.reference.CtVariableReference;
import spoon.reflect.reference.CtWildcardReference;
+import spoon.reflect.visitor.chain.CtQuery;
import java.lang.annotation.Annotation;
import java.util.List;
@@ -150,6 +151,8 @@ public interface Factory {
ConstructorFactory Constructor(); // used 3 times
+ QueryFactory Query();
+
/**
* @see CodeFactory#createAnnotation(CtTypeReference)
*/
@@ -762,4 +765,13 @@ public interface Factory {
*/
CtTypeParameterReference createTypeParameterReference(String name);
+ /**
+ * @see QueryFactory#createQuery()
+ */
+ CtQuery createQuery();
+
+ /**
+ * @see QueryFactory#createQuery(Object))
+ */
+ CtQuery createQuery(Object input);
}
diff --git a/src/main/java/spoon/reflect/factory/FactoryImpl.java b/src/main/java/spoon/reflect/factory/FactoryImpl.java
index 6952ef4f7ab..21d5bcaaac5 100644
--- a/src/main/java/spoon/reflect/factory/FactoryImpl.java
+++ b/src/main/java/spoon/reflect/factory/FactoryImpl.java
@@ -105,6 +105,7 @@
import spoon.reflect.reference.CtUnboundVariableReference;
import spoon.reflect.reference.CtVariableReference;
import spoon.reflect.reference.CtWildcardReference;
+import spoon.reflect.visitor.chain.CtQuery;
import spoon.support.DefaultCoreFactory;
import spoon.support.StandardEnvironment;
@@ -335,6 +336,19 @@ public TypeFactory Type() {
return type;
}
+ private transient QueryFactory query;
+
+ /**
+ * The query sub-factory.
+ */
+ @Override
+ public QueryFactory Query() {
+ if (query == null) {
+ query = new QueryFactory(this);
+ }
+ return query;
+ }
+
/**
* A constructor that takes the parent factory
*/
@@ -1024,4 +1038,13 @@ public CtTypeParameterReference createTypeParameterReference(String name) {
return Type().createTypeParameterReference(name);
}
+ @Override
+ public CtQuery createQuery() {
+ return Query().createQuery();
+ }
+
+ @Override
+ public CtQuery createQuery(Object input) {
+ return Query().createQuery(input);
+ }
}
diff --git a/src/main/java/spoon/reflect/factory/QueryFactory.java b/src/main/java/spoon/reflect/factory/QueryFactory.java
new file mode 100644
index 00000000000..e84b533d67f
--- /dev/null
+++ b/src/main/java/spoon/reflect/factory/QueryFactory.java
@@ -0,0 +1,51 @@
+/**
+ * Copyright (C) 2006-2016 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.factory;
+
+import spoon.reflect.visitor.chain.CtQuery;
+import spoon.reflect.visitor.chain.CtQueryImpl;
+
+/**
+ * A factory to create some queries on the Spoon metamodel.
+ */
+public class QueryFactory extends SubFactory {
+
+ /**
+ * Creates the evaluation factory.
+ */
+ public QueryFactory(Factory factory) {
+ super(factory);
+ }
+
+ /**
+ * Creates a unbound query. Use {@link CtQuery#setInput(Object...)}
+ * before {@link CtQuery#forEach(spoon.reflect.visitor.chain.CtConsumer)}
+ * or {@link CtQuery#list()} is called
+ */
+ public CtQuery createQuery() {
+ return new CtQueryImpl();
+ }
+
+ /**
+ * Creates a bound query. Use directly
+ * {@link CtQuery#forEach(spoon.reflect.visitor.chain.CtConsumer)}
+ * or {@link CtQuery#list()} to evaluate the query
+ */
+ public CtQuery createQuery(Object input) {
+ return new CtQueryImpl(input);
+ }
+}
diff --git a/src/main/java/spoon/support/SerializationModelStreamer.java b/src/main/java/spoon/support/SerializationModelStreamer.java
index 99a32bd3130..0bd7514ce13 100644
--- a/src/main/java/spoon/support/SerializationModelStreamer.java
+++ b/src/main/java/spoon/support/SerializationModelStreamer.java
@@ -21,6 +21,7 @@
import spoon.reflect.declaration.CtElement;
import spoon.reflect.factory.Factory;
import spoon.reflect.visitor.CtScanner;
+import spoon.reflect.visitor.Filter;
import java.io.IOException;
import java.io.InputStream;
@@ -51,13 +52,15 @@ public Factory load(InputStream in) throws IOException {
try {
ObjectInputStream ois = new ObjectInputStream(in);
final Factory f = (Factory) ois.readObject();
- new CtScanner() {
+ //create query using factory directly
+ //because any try to call CtElement#map or CtElement#filterChildren will fail on uninitialized factory
+ f.createQuery(f.getModel().getRootPackage()).filterChildren(new Filter() {
@Override
- public void enter(CtElement e) {
+ public boolean matches(CtElement e) {
e.setFactory(f);
- super.enter(e);
+ return false;
}
- }.scan(f.Package().getAll());
+ }).list();
ois.close();
return f;
} catch (ClassNotFoundException e) {
diff --git a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java
index 9509e097136..b67f9b72b91 100644
--- a/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java
+++ b/src/main/java/spoon/support/reflect/declaration/CtElementImpl.java
@@ -31,7 +31,6 @@
import spoon.reflect.visitor.Filter;
import spoon.reflect.visitor.ModelConsistencyChecker;
import spoon.reflect.visitor.Query;
-import spoon.reflect.visitor.chain.CtQueryImpl;
import spoon.reflect.visitor.chain.CtFunction;
import spoon.reflect.visitor.chain.CtConsumableFunction;
import spoon.reflect.visitor.chain.CtQuery;
@@ -261,17 +260,17 @@ public List getElements(Filter filter) {
@Override
public CtQuery map(CtConsumableFunction queryStep) {
- return new CtQueryImpl(this).map(queryStep);
+ return factory.Query().createQuery(this).map(queryStep);
}
@Override
public CtQuery map(CtFunction function) {
- return new CtQueryImpl(this).map(function);
+ return factory.Query().createQuery(this).map(function);
}
@Override
public
CtQuery filterChildren(Filter
predicate) {
- return new CtQueryImpl(this).filterChildren(predicate);
+ return factory.Query().createQuery(this).filterChildren(predicate);
}
public List getReferences(Filter filter) {
diff --git a/src/test/java/spoon/test/filters/FilterTest.java b/src/test/java/spoon/test/filters/FilterTest.java
index 750d59426a3..482597a6ddb 100644
--- a/src/test/java/spoon/test/filters/FilterTest.java
+++ b/src/test/java/spoon/test/filters/FilterTest.java
@@ -626,7 +626,7 @@ public void testElementMapFunction() throws Exception {
public void testElementMapFunctionOtherContracts() throws Exception {
// contract: when a function returns an array, all non-null values are sent to the next step
final Launcher launcher = new Launcher();
- CtQueryImpl q = new CtQueryImpl().map((String s)->new String[]{"a", null, s});
+ CtQuery q = launcher.getFactory().Query().createQuery().map((String s)->new String[]{"a", null, s});
List list = q.setInput(null).list();
assertEquals(0, list.size());
@@ -642,14 +642,14 @@ public void testElementMapFunctionOtherContracts() throws Exception {
assertEquals("c", list.get(1));
// contract: when input is null then the query function is not called at all.
- CtQueryImpl q2 = new CtQueryImpl().map((String s)->{ throw new AssertionError();});
+ CtQuery q2 = launcher.getFactory().Query().createQuery().map((String s)->{ throw new AssertionError();});
assertEquals(0, q2.setInput(null).list().size());
}
@Test
public void testElementMapFunctionNull() throws Exception {
// contract: when a function returns null, it is discarded at the next step
final Launcher launcher = new Launcher();
- CtQueryImpl q = new CtQueryImpl().map((String s)->null);
+ CtQuery q = launcher.getFactory().Query().createQuery().map((String s)->null);
List list = q.setInput("c").list();
assertEquals(0, list.size());
}
@@ -692,7 +692,7 @@ public void testReuseOfBaseQuery() throws Exception {
CtClass> cls2 = launcher.getFactory().Class().get(Tostada.class);
// here is the query
- CtQuery q = new CtQueryImpl().map((CtClass c) -> c.getSimpleName());
+ CtQuery q = launcher.getFactory().Query().createQuery().map((CtClass c) -> c.getSimpleName());
// using it on a first input
assertEquals("Tacos", q.setInput(cls).list().get(0));
// using it on a second input
@@ -744,7 +744,7 @@ class Context {
CtClass> cls = launcher.getFactory().Class().get(Tacos.class);
// first query
- CtQuery allChildPublicClasses = new CtQueryImpl().filterChildren((CtClass clazz)->clazz.hasModifier(ModifierKind.PUBLIC));
+ CtQuery allChildPublicClasses = launcher.getFactory().Query().createQuery().filterChildren((CtClass clazz)->clazz.hasModifier(ModifierKind.PUBLIC));
// second query,involving the first query
CtQuery q = launcher.getFactory().Package().getRootPackage().map((CtElement in)->allChildPublicClasses.setInput(in).list());
@@ -759,7 +759,7 @@ class Context {
context.count=0; //reset
// again second query, but now with CtConsumableFunction
- CtQuery q2 = launcher.getFactory().Package().getRootPackage().map((CtElement in, CtConsumer