-
Notifications
You must be signed in to change notification settings - Fork 529
8250802: Refactor StringConverter and its subclasses #1880
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
base: master
Are you sure you want to change the base?
8250802: Refactor StringConverter and its subclasses #1880
Conversation
👋 Welcome back nlisker! A progress list of the required criteria for merging this PR into |
❗ This change is not yet ready to be integrated. |
@nlisker has indicated that a compatibility and specification (CSR) request is needed for this pull request. @nlisker please create a CSR request for issue JDK-8250802 with the correct fix version. This pull request cannot be integrated until the CSR request is approved. |
Webrevs
|
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.
Looks fine, left some readability/style comments
* Creates a {@code FormatStringConverter} for the given {@code Format} instance. | ||
* @param format the {@code Format} instance | ||
*/ | ||
/// Creates a `StringConverter` for arbitrary types that uses the given `Format`. |
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.
Shouldn't that be FormatStringConverter
that it creates?
To be honest, I never bother repeating this on constructors, so I just write "Creates a new instance ... "
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.
All the converters used this language prior. The language on constructors isn't unified across JavaFX and the JDK. Between "Creates a new default instance", "Creates a default ", "Constructs a..." and others I don't have a preference. I can replace all class names with "new instance" if you think it's better.
modules/javafx.base/src/main/java/javafx/util/converter/FormatStringConverter.java
Outdated
Show resolved
Hide resolved
modules/javafx.base/src/main/java/javafx/util/converter/FormatStringConverter.java
Outdated
Show resolved
Hide resolved
public CurrencyStringConverter() { | ||
this(Locale.getDefault()); | ||
super(); | ||
} |
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.
super()
here is just noise
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 opted to explicitly use super
in these places for uniformity with the other constructors as it can help understand which constructor is called from where. The "constructor jungle" in these classes made me do things I don't normally do. In LocalDateStringConverter
, for example, the constructor goes through a this
constructor instead of super
, so it can be confusing. Can remove anyway.
this.pattern = pattern; | ||
this.numberFormat = numberFormat; | ||
private NumberFormat createFormat(Locale locale, String pattern) { | ||
locale = Objects.requireNonNullElse(locale, Locale.getDefault()); |
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.
Parameter reassignment is really bad form (Eclipse has a warning for it if you'd care to enable it).
Also I'm not sure I like Objects.requireNonNullElse
; it's actually longer than:
this.locale = locale == null ? Locale.getDefault() : locale;
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.
Parameter reassignment is helpful when immediately "curating", e.g., substituting nulls and clamping (Java doesn't have the language mechanisms for dealing with this problem). It avoids using the wrong value later on by not having access to an illegal variable, and the parameter name still carries the same semantics.
I wasn't sure about Objects.requireNonNullElse
. I used the ternary in other places where I didn't want the alternative result to be evaluate (the parallel of Objects.requireNonNullElseGet
). The number of characters doesn't bother me because the method conveys a simple meaning, unlike the ternary where you actually need to figure out what each branch does. Some people import Objects
statically since these null
checks are very common.
I'll switch it anyway.
/// @param chronology the `Chronology` that will be used by the formatter and parser. If `null`, | ||
/// [IsoChronology#INSTANCE] will be used. | ||
public LocalDateTimeStringConverter(FormatStyle dateStyle, FormatStyle timeStyle, Locale locale, Chronology chronology) { | ||
// JEP-513 could make this look better by moving the null checks before super |
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 seems like an irrelevant comment. I doubt it would even look better if you did this (as you'd require variables). How about just making it nice to read like:
super(
Objects.requireNonNullElse(dateStyle, FormatStyle.SHORT),
Objects.requireNonNullElse(timeStyle, FormatStyle.SHORT),
locale,
chronology
);
Also, you're using here an indent that is not a multiple of 4.
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 think that the checks before calling the constructors make code clearer. I've been using flexible constructor bodies for this for a couple of versions now and I prefer it. As for the indent, it aligns with the parameter above it.
Refactoring of all
StringConverter
s and their tests. General notes:null
parameter rules have been documented.@BeforeAll
s were removed.private final
to guarantee immutability.Incremental commits are provided for easier reviewing:
Parent classes
StringConverter
: updated documentationBaseStringConverter
: a new internal class that implements repeated code from converter implementations and serves as an intermediate superclass. It does empty andnull
string checks that are handled uniformly, except forDefaultStringConverter
, which has a different formatting mechanism.Primitive-related converters
Format converter
null
during constriction time to avoid runtime NPEs.protected Format getFormat()
(as in JDK-8314597 and JDK-8260475.Number and subclasses converters
locale
andpattern
fields were removed (along with their tests). The class generated a new formatter from these on each call. This only makes sense for mutable fields where the resulting formatter can change, but here the formatter can be computed once on construction and stored.null
pattern, which was encapsulated in thegetSpecializedNumberFormat
method.protected NumberFormat getNumberFormat()
was removed. Can be split to its own issue if preferred. In my opinion, it shouldn't exist even internally since testing the internal formatter doesn't help. The only tests here should be for to/from strings, and these are lacking. A followup can be filed for adding more conversion tests.Date/Time converters
java.time
classes instead of the oldDate
class.dateFormat
field was kept, which is created once on construction instead of on each call.getSpecialziedDateFormat
method is the only difference between the classes.protected DateFormat getDateFormat()
has been removed from the subclasses and shouldn't exist internally either in my opinion.LocalDate/Time converters
LdtConverter
implementation by passing their class type, which is not a good design. Instead, an internal superclassBaseTemporalStringConverter<T extends Temporal>
was introduced along with its shim.fixFourDigitYear
, is now checked before creating a formatter, avoiding unneeded formatter creations.parser
andformatter
fields were kept.getLocalizedFormatter
andgetTemporalQuery()
methods are the only differences between the subclasses./reviewers 2
/csr
Progress
Issue
Reviewers
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jfx.git pull/1880/head:pull/1880
$ git checkout pull/1880
Update a local copy of the PR:
$ git checkout pull/1880
$ git pull https://git.openjdk.org/jfx.git pull/1880/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 1880
View PR using the GUI difftool:
$ git pr show -t 1880
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/1880.diff
Using Webrev
Link to Webrev Comment