This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 0ab3a59500 Minor refactoring of the `PropertyFormat` internal class 
for making it more convenient to extend.
0ab3a59500 is described below

commit 0ab3a595005692dc6b35fa5e27a000cc86ede903
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sun May 17 17:26:55 2026 +0200

    Minor refactoring of the `PropertyFormat` internal class for making it more 
convenient to extend.
---
 .../main/org/apache/sis/io/CompoundFormat.java     | 77 ++++++++++++++--------
 .../main/org/apache/sis/math/StatisticsFormat.java | 14 ++--
 .../sis/util/collection/TreeTableFormat.java       | 27 ++++++++
 .../sis/util/internal/shared/PropertyFormat.java   | 74 +++++++++++----------
 .../org/apache/sis/storage/isobmff/TreeNode.java   | 26 +-------
 .../sis/gui/coverage/ImagePropertyExplorer.java    | 12 +---
 .../sis/gui/internal/PropertyValueFormatter.java   |  2 +-
 .../org/apache/sis/gui/metadata/MetadataTree.java  | 12 +---
 8 files changed, 128 insertions(+), 116 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/CompoundFormat.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/CompoundFormat.java
index b3ad608ad6..87579f13bd 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/CompoundFormat.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/CompoundFormat.java
@@ -64,7 +64,7 @@ import static 
org.apache.sis.util.internal.shared.Constants.UTC;
 /**
  * Base class of {@link Format} implementations which delegate part of their 
work to other
  * {@code Format} instances. {@code CompoundFormat} subclasses typically work 
on relatively
- * large blocks of data, for example a metadata tree or a <i>Well Known 
Text</i> (WKT).
+ * large blocks of data, for example a metadata tree or a <i>Well Known 
Text</i> (<abbr>WKT</abbr>).
  * Those blocks of data usually contain smaller elements like numbers and 
dates, whose parsing
  * and formatting can be delegated to {@link NumberFormat} and {@link 
DateFormat} respectively.
  * Subclasses can obtain instances of those formats by call to {@link 
#getFormat(Class)} where
@@ -84,10 +84,9 @@ import static 
org.apache.sis.util.internal.shared.Constants.UTC;
  * </table>
  *
  * <h2>Sources and destinations</h2>
- * Since {@code CompoundFormat} may work on larger texts than the usual {@code 
Format} classes,
- * it defines {@code parse} and {@code format} methods working with arbitrary 
{@link CharSequence}
- * and {@link Appendable} instances. The standard {@code Format} methods 
redirect to the above-cited
- * methods.
+ * Since {@code CompoundFormat} may work on texts larger than the texts 
expected by the standard {@code Format} classes,
+ * it defines {@code parse} and {@code format} methods working with {@link 
CharSequence} and {@link Appendable} types.
+ * The standard {@code Format} methods redirect to the above-cited methods.
  *
  * <h2>Sub-classing</h2>
  * The abstract methods to be defined by subclasses are:
@@ -97,7 +96,7 @@ import static 
org.apache.sis.util.internal.shared.Constants.UTC;
  *   <li>{@link #parse(CharSequence, ParsePosition)}</li>
  * </ul>
  *
- * <h2>Comparison with other API</h2>
+ * <h2>Comparison with other <abbr>API</abbr></h2>
  * In the standard {@link Format} class, the {@code parse} methods either 
accept a {@link ParsePosition} argument
  * and returns {@code null} on error, or does not take position argument and 
throws a {@link ParseException} on error.
  * In this {@code CompoundFormat} class, the {@code parse} method both takes a 
{@code ParsePosition} argument and
@@ -105,7 +104,7 @@ import static 
org.apache.sis.util.internal.shared.Constants.UTC;
  * in case of error.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.6
+ * @version 1.7
  *
  * @param <T>  the base type of objects parsed and formatted by this class.
  *
@@ -118,15 +117,15 @@ public abstract class CompoundFormat<T> extends Format 
implements Localized {
     private static final long serialVersionUID = -689151528653024968L;
 
     /**
-     * The locale given at construction time, or {@link Locale#ROOT} (never 
{@code null}) for
-     * unlocalized format. See {@link #getLocale()} for more information on 
{@code ROOT} locale.
+     * The locale given at construction time, or {@link Locale#ROOT} (never 
{@code null}) for unlocalized format.
+     * See {@link #getLocale()} for more information on {@code ROOT} locale.
      *
      * @see #getLocale()
      */
     private final Locale locale;
 
     /**
-     * The timezone given at construction time, or {@code null} for UTC.
+     * The timezone given at construction time, or {@code null} for 
<abbr>UTC</abbr>.
      *
      * @see #getTimeZone()
      */
