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
commit 45fcb329921882cbe7a7d498a9aa6517a0d0547f Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Wed Jan 12 02:55:10 2022 +0100 Use the localization provided by `CompoundFormat` for formatting values in the metadata tree. This is a cleaner solution than previous code for solving a problem of geographic extent not property formatted. --- .../org/apache/sis/gui/metadata/MetadataTree.java | 4 +- .../sis/internal/gui/PropertyValueFormatter.java | 16 +-- .../org/apache/sis/internal/gui/PropertyView.java | 114 +++++------------- .../org/apache/sis/internal/gui/TextFormats.java | 132 +++++++++++++++++++++ .../apache/sis/internal/util/PropertyFormat.java | 4 +- 5 files changed, 175 insertions(+), 95 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 0effac0..c8c2558 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 @@ -372,7 +372,7 @@ check: if (data != null) { final MetadataTree view = (MetadataTree) cell.getTreeTableView(); Object value = getValue(cell, view.valueSourceColumn); if (value instanceof IdentifiedObject) { - value = IdentifiedObjects.getDisplayName((IdentifiedObject) value, locale); + value = IdentifiedObjects.getDisplayName((IdentifiedObject) value, getLocale()); } try { clear(); @@ -474,7 +474,7 @@ check: if (data != null) { */ private void showPropertyValue(final Object value) { if (propertyViewer == null) { - propertyViewer = new PropertyView(getLocale(), null, null); + propertyViewer = new PropertyView(formatter); propertyDialog = new Dialog<>(); propertyDialog.setResizable(true); propertyDialog.setTitle(Resources.forLocale(getLocale()).getString(Resources.Keys.PropertyValue)); diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyValueFormatter.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyValueFormatter.java index c6adcd2..7d82d79 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyValueFormatter.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyValueFormatter.java @@ -16,7 +16,6 @@ */ package org.apache.sis.internal.gui; -import java.util.Date; import java.util.Locale; import org.apache.sis.util.Classes; import org.apache.sis.util.CharSequences; @@ -34,10 +33,10 @@ import org.apache.sis.internal.util.PropertyFormat; */ public class PropertyValueFormatter extends PropertyFormat { /** - * The locale to use for objects. This is usually {@link Locale#getDefault()}. - * This value is given to {@link InternationalString#toString(Locale)} calls. + * The formats to use for objects. Its locale is usually {@link Locale#getDefault()}. + * The locale is also given to {@link InternationalString#toString(Locale)} calls. */ - protected final Locale locale; + final TextFormats formats; /** * Creates a formatter for the specified locale. @@ -47,8 +46,8 @@ public class PropertyValueFormatter extends PropertyFormat { */ public PropertyValueFormatter(final Appendable buffer, final Locale locale) { super(buffer); - this.locale = locale; setLineSeparator(" ¶ "); + formats = new TextFormats(locale); } /** @@ -56,7 +55,7 @@ public class PropertyValueFormatter extends PropertyFormat { */ @Override public final Locale getLocale() { - return locale; + return formats.getLocale(); } /** @@ -65,8 +64,9 @@ public class PropertyValueFormatter extends PropertyFormat { */ @Override protected String toString(final Object value) { - if (value instanceof Number || value instanceof Date) { // See super-class javadoc. - return value.toString(); + String text = formats.formatValue(value, false); + if (text != null) { + return text; } return Classes.getShortClassName(value) + "(…)"; } diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyView.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyView.java index 2a30407..69cdc19 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyView.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/PropertyView.java @@ -20,11 +20,6 @@ import java.util.Locale; import java.util.Objects; import java.util.Collection; import java.lang.reflect.Array; -import java.io.IOException; -import java.text.Format; -import java.text.FieldPosition; -import java.text.ParsePosition; -import java.text.ParseException; import java.awt.Rectangle; import java.awt.image.RenderedImage; import javafx.beans.value.ChangeListener; @@ -43,9 +38,8 @@ import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; import org.opengis.referencing.IdentifiedObject; -import org.apache.sis.io.CompoundFormat; import org.apache.sis.math.Statistics; -import org.apache.sis.internal.util.Numerics; +import org.apache.sis.util.Localized; import org.apache.sis.util.resources.Vocabulary; @@ -53,16 +47,20 @@ import org.apache.sis.util.resources.Vocabulary; * A viewer for property value. The property may be of various class (array, image, <i>etc</i>). * If the type is unrecognized, the property is shown as text. * - * <p>This class extends {@link CompoundFormat} and implements {@code ChangeListener} for - * implementation convenience only. Users should not rely on this implementation details.</p> + * <p>This class implements {@code ChangeListener} for implementation convenience only. + * Users should not rely on this implementation details.</p> * * @author Martin Desruisseaux (Geomatys) * @version 1.2 * @since 1.1 * @module */ -@SuppressWarnings({"serial","CloneableImplementsClone"}) // Not intended to be serialized. -public final class PropertyView extends CompoundFormat<Object> implements ChangeListener<Number> { +public final class PropertyView implements Localized, ChangeListener<Number> { + /** + * Provider for {@link java.text.NumberFormat}, {@link java.text.DateFormat}, <i>etc</i>. + */ + private final TextFormats formats; + /** * The current property value. This is used for detecting changes. */ @@ -131,18 +129,25 @@ public final class PropertyView extends CompoundFormat<Object> implements Change private ImageConverter runningTask, pendingTask; /** - * Creates a new property view. + * Creates a new property view which will use the given formatter for formatting values. + * + * @param formatter the formatter to use for formatting values. + */ + public PropertyView(final PropertyValueFormatter formatter) { + formats = formatter.formats; + view = new SimpleObjectProperty<>(this, "view"); + } + + /** + * Creates a new property view which will set the node on the given property. * * @param locale the locale for numbers formatting. - * @param view the property where to set the node showing the value, or {@code null} for a default one. + * @param view the property where to set the node showing the value. * @param background the image background color, or {@code null} if none. */ @SuppressWarnings("ThisEscapedInObjectConstruction") - public PropertyView(final Locale locale, ObjectProperty<Node> view, final ObjectProperty<Background> background) { - super(locale, null); - if (view == null) { - view = new SimpleObjectProperty<>(this, "view"); - } + public PropertyView(final Locale locale, final ObjectProperty<Node> view, final ObjectProperty<Background> background) { + formats = new TextFormats(locale); this.view = view; if (background != null) { getImageCanvas().backgroundProperty().bind(background); @@ -150,66 +155,13 @@ public final class PropertyView extends CompoundFormat<Object> implements Change } /** - * Required by {@link CompoundFormat} but not used. - * - * @return the base type of values formatted by this {@code PropertyView} instance. - */ - @Override - public Class<? extends Object> getValueType() { - return Object.class; - } - - /** - * Unsupported operation. + * Returns the locale for formatting messages and values. * - * @param text ignored. - * @param pos ignored. - * @return never return. - * @throws ParseException always thrown. + * @return the locale used by formats. */ @Override - public Object parse(CharSequence text, ParsePosition pos) throws ParseException { - throw new ParseException(null, 0); - } - - /** - * Formats the given property value. Current implementation requires {@code toAppendTo} - * to be an instance of {@link StringBuffer}. This method is not intended to be invoked - * outside internal usage. - * - * @param value the property value to format. - * @param toAppendTo where to append the property value. - */ - @Override - public void format(final Object value, final Appendable toAppendTo) throws IOException { - final Format f = getFormat(value.getClass()); - if (f != null) { - f.format(value, (StringBuffer) toAppendTo, new FieldPosition(0)); - } else { - toAppendTo.append(value.toString()); - } - } - - /** - * Formats a single value. This method does the same work than the inherited - * {@link #format(Object)} final method but in a more efficient way. - */ - private String formatValue(final Object value) { - final Format f = getFormat(value.getClass()); - if (f == null) { - return value.toString(); - } else if (value instanceof Number) { - return Numerics.useScientificNotationIfNeeded(f, value, Format::format); - } else { - return f.format(value); - } - } - - /** - * Formats the given value, using scientific notation if needed. - */ - private static void format(final Format f, final double value, final StringBuffer buffer, final FieldPosition pos) { - Numerics.useScientificNotationIfNeeded(f, value, (nf,v) -> {nf.format(v, buffer, pos); return null;}); + public Locale getLocale() { + return formats.getLocale(); } /** @@ -247,7 +199,7 @@ public final class PropertyView extends CompoundFormat<Object> implements Change } else if (newValue.getClass().isArray()) { content = setList(newValue); } else { - content = setText(formatValue(newValue)); + content = setText(formats.formatValue(newValue, true)); } } view.set(content); @@ -288,7 +240,7 @@ public final class PropertyView extends CompoundFormat<Object> implements Change } final String[] list = new String[Array.getLength(array)]; for (int i=0; i<list.length; i++) { - list[i] = formatValue(Array.get(array, i)); + list[i] = formats.formatValue(Array.get(array, i), true); } listView.getItems().setAll(list); return node; @@ -395,16 +347,12 @@ public final class PropertyView extends CompoundFormat<Object> implements Change String mean = null; if (statistics != null && statistics.length != 0) { final Statistics s = statistics[0]; - final FieldPosition pos = new FieldPosition(0); final StringBuffer buffer = new StringBuffer(); - final Format f = getFormat(Number.class); - format(f, s.minimum(), buffer, pos); buffer.append(" … "); - format(f, s.maximum(), buffer, pos); + formats.formatPair(s.minimum(), " … ", s.maximum(), buffer); range = buffer.toString(); buffer.setLength(0); - format(f, s.mean(), buffer, pos); buffer.append(" ± "); - format(f, s.standardDeviation(false), buffer, pos); + formats.formatPair(s.mean(), " ± ", s.standardDeviation(false), buffer); final Vocabulary vocabulary = Vocabulary.getResources(getLocale()); buffer.append(" (").append(vocabulary.getString(Vocabulary.Keys.StandardDeviation)).append(')'); diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/TextFormats.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/TextFormats.java new file mode 100644 index 0000000..11ca158 --- /dev/null +++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/TextFormats.java @@ -0,0 +1,132 @@ +/* + * 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.gui; + +import java.util.Locale; +import java.util.TimeZone; +import java.text.Format; +import java.text.FieldPosition; +import java.text.ParsePosition; +import java.text.ParseException; +import java.io.IOException; +import org.apache.sis.internal.util.Numerics; +import org.apache.sis.io.CompoundFormat; + + +/** + * A provider for {@link java.text.NumberFormat}, {@link java.text.DateFormat}, <i>etc</i>. + * Used for formatting values in {@link PropertyValueFormatter}. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.2 + * @since 1.2 + * @module + */ +@SuppressWarnings({"serial","CloneableImplementsClone"}) // Not intended to be serialized. +final class TextFormats extends CompoundFormat<Object> { + /** + * Creates a new format for the given locale. + */ + TextFormats(final Locale locale) { + super(locale, TimeZone.getDefault()); + } + + /** + * Required by {@link CompoundFormat} but not used. + * + * @return the base type of values formatted by this {@code PropertyView} instance. + */ + @Override + public final Class<? extends Object> getValueType() { + return Object.class; + } + + /** + * Unsupported operation. + * + * @param text ignored. + * @param pos ignored. + * @return never return. + * @throws ParseException always thrown. + */ + @Override + public Object parse(CharSequence text, ParsePosition pos) throws ParseException { + throw new ParseException(null, 0); + } + + /** + * Formats the given property value. Current implementation requires {@code toAppendTo} + * to be an instance of {@link StringBuffer}. This method is not intended to be invoked + * outside internal usage. + * + * @param value the property value to format. + * @param toAppendTo where to append the property value. + */ + @Override + public void format(final Object value, final Appendable toAppendTo) throws IOException { + final StringBuffer buffer = (StringBuffer) toAppendTo; + final Format f = getFormat(value.getClass()); + if (f != null) { + f.format(value, buffer, new FieldPosition(0)); + } else { + buffer.append(value); + } + } + + /** + * Formats a single value. This method does the same work than the inherited + * {@link #format(Object)} final method but in a more efficient way. + * + * @param value the value to format. + * @param toStringAllowed whether to fallback on {@link Object#toString()} if there is no format + * for the given object. If {@code false}, then {@code null} will be returned instead. + * @return formatted string representation of the given value. + */ + public String formatValue(final Object value, final boolean toStringAllowed) { + final Format f = getFormat(value.getClass()); + if (f == null) { + return value.toString(); + } else if (value instanceof Number) { + return Numerics.useScientificNotationIfNeeded(f, value, Format::format); + } else { + return f.format(value); + } + } + + /** + * Formats a pair of values in the given buffer. If can be used for formatting a range + * like "minimum … maximum" or for formatting a value with uncertainty like "mean ± std". + * + * @param first the first value of the range. + * @param separator string to insert between the two values. + * @param second the maximum value of the range. + * @param toAppendTo where to append the property value. + */ + public void formatPair(final double first, final String separator, final double second, final StringBuffer toAppendTo) { + final FieldPosition pos = new FieldPosition(0); + final Format f = getFormat(Number.class); + format(f, first, toAppendTo, pos); toAppendTo.append(separator); + format(f, second, toAppendTo, pos); + } + + /** + * Formats the given value, using scientific notation if needed. + */ + private static void format(final Format f, final double value, final StringBuffer buffer, final FieldPosition pos) { + Numerics.useScientificNotationIfNeeded(f, value, (nf,v) -> {nf.format(v, buffer, pos); return null;}); + } +} 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 index 3fbcecf..e8595a4 100644 --- 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 @@ -155,8 +155,8 @@ public abstract class PropertyFormat extends LineAppender implements Localized { * 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. + * In particular, {@link Number}, {@link java.util.Date} and {@link org.apache.sis.measure.Angle} + * 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.