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 16a4b9549e Show summary of GeoHEIF box when the tree node is
collapsed. Navigation among HEIF boxes is not easy, so we are trying to help.
16a4b9549e is described below
commit 16a4b9549e05218499c98a2c5f73c26b4c439682
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat May 16 15:46:08 2026 +0200
Show summary of GeoHEIF box when the tree node is collapsed.
Navigation among HEIF boxes is not easy, so we are trying to help.
---
.../apache/sis/storage/metadata/NodeSummary.java | 30 +-
.../main/org/apache/sis/io/LineAppender.java | 6 +-
.../apache/sis/storage/geoheif/GeoHeifStore.java | 2 +-
.../org/apache/sis/storage/isobmff/Extension.java | 10 +-
.../org/apache/sis/storage/isobmff/FullBox.java | 15 +-
.../org/apache/sis/storage/isobmff/Reader.java | 8 +
.../org/apache/sis/storage/isobmff/TreeNode.java | 473 ++++++++++++++-------
.../storage/isobmff/base/ColourInformation.java | 2 +-
.../sis/storage/isobmff/base/CombinaisonType.java | 2 +-
.../apache/sis/storage/isobmff/base/Copyright.java | 1 +
.../sis/storage/isobmff/base/EntityToGroup.java | 2 +
.../storage/isobmff/base/FDItemInfoExtension.java | 2 +-
.../apache/sis/storage/isobmff/base/FileType.java | 2 +-
.../sis/storage/isobmff/base/HandlerReference.java | 2 +-
.../storage/isobmff/base/IdentifiedMediaData.java | 2 +-
.../apache/sis/storage/isobmff/base/ItemInfo.java | 12 +-
.../sis/storage/isobmff/base/ItemInfoEntry.java | 7 +-
.../sis/storage/isobmff/base/ItemLocation.java | 2 +-
.../sis/storage/isobmff/base/ItemProperties.java | 22 +-
.../isobmff/base/ItemPropertyAssociation.java | 13 +-
.../sis/storage/isobmff/base/PrimaryItem.java | 2 +-
.../isobmff/base/SingleItemTypeReference.java | 2 +-
.../sis/storage/isobmff/base/TrackHeader.java | 7 +-
.../apache/sis/storage/isobmff/geo/ModelCRS.java | 3 +-
.../sis/storage/isobmff/geo/ModelTiePoint.java | 23 +-
.../isobmff/geo/TiledImageConfiguration.java | 11 +-
.../sis/storage/isobmff/image/CreationTime.java | 1 +
.../storage/isobmff/image/ModificationTime.java | 1 +
.../sis/storage/isobmff/image/UserDescription.java | 1 +
.../apache/sis/storage/isobmff/mpeg/Component.java | 1 +
.../isobmff/mpeg/UncompressedFrameConfig.java | 2 +-
.../sis/gui/internal/PropertyValueFormats.java | 4 +-
.../org/apache/sis/gui/metadata/MetadataTree.java | 142 +++++--
.../sis/gui/metadata/StandardMetadataTree.java | 6 +-
.../org/apache/sis/gui/metadata/package-info.java | 2 +-
35 files changed, 559 insertions(+), 264 deletions(-)
diff --git
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/package-info.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/metadata/NodeSummary.java
similarity index 52%
copy from
optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/package-info.java
copy to
endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/metadata/NodeSummary.java
index e38c42b847..0f4cd2963f 100644
---
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/package-info.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/metadata/NodeSummary.java
@@ -14,16 +14,30 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package org.apache.sis.storage.metadata;
+
+import org.apache.sis.util.SimpleInternationalString;
+
/**
- * Widgets about metadata.
- * Those widgets can show a {@link org.opengis.metadata.Metadata} instance in
a tree,
- * or show a summary of those metadata in an "overview" panel.
+ * Value or a {@code TreeTable.Node} which is used only for summarizing the
children of the node.
+ * Since this text is redundant with the children, a <abbr>GUI</abbr> can show
this text when the
+ * node is collapsed and hide this text when the node is expanded.
*
- * @author Smaniotto Enzo (GSoC)
- * @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.6
- * @since 1.1
*/
-package org.apache.sis.gui.metadata;
+public final class NodeSummary extends SimpleInternationalString {
+ /**
+ * Serial number for inter-operability with different versions.
+ */
+ private static final long serialVersionUID = 5768405462866237705L;
+
+ /**
+ * Creates a new instance from the given string.
+ *
+ * @param text the string for all locales.
+ */
+ public NodeSummary(final String text) {
+ super(text);
+ }
+}
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
index 3b199c27bc..87ed9595cf 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
@@ -218,7 +218,9 @@ public class LineAppender extends Appender implements
Flushable {
}
/**
- * (@return the length of the current line, in units of Unicode code
points}.
+ * Returns the length of the current line, in units of Unicode code points.
+ *
+ * @return number of code points in the current line.
*
* @since 1.5
*/
@@ -233,7 +235,7 @@ public class LineAppender extends Appender implements
Flushable {
* the output specified to the constructor was not initially empty, or
when the output
* content is modified outside this {@code LineAppender} instance.
*
- * @param lengh the new length of the current line, in units of Unicode
code points.
+ * @param length the new length of the current line, in units of Unicode
code points.
*
* @since 1.5
*/
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/GeoHeifStore.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/GeoHeifStore.java
index 7b6ab9a994..e93e85b050 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/GeoHeifStore.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/geoheif/GeoHeifStore.java
@@ -325,7 +325,7 @@ public class GeoHeifStore extends DataStore implements
Aggregate {
* Logs a warning emitted (usually indirectly) by {@link #components()}.
*/
final void warning(final LogRecord record) {
- record.setLoggerName("org.apache.sis.storage.geoheif");
+ record.setLoggerName(Reader.LOGGER_NAME);
record.setSourceClassName(GeoHeifStore.class.getCanonicalName());
record.setSourceMethodName("components");
listeners.warning(record);
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Extension.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Extension.java
index 0bfa608149..0bedee9003 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Extension.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Extension.java
@@ -68,12 +68,12 @@ public abstract class Extension extends Box {
* Appends properties other than the ones defined by public fields.
* Those properties will be shown first in the tree.
*
- * @param context the tree being formatted. Can be used for fetching
contextual information.
- * @param target the node where to add properties.
+ * @param tree builder of the tree to format.
+ * @param target the node where to add properties.
*/
@Override
- protected void prependTreeNodes(final Tree context, final TreeTable.Node
target) {
- super.prependTreeNodes(context, target);
- Tree.addNode(target, "extendedType", extendedType());
+ protected void prependTreeNodes(final TreeBuilder tree, final
TreeTable.Node target) {
+ super.prependTreeNodes(tree, target);
+ tree.addNode(target, "extendedType", extendedType());
}
}
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/FullBox.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/FullBox.java
index f6a87fbafe..ae84a40422 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/FullBox.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/FullBox.java
@@ -83,27 +83,28 @@ public abstract class FullBox extends Box {
* Appends properties other than the ones defined by public fields.
* Those properties will be shown first in the tree.
*
- * @param context the tree being formatted. Can be used for fetching
contextual information.
- * @param target the node where to add properties.
+ * @param tree builder of the tree to format.
+ * @param target the node where to add properties.
*/
@Override
- protected void prependTreeNodes(final Tree context, final TreeTable.Node
target) {
- super.prependTreeNodes(context, target);
+ protected void prependTreeNodes(final TreeBuilder tree, final
TreeTable.Node target) {
+ super.prependTreeNodes(tree, target);
final int version = version();
final int options = flags & ((1 << VERSION_BIT_SHIFT) - 1);
if (version != 0) {
- Tree.addNode(target, "version", version, String.valueOf(version));
+ TreeBuilder.addNode(target, "version", version,
String.valueOf(version));
}
if (options != 0) {
- appendFlagDescriptions(Tree.addNode(target, "flags", options,
Integer.toBinaryString(options)));
+ appendFlagDescriptions(tree, TreeBuilder.addNode(target, "flags",
options, Integer.toBinaryString(options)));
}
}
/**
* Appends a description of the flags.
*
+ * @param tree builder of the tree to format.
* @param target the {@code flag} node where to add properties.
*/
- protected void appendFlagDescriptions(final TreeTable.Node target) {
+ protected void appendFlagDescriptions(TreeBuilder tree, TreeTable.Node
target) {
}
}
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Reader.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Reader.java
index 0a52fcdc91..fab31bd934 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Reader.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/Reader.java
@@ -57,6 +57,11 @@ import org.apache.sis.util.resources.Errors;
* @author Martin Desruisseaux (Geomatys)
*/
public final class Reader implements Cloneable {
+ /**
+ * The logger used by GeoHEIF stores.
+ */
+ public static final String LOGGER_NAME = "org.apache.sis.storage.geoheif";
+
/**
* The stream from which to read the data.
*
@@ -203,6 +208,7 @@ public final class Reader implements Cloneable {
box = null;
if (isNewWarning(type, Classes.getClass(cause))) {
var record = new LogRecord(Level.WARNING, "Cannot read the “"
+ Box.formatFourCC(type) + "” box.");
+ record.setLoggerName(LOGGER_NAME);
record.setThrown(cause);
listeners.warning(record);
}
@@ -339,6 +345,7 @@ public final class Reader implements Cloneable {
type = Box.formatFourCC(fourCC);
}
var record = new LogRecord(Level.WARNING, "The “" + type + "” type
of box is unrecognized.");
+ record.setLoggerName(LOGGER_NAME);
listeners.warning(record);
}
}
@@ -370,6 +377,7 @@ public final class Reader implements Cloneable {
if (isNewWarning(error.getClass(), value)) {
final LogRecord record = Errors.forLocale(listeners.getLocale())
.createLogRecord(ignoreable ? Level.FINE : Level.WARNING,
Errors.Keys.CanNotParse_1, value);
+ record.setLoggerName(LOGGER_NAME);
record.setThrown(error);
listeners.warning(record);
}
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 a57fe8e0f2..7dbd433c8a 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
@@ -19,6 +19,9 @@ package org.apache.sis.storage.isobmff;
import java.util.Map;
import java.util.HashMap;
import java.util.Locale;
+import java.util.logging.Logger;
+import java.text.NumberFormat;
+import java.math.BigInteger;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -27,20 +30,26 @@ 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.util.Utilities;
+import org.apache.sis.math.NumberType;
+import org.apache.sis.util.Localized;
import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.resources.Vocabulary;
-import org.apache.sis.util.internal.shared.Strings;
+import org.apache.sis.util.collection.Containers;
import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.util.collection.TableColumn;
import org.apache.sis.util.collection.DefaultTreeTable;
import org.apache.sis.util.collection.TreeTableFormat;
+import org.apache.sis.util.internal.shared.PropertyFormat;
import org.apache.sis.storage.isobmff.base.ItemInfoEntry;
+import org.apache.sis.storage.geoheif.GeoHeifStore;
+import org.apache.sis.storage.metadata.NodeSummary;
/**
- * Base class of box or items. This class implements the {@link #toString()}
method with reflection.
+ * Base class of boxes or items. This class implements the {@link #toString()}
method with reflection.
* For allowing reflection, all subclasses must be public (but not necessarily
in exported packages)
* and all fields to show shall be public.
*
@@ -48,11 +57,9 @@ import org.apache.sis.storage.isobmff.base.ItemInfoEntry;
*/
public abstract class TreeNode {
/**
- * Marker annotation for four-character codes (<abbr>4CC</abbr>) or
unsigned integers.
- * When this annotation is present, the field value should be converted
with a call to
- * a method such as {@link Short#toUnsignedInt(short)}. If this annotation
is absent,
- * either negative values are allowed, or the unsigned value has already
been converted
- * to a wider type.
+ * Marker annotation for fields such as four-character codes
(<abbr>4CC</abbr>) or unsigned integers.
+ * When this annotation is present, the field value should be converted
with a call to a method such
+ * as {@link Short#toUnsignedInt(short)}, depending on the {@linkplain
#value() type}.
*/
@Documented
@Target(ElementType.FIELD)
@@ -61,62 +68,124 @@ public abstract class TreeNode {
/**
* How the field value should be interpreted.
*
- * @return field interpretation (<abbr>4CC</abbr> or unsigned integer).
+ * @return field interpretation (e.g., <abbr>4CC</abbr> or unsigned
integer).
*/
Type value();
+
+ /**
+ * Whether the field can be used as a summary of the box that contains
this field.
+ * If more than one field is flagged as a summary, the first field
with a non-null value will be used.
+ *
+ * @return {@code true} if the field can be shown as a box summary.
+ */
+ boolean summary() default false;
}
/**
- * How to interpret an integer value otherwise than as a signed integer.
- * This is used for instructing {@link TreeNode#toString()} to replace
- * the numerical code by a human-readable string.
+ * How to interpret an integer value (if not a signed integer) or a
character string.
+ * This is used for instructing {@link TreeNode#toString()} to replace
numerical codes
+ * by a human-readable strings.
*/
protected enum Type {
/**
- * The integer should be interpreted as a four-character code
(<abbr>4CC</abbr>).
- * Value should be formatted with {@link #formatFourCC(int)}.
+ * No particular interpretation. This annotation can be used with
fields of any type.
+ * The use of this type is equivalent to a field with no annotation.
This type is for
+ * making possible to set the {@link Interpretation#summary()} flag on
a string field.
+ * The {@link #format(TreeBuilder, Number)} method should not be
invoked on this value.
+ */
+ NONE,
+
+ /**
+ * The annotated integer should be interpreted as a four-character
code (<abbr>4CC</abbr>).
+ * The annotated field should be of type {@code int} or {@code int[]}.
+ * Values will be formatted with {@link #formatFourCC(int)}.
*/
FOURCC {
- @Override String format(final Tree context, final Number value) {
+ @Override String format(final TreeBuilder tree, final Number
value) {
return formatFourCC(value.intValue());
}
},
/**
- * An identifier as an integer value.
- * The item name will be appended after the identifier if it is found.
+ * The annotated integer is the identifier of an item, generally in a
{@code itemID} field.
+ * The name of the identified item is specified in a separated {@link
ItemInfoEntry}.
+ * The annotated field should be of type {@code int} or {@code int[]}.
*/
IDENTIFIER {
- @Override String format(final Tree context, final Number value) {
- String name = super.format(context, value);
- ItemInfoEntry entry = context.names.get(value.intValue());
- if (entry != null && entry.itemName != null) {
- name = name + " → " + entry.itemName;
+ @Override String format(final TreeBuilder tree, final Number
value) {
+ return Long.toUnsignedString(switch (value) {
+ case Byte i -> Byte .toUnsignedLong(i);
+ case Short i -> Short .toUnsignedLong(i);
+ case Integer i -> Integer.toUnsignedLong(i);
+ default -> value.longValue();
+ });
+ }
+
+ @Override CharSequence summary(final TreeBuilder tree, final
Number value, final String text) {
+ final String name = tree.getItemName(value);
+ if (name != null) {
+ // Do not wrap in `NodeSummary` for keeping the name
always visible.
+ return name;
}
- return name;
+ return super.summary(tree, value, text);
}
},
/**
- * The integer should be interpreted as an unsigned integer.
- * Value should be formatted with {@link Long#toUnsignedString(long)}.
+ * The annotated integer should be interpreted as an unsigned integer.
+ * Value will be formatted as with {@link Long#toUnsignedString(long)}.
+ * If this annotation is absent, either negative values are allowed,
+ * or the unsigned value has already been converted to a wider type.
*/
UNSIGNED;
/**
* Formats the given integer according the type identified by this
enumeration.
*
- * @param context the tree being formatted. Can be used for fetching
contextual information.
- * @param value the integer value to format.
+ * @param tree builder of the tree to format.
+ * @param value the integer value to format.
* @return the formatted value, or {@code null} if absent.
*/
- String format(final Tree context, final Number value) {
- return Long.toUnsignedString(switch (value) {
- case Byte i -> Byte .toUnsignedLong(i);
- case Short i -> Short .toUnsignedLong(i);
- case Integer i -> Integer.toUnsignedLong(i);
- default -> value.longValue();
- });
+ String format(final TreeBuilder tree, Number value) {
+ switch (value) {
+ case Byte i: value = Byte .toUnsignedInt (i); break;
+ case Short i: value = Short .toUnsignedInt (i); break;
+ case Integer i: value = Integer.toUnsignedLong(i); break;
+ default: {
+ final long n = value.longValue();
+ if (n < 0) {
+ value = new BigInteger(Long.toUnsignedString(n));
+ }
+ break;
+ }
+ };
+ return tree.integerFormat.format(value);
+ }
+
+ /**
+ * Returns the text to show as the summary of a node when the node is
collapsed.
+ *
+ * @param tree builder of the tree to format.
+ * @param value the integer value which has been formatted.
+ * @param text the {@link #format(TreeBuilder, Number)} result.
+ * @return text to show as a summary of a collapsed node.
+ */
+ CharSequence summary(final TreeBuilder tree, final Number value, final
String text) {
+ return new NodeSummary(text);
+ }
+
+ /**
+ * Returns the value of the given annotation, or {@code null} if none.
+ *
+ * @param itpr the annotation, or {@code null}.
+ * @return the annotation value, or {@code null} if none.
+ */
+ static Type of(final Interpretation itpr) {
+ if (itpr != null) {
+ final Type type = itpr.value();
+ if (type != NONE) return type;
+ }
+ return null;
}
}
@@ -126,8 +195,6 @@ public abstract class TreeNode {
*
* @param value the code for which to get the human-readable string
representation.
* @return string representation of the given four-character code, or
{@code null} if the given code is zero.
- *
- * @see #create(Reader, int)
*/
public static String formatFourCC(final int value) {
if (value == 0) return null;
@@ -171,43 +238,81 @@ public abstract class TreeNode {
* Returns <abbr>HEIF</abbr> boxes and their fields as a tree.
* Used for showing native metadata or for debugging purposes.
*
- * @param locale the locale to use, or {@code null} for the default.
- * @param rootName name of the root node.
- * @param withSummary workaround, see {@link Tree#withSummary}.
+ * @param locale the locale to use, or {@code null} for the default.
+ * @param rootName name of the root node.
+ * @param withSummary whether to put a summary text in container nodes.
* @return fields contained in this node, together with child boxes.
*
* @todo It is sometime possible, through the tree cell values, to modify
the content of internal arrays.
* This is unsafe, we should makes this implementation safer before
this module is released.
*/
public final TreeTable toTree(final Locale locale, final String rootName,
final boolean withSummary) {
- final var tree = new Tree(locale, withSummary);
- final TreeTable.Node root = tree.getRoot();
+ final var builder = new TreeBuilder(locale, withSummary);
+ final TreeTable.Node root = builder.tree.getRoot();
root.setValue(TableColumn.NAME, rootName);
- tree.appendProperties(this, root);
- return tree;
+ builder.appendProperties(this, root);
+ return builder.tree;
}
/**
* A tree of box properties, with a string representation that excludes
the {@code VALUES} column.
* The value column is replaced by the {@code VALUE_AS_TEXT} column at
{@link #toString()} time.
- *
- * @todo The selection of the column to show should instead be done by
overriding a method,
- * for making possible for the <abbr>GUI</abbr> to also use that
information.
+ */
+ @SuppressWarnings("serial")
+ private static final class Tree extends DefaultTreeTable implements
Localized {
+ /**
+ * The locale to use, or {@code null} for the default.
+ */
+ private final Locale locale;
+
+ /**
+ * Creates an initially empty tree table.
+ *
+ * @param locale the locale to use in string representations.
+ */
+ Tree(final Locale locale) {
+ super(TableColumn.NAME, TableColumn.VALUE,
TableColumn.VALUE_AS_TEXT);
+ this.locale = locale;
+ }
+
+ /**
+ * Returns the locale used for formatting the tree.
+ *
+ * @return the locale to use, or {@code null} for the default.
+ */
+ @Override
+ public final Locale getLocale() {
+ return locale;
+ }
+
+ /**
+ * Returns a string representation of the tree table.
+ */
+ @Override
+ public final String toString() {
+ final var tf = new TreeTableFormat(locale, null);
+ tf.setColumns(TableColumn.NAME, TableColumn.VALUE_AS_TEXT);
+ return tf.format(this);
+ }
+ }
+
+ /**
+ * Builder of a string representation of tree nodes.
+ * Columns are {@code NAME}, {@code VALUE} and {@code VALUE_AS_TEXT}.
*
* <h2>Properties</h2>
* Producing a better formatting sometime requires contextual information.
* For example, <abbr>HEIF</abbr> declares relationship between boxes with
property index.
* It is much easier to read the tree if property indexes are replaced by
property names.
- * For making that possible, some information must be communicated between
the box that
- * contains property names and the box that use them. This is the purpose
of the
- * {@code set/getContext(…)} methods.
+ * For making that possible, some information must be communicated between
+ * the box that contains property names and the box that use them.
+ * This is the purpose of the {@code set/getContext(…)} methods.
*/
- @SuppressWarnings("serial")
- protected static final class Tree extends DefaultTreeTable {
+ protected static final class TreeBuilder extends PropertyFormat {
/**
- * The locale to use, or {@code null} for the default.
+ * The result for formatting the enclosing node and all children as a
tree table.
*/
- protected final Locale locale;
+ private final Tree tree;
/**
* Properties saved during the creation of the tree.
@@ -216,31 +321,44 @@ public abstract class TreeNode {
/**
* Information about an item referenced by its identifier.
- * This map is populated and read directly by {@link Box} subclasses
that need it.
+ * Keys are {@link ItemInfoEntry#itemID} values.
+ */
+ private final Map<Integer, ItemInfoEntry> names;
+
+ /**
+ * The format to use for integers.
*/
- public final Map<Integer, ItemInfoEntry> names;
+ private final NumberFormat integerFormat;
/**
* Whether the tree should put a summary of children node in the
"value as text" column.
* This summary duplicates the information contained inside the node,
so this field should
* be {@code false} for {@code toString()} implementation. However, it
is helpful when the
* node may be collapsed, as in <abbr>GUI</abbr> applications.
- *
- * @todo This is a workaround for not providing a summary in a more
dynamic way.
- * We should remove this field and replace it by a mechanism in
GUI for showing
- * and hiding the summary depending on whether the node is
expanded or collapsed.
*/
private final boolean withSummary;
/**
* Creates an initially empty tree table.
*/
- Tree(final Locale locale, final boolean withSummary) {
- super(TableColumn.NAME, TableColumn.VALUE,
TableColumn.VALUE_AS_TEXT);
- this.locale = locale;
- properties = new HashMap<>();
- names = new HashMap<>();
- this.withSummary = withSummary;
+ TreeBuilder(final Locale locale, final boolean withSummary) {
+ super(new StringBuilder());
+ setLineSeparator(" ¶ ");
+ this.withSummary = withSummary;
+ this.names = new HashMap<>();
+ this.properties = new HashMap<>();
+ this.tree = new Tree(locale);
+ this.integerFormat = NumberFormat.getIntegerInstance(locale);
+ }
+
+ /**
+ * Returns the locale used for formatting the tree.
+ *
+ * @return the locale to use, or {@code null} for the default.
+ */
+ @Override
+ public final Locale getLocale() {
+ return tree.getLocale();
}
/**
@@ -270,12 +388,55 @@ public abstract class TreeNode {
}
/**
- * Returns a string representation of this tree table.
+ * Remembers the name of the given entry.
+ *
+ * @param entry entry for which to remember the name.
*/
- @Override public final String toString() {
- final var tf = new TreeTableFormat(null, null);
- tf.setColumns(TableColumn.NAME, TableColumn.VALUE_AS_TEXT);
- return tf.format(this);
+ public final void addItemName(final ItemInfoEntry entry) {
+ names.put(entry.itemID, entry); // In case of conflict, keep
the most recent item.
+ }
+
+ /**
+ * Returns the name of the given entry.
+ *
+ * @param itemID identifier of the entry to get.
+ * @return name of the identified entry, or {@code null} if none.
+ */
+ public final String getItemName(final Number itemID) {
+ final ItemInfoEntry entry = names.get(itemID.intValue());
+ return (entry != null) ? entry.itemName : null;
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ protected String toString(final Object value) {
+ if (value instanceof Number &&
NumberType.isInteger(value.getClass())) {
+ return integerFormat.format(value);
+ } else {
+ return super.toString(value);
+ }
+ }
+
+ /**
+ * 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);
+ }
}
/**
@@ -284,91 +445,101 @@ public abstract class TreeNode {
*
* @param source the tree node to format.
* @param target the node where to add fields.
- * @return proposed summary describing the node, or {@code null} if
none.
*/
- private String appendProperties(final TreeNode source, final
TreeTable.Node target) {
- String summary = null;
+ private void appendProperties(final TreeNode source, final
TreeTable.Node target) {
+ CharSequence summary = null;
source.prependTreeNodes(this, target);
for (final Field field : source.getClass().getFields()) {
if (Modifier.isStatic(field.getModifiers())) {
continue;
}
- Object value;
+ final Object value;
try {
value = field.get(source);
if (value == null) continue;
} catch (IllegalAccessException e) {
- // May happen if the class is not public or not accessible.
- value = e;
+
Logging.unexpectedException(Logger.getLogger(Reader.LOGGER_NAME),
GeoHeifStore.class, "getNativeMetadata", e);
+ continue;
}
- /*
- * Convert identifier codes to their four-character code (4CC)
representation.
- * The fields to convert are identified by the
`@Interpretation` annotation.
- */
- final Class<?> componentType =
value.getClass().getComponentType();
- Interpretation format =
field.getAnnotation(Interpretation.class);
- if (format != null) {
- final Type type = format.value();
- if (value instanceof Number) {
- value = type.format(this, (Number) value);
- if (value == null) continue;
- } else if (componentType != null) {
- final var output = new String[Array.getLength(value)];
- for (int i=0; i<output.length; i++) {
- Object element = Array.get(value, i);
- if (element != null) {
- output[i] = (element instanceof Number) ?
type.format(this, (Number) element) : element.toString();
- }
+ if (value instanceof TreeNode[]) {
+ for (final TreeNode child : (TreeNode[]) value) {
+ if (child != null) {
+ final TreeTable.Node node = target.newChild();
+ node.setValue(TableColumn.NAME, child.typeName());
+ appendProperties(child, node);
}
- value = output;
}
- if (withSummary && type == Type.IDENTIFIER && summary ==
null && value instanceof String) {
- summary = (String) value;
- }
- } else if (componentType != null) {
+ } else if (value.getClass().isArray()) {
/*
- * If the value is an array of nested `TreeNode`
instances, append the elements as child nodes.
+ * Case of an array of integers, floating point numbers or
character strings.
+ * Identifiers are added as children because their `VALUE`
column may provide
+ * the names of the identified items.
*/
- final int length = Array.getLength(value);
- if (length == 0) continue;
- if (value instanceof TreeNode[] children) {
- if (length != 1) {
- for (final TreeNode node : children) {
- if (node != null) {
- final TreeTable.Node child =
target.newChild();
- child.setValue(TableColumn.NAME,
node.typeName());
- child.setValue(TableColumn.VALUE_AS_TEXT,
appendProperties(node, child));
- child.setValue(TableColumn.VALUE,
child.getValue(TableColumn.VALUE_AS_TEXT));
- // VALUE should be null, but the GUI
application currently doesn't pickup the
- // VALUE_AS_TEXT if VALUE is null.
+ final Type type =
Type.of(field.getAnnotation(Interpretation.class));
+ final TreeTable.Node addTo = (type == Type.IDENTIFIER) ?
addNode(target, field, value) : null;
+ final var values = new String[Array.getLength(value)];
+ for (int i=0; i < values.length; i++) {
+ final Object element = Array.get(value, i);
+ if (element != null) {
+ if (type != null) {
+ final var n = (Number) element;
+ if (addTo != null) {
+ addNode(addTo, Type.UNSIGNED.format(this,
n), n, getItemName(n));
+ } else {
+ values[i] = type.format(this, n);
}
+ } else {
+ values[i] = format(element);
}
- continue;
- } else {
- value = children[0];
}
- } else {
- final var buffer = new StringBuilder();
- Strings.appendWithHeuristic(value, buffer);
- value = buffer.toString();
}
- }
- /*
- * Finally append the string representation of the value,
- * or the child elements if the value is a nested node.
- */
- final TreeTable.Node child = target.newChild();
- String name = CharSequences.camelCaseToWords(field.getName(),
true).toString();
- child.setValue(TableColumn.NAME, name);
- if (value instanceof TreeNode) {
- appendProperties(((TreeNode) value), child);
+ if (addTo == null) {
+ addNode(target, field.getName(), value, String.join(",
", values));
+ }
} else {
- child.setValue(TableColumn.VALUE, value);
- child.setValue(TableColumn.VALUE_AS_TEXT,
Utilities.deepToString(value));
+ /*
+ * Case of a single element.
+ * Identifier codes will be converted to their
four-character code (4CC) representations.
+ * The fields to convert to 4CC are identified by the
`@Interpretation` annotation.
+ */
+ if (value instanceof TreeNode addTo) {
+ appendProperties(addTo, addNode(target, field, value));
+ } else {
+ final Interpretation itpr =
field.getAnnotation(Interpretation.class);
+ final Type type = Type.of(itpr);
+ final String text;
+ if (type != null) {
+ text = type.format(this, (Number) value);
+ } else {
+ text = format(value);
+ }
+ if (text != null) {
+ addNode(target, field.getName(), value, text);
+ }
+ if (summary == null && withSummary && itpr != null &&
itpr.summary()) {
+ summary = (type != null) ? type.summary(this,
(Number) value, text) : text;
+ }
+ }
}
}
source.appendTreeNodes(this, target);
- return summary;
+ /*
+ * The `VALUE_AS_TEXT` column of a `TreeNode` usually has no value
(only the fields have values).
+ * However, if we can identify a text that summarizes the content
of the node, we will show that
+ * text when the node is collapsed.
+ */
+ if (summary == null) {
+ final TreeTable.Node child =
Containers.peekIfSingleton(target.getChildren());
+ if (child != null) {
+ summary = child.getValue(TableColumn.VALUE_AS_TEXT);
+ if (summary != null && !(summary instanceof NodeSummary)) {
+ summary = new NodeSummary(summary.toString());
+ }
+ }
+ }
+ if (summary != null) {
+ target.setValue(TableColumn.VALUE_AS_TEXT, summary);
+ }
}
/**
@@ -378,9 +549,9 @@ public abstract class TreeNode {
* @param name the programmatic (camel-case) name of the node.
* @param value value of the node, or {@code null} if none.
*/
- public static void addNode(final TreeTable.Node target, final String
name, final Object value) {
+ public final void addNode(final TreeTable.Node target, final String
name, final Object value) {
if (value != null) {
- addNode(target, name, value, value.toString());
+ addNode(target, name, value, format(value));
}
}
@@ -400,6 +571,22 @@ public abstract class TreeNode {
child.setValue(TableColumn.VALUE_AS_TEXT, valueAsText);
return child;
}
+
+ /**
+ * Adds a node for a field. The {@code NAME} and {@code VALUE} columns
are set to values
+ * derived from the given arguments. The {@code VALUE_AS_TEXT} column
is left {@code null}.
+ *
+ * @param target where to add the node.
+ * @param field field of the property to represent as a node.
+ * @param value value of the property to represent as a node.
+ * @return the node which has been added.
+ */
+ private static TreeTable.Node addNode(final TreeTable.Node target,
final Field field, final Object value) {
+ final TreeTable.Node child = target.newChild();
+ child.setValue(TableColumn.NAME,
CharSequences.camelCaseToWords(field.getName(), false).toString());
+ child.setValue(TableColumn.VALUE, value);
+ return child;
+ }
}
/**
@@ -407,10 +594,10 @@ public abstract class TreeNode {
* This method is invoked automatically by {@link #toTree toTree(…)} for
generating
* the first nodes, before the nodes inferred by reflection.
*
- * @param context the tree being formatted. Can be used for fetching
contextual information.
- * @param target the node where to add properties.
+ * @param tree builder of the tree to format.
+ * @param target the node where to add properties.
*/
- protected void prependTreeNodes(Tree context, TreeTable.Node target) {
+ protected void prependTreeNodes(TreeBuilder tree, TreeTable.Node target) {
}
/**
@@ -421,15 +608,15 @@ public abstract class TreeNode {
* <p>The default implementation adds an artificial node saying that some
nodes are missing
* if the class is annotated with {@link Incomplete}.</p>
*
- * @param context the tree being formatted. Can be used for fetching
contextual information.
- * @param target the node where to add properties.
+ * @param tree builder of the tree to format.
+ * @param target the node where to add properties.
*/
- protected void appendTreeNodes(Tree context, TreeTable.Node target) {
+ protected void appendTreeNodes(TreeBuilder tree, TreeTable.Node target) {
if (getClass().isAnnotationPresent(Incomplete.class)) {
- final Vocabulary vocabulary = Vocabulary.forLocale(context.locale);
+ final Vocabulary vocabulary =
Vocabulary.forLocale(tree.getLocale());
final TreeTable.Node child = target.newChild();
- child.setValue(TableColumn.NAME,
vocabulary.getString(Vocabulary.Keys.UnsupportedProperties));
- child.setValue(TableColumn.VALUE,
vocabulary.getString(Vocabulary.Keys.Omitted));
+ child.setValue(TableColumn.NAME,
vocabulary.getString(Vocabulary.Keys.UnsupportedProperties));
+ child.setValue(TableColumn.VALUE_AS_TEXT,
vocabulary.getString(Vocabulary.Keys.Omitted));
}
}
}
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ColourInformation.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ColourInformation.java
index b65bcc8bfa..ed0fdb2fc7 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ColourInformation.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ColourInformation.java
@@ -69,7 +69,7 @@ public final class ColourInformation extends Box {
* Type of color information supplied.
* Can be {@link #NCLX}, {@link #RICC} or {@link #PROF}.
*/
- @Interpretation(Type.FOURCC)
+ @Interpretation(value=Type.FOURCC, summary=true)
public final int colourType;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/CombinaisonType.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/CombinaisonType.java
index 0297196780..b9311baf68 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/CombinaisonType.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/CombinaisonType.java
@@ -48,7 +48,7 @@ public final class CombinaisonType extends Box {
/**
* List of compatible brands.
*/
- @Interpretation(Type.FOURCC)
+ @Interpretation(value=Type.FOURCC, summary=true)
public final int[] compatibleBrands;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Copyright.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Copyright.java
index a32e50e1ef..71dda112e1 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Copyright.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/Copyright.java
@@ -57,6 +57,7 @@ public final class Copyright extends FullBox {
/**
* The copyright notice, or {@code null} if none.
*/
+ @Interpretation(value=Type.NONE, summary=true)
public final String notice;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/EntityToGroup.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/EntityToGroup.java
index 5f26c04fdc..c65ed1fab9 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/EntityToGroup.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/EntityToGroup.java
@@ -37,11 +37,13 @@ public abstract class EntityToGroup extends FullBox {
* Unique identifier assigned to the particular grouping.
* Should not be equal to any other {@code groupID} or {@code itemID}.
*/
+ @Interpretation(value=Type.IDENTIFIER, summary=true)
public final int groupID;
/**
* Identifiers of items in the group.
*/
+ @Interpretation(Type.IDENTIFIER)
public final int[] entityID;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/FDItemInfoExtension.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/FDItemInfoExtension.java
index 9c4fb186c7..75993fea85 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/FDItemInfoExtension.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/FDItemInfoExtension.java
@@ -39,7 +39,7 @@ public final class FDItemInfoExtension extends Box {
/**
* Identifies the extension fields of version 1 with respect to version 0
of the item information entry.
*/
- @Interpretation(Type.FOURCC)
+ @Interpretation(value=Type.FOURCC, summary=true)
public final int extensionType;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/FileType.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/FileType.java
index 19e322324a..76e30bb67d 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/FileType.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/FileType.java
@@ -50,7 +50,7 @@ public final class FileType extends Box {
/**
* Brand identifier.
*/
- @Interpretation(Type.FOURCC)
+ @Interpretation(value=Type.FOURCC, summary=true)
public final int majorBrand;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/HandlerReference.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/HandlerReference.java
index 762b0f9537..fc60135097 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/HandlerReference.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/HandlerReference.java
@@ -52,7 +52,7 @@ public final class HandlerReference extends FullBox {
* The format of the {@code Meta} box content.
* The value {@code "null"} indicates that this box is merely used to hold
resources.
*/
- @Interpretation(Type.FOURCC)
+ @Interpretation(value=Type.FOURCC, summary=true)
public final int handlerType;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/IdentifiedMediaData.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/IdentifiedMediaData.java
index c53c29905c..e46da1e246 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/IdentifiedMediaData.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/IdentifiedMediaData.java
@@ -50,7 +50,7 @@ public final class IdentifiedMediaData extends MediaData {
* An identifier used in setting up data references to the contained media
data.
* Shall be unique within all {@code IdentifiedMediaData} boxes of the
file.
*/
- @Interpretation(Type.UNSIGNED)
+ @Interpretation(value=Type.UNSIGNED, summary=true)
public final int identifier;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemInfo.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemInfo.java
index c6743d82e7..ce35592fe0 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemInfo.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemInfo.java
@@ -95,16 +95,16 @@ public final class ItemInfo extends FullBox {
/**
* Collects information about all items in this box.
- * It will be used by {@link ItemInfo} for more human-readable output.
+ * It will be used by {@link TreeBuilder} for more human-readable output.
*
- * @param context the tree being formatted. Can be used for fetching
contextual information.
- * @param target the node where to add properties.
+ * @param tree builder of the tree to format.
+ * @param target the node where to add properties.
*/
@Override
- protected void prependTreeNodes(final Tree context, final TreeTable.Node
target) {
- super.prependTreeNodes(context, target);
+ protected void prependTreeNodes(final TreeBuilder tree, final
TreeTable.Node target) {
+ super.prependTreeNodes(tree, target);
for (ItemInfoEntry entry : entries) {
- context.names.put(entry.itemID, entry); // In case of
conflict, keep the most recent item.
+ tree.addItemName(entry);
}
}
}
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemInfoEntry.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemInfoEntry.java
index 6bf77c9ec5..ae01af1687 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemInfoEntry.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemInfoEntry.java
@@ -54,12 +54,8 @@ public final class ItemInfoEntry extends FullBox {
/**
* 0 for the primary resource, or the identifier of the item for which the
information are defined.
- *
- * <h4>Implementation note</h4>
- * This field is not annotated with {@link Type#IDENTIFIER} because it
- * would cause this field to repeat the value shown in {@link #itemName}.
*/
- @Interpretation(Type.UNSIGNED)
+ @Interpretation(Type.IDENTIFIER)
public final int itemID;
/**
@@ -87,6 +83,7 @@ public final class ItemInfoEntry extends FullBox {
/**
* Symbolic name of the item (source file), or {@code null} if none.
*/
+ @Interpretation(value=Type.NONE, summary=true)
public final String itemName;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemLocation.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemLocation.java
index e2fd25011b..b4974caea4 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemLocation.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemLocation.java
@@ -102,7 +102,7 @@ public final class ItemLocation extends FullBox {
/**
* Identifier of this item, as an unsigned integer.
*/
- @Interpretation(Type.IDENTIFIER)
+ @Interpretation(value=Type.IDENTIFIER, summary=true)
public final int itemID;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemProperties.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemProperties.java
index 897be8d2cc..25eecba05a 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemProperties.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemProperties.java
@@ -184,14 +184,14 @@ public final class ItemProperties extends ContainerBox {
/**
* Opportunistically saves contextual information before to format the
tree.
- * {@link ItemPropertyAssociation.Entry#appendTreeNodes(Tree,
TreeTable.Node, boolean)}
+ * {@link ItemPropertyAssociation.Entry#appendTreeNodes(TreeBuilder,
TreeTable.Node)}
* will need the children of the property container.
*
- * @param context the tree being formatted. Can be used for fetching
contextual information.
- * @param target the node where to add properties.
+ * @param tree builder of the tree to format.
+ * @param target the node where to add properties.
*/
@Override
- protected void prependTreeNodes(final Tree context, final TreeTable.Node
target) {
+ protected void prependTreeNodes(final TreeBuilder tree, final
TreeTable.Node target) {
Box[] properties = null;
for (final Box box : children) {
if (box.type() == ItemPropertyContainer.BOXTYPE) {
@@ -199,19 +199,19 @@ public final class ItemProperties extends ContainerBox {
break;
}
}
- context.setContext(Box[].class, properties);
- super.prependTreeNodes(context, target);
+ tree.setContext(Box[].class, properties);
+ super.prependTreeNodes(tree, target);
}
/**
* Clears the context after formatting.
*
- * @param context the tree being formatted.
- * @param target the node where to add properties.
+ * @param tree builder of the tree to format.
+ * @param target the node where to add properties.
*/
@Override
- protected void appendTreeNodes(final Tree context, final TreeTable.Node
target) {
- super.appendTreeNodes(context, target);
- context.setContext(Box[].class, null);
+ protected void appendTreeNodes(final TreeBuilder tree, final
TreeTable.Node target) {
+ super.appendTreeNodes(tree, target);
+ tree.setContext(Box[].class, null);
}
}
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemPropertyAssociation.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemPropertyAssociation.java
index 61aebca46c..b86129e430 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemPropertyAssociation.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/ItemPropertyAssociation.java
@@ -61,7 +61,7 @@ public final class ItemPropertyAssociation extends FullBox {
/**
* Identifies the item with which properties are associated.
*/
- @Interpretation(Type.IDENTIFIER)
+ @Interpretation(value=Type.IDENTIFIER, summary=true)
public final int itemID;
/**
@@ -124,15 +124,14 @@ public final class ItemPropertyAssociation extends
FullBox {
* Appends properties other than the ones defined by public fields.
* Those properties will be shown last in the tree.
*
- * @param context the tree being formatted. Can be used for fetching
contextual information.
- * @param target the node where to add properties.
- * @param after {@code false} for the first nodes, or {@code true}
for the last nodes.
+ * @param tree builder of the tree to format.
+ * @param target the node where to add properties.
*
* @see ItemProperties#collect(Map)
*/
@Override
- protected void appendTreeNodes(final Tree context, final
TreeTable.Node target) {
- final Box[] properties = context.getContext(Box[].class);
+ protected void appendTreeNodes(final TreeBuilder tree, final
TreeTable.Node target) {
+ final Box[] properties = tree.getContext(Box[].class);
final TreeTable.Node indexes = target.newChild();
indexes.setValue(TableColumn.NAME, "property index");
for (int i : propertyIndex) {
@@ -149,7 +148,7 @@ public final class ItemPropertyAssociation extends FullBox {
}
}
}
- super.appendTreeNodes(context, target);
+ super.appendTreeNodes(tree, target);
}
}
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/PrimaryItem.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/PrimaryItem.java
index d2480cdfe2..d9091d071e 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/PrimaryItem.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/PrimaryItem.java
@@ -50,7 +50,7 @@ public final class PrimaryItem extends FullBox {
/**
* The identifier of the primary item. Shall be the identifier on an item
in the enclosing {@link Meta} box.
*/
- @Interpretation(Type.IDENTIFIER)
+ @Interpretation(value=Type.IDENTIFIER, summary=true)
public final int itemID;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/SingleItemTypeReference.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/SingleItemTypeReference.java
index 775a4d2199..d676d8cfa5 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/SingleItemTypeReference.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/SingleItemTypeReference.java
@@ -36,7 +36,7 @@ public abstract class SingleItemTypeReference extends Box {
/**
* The {@code itemID} of the item that refers to other items.
*/
- @Interpretation(Type.IDENTIFIER)
+ @Interpretation(value=Type.IDENTIFIER, summary=true)
public final int fromItemID;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/TrackHeader.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/TrackHeader.java
index 3b25217fa6..462b3e0a5c 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/TrackHeader.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/base/TrackHeader.java
@@ -56,7 +56,7 @@ public final class TrackHeader extends HeaderBox {
* An identifier that uniquely identifies this track over the entire
life-time of the presentation.
* Cannot be zero.
*/
- @Interpretation(Type.UNSIGNED)
+ @Interpretation(value=Type.UNSIGNED, summary=true)
public final int identifier;
/**
@@ -105,10 +105,11 @@ public final class TrackHeader extends HeaderBox {
/**
* Appends a description of the flags.
*
+ * @param tree builder of the tree to format.
* @param target the {@code flag} node where to add properties.
*/
@Override
- protected void appendFlagDescriptions(final TreeTable.Node target) {
- Tree.addNode(target, "enabled", isEnabled());
+ protected void appendFlagDescriptions(final TreeBuilder tree, final
TreeTable.Node target) {
+ tree.addNode(target, "enabled", isEnabled());
}
}
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/ModelCRS.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/ModelCRS.java
index 244f0fa9c7..82c19b5b6f 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/ModelCRS.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/ModelCRS.java
@@ -90,7 +90,7 @@ public final class ModelCRS extends FullBox {
* <li>{@code "wkt2"} for a Well-Known Text definition.</li>
* </ul>
*/
- @Interpretation(Type.FOURCC)
+ @Interpretation(value=Type.FOURCC, summary=true)
public final int crsEncoding;
/**
@@ -143,6 +143,7 @@ public final class ModelCRS extends FullBox {
warning = new LogRecord(Level.WARNING, "Cannot decode the
CRS.");
warning.setThrown(e);
}
+ warning.setLoggerName(Reader.LOGGER_NAME);
warning.setSourceClassName(ModelCRS.class.getName());
warning.setSourceMethodName("toCRS");
listeners.warning(warning);
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/ModelTiePoint.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/ModelTiePoint.java
index 64b5ccbb3b..87d440895c 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/ModelTiePoint.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/ModelTiePoint.java
@@ -19,6 +19,7 @@ package org.apache.sis.storage.isobmff.geo;
import java.util.UUID;
import java.io.IOException;
import org.apache.sis.io.stream.ChannelDataInput;
+import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.storage.isobmff.FullBox;
import org.apache.sis.storage.isobmff.TreeNode;
import org.apache.sis.storage.isobmff.Reader;
@@ -111,11 +112,31 @@ public final class ModelTiePoint extends FullBox {
public ModelTiePoint(final Reader reader) throws IOException,
UnsupportedVersionException {
super(reader);
requireVersionZero();
- final int dimension = ((flags & 0x01) == 1) ? 2 : 3;
+ final int dimension = dimension();
final ChannelDataInput input = reader.input;
tiepoints = new TiePoint[input.readUnsignedShort()];
for (int i=0; i<tiepoints.length; i++) {
tiepoints[i] = new TiePoint(input, dimension);
}
}
+
+ /**
+ * Returns the number of dimensions (2 or 3).
+ *
+ * @return number of dimensions.
+ */
+ public final int dimension() {
+ return 2 + (flags & 0x01);
+ }
+
+ /**
+ * Appends a description of the flags.
+ *
+ * @param tree builder of the tree to format.
+ * @param target the {@code flag} node where to add properties.
+ */
+ @Override
+ protected void appendFlagDescriptions(final TreeBuilder tree, final
TreeTable.Node target) {
+ tree.addNode(target, "dimension", dimension());
+ }
}
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/TiledImageConfiguration.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/TiledImageConfiguration.java
index bbd9ca8367..d12d1284c5 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/TiledImageConfiguration.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/geo/TiledImageConfiguration.java
@@ -67,7 +67,7 @@ public final class TiledImageConfiguration extends FullBox {
* The image type used for all tiles.
* Examples: {@code hvc1} for h265 compression or {@code j2k1} for
JPEG2000.
*/
- @Interpretation(Type.FOURCC)
+ @Interpretation(value=Type.FOURCC, summary=true)
public final int tileItemType;
/**
@@ -152,12 +152,13 @@ public final class TiledImageConfiguration extends
FullBox {
/**
* Appends a description of the flags.
*
+ * @param tree builder of the tree to format.
* @param target the {@code flag} node where to add properties.
*/
@Override
- protected void appendFlagDescriptions(final TreeTable.Node target) {
- Tree.addNode(target, "offsetFieldLength", offsetFieldLength());
- Tree.addNode(target, "sizeFieldLength", sizeFieldLength());
- Tree.addNode(target, "sequential", sequential());
+ protected void appendFlagDescriptions(final TreeBuilder tree, final
TreeTable.Node target) {
+ tree.addNode(target, "offsetFieldLength", offsetFieldLength());
+ tree.addNode(target, "sizeFieldLength", sizeFieldLength());
+ tree.addNode(target, "sequential", sequential());
}
}
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/CreationTime.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/CreationTime.java
index fe20c27c52..d1b7a0fa36 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/CreationTime.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/CreationTime.java
@@ -51,6 +51,7 @@ public final class CreationTime extends FullBox {
/**
* The creation time of the item.
*/
+ @Interpretation(value=Type.NONE, summary=true)
public final Instant creationTime;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/ModificationTime.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/ModificationTime.java
index 959a2c68be..eda76ff5d7 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/ModificationTime.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/ModificationTime.java
@@ -51,6 +51,7 @@ public final class ModificationTime extends FullBox {
/**
* The last modification time of the item.
*/
+ @Interpretation(value=Type.NONE, summary=true)
public final Instant modificationTime;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/UserDescription.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/UserDescription.java
index f6273d9f66..7673641e8f 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/UserDescription.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/image/UserDescription.java
@@ -59,6 +59,7 @@ public final class UserDescription extends FullBox {
/**
* Human-readable name for the item, or {@code null} if none.
*/
+ @Interpretation(value=Type.NONE, summary=true)
public final String name;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/Component.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/Component.java
index 674bdce754..0abf1ec19d 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/Component.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/Component.java
@@ -55,6 +55,7 @@ public final class Component extends TreeNode {
*
* @see ComponentDefinition#componentTypes
*/
+ @Interpretation(value=Type.NONE, summary=true)
public Object type;
/**
diff --git
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/UncompressedFrameConfig.java
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/UncompressedFrameConfig.java
index 1ead3c9c16..dc7f535791 100644
---
a/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/UncompressedFrameConfig.java
+++
b/incubator/src/org.apache.sis.storage.geoheif/main/org/apache/sis/storage/isobmff/mpeg/UncompressedFrameConfig.java
@@ -78,7 +78,7 @@ public final class UncompressedFrameConfig extends FullBox {
* Predefined configuration as a code such as {@link #RGB}, {@link #RGBA}
or {@link #ARGB}.
* Value 0 means no profile.
*/
- @Interpretation(Type.FOURCC)
+ @Interpretation(value=Type.FOURCC, summary=true)
public final int profile;
/**
diff --git
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/PropertyValueFormats.java
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/PropertyValueFormats.java
index 52c1d5cbdf..74a04799f6 100644
---
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/PropertyValueFormats.java
+++
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/PropertyValueFormats.java
@@ -76,7 +76,7 @@ public class PropertyValueFormats extends
CompoundFormat<Object> {
*/
@Override
public final void format(final Object value, final Appendable toAppendTo) {
- final StringBuffer buffer = (StringBuffer) toAppendTo;
+ final var buffer = (StringBuffer) toAppendTo;
final Format f = getFormat(value.getClass());
if (f != null) {
f.format(value, buffer, new FieldPosition(0));
@@ -115,7 +115,7 @@ public class PropertyValueFormats extends
CompoundFormat<Object> {
* @param toAppendTo where to append the property value.
*/
public final void formatPair(final double first, final String separator,
final double second, final StringBuffer toAppendTo) {
- final FieldPosition pos = new FieldPosition(0);
+ final var pos = new FieldPosition(0);
final Format f = getFormat(Number.class);
format(f, first, toAppendTo, pos); toAppendTo.append(separator);
format(f, second, toAppendTo, pos);
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 26b047f14d..1c9fadea7b 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
@@ -18,13 +18,16 @@ 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;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
+import javafx.beans.value.WeakChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.control.Dialog;
import javafx.scene.control.DialogPane;
@@ -50,6 +53,7 @@ import org.apache.sis.gui.internal.ExceptionReporter;
import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.util.collection.TableColumn;
import org.apache.sis.util.resources.Vocabulary;
+import org.apache.sis.storage.metadata.NodeSummary;
/**
@@ -76,7 +80,7 @@ import org.apache.sis.util.resources.Vocabulary;
*
* @author Siddhesh Rane (GSoC)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.4
+ * @version 1.7
* @since 1.1
*/
@DefaultProperty("content")
@@ -88,9 +92,10 @@ public class MetadataTree extends
TreeTableView<TreeTable.Node> {
/**
* The column for metadata property value in the view.
- * Values are typically {@link InternationalString}, {@link Number} or
dates.
+ * Values are typically {@link InternationalString}, {@link Number} or
dates
+ * formatted as character strings by {@link Formatter}.
*/
- private final TreeTableColumn<TreeTable.Node, Object> valueColumn;
+ private final TreeTableColumn<TreeTable.Node, String> valueColumn;
/**
* The column for metadata property value in the model.
@@ -129,9 +134,9 @@ public class MetadataTree extends
TreeTableView<TreeTable.Node> {
* The columns where to search for values, in preference order.
*/
private static final TableColumn<?>[] VALUES_COLUMNS = {
- TableColumn.VALUE,
TableColumn.VALUE_AS_NUMBER,
- TableColumn.VALUE_AS_TEXT
+ TableColumn.VALUE_AS_TEXT,
+ TableColumn.VALUE
};
/** Creates a new property. */
@@ -304,32 +309,25 @@ check: if (data != null) {
final var children = super.getChildren();
if (children.isEmpty()) {
// Fire a single event instead of multiple `add`.
-
children.setAll(getValue().getChildren().stream().map(Item::new).toList());
+ children.setAll(getValue().getChildren().stream()
+ .filter(TreeTable.Node::isVisible)
+ .map(Item::new)
+ .toList());
}
return children;
}
}
- /**
- * Returns the value for the specified column.
- * The generic type in this method signature reduces the risk that we
confuse columns.
- *
- * @param <T> the type of values in the column.
- * @param cell a wrapper around the {@link TreeTable.Node} from which
to get the value.
- * @param column column of the desired value.
- * @return value in the specified column. May be {@code null}.
- */
- private static <T> T getValue(final CellDataFeatures<TreeTable.Node, ?>
cell, final TableColumn<T> column) {
- final TreeTable.Node node = cell.getValue().getValue();
- return node.getValue(column);
- }
-
/**
* Returns the name of the metadata property wrapped by the given argument.
* This method is invoked by JavaFX when a new cell needs to be rendered.
+ *
+ * @param cell a wrapper around the {@link TreeTable.Node} from which to
get the name.
+ * @return the name. May be {@code null}.
*/
private static ObservableValue<String> getPropertyName(final
CellDataFeatures<TreeTable.Node, String> cell) {
- final CharSequence value = getValue(cell, TableColumn.NAME);
+ final TreeTable.Node node = cell.getValue().getValue();
+ final CharSequence value = node.getValue(TableColumn.NAME);
final String text;
if (value instanceof InternationalString) {
final var view = (MetadataTree) cell.getTreeTableView();
@@ -342,16 +340,26 @@ check: if (data != null) {
/**
* 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>.
+ * many object classes like {@link InternationalString}, <i>etc</i>. This
handling is done by the
+ * {@link org.apache.sis.util.internal.shared.PropertyFormat} parent class.
*/
private static final class Formatter extends PropertyValueFormatter
- implements Callback<CellDataFeatures<TreeTable.Node, Object>,
ObservableValue<Object>>
+ implements Callback<CellDataFeatures<TreeTable.Node, String>,
ObservableValue<String>>
{
+ /**
+ * The observable properties created for each tree table node.
+ * We need to reuse the instances created for each node in order to
keep listeners.
+ *
+ * @see #call(TreeTableColumn.CellDataFeatures)
+ */
+ private final WeakHashMap<TreeTable.Node, SimpleStringProperty>
observables;
+
/**
* Creates a new formatter for the given locale.
*/
Formatter(final Locale locale) {
super(new StringBuilder(), locale);
+ observables = new WeakHashMap<>();
}
/**
@@ -359,23 +367,65 @@ check: if (data != null) {
* 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 var view = (MetadataTree) cell.getTreeTableView();
- Object value = getValue(cell, view.valueSourceColumn);
- if (value instanceof IdentifiedObject) {
- value = IdentifiedObjects.getDisplayName((IdentifiedObject)
value, getLocale());
- }
- try {
- clear();
- final var buffer = (StringBuilder) out;
- buffer.setLength(0);
- appendValue(value);
- flush();
- value = buffer.toString();
- } catch (IOException e) { // Should never happen
because we append in a StringBuilder.
- throw new AssertionError(e);
+ public ObservableValue<String> call(final
CellDataFeatures<TreeTable.Node, String> cell) {
+ final TreeItem<TreeTable.Node> item = cell.getValue();
+ final TreeTable.Node node = item.getValue();
+ SimpleStringProperty property = observables.get(node);
+ if (property == null) {
+ final var view = (MetadataTree) cell.getTreeTableView();
+ final Object value = node.getValue(view.valueSourceColumn);
+ 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);
+ }
+ if (value instanceof NodeSummary) {
+ final var summary = new SummaryProperty(text);
+ item.expandedProperty().addListener(new
WeakChangeListener<>(summary));
+ property = summary;
+ } else {
+ property = new SimpleStringProperty(text);
+ }
+ observables.put(node, property);
}
- return new SimpleObjectProperty<>(value);
+ return property;
+ }
+ }
+
+ /**
+ * A property with a text which is shown or hidden depending on whether
the node is collapsed or expanded.
+ * This is used for content of {@link NodeSummary}. We want to hide this
content when the node is expanded
+ * because it become redundant with the children, and this redundancy is
distracting when the user wants
+ * to look for the child that contains this information.
+ */
+ private static final class SummaryProperty extends SimpleStringProperty
implements ChangeListener<Boolean> {
+ /**
+ * The text to show or hide.
+ */
+ private final String text;
+
+ /**
+ * Creates a new property with the given summary text.
+ */
+ SummaryProperty(final String text) {
+ super(text);
+ this.text = text;
+ }
+
+ /**
+ * Invoked when the node is expanded or collapsed.
+ */
+ @Override
+ public void changed(ObservableValue<? extends Boolean> property,
Boolean oldValue, Boolean newValue) {
+ set(newValue ? null : text);
}
}
@@ -396,6 +446,7 @@ check: if (data != null) {
/**
* Creates a new row for the given tree table.
*/
+ @SuppressWarnings("LeakingThisInConstructor")
Row(final TreeTableView<TreeTable.Node> owner) {
final var md = (MetadataTree) owner;
final Resources localized = Resources.forLocale(md.getLocale());
@@ -424,11 +475,14 @@ check: if (data != null) {
*/
private Object getValue() {
final TreeTable.Node node = getItem();
- if (node != null) {
- final Object obj = node.getUserObject();
- return (obj != null) ? obj : node.getValue(((MetadataTree)
getTreeTableView()).valueSourceColumn);
+ if (node == null) {
+ return null;
+ }
+ Object value = node.getUserObject();
+ if (value == null) {
+ value = node.getValue(((MetadataTree)
getTreeTableView()).valueSourceColumn);
}
- return null;
+ return value;
}
/**
@@ -440,7 +494,7 @@ check: if (data != null) {
public void handle(final ActionEvent event) {
final Object value = getValue();
if (value != null) {
- final ClipboardContent content = new ClipboardContent();
+ final var content = new ClipboardContent();
content.putString(toString(value));
Clipboard.getSystemClipboard().setContent(content);
}
diff --git
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/StandardMetadataTree.java
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/StandardMetadataTree.java
index 180fd653e1..6dfd5f0b2e 100644
---
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/StandardMetadataTree.java
+++
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/StandardMetadataTree.java
@@ -102,8 +102,8 @@ public class StandardMetadataTree extends MetadataTree {
final TreeTable tree;
if (metadata == null) {
tree = null;
- } else if (metadata instanceof AbstractMetadata) {
- tree = ((AbstractMetadata) metadata).asTreeTable();
+ } else if (metadata instanceof AbstractMetadata md) {
+ tree = md.asTreeTable();
} else {
// `COMPACT` is the default policy of
`AbstractMetadata.asTreeTable()`.
tree = MetadataStandard.ISO_19115.asTreeTable(metadata,
Metadata.class, ValueExistencePolicy.COMPACT);
@@ -128,6 +128,7 @@ public class StandardMetadataTree extends MetadataTree {
/**
* Creates a new row for the given tree table.
*/
+ @SuppressWarnings("LeakingThisInConstructor")
Row(final TreeTableView<TreeTable.Node> view) {
super(view);
final var md = (StandardMetadataTree) view;
@@ -178,6 +179,7 @@ public class StandardMetadataTree extends MetadataTree {
* menu items.
*/
@Override
+ @SuppressWarnings("UseSpecificCatch")
public void handle(final ActionEvent event) {
final TreeTable.Node node = getItem();
if (node != null) {
diff --git
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/package-info.java
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/package-info.java
index e38c42b847..3780afa317 100644
---
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/package-info.java
+++
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/metadata/package-info.java
@@ -23,7 +23,7 @@
* @author Smaniotto Enzo (GSoC)
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.6
+ * @version 1.7
* @since 1.1
*/
package org.apache.sis.gui.metadata;