sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Move out the formatting code that check for special types (InternationalString, ControlledVocabulary, Enum, Locale, TimeZone, Currency, Charset, etc.) so it can be shared by the JavaFX application. The intent is to have range of values (given as array of 2 elements) shown in a readable way.
Date Fri, 28 Feb 2020 14:22:47 GMT
This is an automated email from the ASF dual-hosted git repository.

desruisseaux 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 d1e0024  Move out the formatting code that check for special types (InternationalString,
ControlledVocabulary, Enum, Locale, TimeZone, Currency, Charset, etc.) so it can be shared
by the JavaFX application. The intent is to have range of values (given as array of 2 elements)
shown in a readable way.
d1e0024 is described below

commit d1e00249147cf77bb0582ef267264265aaa8bdef
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Fri Feb 28 15:20:31 2020 +0100

    Move out the formatting code that check for special types (InternationalString, ControlledVocabulary,
Enum, Locale, TimeZone, Currency, Charset, etc.) so it can be shared by the JavaFX application.
    The intent is to have range of values (given as array of 2 elements) shown in a readable
way.
---
 .../org/apache/sis/gui/metadata/MetadataTree.java  |  98 +++++++---
 .../sis/gui/metadata/StandardMetadataTree.java     |   4 +-
 .../apache/sis/internal/util/PropertyFormat.java   | 216 +++++++++++++++++++++
 .../sis/util/collection/TreeTableFormat.java       | 207 +++++---------------
 .../apache/sis/util/collection/package-info.java   |   2 +-
 5 files changed, 334 insertions(+), 193 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
index 5b7c3c7..b41a9d0 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
@@ -20,6 +20,8 @@ import java.util.Locale;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.io.IOException;
+import javafx.util.Callback;
 import javafx.beans.DefaultProperty;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.ReadOnlyObjectWrapper;
@@ -32,12 +34,13 @@ import javafx.scene.control.TreeTableView;
 import javafx.scene.control.TreeTableColumn;
 import javafx.scene.control.TreeTableColumn.CellDataFeatures;
 import org.opengis.util.InternationalString;
-import org.opengis.util.ControlledVocabulary;
 import org.opengis.referencing.IdentifiedObject;
 import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.internal.util.PropertyFormat;
+import org.apache.sis.internal.system.Modules;
 import org.apache.sis.util.collection.TreeTable;
 import org.apache.sis.util.collection.TableColumn;
-import org.apache.sis.util.iso.Types;
+import org.apache.sis.util.logging.Logging;
 
 
 /**
@@ -113,10 +116,9 @@ public class MetadataTree extends TreeTableView<TreeTable.Node>
{
     }
 
     /**
-     * The locale to use for texts. This is usually {@link Locale#getDefault()}.
-     * This value is given to {@link InternationalString#toString(Locale)} calls.
+     * The object to use for formatting property values.
      */
