-
-
Notifications
You must be signed in to change notification settings - Fork 358
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
fix: print each resource in CtTryWithResource
exactly once and retain separator
#4309
Conversation
The context is pushed before printing the resources, however that same context is not used when we are printing the separator. Apparently, |
I went along these lines and pushed a fix for it. I debugged where the context was lost and it was in One major problem I see in this patch is that I am hard-coding @slarse @MartinWitt @monperrus , could you please review it? |
CtTryWithResource
exactly onceCtTryWithResource
exactly once and retain separator
List<SourceFragment> elementSourceFragments = childFragments.stream() | ||
.filter(fragment -> fragment instanceof ElementSourceFragment) | ||
.collect(Collectors.toList()); | ||
for (SourceFragment sourceFragment: elementSourceFragments) { | ||
ElementSourceFragment elementSourceFragment = ((ElementSourceFragment) sourceFragment); | ||
return elementSourceFragment.getRoleInParent() == role; | ||
} | ||
return false; | ||
} |
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.
This is really hard to read and should be refactored in a single loop. A loop that always returns and doesn't loop is kinda unexpected.
} else if (tpe.getToken().equals(";")) { | ||
if (checkIfPartOfRole(CtRole.TRY_RESOURCE)) { |
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.
Collapse
} else if (tpe.getToken().equals(";")) { | |
if (checkIfPartOfRole(CtRole.TRY_RESOURCE)) { | |
} else if (tpe.getToken().equals(";") && checkIfPartOfRole(CtRole.TRY_RESOURCE)) { |
@@ -68,6 +73,17 @@ public boolean knowsHowToPrint(PrinterEvent event) { | |||
throw new SpoonException("Unexpected PrintEvent: " + event.getClass()); | |||
} | |||
|
|||
private boolean checkIfPartOfRole(CtRole role) { |
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 reading the invocation of this method, I have no idea of what its intention is. Check if what is part of a role? What does it mean to be part of a role?
check
is also usually reserved for methods that throw. For methods that are meant to return a boolean, I'd recommend starting with is
, has
etc.
Looking at the method body, this method returns true if the first ElementSourceFragment
of childFragments
matches the provided role. Otherwise it returns false. Can you explain those semantics? I also agree with @MartinWitt that the method needs refactoring, but first I want to try to understand its intent.
@MartinWitt @slarse The intent of the method is to check if the role of the current
I assumed that if the first element has the role I have explained why this code was required in the first place here. |
I have refactored the method according to what @slarse and @MartinWitt suggested. I named the method |
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.
Added a further suggestion for clarification now that I understand the intent.
private boolean childFragmentHasSpecifiedRoleInParent(CtRole roleInParent) { | ||
Optional<SourceFragment> optionSourceFragment = childFragments.stream() | ||
.filter(fragment -> fragment instanceof ElementSourceFragment) | ||
.findFirst(); | ||
if (optionSourceFragment.isPresent()) { | ||
ElementSourceFragment elementSourceFragment = (ElementSourceFragment) optionSourceFragment.get(); | ||
return elementSourceFragment.getRoleInParent() == roleInParent; | ||
} | ||
return false; | ||
} |
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 assumed that if the first element has the role CtRole.TRY_RESOURCE, then the rest elements will also have the same role and I guess it's safe to do so because you can only used objects inside it which implement
That's probably safe, yes. The thingy that builds the CollectionSourceFragment
(which is later fed into the collection context) groups the children by role. I don't have a great understanding of how often this check will run, but I don't think a solitary ;
is printed very often. In the entire sniper printer unit test suite, it only happens 4 times, 2 of which are in your tests. So probably this is fine from a performance perspective, but it bears keeping in mind.
Reading the method title, I'm still thinking "which child fragment?". And the answer to that is of course any child fragment. I would therefore suggest a renaming like below, and also further idiomatic use of the stream.
private boolean childFragmentHasSpecifiedRoleInParent(CtRole roleInParent) { | |
Optional<SourceFragment> optionSourceFragment = childFragments.stream() | |
.filter(fragment -> fragment instanceof ElementSourceFragment) | |
.findFirst(); | |
if (optionSourceFragment.isPresent()) { | |
ElementSourceFragment elementSourceFragment = (ElementSourceFragment) optionSourceFragment.get(); | |
return elementSourceFragment.getRoleInParent() == roleInParent; | |
} | |
return false; | |
} | |
private boolean anyChildFragmentHasRole(CtRole role) { | |
return childFragments.stream() | |
.filter(ElementSourceFragment.class::isInstance) | |
.map(ElementSourceFragment.class::cast) | |
.map(ElementSourceFragment::getRoleInParent) | |
.map(role::equals) | |
.findAny() | |
.orElse(false); | |
} |
I'm using findAny()
here to emphasize that there's nothing special about the first element, we just assume that if any element has a given role, the others do too.
@@ -52,6 +53,8 @@ public boolean knowsHowToPrint(PrinterEvent event) { | |||
return false; | |||
} else if (tpe.getType() == TokenType.IDENTIFIER) { | |||
return findIndexOfNextChildTokenByType(TokenType.IDENTIFIER) >= 0; | |||
} else if (tpe.getToken().equals(";") && childFragmentHasSpecifiedRoleInParent(CtRole.TRY_RESOURCE)) { |
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.
With the suggested renaming, this becomes somewhat easier to interpret, I think:
} else if (tpe.getToken().equals(";") && childFragmentHasSpecifiedRoleInParent(CtRole.TRY_RESOURCE)) { | |
} else if (tpe.getToken().equals(";") && anyChildFragmentHasRole(CtRole.TRY_RESOURCE)) { |
We can add a comment for this because it might confound future developers to see such a thing. It's true for future-me at least.
Right. We could do this with I will push the changes. |
@slarse @MartinWitt incorporated your changes. I have also added a short comment explaining the |
LGTM, if @slarse is fine with it, we can merge. |
@slarse reminder to merge this PR, if you are okay with the changes. :) |
Reference: ASSERT-KTH/sorald#600
The second resource in the catch block,
BufferedWriter writer = newBufferedWriter(outputFilePath, charset)
, is printed twice. For example,is changed to
when printed using the sniper printer.
I think it is happening because we add a separator,
;
, after firstBufferedWriter writer = newBufferedWriter(outputFilePath, charset)
which changesmuted
fromtrue
tofalse
. Although the separator is not needed here, it is still printed. This may be similar to #4306 .Please note that
muted
should remain true till we print the second resource because all resources are printed at once because of this line here. Basically, if a collection fragment is not modified, we print it at once and setmuted
totrue
for upcoming elements in the collection.