-
-
Notifications
You must be signed in to change notification settings - Fork 351
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
feat: Adds unique serializable path for each CtElement from Root Element. #1874
Conversation
@@ -536,4 +540,12 @@ public CtElement clone() { | |||
rh.setValue(this, value); | |||
return (E) this; | |||
} | |||
|
|||
public CtPath getPath() { | |||
try { |
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 not removing the try/catch and let the exception go?
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.
The exception is supposed to be risen from CtElementPathBuilder().fromElement(CtElement el, CtElement from) when from is not a parent of el. So in this case, I think it can't happen and I didn't want to annoy the user with the handling of an exception which is not supposed to happen.
But still, we can let it go.
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.
"let it go" then :-)
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 have a counter proposal: Why not catch CtPathException
(which should not happen in that context) and throw a SpoonException
to show that it's an spoon implementation error. Further more it would avoid that the user has to handle it.
try {
return new CtElementPathBuilder().fromElement(this, getParent(CtModelImpl.CtRootPackage.class));
} catch (CtPathException e) {
throw new SpoonException();
}
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.
OK for me.
assertEquals(returnedElements.size(), 1); | ||
CtElement actualElement = (CtElement) returnedElements.toArray()[0]; | ||
//contract: Element -> Path -> String -> Path -> Element leads to the original element | ||
assertEquals(element, actualElement); |
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.
assertSame?
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.
Yes, that sounds better. I'll fix it ASAP.
looks super cool, I love the strong generic test. are you working on fixing #1872? |
@monperrus I tried to add: static class CtCatchVariable_TYPE_RoleHandler extends ListHandler<CtCatchVariable, CtTypeReference<?>> {
private CtCatchVariable_TYPE_RoleHandler() {
super(CtRole.TYPE, CtCatchVariable.class, CtTypeReference.class);
}
@SuppressWarnings("unchecked")
@Override
public <T, U> U getValue(T element) {
return ((U) ((Object) (castTarget(element).getMultiTypes())));
}
@Override
public <T, U> void setValue(T element, U value) {
castTarget(element).setMultiTypes(castValue(value));
}
} to |
@monperrus I'm not sure why does the Travis CI build fails. It mentions Javadoc errors in files that I haven't changed. Any ideas? Do you want to keep the current syntax for |
I propose to replace the previous implementation, to keep the old syntax with slash, and thus to update WDYT? |
Fixed the Javadoc error at CtPathStringBuilder.java line 71 (please pull). (Maven prefixes "warnings" of the Javadoc linter by |
Yes that would be great, see #1874 (comment) |
We can keep the wildcards, they are a part of new CtPathStringBuilder().fromString(".spoon.test.path.Foo.foo#body[index=0]"); becomes new CtPathStringBuilder().fromString(".spoon.test.path.Foo.foo#body#statement[index=0]"); |
the change is OK for me. it seems we're close to merge! |
CtElement el = root.getValueByRole(getRole()); | ||
matchs.add(el); | ||
} | ||
} catch (SpoonException e) { } //When no element are found for a given role, return empty list. |
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.
same thing here, do we want this exception to be silent?
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.
Yes, imo, because as it is, if we do not silence this exception, wildcards are broken.
It is thrown when we query a node for children with a given role that does not exist among its children.
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 one alternative would be adding a method boolean hasChildWith(CtRole role)
to CtElement
.
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.
It is already there. See RoleHandlerHelper#getOptionalRoleHandler
Have a look also at RoleHandler#getContainerKind
, which might be used instead of root.getValueByRole(getRole()) instanceof Set
and similar
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.
@pvojtechovsky Thanks for the tip.
Problem solved.
…feature-unique-path
OK for me. Will merge it. That's a really nice contribution to Spoon, thanks! |
what about changing the signature of |
sorry @nharrand , I fixed the test by calling |
Yes, I see that! I'm looking into that. |
Ok this should work now. |
if (getArguments().containsKey("index")) { | ||
int index = Integer.parseInt(getArguments().get("index")); | ||
matchs.add((CtElement) roleHandler.asList(root).get(index)); | ||
} |
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.
else it should add all items of the into matchs
. WDYT?
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.
and what about case when index >= size()
?
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.
Yes, you are right. I only had in mind my Unique path case in mind here, but yes. I'll fix this
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.
Right, that also means that I should add negative test cases...
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.
else it should add all items of the into
matchs
.
We should extends that behavior to sets and maps, don't you think?
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.
Yes, I agree. For Set it should add all items of the Set. And for Map it should add all values of the map.
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.
Implemented in 67ec78b
case MAP: | ||
if (getArguments().containsKey("key")) { | ||
String name = getArguments().get("key"); | ||
matchs.add((CtElement) roleHandler.asMap(root).get(name)); |
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 it is not correct to add null into matchs, when map doesn't contain a key
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.
Indeed. I'll fix this too.
try { | ||
return new CtElementPathBuilder().fromElement(this, getParent(CtModelImpl.CtRootPackage.class)); | ||
} catch (CtPathException e) { | ||
throw new SpoonException(); |
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.
throw new SpoonException(e);
... the orgin exception should be used as cause of the SpoonException
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.
Ok. But, my idea was that CtElementPathBuilder().fromElement(CtElement to, CtElement from)
would throw a CtPathException if from
was no parent of to
which means that the user made a mistake. But in the specific context of this call, as the arguments are an element and root package, no exception should happen unless the spoon implementation is at fault, therefor SpoonException
.
WDYT?
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.
Yes, no exception should happen, but if it happens because of some mistake in spoon code, then your current implementation will avoid efficient detection of the cause, because the cause of the problem is lost. So just type in e
into brackets and I am OK with that ;-)
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.
just type in e into brackets
What do you mean? Shall I change throw new SpoonException();
into throw new SpoonException(e);
?
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.
yes please
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.
Done in 8cfc1ad
…eturns an empty collection. Add test for such events
…d maps. add tests
return STRING + getRole().toString() + getParamString(); | ||
} | ||
|
||
public CtElement getFromSet(Set set, String name) throws CtPathException { |
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.
do we need it public? Note: In Spoon we try to make public API as small as possible. It is easier to maintain and refactor then.
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.
No, we don't. I'll change it.
throw new CtPathException(); | ||
} | ||
} | ||
throw new CtPathException(); |
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.
throwing of exception as part of normal program flow is not a good idea. It has a performance impact. So it is much faster (main line debug mode), when you return null and check it in caller.
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.
Fixed in 7ee9921
…hen element is not found in set
It looks good to me. I am looking forward to use this new Spoon feature! Thank You @nharrand . I guess we can merge. @monperrus WDYT? |
API changes: 2 (Detected by Revapi) Old API: fr.inria.gforge.spoon:spoon-core:jar:6.2.0-20180225.235035-111 / New API: fr.inria.gforge.spoon:spoon-core:jar:6.2.0-SNAPSHOT
|
OK to merge! This opens super exciting new usages. Kudos and thanks to @nharrand |
You are welcome. Thanks for the reviews. I'm a little disappointed that it counts as only one commit though... ;-) |
that's the tough law of squashing!
|
Dear all, I'd like to nominate Nicolas Harrand (@nharrand) to become an integrator in Spoon. Nicolas has done several key contributions over the past year: * he has added support for paths in order to uniquely identify AST nodes (#1874), this feature is unique (to my knowledge, it does not exist in any other source code analysis library for Java), and enables original source code analysis over time and versions. * he has added support for analysis of binary files with decompilation (#2455) * he has laid down the foundations of a modularized Spoon, which will likely be key in the future * he has ported Spoon to JDK 11 (#2789). This kind of infrastructure contribution is also very important in the long term. In addition, Nicolas has proven his ability to communicate constructively and respectfully over issues and pull-requests. What do you think?
The goal of this PR is to add support for getting an unique
CtPath
for eachCtElement
of a model. In particular this could be used for persistence. Any givenCtElement
can be access through an unique deterministic serializable path. All the following transformations are supported:CtElement
->CtPath
->String
->CtPath
->CtElement
.Main content:
CtPath CtElement.getPath()
: Returns the path from root element tothis
.CtElementPathBuilder
: Build aCtPath
from a givenCtElement
CtUniqueRolePathElement
: Extends CtRolePathElement with a new behavior and a new syntax.MainTest.testElementToPathToElementEquivalency()
: TestCtElement
->CtPath
->String
->CtPath
->CtElement
In order to integrate this to
CtPathStringBuilder
without conflict, the char '@' is used for this new path element.The syntax for paths is the following
@role
@role[index=ID]
@role[name=element.getSimpleName()]
@role[key=String]
@subPackage[name=spoon]@subPackage[name=compiler]@containedType[name=Environment]@typeMember[index=0]
->spoon.compiler.Environment
's first type member ie:int getComplianceLevel();
Currently the test fails (related to #1872).
What do you think?