-
-
Notifications
You must be signed in to change notification settings - Fork 344
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feature: reusable and efficient CtQueries #1090
Changes from 27 commits
b8f0ba4
e7ff290
7a8b8e5
8673d06
065a7c1
ca635d1
15f1560
9d2d62f
a2ef090
3a8859a
a525092
67e1c92
33a92fb
741c6b6
e242717
0fcdde1
6f00843
62b9a03
bb0bc1e
7317b9a
7868e2d
9d0f355
ff8a250
f95defb
fd13bbb
323ea3a
954c371
7705aa0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/** | ||
* 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.visitor.chain; | ||
|
||
/** | ||
* Represents a function, as {@link CtFunction}. However, the main difference is that | ||
* while a {@link CtFunction} returns something with a standard Java return keyword, | ||
* a {@link CtConsumableFunction} returns something by passing the returned object | ||
* as parameter to the given outpuConsumer#accept. This enables to write efficient and concise code in certain situations. | ||
* It also enables one to emulate several returns, by simply calling several times accept, while not paying | ||
* the code or performance price of creating a list or an iterable object. | ||
* | ||
* It is typically used as parameter of {@link CtQueryable#map(CtConsumableFunction)}, can be written as one-liners | ||
* with Java8 lambdas:.`cls.map((CtClass&t;?> c, CtConsumer&t;Object> out)->out.accept(c.getParent()))` | ||
* | ||
* @param <T> the type of the input to the function | ||
*/ | ||
public interface CtConsumableFunction<T> { | ||
/** | ||
* Evaluates the function on the given input. | ||
* @param input the input of the function | ||
* @param outputConsumer the consumer which accepts the results of this function. | ||
*/ | ||
void apply(T input, CtConsumer<Object> outputConsumer); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
/** | ||
* 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.visitor.chain; | ||
|
||
/** | ||
* The functional interface used to receive objects. | ||
* It is used for example to receive results of the query in {@link CtQuery#forEach(CtConsumer)} | ||
* | ||
* @param <T> - the type of accepted elements | ||
*/ | ||
public interface CtConsumer<T> { | ||
void accept(T t); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,56 +16,124 @@ | |
*/ | ||
package spoon.reflect.visitor.chain; | ||
|
||
import spoon.reflect.declaration.CtElement; | ||
import spoon.reflect.visitor.Filter; | ||
|
||
import java.util.List; | ||
|
||
/** | ||
* CtQuery represents a query, which can be used to traverse a spoon model and collect | ||
* children elements in several ways.<br> | ||
* children elements in several ways.<br/> | ||
* | ||
* <br> | ||
* Use {@link CtQueryable#map(CtFunction)}} or {@link CtQueryable#filterChildren(Filter)} to create a new query starting from an existing element.<br> | ||
* Creation: A query is created either from a {@link CtElement}, or it can be defined first from {@link CtQueryImpl} and bound to root elements | ||
* afterwards using {@link CtQuery#setInput(Object...)}.<br/> | ||
* | ||
* The main methods are: | ||
* <ul> | ||
* <li> {@link CtQueryable#map(CtFunction)} - uses a lambda expression to return any model elements that are directly accessible from an input element. | ||
* <li> {@link CtQueryable#filterChildren(Filter)} - uses {@link Filter} instances to filter children of an element | ||
* <li> {@link #list()} - to evaluate the query and return a list of elements produced by this query. | ||
* </ul> | ||
* The query can be used several times.<br> | ||
* A CtQuery is lazily evaluated once {{@link #list()}} is called. | ||
* Usually a new query is created each time when one needs to query something. | ||
* However, reusing a {@link CtQuery} instance makes sense when the same query has to be evaluated | ||
* several times in a loop. | ||
* Chaining: In a query several steps can be chained, by chaining calls to map functions. The non-null outputs of one step | ||
* are given as input to the next step. An iterable or array output is considered as a set of different inputs for the next step. | ||
* | ||
* Evaluation: A CtQuery is lazily evaluated once {@link CtQuery#list()} or {@link CtQuery#forEach(CtConsumer)} are called.<br/> | ||
* | ||
* @param <O> the type of the element produced by this query | ||
*/ | ||
public interface CtQuery<O> extends CtQueryable { | ||
public interface CtQuery extends CtQueryable { | ||
|
||
/** | ||
* Recursively scans all children elements of an input element. | ||
* The matched child element for which (filter.matches(element)==true) are sent to the next query step. | ||
* Essentially the same as {@link CtElement#getElements(Filter)} but more powerful, because it | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to be sure here: when you say filtered elements are sent to the next query step, it's not mandatory: you can just get the list of filtered elements through There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I do not understand |
||
* can be chained with other subsequent queries. | ||
* | ||
* Note: the input element (the root of the query, `this` if you're in {@link CtElement}) is also checked and may thus be also sent to the next step. | ||
* The elements which throw {@link ClassCastException} during {@link Filter#matches(CtElement)} | ||
* are considered as **not matching**, ie. are excluded. | ||
* | ||
* @param filter used to filter scanned children elements of the AST tree | ||
* @return this to support fluent API | ||
*/ | ||
@Override | ||
<R extends CtElement> CtQuery filterChildren(Filter<R> filter); | ||
|
||
|
||
/** | ||
* Query elements based on a function, the behavior depends on the return type of the function. | ||
* <table> | ||
* <tr><td><b>Return type of `function`</b><td><b>Behavior</b> | ||
* <tr><td>{@link Boolean}<td>Select elements if the returned value of `function` is true (as for {@link Filter}). | ||
* <tr><td>? extends {@link Object}<td>Send the returned value of `function` to the next step | ||
* <tr><td>{@link Iterable}<td>Send each item of the collection to the next step | ||
* <tr><td>{@link Object[]}<td>Send each item of the array to the next step | ||
* </table><br> | ||
* | ||
* @param function a Function with one parameter of type I returning a value of type R | ||
* @return this to support fluent API | ||
*/ | ||
@Override | ||
<I, R> CtQuery map(CtFunction<I, R> function); | ||
|
||
/** | ||
* Sets (binds) the input of the query. | ||
* If the query is created by {@link CtElement#map} or {@link CtElement#filterChildren(Filter)}, | ||
* then the query is already bound to this element. | ||
* A new call of {@link CtQuery#setInput(Object...)} is always possible, it resets the current binding and sets the new one. | ||
* | ||
* @param input | ||
* @return this to support fluent API | ||
*/ | ||
<T extends CtQuery> T setInput(Object... input); | ||
|
||
/** | ||
* Actually evaluates the query and for each produced output element of the last step, | ||
* calls `consumer.accept(outputElement)`. | ||
* | ||
* This avoids to create useless intermediate lists. | ||
* | ||
* @param consumer The consumer which accepts the results of the query | ||
*/ | ||
<R> void forEach(CtConsumer<R> consumer); | ||
|
||
/** | ||
* Actually evaluates the query and returns all the elements produced in the last step.<br> | ||
* Note: The type R of the list is not checked by the query. So use the type, which matches the results of your query, | ||
* otherwise the ClassCastException will be thrown when reading the list. | ||
* @return the list of elements collected by the query. | ||
* @see #forEach(CtConsumer) for an efficient way of manipulating the elements without creating an intermediate list. | ||
*/ | ||
<R extends Object> List<R> list(); | ||
|
||
/** | ||
* actually evaluates the query and returns all the produced elements collected in a List | ||
* Same as {@link CtQuery#list()}, but with static typing on the return type | ||
* and the final filtering, which matches only results, which are assignable from that return type. | ||
* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So classes which are not assignable for that return type are just ignored and you cannot have a ClassCastException? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes, exactly |
||
* @return the list of elements collected by the query. | ||
*/ | ||
List<O> list(); | ||
<R> List<R> list(Class<R> itemClass); | ||
|
||
/** | ||
* Defines whether this query will throw {@link ClassCastException} | ||
* when the output of the previous step cannot be cast to type of input of next step. | ||
* The default value is {@link QueryFailurePolicy#FAIL}<br> | ||
* The default value is {@link QueryFailurePolicy#FAIL}, which means than exception is thrown when there is a mismatch<br> | ||
* | ||
* Note: The {@link CtQueryable#filterChildren(Filter)} step never throws {@link ClassCastException} | ||
* | ||
* @param policy the policy | ||
* @return this to support fluent API | ||
*/ | ||
CtQuery<O> failurePolicy(QueryFailurePolicy policy); | ||
CtQuery failurePolicy(QueryFailurePolicy policy); | ||
|
||
/** | ||
* Sets the name of current query, to identify the current step during debugging of a query | ||
* @param name | ||
* @return this to support fluent API | ||
*/ | ||
CtQuery<O> name(String name); | ||
CtQuery name(String name); | ||
|
||
/** | ||
* Same as {@link CtQuery#map(CtFunction)}, but the returned object is not handled | ||
* by java's return statement, but by a call to {@link CtConsumer#accept(Object)}, this | ||
* allows efficient and easy to write chained processing, see {@link CtConsumableFunction}. | ||
* | ||
* @param queryStep | ||
* @return this to support fluent API | ||
*/ | ||
@Override | ||
<I> CtQuery map(CtConsumableFunction<I> queryStep); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why don't you keep an example witch chaining queries here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess @monperrus removed it.