-
-
Notifications
You must be signed in to change notification settings - Fork 358
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
review: introduction of CtScannerFunction #1180
Conversation
Revapi Analysis resultsOld API: fr.inria.gforge.spoon:spoon-core:jar:5.6.0-20170209.143117-32 New API: fr.inria.gforge.spoon:spoon-core:jar:5.6.0-SNAPSHOT Detected changes: 0. |
02c41c6
to
bc9f0c7
Compare
It is still not finished, but API will be like this. public interface CtQueryListener {
/**
* Called when new object enters query step, before the object is processed by mapping function of that step.
* <br>
* The returning false or throwing an exception causes that processing of input object and all it's children is skipped.
* The returning true causes that input object is normally processed, and exit is called for that object.
* @param element the processed element
* @param context context the query execution context
* @return true to continue processing this step and it's children,
* false to skip this step and it's children.
*/
boolean enter(Object element, CtQueryContext context);
/**
* The exit is called always only after `enter` returns true and input element and all children are processed or they throw an exception.
* @param element the processed element
* @param context the query execution context
* @param exception null or an exception if the processing of the mapping function failed
*/
void exit(Object element, CtQueryContext context, Throwable exception);
} and the listener can be assigned to the CtQuery using new methods Please note that there is also new interface
In future it might be used to get access to the:
The one query step and processing of all it's children can be terminated by returning false from |
Hi Pavel, The previous one had two new protected methods in an implementation class "enter" and "exit", and I liked it very much. Here we have two new interfaces CtQuetyListener and CtQueryContext. I'm afraid that this more complex design will be more difficult to document, maintain and understand for new users. WDYT? |
Hi Martin, thanks for feedback. I will try to make
It was just in the middle of work. It was no real solution. There were protected methods on private class and there was no way how use that extended class, even if it would be protected. But I will think about that solution too. I will experiment with current design from client's point of view. I will try to use it in refactoring code and get some usability experience. |
Do we need multi-threading support in CtQuery? I mean to create one instance of CtQuery and then evaluate it parallel in multiple threads? Actually it is partially possible (depending on used mapping function). But that support, brings some extra complexity, which influences design of this PR too. So do you agree to make CtQuery and it's future API design single threading? |
bc9f0c7
to
2f3be98
Compare
I know you like KISS, so your answer is 99.9% yes. So now the CtQueryImpl is refactored and the query evaluation context is directly part of the CtQuery.
public interface CtQueryListener {
boolean enter(Object element, CtQuery context);
void exit(Object element, CtQuery context, Throwable exception);
} I have added CtQueryImpl$QueryConsumer here too, which is used in this PR by |
So do you agree to make CtQuery and it's future API design single threading?
I know you like KISS, so your answer is 99.9% yes.
Correct :-) I'm fundamentally KISS.
|
* The exit is called always only after `enter` returns true and input element and all children are processed or they throw an exception. | ||
* @param element the processed element | ||
* @param context the query execution context | ||
* @param exception null or an exception if the processing of the mapping function failed |
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.
What's parameter "exception" used for?.
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.
to detect whether processing of the children passed correctly or failed. I do not need it now, because my code does not throw exceptions, but it is API so it should be generic enough
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.
actually it is used like this:
listener.enter(item, query);
try {
... process children
} catch (Throwable e) {
listener.exit(item, query, e);
throw e;
}
listener.exit(item, query, null);
so the exit is called always, and you can detect in exit whether it failed or not by checking exception parameter.
Optionally we might implement it like this
listener.enter(item, query);
try {
... process children
} catch (Throwable e) {
listener.failureExit(item, query, e);
throw e;
}
listener.exit(item, query);
where failureExit
method might added to the API later, after somebody needs it.
In such case we should provide an default implementation of listener, so the clients can extend that default implementation and will be not forced to change their code later, after we add method failureExit into API.
I do not need it now
Then, I propose to remove it. Unchecked exceptions can still be thrown if necessary.
|
6a146ba
to
e73edea
Compare
During experimenting with What I did? BS 1. The BS 2. The BS 3. The existing private class BS 4. The new (The BS means Baby Step ;-) ... I know there are 4, but may be they are acceptable in this one PR...) |
d538fdc
to
b02ed2a
Compare
@monperrus Is it problem of my implementation or problem of IntercessionTest? |
Thanks for the proposal. The BS you propose seem reasonable, even if 4 BS may hinder a quick understanding and converging. Now, I feel a bit lost now. Before discussing further, I'd like to understand the relation between the BS and the requirements of this PR (at the top of this thread):
Do you still address them all? Do you address new ones? |
I still address them. With this little change
They are covered by BS4, which needs BS3, which needs BS2 and BS1 I address new: |
OK, let's discuss on BS1 and BS2 since since the others depend on it. BS1: OK. BS2: I'm not convinced because: 1) the responsibility of CtQueryAware is trivial, it's not worth introducing a new interface for this. 2) nobody expects a CtQueryAware (I didn't find it) 3) it introduces some state in function, which is essentially a stateless concept (and stateless is better). Am I missing something? |
yes, it is trivial. I learned that concept by Spring framework. It is an easy way, how to declare, that some object needs a knowledge about something outside.
I am different opinion :-). But do not care. The question is: Do you have an alternative solution for: How to deliver CtQuery into instance of
I do not understand this note. Do you mean nobody needs that? It is used by
I also wanted stateless at the beginning. But that costs something ... higher complexity ... less KISS. By this question:
I asked for the agreement to leave stateless concept. |
I was wrong. The context is needed in CConsumableFunction only. Other two mapping functions are not called when query is terminated. And CtConsumableFunction gets second parameter CtConsumer, which might be change or cast to something what can return the query. But it is true that current implementation of mapping functions is usually no stateless. |
another change of my opinion :-)
This is correct question, because CtFunction and Filter sometime might need context (CtQuery) to be able to terminate the query. I already found this use case in implementation of PR #1005 |
This PR is finished from my side. Or do you have a better - stateless - way how to deliver |
We make great progress, thanks! one more question about FilterChildrenFunction. What I understand now is that it provides the capability of CtScanner in the context of queries, efficiently. Now, do we really need to have a filter there? Since the query architecture is step-based, and lazily evaluated, having the filter as the next step would be functionally equivalent and similarly efficient. Correct? Can we we remove the filtering responsibility from FilterChildrenFunction? |
probably yes. It is an interesting idea. I see only one (little?) problem public <R extends CtElement> CtQueryImpl filterChildren(Filter<R> filter) {
map(new FilterChildrenFunction()); //1st step
select(filter); //2nd step
return this;
} it means CtQuery#filterChildren will internally add 2 query steps, while client might expect only one query step. Then the I vote for doing that. And then we have to rename |
09c2750
to
84333af
Compare
To me it's not a problem, steps are internal, they are not part of the API.
Yes. We're along the same line. We're done for the core design decisions. Thanks. Now the final steps are documentation and tests. I'll do the documentation part. Can you add the relevant tests? |
84333af
to
df02d1c
Compare
Revapi Analysis resultsOld API: fr.inria.gforge.spoon:spoon-core:jar:5.6.0-20170307.234505-66 New API: fr.inria.gforge.spoon:spoon-core:jar:5.6.0-SNAPSHOT Detected changes: 2. Change 1
Change 2
|
df02d1c
to
3802562
Compare
Revapi Analysis resultsOld API: fr.inria.gforge.spoon:spoon-core:jar:5.6.0-20170307.234505-66 New API: fr.inria.gforge.spoon:spoon-core:jar:5.6.0-SNAPSHOT Detected changes: 2. Change 1
Change 2
|
2f3d204
to
80845d4
Compare
What about return value of
May be we might change boolean to enum SCANNING_MODE {NORMAL, SKIP_CHILDREN, SKIP_ALL} What do you think? |
May be we might change boolean to enum SCANNING_MODE {NORMAL, SKIP_CHILDREN, SKIP_ALL}
Excellent idea! It's more readable and allows for future changes and extension.
|
80845d4
to
452381a
Compare
merged #1210, you can rebase this one, and I'll pass over the doc. |
452381a
to
72b4c39
Compare
rebased |
Doc PR on this one: pvojtechovsky#14 |
Looks like it is ready for merge. I will then rebase other PRs |
Thanks a lot for the effort put in this very well-done contribution. |
You are welcome! 👍 |
As I mentioned in #1005 it is sometime needed to
There is already a Query Execution Context (QEC) impelemented by class
CtQueryImpl$CurrentStep
.There are needed these two things to implement this PR:
Access to QEC
The client might get access to QEC, by these ways:
Access1) CtQuery#createQueryContext()
By creation of QEC before query is executed. Then that instance can be used in functions of query to drive query
Access2) CtQuery#setQueryContext()
By creating an own instance of QEC and setting it to query
Access3) (CtQueryContext)outputConsumer
By conversion of outputConsumer of
CtConsumableFunction(Object,CtConsumer)
to QEC.Access4) CtQuery extends CtQueryContext
CtQuery extends CtQueryContext, so all the methods are available through query instance.
API of QEC
There might be these methods on QEC:
Example
...to be continued...
Feedbacks and suggestions are welcome