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 out)->allChildPublicClasses.setInput(in).forEach(x -> out.accept(x))); + CtQuery q2 = launcher.getFactory().Package().getRootPackage().map((CtElement in, CtConsumer out)->allChildPublicClasses.setInput(in).forEach(out)); // now the assertions q2.forEach((CtElement clazz)->{