@@ -144,7 +143,7 @@ public abstract class CompoundFormat<T> extends Format 
implements Localized {
      * See {@link #getLocale()} for more information about the {@code ROOT} 
locale.
      *
      * @param  locale    the locale for the new {@code Format}, or {@code 
null} for {@code Locale.ROOT}.
-     * @param  timezone  the timezone, or {@code null} for UTC.
+     * @param  timezone  the timezone, or {@code null} for <abbr>UTC</abbr>.
      */
     protected CompoundFormat(final Locale locale, final TimeZone timezone) {
         this.locale   = (locale != null) ? locale : Locale.ROOT;
@@ -181,10 +180,10 @@ public abstract class CompoundFormat<T> extends Format 
implements Localized {
      * For subclasses that do not override this method, the default 
implementation returns {@link #getLocale()}.
      *
      * <h4>Example</h4>
-     * The ISO 19162 (<cite>Well Known Text</cite>) standard requires a number 
format similar to the one defined by
-     * {@code Locale.ROOT} while it allows informative texts (remarks, 
<i>etc.</i>) to be formatted according the
-     * user's locale. Consequently, {@code WKTFormat} fixes (usually) the 
locale for {@code Category.FORMAT} to
-     * {@code Locale.ROOT} and let {@code Category.DISPLAY} be any locale.
+     * The <abbr>ISO</abbr> 19162 (<cite>Well Known Text</cite>) standard 
requires a number format similar to the
+     * one defined by {@code Locale.ROOT} while it allows informative texts 
(remarks, <i>etc.</i>) to be formatted
+     * according the user's locale. Therefore, {@code WKTFormat} fixes 
(usually) the locale for {@code Category.FORMAT}
+     * to {@code Locale.ROOT} and let {@code Category.DISPLAY} be any locale.
      *
      * @param  category  the category for which a locale is desired.
      * @return the locale for the given category (never {@code null}).
@@ -199,7 +198,7 @@ public abstract class CompoundFormat<T> extends Format 
implements Localized {
     /**
      * Returns the timezone used by this format.
      *
-     * @return the timezone used for this format, or UTC for unlocalized 
format.
+     * @return the timezone used for this format, or <abbr>UTC</abbr> for 
unlocalized format.
      */
     public TimeZone getTimeZone() {
         return (timezone != null) ? (TimeZone) timezone.clone() : 
TimeZone.getTimeZone(UTC);
@@ -308,13 +307,14 @@ public abstract class CompoundFormat<T> extends Format 
implements Localized {
      * The default implementation delegates to {@link #parse(CharSequence, 
ParsePosition)}
      * and ensures that the given string has been fully used, ignoring trailing
      * {@linkplain Character#isSpaceChar(int) spaces} and
-     * {@linkplain Character#isISOControl(int) ISO control characters}.
+     * {@linkplain Character#isISOControl(int) <abbr>ISO</abbr> control 
characters}.
      *
      * <h4>Whitespaces</h4>
-     * The usual SIS policy, as documented in the {@link 
org.apache.sis.util.CharSequences} class, is to test for
-     * whitespaces using the {@code Character.isWhitespace(…)} method. The 
combination of {@code isSpaceChar(…)}
-     * and {@code isISOControl(…)} done in this {@code parseObject(…)} method 
is more permissive since it encompasses
-     * all whitespace characters, plus non-breaking spaces and non-white ISO 
controls.
+     * The usual <abbr>SIS</abbr> policy, as documented in the {@link 
org.apache.sis.util.CharSequences} class,
+     * is to test for whitespaces using the {@code Character.isWhitespace(…)} 
method.
+     * The combination of {@code isSpaceChar(…)} and {@code isISOControl(…)} 
done in this {@code parseObject(…)}
+     * method is more permissive since it encompasses all whitespace 
characters,
+     * plus non-breaking spaces and non-white <abbr>ISO</abbr> controls.
      *
      * @param  text  the string representation of the object to parse.
      * @return the parsed object.
@@ -339,6 +339,30 @@ public abstract class CompoundFormat<T> extends Format 
implements Localized {
         throw new LocalizedParseException(getLocale(Locale.Category.DISPLAY), 
getValueType(), text, pos);
     }
 
+    /**
+     * Writes a textual representation of an object of arbitrary class in the 
given stream or buffer.
+     * This method verifies that the given object is an instance of the 
expected the type returned by
+     * {@link #getValueType()}, then delegates to {@link #format(Object, 
Appendable)}.
+     *
+     * @param  object      the object to format.
+     * @param  toAppendTo  where to format the object.
+     * @throws IllegalArgumentException if the given object is not an instance
+     *         of the expected {@linkplain #getValueType() value type}.
+     * @throws IOException if an error occurred while writing to the given 
appendable.
+     *
+     * @since 1.7
+     */
+    public void formatObject(final Object object, final Appendable toAppendTo) 
throws IOException {
+        final T value;
+        final Class<? extends T> valueType = getValueType();
+        try {
+            value = valueType.cast(object);
+        } catch (ClassCastException e) {
+            throw new 
IllegalArgumentException(Errors.format(Errors.Keys.IllegalClass_2, valueType, 
Classes.getClass(object)), e);
+        }
+        format(value, toAppendTo);
+    }
+
     /**
      * Writes a textual representation of the given object in the given stream 
or buffer.
      *
@@ -370,7 +394,7 @@ public abstract class CompoundFormat<T> extends Format 
implements Localized {
 
     /**
      * Writes a textual representation of the specified object in the given 
buffer.
-     * This method delegates the work to {@link #format(Object, Appendable)}.
+     * This method delegates the work to {@link #formatObject(Object, 
Appendable)}.
      * If an {@link IOException} occurs (for example, because {@code 
format(…)} performs
      * some I/O operations on other objects than the given {@link 
StringBuffer}),
      * the exception is wrapped in {@link UncheckedIOException}.
@@ -379,14 +403,13 @@ public abstract class CompoundFormat<T> extends Format 
implements Localized {
      * @param  toAppendTo  where to format the object.
      * @param  pos         ignored in current implementation.
      * @return the given buffer, returned for convenience.
+     * @throws IllegalArgumentException if the given object is not an instance
+     *         of the expected {@linkplain #getValueType() value type}.
      */
     @Override
     public StringBuffer format(final Object object, final StringBuffer 
toAppendTo, final FieldPosition pos) {
-        final Class<? extends T> valueType = getValueType();
         try {
-            format(valueType.cast(object), toAppendTo);
-        } catch (ClassCastException e) {
-            throw new 
IllegalArgumentException(Errors.format(Errors.Keys.IllegalClass_2, valueType, 
Classes.getClass(object)), e);
+            formatObject(object, toAppendTo);
         } catch (IOException e) {
             /*
              * Should never happen when writing into a StringBuffer, unless 
the error
@@ -594,7 +617,7 @@ public abstract class CompoundFormat<T> extends Format 
implements Localized {
     @Override
     public CompoundFormat<T> clone() {
         @SuppressWarnings("unchecked")
-        final CompoundFormat<T> clone = (CompoundFormat<T>) super.clone();
+        final var clone = (CompoundFormat<T>) super.clone();
         if (clone.formats != null) {
             clone.formats = new IdentityHashMap<>(clone.formats);
             for (final Map.Entry<Class<?>,Format> entry : 
clone.formats.entrySet()) {
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/StatisticsFormat.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/StatisticsFormat.java
index 6211240521..499ebb6a48 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/StatisticsFormat.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/math/StatisticsFormat.java
@@ -23,7 +23,6 @@ import java.io.IOException;
 import java.text.Format;
 import java.text.NumberFormat;
 import java.text.DecimalFormat;
-import java.text.FieldPosition;
 import java.text.ParsePosition;
 import java.text.ParseException;
 import org.opengis.util.InternationalString;
@@ -37,7 +36,7 @@ import org.apache.sis.util.internal.shared.Numerics;
 
 
 /**
- * Formats a {@link Statistics} object.
+ * Formats a {@code Statistics} object.
  * By default, newly created {@code StatisticsFormat} instances will format 
statistical values
  * in a tabular format using spaces as the column separator.
  *
@@ -57,7 +56,7 @@ import org.apache.sis.util.internal.shared.Numerics;
  * </ul>
  *
  * @author  Martin Desruisseaux (MPO, IRD, Geomatys)
- * @version 1.0
+ * @version 1.7
  *
  * @see Statistics#toString()
  *
@@ -236,19 +235,18 @@ public class StatisticsFormat extends 
TabularFormat<Statistics> {
      *
      * @param  object      the object to format.
      * @param  toAppendTo  where to format the object.
-     * @param  pos         ignored in current implementation.
-     * @return the given buffer, returned for convenience.
+     * @throws IllegalArgumentException if the given object is not an instance 
of one of the above-cited types.
+     * @throws IOException if an error occurred while writing to the given 
appendable.
      */
     @Override
-    public StringBuffer format(final Object object, final StringBuffer 
toAppendTo, final FieldPosition pos) {
+    public void formatObject(final Object object, final Appendable toAppendTo) 
throws IOException {
         if (object instanceof Statistics[]) try {
             format((Statistics[]) object, toAppendTo);
-            return toAppendTo;
         } catch (IOException e) {
             // Same exception handling as in the super-class.
             throw new BackingStoreException(e);
         } else {
-            return super.format(object, toAppendTo, pos);
+            super.formatObject(object, toAppendTo);
         }
     }
 
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/TreeTableFormat.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/TreeTableFormat.java
index 54018b3535..a353e52f16 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/TreeTableFormat.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/collection/TreeTableFormat.java
@@ -31,6 +31,7 @@ import java.text.Format;
 import java.text.DecimalFormat;
 import java.text.ParsePosition;
 import java.text.ParseException;
+import org.apache.sis.io.CompoundFormat;
 import org.apache.sis.io.TableAppender;
 import org.apache.sis.io.TabularFormat;
 import org.apache.sis.measure.UnitFormat;
@@ -639,6 +640,12 @@ public class TreeTableFormat extends 
TabularFormat<TreeTable> {
          */
         private final Format[] formats;
 
+        /**
+         * The format for the column in process of being written. This is a 
format to use for the column as a whole.
+         * This field is updated for every new column to write. May be {@code 
null} if the format is unspecified.
+         */
+        private transient Format columnFormat;
+
         /**
          * The node values to format.
          */
@@ -692,6 +699,26 @@ public class TreeTableFormat extends 
TabularFormat<TreeTable> {
             return TreeTableFormat.this.getLocale();
         }
 
+        /**
+         * Appends a textual representation of the given value.
+         *
+         * @param  value  the value to format (may be {@code null}).
+         * @throws IOException if an error occurred while writing the value.
+         */
+        @Override
+        public final void appendValue(final Object value) throws IOException {
+            if (value != null && columnFormat != null) {
+                if (columnFormat instanceof CompoundFormat<?>) {
+                    final var format = (CompoundFormat<?>) columnFormat;
+                    format.formatObject(value, this);
+                } else {
+                    append(columnFormat.format(value));
+                }
+            } else {
+                super.appendValue(value);
+            }
+        }
+
         /**
          * Invoked by {@link PropertyFormat} for formatting a value which has 
not been recognized as one of
          * the types to be handled in a special way. In particular numbers and 
dates should be handled here.
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/PropertyFormat.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/PropertyFormat.java
index f3d057f175..50cfe95def 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/PropertyFormat.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/PropertyFormat.java
@@ -16,22 +16,20 @@
  */
 package org.apache.sis.util.internal.shared;
 
-import java.text.Format;
 import java.util.Map;
 import java.util.Arrays;
 import java.util.Currency;
 import java.util.Locale;
 import java.util.TimeZone;
 import java.io.IOException;
+import java.io.UncheckedIOException;
 import java.nio.charset.Charset;
 import org.opengis.util.Type;
 import org.opengis.util.Record;
 import org.opengis.util.GenericName;
 import org.opengis.util.InternationalString;
-import org.apache.sis.io.CompoundFormat;
 import org.apache.sis.io.LineAppender;
 import org.apache.sis.util.CharSequences;
-import org.apache.sis.util.Workaround;
 import org.apache.sis.util.Localized;
 import org.apache.sis.util.resources.Vocabulary;
 
@@ -44,21 +42,22 @@ import org.opengis.util.ControlledVocabulary;
  * Tabulations are replaced by spaces, and line feeds can optionally
  * be replaced by the Pilcrow character.
  *
- * Subclasses need to override {@link #getLocale()}, and should also override 
{@link #toString(Object)}.
+ * <p>Subclasses need to override {@link #getLocale()}, and should also 
override {@link #toString(Object)}
+ * for formatting dates and numbers</p>.
  *
  * @author  Martin Desruisseaux (Geomatys)
  */
 public abstract class PropertyFormat extends LineAppender implements Localized 
{
     /**
      * The string to insert for missing values.
+     * This is a string to be appended into {@link LineAppender}, not a public 
return value.
      */
     private static final String MISSING = " ";
 
     /**
-     * The format for the column in process of being written. This is a format 
to use for the column as a whole.
-     * This field is updated for every new column to write. May be {@code 
null} if the format is unspecified.
+     * {@code true} if this method is invoking itself for writing collection 
values.
      */
-    protected transient Format columnFormat;
+    private transient boolean recursive;
 
     /**
      * Creates a new instance which will write to the given appendable.
@@ -70,31 +69,37 @@ public abstract class PropertyFormat extends LineAppender 
implements Localized {
     }
 
     /**
-     * Appends a textual representation of the given value.
+     * Returns a textual representation of the given value. This 
implementation assumes
+     * that this {@code PropertyFormat} was created with a {@link 
StringBuilder} argument.
+     * This is a convenience method for a common case.
      *
      * @param  value  the value to format (may be {@code null}).
-     * @throws IOException if an error occurred while writing the value.
+     * @return string representation of the given value.
+     * @throws ClassCastException if {@link #out} is not an instance of {@link 
StringBuilder}.
      */
-    public final void appendValue(final Object value) throws IOException {
-        appendValue(value, false);
+    public final String formatUsingStringBuilder(final Object value) {
+        final var buffer = (StringBuilder) out;
+        buffer.setLength(0);
+        try {
+            clear();
+            appendValue(value);
+            flush();
+        } catch (IOException e) {           // Should never happen since we 
write in a StringBuilder.
+            throw new UncheckedIOException(e);
+        }
+        return Strings.trimOrNull(buffer.toString());
     }
 
     /**
-     * Appends a textual representation of the given value, with a check for 
nested collections.
+     * Appends a textual representation of the given value.
      *
-     * @param  value      the value to format (may be {@code null}).
-     * @param  recursive  {@code true} if this method is invoking itself for 
writing collection values.
+     * @param  value  the value to format (may be {@code null}).
+     * @throws IOException if an error occurred while writing the value.
      */
-    private void appendValue(final Object value, final boolean recursive) 
throws IOException {
+    public void appendValue(final Object value) throws IOException {
         final CharSequence text;
         if (value == null) {
             text = MISSING;
-        } else if (columnFormat != null) {
-            if (columnFormat instanceof CompoundFormat<?>) {
-                appendCompound((CompoundFormat<?>) columnFormat, value);
-                return;
-            }
-            text = columnFormat.format(value);
         } else if (value instanceof InternationalString) {
             text = freeText(((InternationalString) 
value).toString(getLocale()));
         } else if (value instanceof CharSequence) {
@@ -121,13 +126,13 @@ public abstract class PropertyFormat extends LineAppender 
implements Localized {
             final Locale locale = getLocale();
             text = (locale != Locale.ROOT) ? ((Currency) 
value).getDisplayName(locale) : value.toString();
         } else if (value instanceof Record) {
-            appendCollection(((Record) value).getFields().values(), recursive);
+            appendCollection(((Record) value).getFields().values());
             return;
         } else if (value instanceof Iterable<?>) {
-            appendCollection((Iterable<?>) value, recursive);
+            appendCollection((Iterable<?>) value);
             return;
         } else if (value instanceof Object[]) {
-            appendCollection(Arrays.asList((Object[]) value), recursive);
+            appendCollection(Arrays.asList((Object[]) value));
             return;
         } else if (value instanceof Map.Entry<?,?>) {
             final Map.Entry<?,?> entry = (Map.Entry<?,?>) value;
@@ -136,11 +141,11 @@ public abstract class PropertyFormat extends LineAppender 
implements Localized {
             if (k == null) {
                 append(null);
             } else {
-                appendValue(k, recursive);
+                appendValue(k);
             }
             if (v != null) {
                 append(" → ");
-                appendValue(v, recursive);
+                appendValue(v);
             }
             return;
         } else {
@@ -185,7 +190,7 @@ public abstract class PropertyFormat extends LineAppender 
implements Localized {
      * If the collection contains other collections, the other collections 
will <strong>not</strong>
      * be written recursively.
      */
-    private void appendCollection(final Iterable<?> values, final boolean 
recursive) throws IOException {
+    private void appendCollection(final Iterable<?> values) throws IOException 
{
         if (values != null) {
             if (recursive) {
                 append('…');                                // Do not format 
collections inside collections.
@@ -194,7 +199,12 @@ public abstract class PropertyFormat extends LineAppender 
implements Localized {
                 for (final Object value : values) {
                     if (value != null) {
                         if (count != 0) append(", ");
-                        appendValue(value, true);
+                        try {
+                            recursive = true;
+                            appendValue(value);
+                        } finally {
+                            recursive = false;
+                        }
                         if (++count == 10) {                // Arbitrary limit.
                             append(", …");
                             break;
@@ -205,14 +215,6 @@ public abstract class PropertyFormat extends LineAppender 
implements Localized {
         }
     }
 
-    /**
-     * Workaround for the inability to define the variable {@code <V>} locally.
-     */
-    @Workaround(library="JDK", version="1.7")
-    private <V> void appendCompound(final CompoundFormat<V> format, final 
Object value) throws IOException {
-        format.format(format.getValueType().cast(value), this);
-    }
-
     /**
      * Localizes the given name in the display locale, or formats "(Unnamed)" 
if no localized value is found.
      */
diff --git 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/TreeNode.java
 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/TreeNode.java
index 7dbd433c8a..0bd120a61d 100644
--- 
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/TreeNode.java
+++ 
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/TreeNode.java
@@ -30,7 +30,6 @@ import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
-import java.io.IOException;
 import java.nio.charset.StandardCharsets;
 import org.apache.sis.math.NumberType;
 import org.apache.sis.util.Localized;
@@ -420,25 +419,6 @@ public abstract class TreeNode {
             }
         }
 
-        /**
-         * Formats the given value.
-         *
-         * @param  value  the value to format.
-         * @return the formatted value.
-         */
-        private String format(final Object value) {
-            try {
-                clear();
-                final var buffer = (StringBuilder) out;
-                buffer.setLength(0);
-                appendValue(value);
-                flush();
-                return buffer.toString();
-            } catch (IOException e) {               // Should never happen 
because we append in a StringBuilder.
-                throw new AssertionError(e);
-            }
-        }
-
         /**
          * Appends public fields as child nodes of the given root, ignoring 
null values and empty arrays.
          * This method may invoke itself recursively if some values are other 
{@code TreeNode} instances.
@@ -489,7 +469,7 @@ public abstract class TreeNode {
                                     values[i] = type.format(this, n);
                                 }
                             } else {
-                                values[i] = format(element);
+                                values[i] = formatUsingStringBuilder(element);
                             }
                         }
                     }
@@ -511,7 +491,7 @@ public abstract class TreeNode {
                         if (type != null) {
                             text = type.format(this, (Number) value);
                         } else {
-                            text = format(value);
+                            text = formatUsingStringBuilder(value);
                         }
                         if (text != null) {
                             addNode(target, field.getName(), value, text);
@@ -551,7 +531,7 @@ public abstract class TreeNode {
          */
         public final void addNode(final TreeTable.Node target, final String 
name, final Object value) {
             if (value != null) {
-                addNode(target, name, value, format(value));
+                addNode(target, name, value, formatUsingStringBuilder(value));
             }
         }
 
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
index ba33fc6082..565cb87181 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/ImagePropertyExplorer.java
@@ -22,7 +22,6 @@ import java.util.Map;
 import java.util.IdentityHashMap;
 import java.util.function.Predicate;
 import java.text.NumberFormat;
-import java.io.IOException;
 import java.awt.Rectangle;
 import java.awt.image.RenderedImage;
 import javafx.beans.property.BooleanProperty;
@@ -716,16 +715,7 @@ public class ImagePropertyExplorer extends Widget {
          */
         @Override protected void updateItem(final Object value, final boolean 
empty) {
             super.updateItem(value, empty);
-            String text = null;
-            if (!empty) try {
-                buffer.setLength(0);
-                format.appendValue(value);
-                format.flush();
-                text = buffer.toString();
-            } catch (IOException e) {           // Should never happen since 
we write in a StringBuilder.
-                text = e.toString();
-            }
-            setText(text);
+            setText(empty ? null : format.formatUsingStringBuilder(value));
         }
     }
 
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/PropertyValueFormatter.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/PropertyValueFormatter.java
index 7ac251a529..71e88517f6 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/PropertyValueFormatter.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/PropertyValueFormatter.java
@@ -23,7 +23,7 @@ import org.apache.sis.util.internal.shared.PropertyFormat;
 
 
 /**
- * Creates string representation of property values of unknown type.
+ * Creates string representation of property values of unknown types.
  * Tabulations are replaced by spaces and line feeds are replaced by the 
Pilcrow character.
  *
  * @author  Martin Desruisseaux (Geomatys)
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/MetadataTree.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/MetadataTree.java
index 1c9fadea7b..d68ec03aef 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/MetadataTree.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/MetadataTree.java
@@ -19,7 +19,6 @@ package org.apache.sis.gui.metadata;
 import java.util.Locale;
 import java.util.List;
 import java.util.WeakHashMap;
-import java.io.IOException;
 import javafx.util.Callback;
 import javafx.beans.DefaultProperty;
 import javafx.beans.property.ObjectProperty;
@@ -377,15 +376,8 @@ check:      if (data != null) {
                 final String text;
                 if (value instanceof IdentifiedObject) {
                     text = IdentifiedObjects.getDisplayName((IdentifiedObject) 
value, getLocale());
-                } else try {
-                    clear();
-                    final var buffer = (StringBuilder) out;
-                    buffer.setLength(0);
-                    appendValue(value);
-                    flush();
-                    text = buffer.toString();
-                } catch (IOException e) {               // Should never happen 
because we append in a StringBuilder.
-                    throw new AssertionError(e);
+                } else {
+                    text = formatUsingStringBuilder(value);
                 }
                 if (value instanceof NodeSummary) {
                     final var summary = new SummaryProperty(text);

Reply via email to