Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 23 additions & 23 deletions modules/javafx.base/src/main/java/javafx/util/StringConverter.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,35 +25,35 @@

package javafx.util;

/**
* Converter defines conversion behavior between strings and objects.
* The type of objects and formats of strings are defined by the subclasses
* of Converter.
*
* @param <T> the type of the object being converted
* @since JavaFX 2.0
*/
/// Defines conversion behaviors between `String` and other types. Each subclass defines a formatting (type to string)
/// and a parsing (string to type) behaviors. `StringConverter`s are usually used in controls for converting between the
/// underlying model (represented by type `T`) and the visual `String` representation.
///
/// Subclasses are provided for primitive wrapper types, numbers, dates and times, and custom formats.
///
/// @implNote
/// JavaFX's implementations follow these behaviors, which are not required by implementing classes:
/// - Except for `DefaultStringConverter`, formatting `null` returns an empty string, otherwise the type's `toString` is
/// used if it is suitable; parsing `null` or an empty string returns `null`.
/// - Immutable (the same converter can be reused).
///
/// @param <T> the type associated with the string conversions
/// @since JavaFX 2.0
public abstract class StringConverter<T> {

/**
* Creates a default {@code StringConverter}.
*/
/// Creates a default {@code StringConverter}.
public StringConverter() {
}

/**
* Converts the object provided into its string form.
* Format of the returned string is defined by the specific converter.
* @param object the object of type {@code T} to convert
* @return a string representation of the object passed in
*/
/// Converts an object to a string form. The format of the returned string is defined by the specific converter.
///
/// @param object the object of type `T` to convert
/// @return a string representation of the object passed in
public abstract String toString(T object);

/**
* Converts the string provided into an object defined by the specific converter.
* Format of the string and type of the resulting object is defined by the specific converter.
* @param string the {@code String} to convert
* @return an object representation of the string passed in.
*/
/// Converts a string to an object. The parsing mechanism is defined by the specific converter.
///
/// @param string the `String` to convert
/// @return an object of type `T` created from the string passed in.
public abstract T fromString(String string);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand All @@ -25,32 +25,37 @@

package javafx.util.converter;

import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.util.Locale;

public class LocalDateStringConverterShim {

public static DateTimeFormatter getldtConverterFormatter(LocalDateStringConverter c) {
return c.ldtConverter.formatter;
}
//-----------------------------------------------------

//---------
public static Locale getldtConverterLocale(LocalDateStringConverter c) {
return c.ldtConverter.locale;
import javafx.util.StringConverter;

/// A base class containing common implementations for `StringConverter`s as noted in the @implNote of `StringConverter`.
abstract class BaseStringConverter<T> extends StringConverter<T> {

@Override
public T fromString(String string) {
if (string == null) {
return null;
}
string = string.trim();
if (string.isEmpty()) {
return null;
}
return fromNonEmptyString(string);
}

public static DateTimeFormatter getldtConverterParser(LocalDateStringConverter c) {
return c.ldtConverter.parser;
}
/// Returns an object parsed from a non-{@code null} non-empty string.
///
/// Treat as protected (implementing classes are public so they can't add a new protected method).
abstract T fromNonEmptyString(String string);

public static FormatStyle getldtConverterTimeStyle(LocalDateStringConverter c) {
return c.ldtConverter.timeStyle;
@Override
public String toString(T object) {
return object == null ? "" : toStringFromNonNull(object);
}

public static FormatStyle getldtConverterDateStyle(LocalDateStringConverter c) {
return c.ldtConverter.dateStyle;
/// Returns a string from a non-`null` reference.
///
/// Treat as protected (implementing classes are public so they can't add a new protected method).
String toStringFromNonNull(T object) {
return object.toString();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package javafx.util.converter;

import java.time.chrono.Chronology;
import java.time.chrono.IsoChronology;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.DecimalStyle;
import java.time.format.FormatStyle;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalQuery;
import java.util.Locale;
import java.util.Objects;

/// Base class for the java.time types converters.
abstract class BaseTemporalStringConverter<T extends Temporal> extends BaseStringConverter<T> {

private static final Locale DEFAULT_LOCALE = Locale.getDefault(Locale.Category.FORMAT);
private static final Chronology DEFAULT_CHRONO = IsoChronology.INSTANCE;

private final DateTimeFormatter formatter;
private final DateTimeFormatter parser;

protected BaseTemporalStringConverter(FormatStyle dateStyle, FormatStyle timeStyle, Locale locale, Chronology chrono) {
locale = Objects.requireNonNullElse(locale, DEFAULT_LOCALE);
chrono = Objects.requireNonNullElse(chrono, DEFAULT_CHRONO);
Comment on lines +49 to +50
Copy link
Collaborator

Choose a reason for hiding this comment

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

Parameter reassignment here. It all looks like fields, but only the last two are. Fields could be prefixed with this. to make this clearer, but IMHO parameter assignment is always bad.

Copy link
Collaborator Author

@nlisker nlisker Aug 25, 2025

Choose a reason for hiding this comment

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

IDEs can show fields and variables differently so it's very easy to discern. Perhaps it depends on your text editor settings. Adding "this" tends to be more noise than it's worth in my opinion. Can change if there's an agreement.

formatter = createFormatter(dateStyle, timeStyle, locale, chrono);
parser = createParser(dateStyle, timeStyle, locale, chrono);
}

protected BaseTemporalStringConverter(DateTimeFormatter formatter, DateTimeFormatter parser,
FormatStyle dateStyle, FormatStyle timeStyle) {
this.formatter = formatter != null ? formatter :
createFormatter(dateStyle, timeStyle, DEFAULT_LOCALE, DEFAULT_CHRONO);
this.parser = parser != null ? parser :
formatter != null ? formatter :
createParser(dateStyle, timeStyle, DEFAULT_LOCALE, DEFAULT_CHRONO);
}

private final DateTimeFormatter createParser(FormatStyle dateStyle, FormatStyle timeStyle, Locale locale, Chronology chrono) {
String pattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, chrono, locale);
return new DateTimeFormatterBuilder().parseLenient()
.appendPattern(pattern)
.toFormatter()
.withChronology(chrono)
.withDecimalStyle(DecimalStyle.of(locale));
}

/// To satisfy the requirements of date formatters as described in, e.g.,
/// [LocalDateStringConverter#LocalDateStringConverter()], the method checks if there's a need to modify the pattern.
private final DateTimeFormatter createFormatter(FormatStyle dateStyle, FormatStyle timeStyle, Locale locale, Chronology chrono) {
if (dateStyle != null) {
String pattern = DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, chrono, locale);
if (pattern.contains("yy") && !pattern.contains("yyy")) {
// Modify pattern to show four-digit year, including leading zeros.
String newPattern = pattern.replace("yy", "yyyy");
return DateTimeFormatter.ofPattern(newPattern).withDecimalStyle(DecimalStyle.of(locale));
}
}
return getLocalizedFormatter(dateStyle, timeStyle)
.withLocale(locale)
.withChronology(chrono)
.withDecimalStyle(DecimalStyle.of(locale));
}

abstract DateTimeFormatter getLocalizedFormatter(FormatStyle dateStyle, FormatStyle timeStyle);

@Override
String toStringFromNonNull(T value) {
return formatter.format(value);
}

@Override
T fromNonEmptyString(String string) {
return parser.parse(string, getTemporalQuery());
}

abstract TemporalQuery<T> getTemporalQuery();

/// For tests only
DateTimeFormatter getFormatter() {
return formatter;
}

/// For tests only
DateTimeFormatter getParser() {
return parser;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,43 +26,18 @@
package javafx.util.converter;

import java.math.BigDecimal;
import javafx.util.StringConverter;

/**
* <p>{@link StringConverter} implementation for {@link BigDecimal} values.</p>
* @since JavaFX 2.1
*/
public class BigDecimalStringConverter extends StringConverter<BigDecimal> {
/// A `StringConverter` implementation for [BigDecimal] values.
///
/// @since JavaFX 2.1
public class BigDecimalStringConverter extends BaseStringConverter<BigDecimal> {

/**
* Creates a default {@code BigDecimalStringConverter}.
*/
/// Creates a default `BigDecimalStringConverter`.
public BigDecimalStringConverter() {
}

/** {@inheritDoc} */
@Override public BigDecimal fromString(String value) {
// If the specified value is null or zero-length, return null
if (value == null) {
return null;
}

value = value.trim();

if (value.length() < 1) {
return null;
}

return new BigDecimal(value);
}

/** {@inheritDoc} */
@Override public String toString(BigDecimal value) {
// If the specified value is null, return a zero-length String
if (value == null) {
return "";
}

return value.toString();
@Override
BigDecimal fromNonEmptyString(String string) {
return new BigDecimal(string);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,43 +26,18 @@
package javafx.util.converter;

import java.math.BigInteger;
import javafx.util.StringConverter;

/**
* <p>{@link StringConverter} implementation for {@link BigInteger} values.</p>
* @since JavaFX 2.1
*/
public class BigIntegerStringConverter extends StringConverter<BigInteger> {
/// A `StringConverter` implementation for [BigInteger] values.
///
/// @since JavaFX 2.1
public class BigIntegerStringConverter extends BaseStringConverter<BigInteger> {

/**
* Creates a default {@code BigIntegerStringConverter}.
*/
/// Creates a default `BigIntegerStringConverter`.
public BigIntegerStringConverter() {
}

/** {@inheritDoc} */
@Override public BigInteger fromString(String value) {
// If the specified value is null or zero-length, return null
if (value == null) {
return null;
}

value = value.trim();

if (value.length() < 1) {
return null;
}

return new BigInteger(value);
}

/** {@inheritDoc} */
@Override public String toString(BigInteger value) {
// If the specified value is null, return a zero-length String
if (value == null) {
return "";
}

return value.toString();
@Override
BigInteger fromNonEmptyString(String string) {
return new BigInteger(string);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,44 +25,18 @@

package javafx.util.converter;

import javafx.util.StringConverter;
/// A `StringConverter` implementation for `Boolean` (and `boolean`) values. Formatting is done by [Boolean#toString()]
/// for non-`null` values, and parsing with [Boolean#valueOf(String)] for non-`null` non-empty strings.
///
/// @since JavaFX 2.1
public class BooleanStringConverter extends BaseStringConverter<Boolean> {

/**
* <p>{@link StringConverter} implementation for {@link Boolean}
* (and boolean primitive) values.</p>
* @since JavaFX 2.1
*/
public class BooleanStringConverter extends StringConverter<Boolean> {

/**
* Creates a default {@code BooleanStringConverter}.
*/
/// Creates a default `BooleanStringConverter`.
public BooleanStringConverter() {
}

/** {@inheritDoc} */
@Override public Boolean fromString(String value) {
// If the specified value is null or zero-length, return null
if (value == null) {
return null;
}

value = value.trim();

if (value.length() < 1) {
return null;
}

return Boolean.valueOf(value);
}

/** {@inheritDoc} */
@Override public String toString(Boolean value) {
// If the specified value is null, return a zero-length String
if (value == null) {
return "";
}

return value.toString();
@Override
Boolean fromNonEmptyString(String string) {
return Boolean.valueOf(string);
}
}
Loading