-    final Locale textLocale;
+    private final Formatter formatter;
 
     /**
      * Creates a new initially empty metadata tree.
@@ -143,16 +145,18 @@ public class MetadataTree extends TreeTableView<TreeTable.Node>
{
      */
     @SuppressWarnings("ThisEscapedInObjectConstruction")
     MetadataTree(final MetadataSummary controller, final boolean standard) {
+        final Locale locale;
         if (controller != null) {
-            textLocale = controller.localized.getLocale();
+            locale = controller.localized.getLocale();
         } else {
-            textLocale = Locale.getDefault(Locale.Category.DISPLAY);
+            locale = Locale.getDefault(Locale.Category.DISPLAY);
         }
+        formatter       = new Formatter(locale);
         contentProperty = new ContentProperty(this);
-        nameColumn      = new TreeTableColumn<>(TableColumn.NAME .getHeader().toString(textLocale));
-        valueColumn     = new TreeTableColumn<>(TableColumn.VALUE.getHeader().toString(textLocale));
+        nameColumn      = new TreeTableColumn<>(TableColumn.NAME .getHeader().toString(locale));
+        valueColumn     = new TreeTableColumn<>(TableColumn.VALUE.getHeader().toString(locale));
         nameColumn .setCellValueFactory(MetadataTree::getPropertyName);
-        valueColumn.setCellValueFactory(MetadataTree::getPropertyValue);
+        valueColumn.setCellValueFactory(formatter);
 
         setColumnResizePolicy(CONSTRAINED_RESIZE_POLICY);
         getColumns().setAll(nameColumn, valueColumn);
@@ -166,6 +170,14 @@ public class MetadataTree extends TreeTableView<TreeTable.Node>
{
     }
 
     /**
+     * The locale to use for texts. This is usually {@link Locale#getDefault()}.
+     * This value is given to {@link InternationalString#toString(Locale)} calls.
+     */
+    final Locale getLocale() {
+        return formatter.getLocale();
+    }
+
+    /**
      * Sets the data to show.
      * This is a convenience method for setting {@link #contentProperty} value.
      * The given {@link TreeTable} shall contain at least the following columns:
@@ -281,7 +293,7 @@ public class MetadataTree extends TreeTableView<TreeTable.Node>
{
         final String text;
         if (value instanceof InternationalString) {
             final MetadataTree view = (MetadataTree) cell.getTreeTableView();
-            text = ((InternationalString) value).toString(view.textLocale);
+            text = ((InternationalString) value).toString(view.getLocale());
         } else {
             text = (value != null) ? value.toString() : null;
         }
@@ -289,25 +301,57 @@ public class MetadataTree extends TreeTableView<TreeTable.Node>
{
     }
 
     /**
-     * Returns the value of the metadata property wrapped by the given argument.
-     * This method is invoked by JavaFX when a new cell needs to be rendered.
-     *
-     * @todo Format other kinds of objects (numbers, dates, timezones, etc.).
-     *       See {@link org.apache.sis.util.collection.TreeTableFormat},
-     *       if possible by putting some code in common.
+     * Formatter for metadata property value in a tree cell. This formatter handles in a
special way
+     * many object classes like {@link InternationalString}, <i>etc</i>.
      */
-    private static ObservableValue<Object> getPropertyValue(final CellDataFeatures<TreeTable.Node,
Object> cell) {
-        final MetadataTree view = (MetadataTree) cell.getTreeTableView();
-        Object value = getValue(cell, TableColumn.VALUE);
-        if (value instanceof IdentifiedObject) {
-            value = IdentifiedObjects.getDisplayName((IdentifiedObject) value, view.textLocale);
+    private static final class Formatter extends PropertyFormat
+            implements Callback<CellDataFeatures<TreeTable.Node, Object>, ObservableValue<Object>>
+    {
+        /**
+         * The locale to use for texts. This is usually {@link Locale#getDefault()}.
+         * This value is given to {@link InternationalString#toString(Locale)} calls.
+         */
+        private final Locale locale;
+
+        /**
+         * Creates a new formatter for the given locale.
+         */
+        Formatter(final Locale locale) {
+            super(new StringBuilder());
+            this.locale = locale;
         }
-        if (value instanceof ControlledVocabulary) {
-            value = Types.getCodeTitle((ControlledVocabulary) value);
+
+        /**
+         * The locale to use for formatting textual content.
+         */
+        @Override
+        public Locale getLocale() {
+            return locale;
         }
-        if (value instanceof InternationalString) {
-            value = ((InternationalString) value).toString(view.textLocale);
+
+        /**
+         * Returns the value of the metadata property wrapped by the given argument.
+         * This method is invoked by JavaFX when a new cell needs to be rendered.
+         */
+        @Override
+        public ObservableValue<Object> call(final CellDataFeatures<TreeTable.Node,
Object> cell) {
+            final MetadataTree view = (MetadataTree) cell.getTreeTableView();
+            Object value = getValue(cell, TableColumn.VALUE);
+            if (value instanceof IdentifiedObject) {
+                value = IdentifiedObjects.getDisplayName((IdentifiedObject) value, locale);
+            }
+            try {
+                clear();
+                final StringBuilder buffer = (StringBuilder) out;
+                buffer.setLength(0);
+                appendValue(value);
+                flush();
+                value = buffer.toString();
+            } catch (IOException e) {               // Should never happen because we append
in a StringBuilder.
+                Logging.unexpectedException(Logging.getLogger(Modules.APPLICATION), Formatter.class,
"call", e);
+                // Leave `value` as-is. It will be formatted using `Object.toString()`.
+            }
+            return new ReadOnlyObjectWrapper<>(value);
         }
-        return new ReadOnlyObjectWrapper<>(value);
     }
 }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/StandardMetadataTree.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/StandardMetadataTree.java
index 77d280d..10c90f4 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/StandardMetadataTree.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/StandardMetadataTree.java
@@ -94,7 +94,7 @@ public class StandardMetadataTree extends MetadataTree {
      */
     public StandardMetadataTree(final MetadataSummary controller) {
         super(controller, true);
-        final Resources localized = Resources.forLocale(textLocale);
+        final Resources localized = Resources.forLocale(getLocale());
         copy   = localized.getString(Resources.Keys.Copy);
         copyAs = localized.getString(Resources.Keys.CopyAs);
         setRowFactory(Row::new);
@@ -227,7 +227,7 @@ public class StandardMetadataTree extends MetadataTree {
                             text = value.toString();
                         }
                     } catch (Exception e) {
-                        final Resources localized = Resources.forLocale(((StandardMetadataTree)
getTreeTableView()).textLocale);
+                        final Resources localized = Resources.forLocale(((StandardMetadataTree)
getTreeTableView()).getLocale());
                         ExceptionReporter.show(localized.getString(Resources.Keys.ErrorExportingData),
                                                localized.getString(Resources.Keys.CanNotCreateXML),
e);
                         return;
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/util/PropertyFormat.java
b/core/sis-utility/src/main/java/org/apache/sis/internal/util/PropertyFormat.java
new file mode 100644
index 0000000..ae8272b
--- /dev/null
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/PropertyFormat.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.util;
+
+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.nio.charset.Charset;
+import java.io.IOException;
+import org.opengis.util.Type;
+import org.opengis.util.Record;
+import org.opengis.util.GenericName;
+import org.opengis.util.InternationalString;
+import org.opengis.util.ControlledVocabulary;
+import org.apache.sis.io.CompoundFormat;
+import org.apache.sis.io.LineAppender;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.util.Workaround;
+import org.apache.sis.util.Localized;
+
+
+/**
+ * Creates string representation of property values of unknown type.
+ * 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)}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+public abstract class PropertyFormat extends LineAppender implements Localized {
+    /**
+     * 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.
+     */
+    protected transient Format columnFormat;
+
+    /**
+     * Creates a new instance which will write to the given appendable.
+     *
+     * @param  out  where to format the tree.
+     */
+    protected PropertyFormat(final Appendable out) {
+        super(out);
+    }
+
+    /**
+     * 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.
+     */
+    public final void appendValue(final Object value) throws IOException {
+        appendValue(value, false);
+    }
+
+    /**
+     * Appends a textual representation of the given value, with a check against nested collections.
+     *
+     * @param  value      the value to format (may be {@code null}).
+     * @param  recursive  {@code true} if this method is invoking itself for writing collection
values.
+     */
+    private void appendValue(final Object value, final boolean recursive) throws IOException
{
+        final CharSequence text;
+        if (value == null) {
+            text = " ";                                             // String for missing
value.
+        } else if (columnFormat != null) {
+            if (columnFormat instanceof CompoundFormat<?>) {
+                appendCompound((CompoundFormat<?>) columnFormat, value);
+                return;
+            }
+            text = columnFormat.format(value);
+        } else if (value instanceof InternationalString) {
+            text = ((InternationalString) value).toString(getLocale());
+        } else if (value instanceof CharSequence) {
+            text = value.toString();
+        } else if (value instanceof ControlledVocabulary) {
+            text = MetadataServices.getInstance().getCodeTitle((ControlledVocabulary) value,
getLocale());
+        } else if (value instanceof Enum<?>) {
+            text = CharSequences.upperCaseToSentence(((Enum<?>) value).name());
+        } else if (value instanceof Type) {
+            appendName(((Type) value).getTypeName());
+            return;
+        } else if (value instanceof Locale) {
+            final Locale locale = getLocale();
+            text = (locale != Locale.ROOT) ? ((Locale) value).getDisplayName(locale) : value.toString();
+        } else if (value instanceof TimeZone) {
+            final Locale locale = getLocale();
+            text = (locale != Locale.ROOT) ? ((TimeZone) value).getDisplayName(locale) :
((TimeZone) value).getID();
+        } else if (value instanceof Charset) {
+            final Locale locale = getLocale();
+            text = (locale != Locale.ROOT) ? ((Charset) value).displayName(locale) : ((Charset)
value).name();
+        } else if (value instanceof Currency) {
+            final Locale locale = getLocale();
+            text = (locale != Locale.ROOT) ? ((Currency) value).getDisplayName(locale) :
value.toString();
+        } else if (value instanceof Record) {
+            appendCollection(((Record) value).getAttributes().values(), recursive);
+            return;
+        } else if (value instanceof Iterable<?>) {
+            appendCollection((Iterable<?>) value, recursive);
+            return;
+        } else if (value instanceof Object[]) {
+            appendCollection(Arrays.asList((Object[]) value), recursive);
+            return;
+        } else if (value instanceof Map.Entry<?,?>) {
+            final Map.Entry<?,?> entry = (Map.Entry<?,?>) value;
+            final Object k = entry.getKey();
+            final Object v = entry.getValue();
+            if (k == null) {
+                append(null);
+            } else {
+                appendValue(k, recursive);
+            }
+            if (v != null) {
+                append(" → ");
+                appendValue(v, recursive);
+            }
+            return;
+        } else {
+            text = toString(value);
+        }
+        append(text);
+    }
+
+    /**
+     * 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. Some of the types handled in a special way are {@link
InternationalString},
+     * {@link ControlledVocabulary}, {@link Enum}, {@link Type}, {@link Locale}, {@link TimeZone},
{@link Charset},
+     * {@link Currency}, {@link Record}, {@link Iterable} and arrays. Other types should
be handled by this method.
+     * In particular, {@link Number} and {@link java.util.Date} are <strong>not</strong>
handled by default by this
+     * {@link PropertyFormat} class and should be handled here.
+     *
+     * @param  value  the value to format (never {@code null}).
+     * @return the formatted value.
+     */
+    protected String toString(final Object value) {
+        return value.toString();
+    }
+
+    /**
+     * Writes the values of the given collection. A maximum of 10 values will be written.
+     * 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 {
+        if (values != null) {
+            if (recursive) {
+                append('…');                                // Do not format collections
inside collections.
+            } else {
+                int count = 0;
+                for (final Object value : values) {
+                    if (value != null) {
+                        if (count != 0) append(", ");
+                        appendValue(value, true);
+                        if (++count == 10) {                // Arbitrary limit.
+                            append(", …");
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * 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.
+     */
+    private void appendName(final GenericName name) throws IOException {
+        final Locale locale = getLocale();
+        if (name != null) {
+            final InternationalString i18n = name.toInternationalString();
+            if (i18n != null) {
+                final String localized = i18n.toString(locale);
+                if (localized != null) {
+                    append(localized);
+                    return;
+                }
+            }
+            final String localized = name.toString();
+            if (localized != null) {
+                append(localized);
+                return;
+            }
+        }
+        append('(').append(Vocabulary.getResources(locale).getString(Vocabulary.Keys.Unnamed)).append(')');
+    }
+}
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
b/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
index 1b4188d..c00e883 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/collection/TreeTableFormat.java
@@ -24,7 +24,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.TimeZone;
-import java.util.Currency;
 import java.util.ConcurrentModificationException;
 import java.util.function.Predicate;
 import java.io.IOException;
@@ -33,26 +32,17 @@ import java.text.DecimalFormat;
 import java.text.ParsePosition;
 import java.text.ParseException;
 import java.util.regex.Matcher;
-import java.nio.charset.Charset;
-import org.opengis.util.Type;
-import org.opengis.util.Record;
-import org.opengis.util.GenericName;
-import org.opengis.util.ControlledVocabulary;
-import org.opengis.util.InternationalString;
-import org.apache.sis.io.LineAppender;
 import org.apache.sis.io.TableAppender;
 import org.apache.sis.io.TabularFormat;
-import org.apache.sis.io.CompoundFormat;
 import org.apache.sis.measure.UnitFormat;
 import org.apache.sis.util.Numbers;
-import org.apache.sis.util.Workaround;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.math.DecimalFunctions;
 import org.apache.sis.internal.util.Acyclic;
-import org.apache.sis.internal.util.MetadataServices;
+import org.apache.sis.internal.util.PropertyFormat;
 import org.apache.sis.internal.util.LocalizedParseException;
 import org.apache.sis.internal.util.TreeFormatCustomization;
 
@@ -106,7 +96,7 @@ import static org.apache.sis.util.Characters.NO_BREAK_SPACE;
  * than the user object of a parent node <var>A</var>, then the children of the
<var>C</var> node will not be formatted.
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.3
  * @module
  */
@@ -636,7 +626,7 @@ public class TreeTableFormat extends TabularFormat<TreeTable> {
      * <p>Instances of {@link Writer} are created temporarily before to begin the formatting
      * of a node, and discarded when the formatting is finished.</p>
      */
-    private final class Writer extends LineAppender {
+    private final class Writer extends PropertyFormat {
         /**
          * Combination of {@link #nodeFilter} with other filter that may be specified by
the tree table to format.
          * The {@code TreeTable}-specific filter is specified by {@link TreeFormatCustomization}.
@@ -676,12 +666,6 @@ public class TreeTableFormat extends TabularFormat<TreeTable> {
         private final Set<TreeTable.Node> recursivityGuard;
 
         /**
-         * 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;
-
-        /**
          * Creates a new instance which will write to the given appendable.
          *
          * @param  out               where to format the tree.
@@ -714,158 +698,55 @@ public class TreeTableFormat extends TabularFormat<TreeTable>
{
         }
 
         /**
-         * Localizes the given name in the display locale, or returns "(Unnamed)" if no localized
value is found.
+         * Returns the locale to use for formatting property value.
+         * This method is invoked by {@link PropertyFormat} when needed.
          */
-        private String toString(final GenericName name) {
-            final Locale locale = getDisplayLocale();
-            if (name != null) {
-                final InternationalString i18n = name.toInternationalString();
-                if (i18n != null) {
-                    final String localized = i18n.toString(locale);
-                    if (localized != null) {
-                        return localized;
-                    }
-                }
-                final String localized = name.toString();
-                if (localized != null) {
-                    return localized;
-                }
-            }
-            return '(' + Vocabulary.getResources(locale).getString(Vocabulary.Keys.Unnamed)
+ ')';
+        @Override
+        public Locale getLocale() {
+            return getDisplayLocale();
         }
 
         /**
-         * 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.
+         * 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.
+         * This method checks for a value-by-value format and should be invoked only in last
resort.
+         * If a column-wide format was specified by the {@link #columnFormat} field, then
that format should
+         * have been used by {@link #appendValue(Object)} code in order to produce a more
uniform formatting.
          */
-        private void formatValue(final Object value, final boolean recursive) throws IOException
{
-            final CharSequence text;
-            if (value == null) {
-                text = " ";                                             // String for missing
value.
-            } else if (columnFormat != null) {
-                if (columnFormat instanceof CompoundFormat<?>) {
-                    formatValue((CompoundFormat<?>) columnFormat, value);
-                    return;
-                }
-                text = columnFormat.format(value);
-            } else if (value instanceof InternationalString) {
-                text = ((InternationalString) value).toString(getDisplayLocale());
-            } else if (value instanceof CharSequence) {
-                text = value.toString();
-            } else if (value instanceof ControlledVocabulary) {
-                text = MetadataServices.getInstance().getCodeTitle((ControlledVocabulary)
value, getDisplayLocale());
-            } else if (value instanceof Enum<?>) {
-                text = CharSequences.upperCaseToSentence(((Enum<?>) value).name());
-            } else if (value instanceof Type) {
-                text = toString(((Type) value).getTypeName());
-            } else if (value instanceof Locale) {
-                final Locale locale = getDisplayLocale();
-                text = (locale != Locale.ROOT) ? ((Locale) value).getDisplayName(locale)
: value.toString();
-            } else if (value instanceof TimeZone) {
-                final Locale locale = getDisplayLocale();
-                text = (locale != Locale.ROOT) ? ((TimeZone) value).getDisplayName(locale)
: ((TimeZone) value).getID();
-            } else if (value instanceof Charset) {
-                final Locale locale = getDisplayLocale();
-                text = (locale != Locale.ROOT) ? ((Charset) value).displayName(locale) :
((Charset) value).name();
-            } else if (value instanceof Currency) {
-                final Locale locale = getDisplayLocale();
-                text = (locale != Locale.ROOT) ? ((Currency) value).getDisplayName(locale)
: value.toString();
-            } else if (value instanceof Record) {
-                formatCollection(((Record) value).getAttributes().values(), recursive);
-                return;
-            } else if (value instanceof Iterable<?>) {
-                formatCollection((Iterable<?>) value, recursive);
-                return;
-            } else if (value instanceof Object[]) {
-                formatCollection(Arrays.asList((Object[]) value), recursive);
-                return;
-            } else if (value instanceof Map.Entry<?,?>) {
-                final Map.Entry<?,?> entry = (Map.Entry<?,?>) value;
-                final Object k = entry.getKey();
-                final Object v = entry.getValue();
-                if (k == null) {
-                    append(null);
-                } else {
-                    formatValue(k, recursive);
-                }
-                if (v != null) {
-                    append(" → ");
-                    formatValue(v, recursive);
-                }
-                return;
-            } else {
-                /*
-                 * Check for a value-by-value format only as last resort. If a column-wide
format was specified by
-                 * the 'columnFormat' field, that format should have been used by above code
in order to produce a
-                 * more uniform formatting.
-                 */
-                final Format format = getFormat(value.getClass());
-                if (format instanceof DecimalFormat && Numbers.isFloat(value.getClass()))
{
-                    final double number = ((Number) value).doubleValue();
-                    if (number != (int) number) {   // Cast to 'int' instead of 'long' as
a way to limit to about 2E9.
-                        /*
-                         * The default floating point format uses only 3 fraction digits.
We adjust that to the number
-                         * of digits required by the number to format. We do that only if
no NumberFormat was inferred
-                         * for the whole column (in order to keep column format uniform).
 We use enough precision for
-                         * all fraction digits except the last 2, in order to let DecimalFormat
round the number.
-                         */
-                        if (adaptableFormat == null) {
-                            adaptableFormat = (DecimalFormat) format.clone();
-                            defaultPattern = adaptableFormat.toPattern();
-                        }
-                        final int nf = DecimalFunctions.fractionDigitsForValue(number);
-                        final boolean preferScientificNotation = (nf > 20 || nf < 7);
      // == (value < 1E-4 || value > 1E+9)
-                        if (preferScientificNotation != usingScientificNotation) {
-                            usingScientificNotation = preferScientificNotation;
-                            adaptableFormat.applyPattern(preferScientificNotation ? "0.0############E0"
: defaultPattern);
-                        }
-                        if (!preferScientificNotation) {
-                            adaptableFormat.setMaximumFractionDigits(nf - 2);       // All
significand fraction digits except last two.
-                        }
-                        text = adaptableFormat.format(value);
-                    } else {
-                        text = format.format(value);
+        @Override
+        protected final String toString(final Object value) {
+            final String text;
+            final Format format = getFormat(value.getClass());
+            if (format instanceof DecimalFormat && Numbers.isFloat(value.getClass()))
{
+                final double number = ((Number) value).doubleValue();
+                if (number != (int) number) {   // Cast to 'int' instead of 'long' as a way
to limit to about 2E9.
+                    /*
+                     * The default floating point format uses only 3 fraction digits. We
adjust that to the number
+                     * of digits required by the number to format. We do that only if no
NumberFormat was inferred
+                     * for the whole column (in order to keep column format uniform).  We
use enough precision for
+                     * all fraction digits except the last 2, in order to let DecimalFormat
round the number.
+                     */
+                    if (adaptableFormat == null) {
+                        adaptableFormat = (DecimalFormat) format.clone();
+                        defaultPattern = adaptableFormat.toPattern();
                     }
-                } else {
-                    text = (format != null) ? format.format(value) : value.toString();
-                }
-            }
-            append(text);
-        }
-
-        /**
-         * Writes the values of the given collection. A maximum of 10 values will be written.
-         * If the collection contains other collections, the other collections will <strong>not</strong>
-         * be written recursively.
-         */
-        private void formatCollection(final Iterable<?> values, final boolean recursive)
throws IOException {
-            if (values != null) {
-                if (recursive) {
-                    append('…');                                // Do not format collections
inside collections.
-                } else {
-                    int count = 0;
-                    for (final Object value : values) {
-                        if (value != null) {
-                            if (count != 0) append(", ");
-                            formatValue(value, true);
-                            if (++count == 10) {                // Arbitrary limit.
-                                append(", …");
-                                break;
-                            }
-                        }
+                    final int nf = DecimalFunctions.fractionDigitsForValue(number);
+                    final boolean preferScientificNotation = (nf > 20 || nf < 7); 
     // == (value < 1E-4 || value > 1E+9)
+                    if (preferScientificNotation != usingScientificNotation) {
+                        usingScientificNotation = preferScientificNotation;
+                        adaptableFormat.applyPattern(preferScientificNotation ? "0.0############E0"
: defaultPattern);
                     }
+                    if (!preferScientificNotation) {
+                        adaptableFormat.setMaximumFractionDigits(nf - 2);       // All significand
fraction digits except last two.
+                    }
+                    text = adaptableFormat.format(value);
+                } else {
+                    text = format.format(value);
                 }
+            } else {
+                text = (format != null) ? format.format(value) : value.toString();
             }
-        }
-
-        /**
-         * Work around for the inability to define the variable {@code <V>} locally.
-         */
-        @Workaround(library="JDK", version="1.7")
-        private <V> void formatValue(final CompoundFormat<V> format, final Object
value) throws IOException {
-            format.format(format.getValueType().cast(value), this);
+            return text;
         }
 
         /**
@@ -904,7 +785,7 @@ public class TreeTableFormat extends TabularFormat<TreeTable> {
                     writeColumnSeparator(i, (TableAppender) out);
                 }
                 columnFormat = formats[i];
-                formatValue(values[i], false);
+                appendValue(values[i]);
                 clear();
             }
             out.append(lineSeparator);
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/collection/package-info.java
b/core/sis-utility/src/main/java/org/apache/sis/util/collection/package-info.java
index c962066..9c844bb 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/collection/package-info.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/collection/package-info.java
@@ -51,7 +51,7 @@
  * </ul>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.0
+ * @version 1.1
  * @since   0.3
  * @module
  */


Mime
View raw message