Skip to content
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

Add NullSafeTypeAdapter to prevent TypeAdapter.nullSafe() from returning nested null-safe type adapters (#2729) #2731

Merged
merged 1 commit into from
Sep 3, 2024

Conversation

lyubomyr-shaydariv
Copy link
Contributor

@lyubomyr-shaydariv lyubomyr-shaydariv commented Aug 30, 2024

Add NullSafeTypeAdapter to prevent TypeAdapter.nullSafe() from returning nested null-safe type adapters (#2729)

Purpose / Description

Resolves #2729.

Checklist

  • New code follows the Google Java Style Guide
    This is automatically checked by mvn verify, but can also be checked on its own using mvn spotless:check.
    Style violations can be fixed using mvn spotless:apply; this can be done in a separate commit to verify that it did not cause undesired changes.
  • If necessary, new public API validates arguments, for example rejects null
  • New public API has Javadoc
    • Javadoc uses @since $next-version$
      ($next-version$ is a special placeholder which is automatically replaced during release)
  • If necessary, new unit tests have been added
    • Assertions in unit tests use Truth, see existing tests
    • No JUnit 3 features are used (such as extending class TestCase)
    • If this pull request fixes a bug, a new test was added for a situation which failed previously and is now fixed
  • mvn clean verify javadoc:jar passes without errors

Closes #2729.

Copy link
Collaborator

@Marcono1234 Marcono1234 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot!

I have added a few comments, but feel free to consider them only suggestions. Hopefully they are useful.

Also, don't worry too much about the Git history of this PR branch; the commits will be squashed on merge anyway.

} else {
TypeAdapter.this.write(out, value);
}
if (!NullSafeTypeAdapter.class.isInstance(this)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, but why not !(this instanceof NullSafeTypeAdapter)?

Copy link
Contributor Author

@lyubomyr-shaydariv lyubomyr-shaydariv Sep 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Marcono1234

Yeah, I had instanceof in mind, but not sure how it all works as of today. The rule org.apache.maven.enforcer.rules.version.RequireJavaVersion fails with

Detected JDK version 21 (JAVA_HOME=/opt/.bin/jdk-21) is not in the allowed range [11,18)

If the type check is changed to !(this instanceof NullSafeTypeAdapter), then:

  • for JDK 11, it fails with /src/main/java/com/google/gson/TypeAdapter.java:[292,27] illegal generic type for instanceof;
  • for JDK 17, it fails with /src/main/java/com/google/gson/TypeAdapter.java:[292,16] reifiable types in instanceof are not supported in -source 7.

The Class.instanceOf check works for all. However, according to the source code, it can work with TypeAdapter<?> at least in GsonBuilder.

I seem to have some lack of the Java language knowledge. Please suggest.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah sorry, it seems !(this instanceof NullSafeTypeAdapter) is not possible because NullSafeTypeAdapter is a non-static inner class (and the enclosing class TypeAdapter<T> is generic?).

The compiler error message is really not helpful, but in Eclipse IDE the error message included the suggested fix, qualify with the enclosing type: TypeAdapter.NullSafeTypeAdapter. Can you please try this instead?

Suggested change
if (!NullSafeTypeAdapter.class.isInstance(this)) {
if (!(this instanceof TypeAdapter.NullSafeTypeAdapter)) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never encountered this error before preferring static inner classes to capture as few as possible, and now I see there are still "good-to-knows". But no doubt, isAssignable looked really weird and unnatural. Thank you!

}

private static final TypeAdapter<String> assertionErrorAdapter =
new TypeAdapter<String>() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: String can probably be omitted here since it can be inferred

Suggested change
new TypeAdapter<String>() {
new TypeAdapter<>() {

Copy link
Contributor Author

@lyubomyr-shaydariv lyubomyr-shaydariv Sep 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I merely moved the `TypeAdapter` instance from the method to the field leaving the type parameter as it was previously in method. My rationale is that I should follow the convention and do not touch use other language level for code I don't own and that can be a subject of refactoring in the future.

If you're really fine with changing from TypeAdapter<String> to TypeAdapter<>, I'll change <String> to <>.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: String can probably be omitted here since it can be inferred

My bad: I seem to have missed the line change I made accidentally. Also I removed the assertionErrorAdapter TypeAdapter type from <String> to <> -- I saw the diamond operator has been in use at least in this test class.

Comment on lines 76 to 87
new TypeAdapter<>() {
new TypeAdapter<String>() {
Copy link
Collaborator

@Marcono1234 Marcono1234 Sep 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not needed since we compile the test code with Java 11.

Though IDEs sometimes have problems detecting this, and erroneously assume Java 7 is used for test compilation as well.

return this;
}

private final class NullSafeTypeAdapter extends TypeAdapter<T> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As suggested in #2729 (comment), do you want to take the chance and implement toString as well. I think at least some other adapters implement it, and it would then make it easier to see which adapter is wrapped by this one.

Copy link
Contributor Author

@lyubomyr-shaydariv lyubomyr-shaydariv Sep 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Marcono1234

Oh, I seem to have misterstood your toString() suggestion earlier leaving it out of scope (I mistakenly assumed it leads to a stack overflow error, but it doesn't).

Could you please suggest the NullSafeTypeAdapter.toString() pattern you'd like to see?

Copy link
Contributor Author

@lyubomyr-shaydariv lyubomyr-shaydariv Sep 2, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As suggested in #2729 (comment), do you want to take the chance and implement toString as well. I think at least some other adapters implement it, and it would then make it easier to see which adapter is wrapped by this one.

I applied the NullSafeTypeAdapter[%s] pattern you suggested in #2729 (comment).

@Marcono1234
Copy link
Collaborator

And could you please edit the description of the pull request to say "Closes #2729"? Otherwise GitHub does not understand this, and will keep the issue open after merge, unless they are linked manually.

@lyubomyr-shaydariv
Copy link
Contributor Author

lyubomyr-shaydariv commented Sep 1, 2024

And could you please edit the description of the pull request to say "Closes #2729"? Otherwise GitHub does not understand this, and will keep the issue open after merge, unless they are linked manually.

Do you mean to add Closes #2729 literally, so that GitHub detects the ticked #-id following the Closes ? I was not aware of this behavior, and thought that ... type adapters (#2729) would be sufficient.

Maybe I'm misusing git but git rev-list --perl-regexp --count --grep='\bClose:?\b' fix-2729 returns no commmits. (My local fix-2729 branch is currently pointing to 91246be in my fork repo.)

@Marcono1234
Copy link
Collaborator

Marcono1234 commented Sep 2, 2024

Do you mean to add Closes #2729 literally, so that GitHub detects the ticked #-id following the Closes ?

Yes, just edit the description of your pull request text and add that anywhere. See for example #2704 which used "Resolves #...".

GitHub recognizes certain keywords and in that case will automatically close the linked issue.
It will also show this information then on the right sidebar of

  • the issue
    issue screenshot
  • the pull request
    pull request screenshot

You can also link issue and pull request manually1, but it is easier if the creator of the pull request directly does this themselves through the keywords. Otherwise it is easy to forget manually linking the issue and pull request.

That works as well if you write it in the commit message (and the commit is then merged), but personally I avoid this (unless the contribution guide of a project requests it) since for each commit this already shows in the GitHub UI for the issue a "added a commit to ... that referenced this issue" message, which can be rather distracting. So I prefer to only add it to the pull request description.

I was not aware of this behavior, and thought that ... type adapters (#2729) would be sufficient.

It seems that just creates a regular "mention" without having any other effect. See for example #2729 which you referenced:
issue screenshot

(No worries though about the multiple commit mentions there.)

Footnotes

  1. If you are a member of the project (?). But I am not a direct member of the Gson project, so I cannot do it here. See also the GitHub documentation.

@lyubomyr-shaydariv
Copy link
Contributor Author

Do you mean to add Closes #2729 literally, so that GitHub detects the ticked #-id following the Closes ?

Yes, just edit the description of your pull request text and add that anywhere.

The funny thing is that I thought you meant the git commit message, not the PR description, and this is why I referenced the git-rev-list command output that produced empty result. And now I also see why it is not a good idea for GitHub, kind of shame on GitHub from this perspective. Thank you for the comprehensive explanation!

from returning nested null-safe type adapters (#2729)
Copy link
Collaborator

@Marcono1234 Marcono1234 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@eamonnmcmanus, what do you think about this pull request?

Copy link
Member

@eamonnmcmanus eamonnmcmanus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this!

@eamonnmcmanus eamonnmcmanus merged commit 7c55d8b into google:main Sep 3, 2024
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants