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 130aab7172 Apply to ISO-19115 metadata the same mechanism used by
GeoHEIF for summarizing metadata.
130aab7172 is described below
commit 130aab7172a1255cf952021be7dd1e0988ba9117
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon May 18 01:07:13 2026 +0200
Apply to ISO-19115 metadata the same mechanism used by GeoHEIF for
summarizing metadata.
---
.../org/apache/sis/metadata/MetadataStandard.java | 2 +-
.../org/apache/sis/metadata/TitleProperty.java | 2 +-
.../main/org/apache/sis/metadata/TreeNode.java | 6 +-
.../org/apache/sis/metadata/TreeNodeChildren.java | 35 +++++--
.../org/apache/sis/metadata/TreeTableView.java | 14 ++-
.../apache/sis/metadata/ValueExistencePolicy.java | 104 +++++++++++++--------
.../main/org/apache/sis/metadata/package-info.java | 2 +-
.../main/org/apache/sis/xml/bind/gcx/Anchor.java | 9 +-
.../apache/sis/metadata/TreeNodeChildrenTest.java | 14 ++-
.../test/org/apache/sis/metadata/TreeNodeTest.java | 11 ++-
.../org/apache/sis/metadata/TreeTableViewTest.java | 3 +-
.../apache/sis/storage/metadata/NodeSummary.java | 18 +++-
.../sis/util/AbstractInternationalString.java | 22 ++---
.../sis/util/internal/shared/TreeTableForGUI.java | 42 +++++++++
.../org/apache/sis/storage/isobmff/TreeNode.java | 22 +++--
.../org/apache/sis/gui/metadata/MetadataTree.java | 26 ++++--
.../sis/gui/metadata/StandardMetadataTree.java | 8 +-
17 files changed, 237 insertions(+), 103 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java
index f7ec708fae..ddc62e51ca 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java
@@ -669,7 +669,7 @@ public class MetadataStandard implements Serializable {
* @return the title property value of the given metadata, or {@code null}
if none.
*
* @see TitleProperty
- * @see ValueExistencePolicy#COMPACT
+ * @see ValueExistencePolicy#TITLED
*/
final Object getTitle(final Object metadata) {
if (metadata != null) {
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TitleProperty.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TitleProperty.java
index 8efa67e88b..42e15f4433 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TitleProperty.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TitleProperty.java
@@ -61,7 +61,7 @@ import java.lang.annotation.Documented;
* @version 0.8
* @since 0.8
*
- * @see ValueExistencePolicy#COMPACT
+ * @see ValueExistencePolicy#TITLED
*/
@Documented
@Inherited
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java
index a4abb46eba..2261a1b70d 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java
@@ -1026,10 +1026,12 @@ class TreeNode implements Node {
}
/**
- * Returns the children if the value policy is {@link
ValueExistencePolicy#COMPACT}, or {@code null} otherwise.
+ * Returns the children if the value policy puts a title on metadata
objects, or {@code null} otherwise.
+ * The callers are not interested in the collection of children, but
rather in specialized methods such
+ * as {@link TreeNodeChildren#getParentTitle()}.
*/
private TreeNodeChildren getCompactChildren() {
- if (table.valuePolicy == ValueExistencePolicy.COMPACT) {
+ if (table.valuePolicy.isTitled()) {
@SuppressWarnings("LocalVariableHidesMemberVariable")
final Collection<Node> children = getChildren();
if (children instanceof TreeNodeChildren) {
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java
index 95d0fce1ab..92dca2a1f9 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java
@@ -96,8 +96,9 @@ final class TreeNodeChildren extends
AbstractCollection<TreeTable.Node> {
/**
* Index of the property to write in the parent node instead of as a child.
- * If a property has the same name as the parent property that contains it,
- * we write its value in that parent property. For example, instead of:
+ * If a property is specified in a {@link TitleProperty} class annotation,
+ * we write the property value value in the parent node.
+ * For example, instead of:
*
* <pre class="text">
* Citation
@@ -116,6 +117,13 @@ final class TreeNodeChildren extends
AbstractCollection<TreeTable.Node> {
*/
final int titleProperty;
+ /**
+ * Index of the property to hide, or -1 if none.
+ * This is positive only in the {@link ValueExistencePolicy#COMPACT} case,
+ * in which case this value is the same as {@link #titleProperty}.
+ */
+ private final int hiddenProperty;
+
/**
* Modification count, incremented when the content of this collection is
modified. This check
* is done on a <em>best effort basis</em> only, since we cannot not track
the changes which
@@ -137,10 +145,10 @@ final class TreeNodeChildren extends
AbstractCollection<TreeTable.Node> {
this.children = new TreeNode.Element[accessor.count()];
/*
* Search for something that looks like the main property, to be
associated with the parent node
- * instead of provided as a child. The intent is to have more compact
and easy to read trees.
+ * instead of provided as a child. The intent is to make trees more
compact and easier to read.
* That property shall be a singleton for a simple value (not another
metadata object).
*/
- if (parent.table.valuePolicy == ValueExistencePolicy.COMPACT) {
+ if (parent.table.valuePolicy.isTitled()) {
TitleProperty an =
accessor.implementation.getAnnotation(TitleProperty.class);
if (an == null) {
Class<?> implementation =
parent.table.standard.getImplementation(accessor.type);
@@ -152,12 +160,21 @@ final class TreeNodeChildren extends
AbstractCollection<TreeTable.Node> {
final int index = accessor.indexOf(an.name(), false);
final Class<?> type = accessor.type(index,
TypeValuePolicy.ELEMENT_TYPE);
if (type != null && !parent.isMetadata(type) && type ==
accessor.type(index, TypeValuePolicy.PROPERTY_TYPE)) {
+ hiddenProperty = (parent.table.valuePolicy ==
ValueExistencePolicy.COMPACT) ? index : -1;
titleProperty = index;
return;
}
}
}
- titleProperty = -1;
+ titleProperty = -1;
+ hiddenProperty = -1;
+ }
+
+ /**
+ * Returns whether the value returned by this node is a title fetched from
another property.
+ */
+ final boolean isNodeTitle() {
+ return titleProperty >= 0;
}
/**
@@ -178,7 +195,7 @@ final class TreeNodeChildren extends
AbstractCollection<TreeTable.Node> {
/**
* Sets the value associated to the parent node, if possible.
- * This returned boolean tells whether the value has been written.
+ * The returned Boolean tells whether the value has been written.
*/
final boolean setParentTitle(final Object value) {
if (titleProperty < 0) {
@@ -306,7 +323,7 @@ final class TreeNodeChildren extends
AbstractCollection<TreeTable.Node> {
@Override
public int size() {
int count = accessor.count(metadata, parent.table.valuePolicy,
PropertyAccessor.COUNT_DEEP);
- if (titleProperty >= 0 && !isSkipped(valueAt(titleProperty))) count--;
+ if (hiddenProperty >= 0 && !isSkipped(valueAt(hiddenProperty)))
count--;
return count;
}
@@ -316,7 +333,7 @@ final class TreeNodeChildren extends
AbstractCollection<TreeTable.Node> {
*/
@Override
public boolean isEmpty() {
- if (titleProperty >= 0) return size() == 0; // COUNT_FIRST is not
reliable in this case.
+ if (hiddenProperty >= 0) return size() == 0; // COUNT_FIRST is not
reliable in this case.
return accessor.count(metadata, parent.table.valuePolicy,
PropertyAccessor.COUNT_FIRST) == 0;
}
@@ -475,7 +492,7 @@ final class TreeNodeChildren extends
AbstractCollection<TreeTable.Node> {
*/
final int count = childCount();
while (nextInAccessor < count) {
- if (nextInAccessor != titleProperty) {
+ if (nextInAccessor != hiddenProperty) {
nextValue = valueAt(nextInAccessor);
if (!isSkipped(nextValue)) {
if (isCollectionOrMap(nextInAccessor)) {
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeTableView.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeTableView.java
index 681e8c7f8e..56fbb43c45 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeTableView.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeTableView.java
@@ -17,15 +17,16 @@
package org.apache.sis.metadata;
import java.util.List;
+import java.util.Collection;
import java.io.Serializable;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.apache.sis.util.ArraysExt;
-import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.util.collection.TableColumn;
import org.apache.sis.util.collection.TreeTableFormat;
import org.apache.sis.util.collection.Containers;
+import org.apache.sis.util.internal.shared.TreeTableForGUI;
import org.apache.sis.system.Semaphores;
@@ -46,7 +47,7 @@ import org.apache.sis.system.Semaphores;
*
* @author Martin Desruisseaux (Geomatys)
*/
-final class TreeTableView implements TreeTable, Serializable {
+final class TreeTableView implements TreeTableForGUI, Serializable {
/**
* For cross-version compatibility.
*/
@@ -124,6 +125,15 @@ final class TreeTableView implements TreeTable,
Serializable {
return root;
}
+ /**
+ * Returns whether the given value produces by the given node is a title.
+ */
+ @Override
+ public boolean isNodeTitle(final Node node, final Object value) {
+ final Collection<Node> children = node.getChildren();
+ return (children instanceof TreeNodeChildren) && ((TreeNodeChildren)
children).isNodeTitle();
+ }
+
/**
* Returns a string representation of this tree table.
* The current implementation uses a shared instance of {@link
TreeTableFormat}.
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ValueExistencePolicy.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ValueExistencePolicy.java
index e050b230f8..c283cef3c3 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ValueExistencePolicy.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ValueExistencePolicy.java
@@ -38,7 +38,7 @@ import org.apache.sis.xml.NilReason;
* Those explanations can be obtained by calls to the {@link
org.apache.sis.xml.NilReason#forObject(Object)} method.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.7
*
* @see MetadataStandard#asValueMap(Object, Class, KeyNamePolicy,
ValueExistencePolicy)
*
@@ -47,13 +47,13 @@ import org.apache.sis.xml.NilReason;
public enum ValueExistencePolicy {
/*
* Implementation note: enumeration order matter.
- * The `acceptNilValues()` method relies on it.
+ * The `acceptNilValues()` and `isTitled()` methods rely on this order.
*/
/**
* Includes all entries in the map, including those having a null value or
an empty collection.
*/
- ALL() {
+ ALL {
/** Never skip values. */
@Override boolean isSkipped(final Object value) {
return false;
@@ -73,7 +73,7 @@ public enum ValueExistencePolicy {
*
* <p>The set of {@code NON_NULL} properties is a subset of {@link #ALL}
properties.</p>
*/
- NON_NULL() {
+ NON_NULL {
/** Skips all null values. */
@Override boolean isSkipped(final Object value) {
return (value == null);
@@ -94,7 +94,7 @@ public enum ValueExistencePolicy {
*
* @since 0.4
*/
- NON_NIL() {
+ NON_NIL {
/** Skips all null or nil values. */
@Override boolean isSkipped(final Object value) {
return (value == null) || (value instanceof NilObject) ||
NilReason.forObject(value) != null;
@@ -122,34 +122,47 @@ public enum ValueExistencePolicy {
*
* <p>The set of {@code NON_EMPTY} properties is a subset of {@link
#NON_NIL} properties.</p>
*/
- NON_EMPTY() {
- /** Skips all null or empty values. */
- @Override boolean isSkipped(final Object value) {
- return isNullOrEmpty(value);
- }
+ NON_EMPTY,
- /** Never substitute null or empty collections since they should be
skipped. */
- @Override boolean substituteByNullElement(final Collection<?> values) {
- return false;
- }
- },
+ /**
+ * Same as {@code NON_EMPTY}, but with the addition of titles associated
to metadata objects.
+ * Metadata objects such as {@link Citation} are normally associated to no
textual value.
+ * Texts are rather associated to <em>properties</em> of the metadata
object.
+ * But some metadata classes have a property which can summarize the whole
object.
+ * For example, for {@link org.opengis.metadata.citation.Citation}
objects, this property
+ * is the {@linkplain org.opengis.metadata.citation.Citation#getTitle()
citation title}.
+ *
+ * <p>The property to use as the title of a metadata object is specified by
+ * the {@link TitleProperty} annotation on the metadata implementation
class
+ * (for example, {@link
org.apache.sis.metadata.iso.citation.DefaultCitation}).
+ * When using this {@code TITLED} or the {@link #COMPACT} policy, the
values of
+ * the properties identified by the {@link TitleProperty} annotations are
copied
+ * in the root of the metadata sub-tree.
+ * See {@link #COMPACT} Javadoc for an example.</p>
+ *
+ * @see TitleProperty
+ *
+ * @since 1.7
+ */
+ TITLED,
/**
- * Includes non-empty properties but omits {@linkplain TitleProperty title
properties}.
- * Values associated to title properties are instead associated with the
parent node.
- * This policy is relevant for metadata classes annotated with {@link
TitleProperty};
- * for all other classes, this policy is identical to {@link #NON_EMPTY}.
+ * Same as {@code TITLED} but omitting the properties which have been used
as titles.
+ * This omission removes a redundancy, thus making the tree a little bit
more compact,
+ * at the expanse of a slight departure from the metadata model. For
metadata classes
+ * without {@link TitleProperty} annotation, this policy is equivalent to
{@link #NON_EMPTY}.
*
* <h4>Example</h4>
- * the {@link org.apache.sis.metadata.iso.citation.DefaultCitation} and
+ * The {@link org.apache.sis.metadata.iso.citation.DefaultCitation} and
* {@link org.apache.sis.metadata.iso.citation.DefaultCitationDate}
classes are annotated with
* <code>@TitleProperty(name="title")</code> and
<code>@TitleProperty(name="date")</code>
- * respectively. The following table compares the trees produced by two
policies:
+ * respectively. The following table compares the trees produced by three
policies:
*
* <table class="sis">
- * <caption>Comparison of "non-empty" and "compact" policy on the same
metadata</caption>
+ * <caption>Comparison of policies on the same metadata</caption>
* <tr>
* <th>{@code NON_EMPTY}</th>
+ * <th class="sep">{@code TITLED}</th>
* <th class="sep">{@code COMPACT}</th>
* </tr><tr><td>
* <pre class="text">
@@ -161,29 +174,33 @@ public enum ValueExistencePolicy {
* </td><td class="sep">
* <pre class="text">
* Citation……………………… My document
+ * ├─Title……………………… My document
+ * └─Date………………………… 2012/01/01
+ * ├─Date………………… 2012/01/01
+ * └─Date type…… Creation</pre>
+ * </td><td class="sep">
+ * <pre class="text">
+ * Citation……………………… My document
* └─Date………………………… 2012/01/01
* └─Date type…… Creation</pre>
* </td></tr>
* </table>
*
- * This policy is the default behavior of {@link
AbstractMetadata#asTreeTable()},
- * and consequently defines the default rendering of {@link
AbstractMetadata#toString()}.
+ * This {@code COMPACT} policy is the default behavior of {@link
AbstractMetadata#asTreeTable()}.
+ * Therefore, this policy defines the default rendering of {@link
AbstractMetadata#toString()}.
*
* @see TitleProperty
*
* @since 0.8
*/
- COMPACT() {
- /** Skips all null or empty values. */
- @Override boolean isSkipped(final Object value) {
- return isNullOrEmpty(value);
- }
+ COMPACT;
- /** Never substitute null or empty collections since they should be
skipped. */
- @Override boolean substituteByNullElement(final Collection<?> values) {
- return false;
- }
- };
+ /**
+ * Returns whether this policy add a title in metadata objects.
+ */
+ final boolean isTitled() {
+ return ordinal() >= TITLED.ordinal();
+ }
/**
* Returns whether this policy accepts nil values.
@@ -194,18 +211,29 @@ public enum ValueExistencePolicy {
/**
* Returns {@code true} if the given value shall be skipped for this
policy.
+ * By default, this method implements the {@link #NON_EMPTY} policy
+ * because that policy is reused in more than one enumeration values.
*/
- abstract boolean isSkipped(Object value);
+ boolean isSkipped(final Object value) {
+ return isNullOrEmpty(value);
+ }
/**
* Returns {@code true} if {@link TreeNode} shall substitute the given
collection by
* a singleton containing only a null element.
*
- * <h4>Purpose</h4>
+ * <p><b>Purpose:</b>
* When a collection is null or empty, while not excluded according this
{@code ValueExistencePolicy},
- * we need an empty space for making the metadata property visible in
{@code TreeNode}.
+ * we need an empty space for making the metadata property visible in
{@code TreeNode}.</p>
+ *
+ * <p>By default, this method implements the {@link #NON_EMPTY} policy
+ * because that policy is reused in more than one enumeration values.
+ * This default never substitute null or empty collections since they
should be
+ * skipped according the default implementation of {@link
#isSkipped(Object)}.</p>
*/
- abstract boolean substituteByNullElement(Collection<?> values);
+ boolean substituteByNullElement(Collection<?> values) {
+ return false;
+ }
/**
* Returns {@code true} if the specified object is null or an empty
collection, array or string.
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/package-info.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/package-info.java
index 53851b5ce2..28ea1288ae 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/package-info.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/package-info.java
@@ -120,7 +120,7 @@
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @author Adrian Custer (Geomatys)
- * @version 1.6
+ * @version 1.7
* @since 0.3
*/
@XmlAccessorType(XmlAccessType.NONE)
diff --git
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gcx/Anchor.java
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gcx/Anchor.java
index 0bc9fe8bcf..744468fba6 100644
---
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gcx/Anchor.java
+++
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/xml/bind/gcx/Anchor.java
@@ -86,6 +86,8 @@ public final class Anchor extends XLink implements
InternationalString {
/**
* Returns the text as a string, or {@code null} if none. The null value
is needed for proper
* working of {@link
org.apache.sis.xml.bind.gco.GO_CharacterString#toString()} method.
+ *
+ * @return the string in the default locale.
*/
@Override
public String toString() {
@@ -126,13 +128,14 @@ public final class Anchor extends XLink implements
InternationalString {
* appropriate for the sub-sequence.
*/
@Override
+ @SuppressWarnings("StringEquality")
public CharSequence subSequence(final int start, final int end) {
String original = value;
if (original == null) {
original = "";
}
final String substring = original.substring(start, end);
- if (substring == original) { //
Identity comparison is ok here.
+ if (substring == original) { // Identity comparison is ok here.
return this;
}
return new Anchor(this, substring);
@@ -164,7 +167,7 @@ public final class Anchor extends XLink implements
InternationalString {
return true;
}
if (super.equals(object)) {
- final Anchor that = (Anchor) object;
+ final var that = (Anchor) object;
return Objects.equals(this.value, that.value);
}
return false;
@@ -172,6 +175,8 @@ public final class Anchor extends XLink implements
InternationalString {
/**
* Returns a hash code value for this anchor type.
+ *
+ * @return an arbitrary hash code value.
*/
@Override
public int hashCode() {
diff --git
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeChildrenTest.java
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeChildrenTest.java
index 5ab3297d0f..b38e966ce3 100644
---
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeChildrenTest.java
+++
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeChildrenTest.java
@@ -72,7 +72,7 @@ public final class TreeNodeChildrenTest extends TestCase {
* └─Other citation details…… Some other details</pre>
*/
static DefaultCitation metadataWithoutCollections() {
- final DefaultCitation citation = new DefaultCitation("Some title");
+ final var citation = new DefaultCitation("Some title");
citation.setEdition(new SimpleInternationalString("Some edition"));
citation.setOtherCitationDetails(Set.of(new
SimpleInternationalString("Some other details")));
return citation;
@@ -138,8 +138,8 @@ public final class TreeNodeChildrenTest extends TestCase {
* @see <a href="https://issues.apache.org/jira/browse/SIS-298">SIS-298</a>
*/
static DefaultCitation metadataSimplifiable() {
- final DefaultCitation citation = new DefaultCitation();
- final DefaultCitationDate date = new
DefaultCitationDate(LocalDate.of(2012, 1, 1), DateType.CREATION);
+ final var citation = new DefaultCitation();
+ final var date = new DefaultCitationDate(LocalDate.of(2012, 1, 1),
DateType.CREATION);
assertTrue(citation.getDates().add(date));
return citation;
}
@@ -232,8 +232,8 @@ public final class TreeNodeChildrenTest extends TestCase {
* We need to perform the tests on the "Date" node, not on the
"DefaultCitation" node.
*/
final TreeTable.Node node = assertSingleton(create(citation,
ValueExistencePolicy.COMPACT));
- assertEquals(15340, ((LocalDate)
node.getValue(TableColumn.VALUE)).toEpochDay());
- final TreeNodeChildren children = (TreeNodeChildren)
node.getChildren();
+ assertEquals(15340, assertInstanceOf(LocalDate.class,
node.getValue(TableColumn.VALUE)).toEpochDay());
+ final TreeNodeChildren children =
assertInstanceOf(TreeNodeChildren.class, node.getChildren());
final String[] expected = {
// The "Date" node should be omitted because merged with the
parent "Date" node.
"DateType.CREATION"
@@ -253,9 +253,7 @@ public final class TreeNodeChildrenTest extends TestCase {
final TreeNodeChildren children = create(citation,
ValueExistencePolicy.NON_EMPTY);
assertEquals(-1, children.titleProperty);
- final DefaultTreeTable.Node toAdd = new DefaultTreeTable.Node(new
DefaultTreeTable(
- TableColumn.IDENTIFIER,
- TableColumn.VALUE));
+ final var toAdd = new DefaultTreeTable.Node(new
DefaultTreeTable(TableColumn.IDENTIFIER, TableColumn.VALUE));
final String[] expected = {
"Some title",
"First alternate title",
diff --git
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeTest.java
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeTest.java
index e5325ecaad..f2529ec333 100644
---
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeTest.java
+++
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeNodeTest.java
@@ -55,6 +55,7 @@ import
org.apache.sis.metadata.iso.citation.DefaultResponsibility;
*
* @author Martin Desruisseaux (Geomatys)
*/
+@SuppressWarnings("exports")
public final class TreeNodeTest extends TestCase {
/**
* Creates a new test case.
@@ -94,8 +95,8 @@ public final class TreeNodeTest extends TestCase {
assertTrue(citation.getCitedResponsibleParties().add(responsibility));
// Add a second responsible party with deeper hierarchy.
- final DefaultContact contact = new DefaultContact();
- final DefaultAddress address = new DefaultAddress();
+ final var contact = new DefaultContact();
+ final var address = new DefaultAddress();
address.setElectronicMailAddresses(Set.of("Some email"));
contact.setAddresses(Set.of(address));
party = new DefaultIndividual("Some person of contact", null, contact);
@@ -114,8 +115,8 @@ public final class TreeNodeTest extends TestCase {
*/
private <T extends AbstractMetadata> TreeNode create(final T metadata,
final Class<? super T> baseType) {
final MetadataStandard standard = MetadataStandard.ISO_19115;
- final TreeTableView table = new TreeTableView(standard, metadata,
baseType, valuePolicy);
- return (TreeNode) table.getRoot();
+ final var table = new TreeTableView(standard, metadata, baseType,
valuePolicy);
+ return assertInstanceOf(TreeNode.class, table.getRoot());
}
/**
@@ -133,7 +134,7 @@ public final class TreeNodeTest extends TestCase {
assertNull ( node.getParent());
assertFalse ( node.isLeaf());
- final TreeNodeChildren children = (TreeNodeChildren)
node.getChildren();
+ final TreeNodeChildren children =
assertInstanceOf(TreeNodeChildren.class, node.getChildren());
assertEquals(-1, children.titleProperty);
assertSame (citation, children.metadata);
assertFalse (node.getChildren().isEmpty());
diff --git
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeTableViewTest.java
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeTableViewTest.java
index 0fb5d71398..64e6570ae4 100644
---
a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeTableViewTest.java
+++
b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/TreeTableViewTest.java
@@ -63,7 +63,8 @@ public final class TreeTableViewTest extends TestCase {
/**
* The expected string representation of the tree created by {@link
#create(ValueExistencePolicy)}
- * with {@link ValueExistencePolicy#NON_EMPTY}.
+ * with {@link ValueExistencePolicy#COMPACT}. The citation title property
is omitted, replaced by
+ * the title at the level of the {@code Citation} object.
*/
private static final String EXPECTED =
"Citation………………………………………………………………………………………………… Some title\n" +
diff --git
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/metadata/NodeSummary.java
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/metadata/NodeSummary.java
index 0f4cd2963f..7634c498ad 100644
---
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/metadata/NodeSummary.java
+++
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/metadata/NodeSummary.java
@@ -25,6 +25,8 @@ import org.apache.sis.util.SimpleInternationalString;
* node is collapsed and hide this text when the node is expanded.
*
* @author Martin Desruisseaux (Geomatys)
+ *
+ * @see org.apache.sis.metadata.TitleProperty
*/
public final class NodeSummary extends SimpleInternationalString {
/**
@@ -37,7 +39,21 @@ public final class NodeSummary extends
SimpleInternationalString {
*
* @param text the string for all locales.
*/
- public NodeSummary(final String text) {
+ private NodeSummary(final String text) {
super(text);
}
+
+ /**
+ * Returns the given text as a {@code NodeSummary} instance.
+ *
+ * @param text the text to wrap, or {@code null}.
+ * @return the wrapped text, or {@code null} if the given text was null.
+ */
+ public static NodeSummary of(final CharSequence text) {
+ if (text == null || text instanceof NodeSummary) {
+ return (NodeSummary) text;
+ } else {
+ return new NodeSummary(text.toString());
+ }
+ }
}
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/AbstractInternationalString.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/AbstractInternationalString.java
index 85ae05d991..9f99decd65 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/AbstractInternationalString.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/AbstractInternationalString.java
@@ -34,7 +34,7 @@ import org.apache.sis.util.internal.shared.Strings;
* The {@linkplain Comparable natural ordering} is defined by the value
returned by {@link #toString()}.</p>
*
* <h2>Substituting a free text by a code list</h2>
- * The ISO standard allows to substitute some character strings in the <q>free
text</q> domain
+ * The <abbr>ISO</abbr> standard allows to substitute some character strings
in the <q>free text</q> domain
* by a {@link org.opengis.util.CodeList} value. This can be done with:
*
* <ul>
@@ -56,8 +56,7 @@ public abstract class AbstractInternationalString implements
InternationalString
*
* <h4>Thread safety</h4>
* For thread safety this field shall either be read and written in a
synchronized block,
- * or be fixed at construction time and never changed after than point.
All other usages
- * are prohibited.
+ * or be fixed at construction time and never changed after than point.
*
* <h4>Serialization</h4>
* This field is not serialized because serialization is often used for
data transmission
@@ -73,7 +72,7 @@ public abstract class AbstractInternationalString implements
InternationalString
}
/**
- * Returns the length of the string in the {@linkplain Locale#getDefault()
default locale}.
+ * Returns the length of the string in the default locale.
* This is the length of the string returned by {@link #toString()}.
*
* @return length of the string in the default locale.
@@ -84,8 +83,8 @@ public abstract class AbstractInternationalString implements
InternationalString
}
/**
- * Returns the character of the string in the {@linkplain
Locale#getDefault() default locale}
- * at the specified index. This is a character of the string returned by
{@link #toString()}.
+ * Returns the character of the string in the default locale at the
specified index.
+ * This is a character of the string returned by {@link #toString()}.
*
* @param index the index of the character.
* @return the character at the specified index.
@@ -97,7 +96,7 @@ public abstract class AbstractInternationalString implements
InternationalString
}
/**
- * Returns a subsequence of the string in the {@linkplain
Locale#getDefault() default locale}.
+ * Returns a subsequence of the string in the default locale.
* The subsequence is a {@link String} object starting with the character
value at the specified
* index and ending with the character value at index {@code end - 1}.
*
@@ -116,14 +115,14 @@ public abstract class AbstractInternationalString
implements InternationalString
* then some fallback locale is used. The fallback locale is
implementation-dependent, and
* is not necessarily the same as the default locale used by the {@link
#toString()} method.
*
- * <h4>Handling of <code>Locale.ROOT</code> argument value</h4>
+ * <h4>Handling of {@code Locale.ROOT} argument value</h4>
* {@link Locale#ROOT} can be given to this method for requesting a
"unlocalized" string,
* typically some programmatic values like enumerations or identifiers.
While identifiers
* often look like English words, {@code Locale.ROOT} is not considered
synonymous to
* {@link Locale#ENGLISH} because the values may differ in the way numbers
and dates are
* formatted (e.g. using the ISO 8601 standard for dates instead of
English conventions).
*
- * <h4>Handling of <code>null</code> argument value</h4>
+ * <h4>Handling of {@code null} argument value</h4>
* The {@code Locale.ROOT} constant is new in Java 6. Some other libraries
designed for Java 5
* use the {@code null} value for "unlocalized" strings. Apache SIS
accepts {@code null} value
* for inter-operability with those libraries. However, the behavior is
implementation dependent:
@@ -183,9 +182,8 @@ public abstract class AbstractInternationalString
implements InternationalString
}
/**
- * Compares this string with the specified object for order. This method
compares
- * the string in the {@linkplain Locale#getDefault() default locale}, as
returned
- * by {@link #toString()}.
+ * Compares this string with the specified object for order.
+ * This method compares the strings in the default locale, as returned by
{@link #toString()}.
*
* @param object the string to compare with this string.
* @return a negative number if this string is before the given string,
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/TreeTableForGUI.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/TreeTableForGUI.java
new file mode 100644
index 0000000000..9a7f40eb96
--- /dev/null
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/TreeTableForGUI.java
@@ -0,0 +1,42 @@
+/*
+ * 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.util.internal.shared;
+
+import org.apache.sis.util.collection.TreeTable;
+
+
+/**
+ * An extension of the {@code TreeTable} interface for use with the
<abbr>GUI</abbr> module.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ */
+public interface TreeTableForGUI extends TreeTable {
+ /**
+ * Returns whether the given value produces by the given node is a title.
+ * Title are a short description of the node, typically copied from one of
the children.
+ * For example for the code of a {@code Citation} object, this is the
{@code title} property.
+ *
+ * <p>This information is used by <abbr>GUI</abbr> for showing the value
when the node is collapsed,
+ * and hiding the value when the node is expanded for avoiding redundancy
with the child that really
+ * provides the value.</p>
+ *
+ * @param node the node where the value come from.
+ * @param value the value provided by the node.
+ * @return whether the given value is a title.
+ */
+ boolean isNodeTitle(Node node, Object value);
+}
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 0bd120a61d..510c2125ba 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
@@ -42,6 +42,7 @@ 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.util.internal.shared.TreeTableForGUI;
import org.apache.sis.storage.isobmff.base.ItemInfoEntry;
import org.apache.sis.storage.geoheif.GeoHeifStore;
import org.apache.sis.storage.metadata.NodeSummary;
@@ -123,7 +124,7 @@ public abstract class TreeNode {
@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.
+ // Do not wrap in `NodeSummary` because we want to keep
that name always visible.
return name;
}
return super.summary(tree, value, text);
@@ -157,7 +158,7 @@ public abstract class TreeNode {
}
break;
}
- };
+ }
return tree.integerFormat.format(value);
}
@@ -170,7 +171,7 @@ public abstract class TreeNode {
* @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);
+ return NodeSummary.of(text);
}
/**
@@ -258,7 +259,7 @@ public abstract class TreeNode {
* The value column is replaced by the {@code VALUE_AS_TEXT} column at
{@link #toString()} time.
*/
@SuppressWarnings("serial")
- private static final class Tree extends DefaultTreeTable implements
Localized {
+ private static final class Tree extends DefaultTreeTable implements
TreeTableForGUI, Localized {
/**
* The locale to use, or {@code null} for the default.
*/
@@ -284,6 +285,14 @@ public abstract class TreeNode {
return locale;
}
+ /**
+ * Returns whether the given value produces by the given node is a
title.
+ */
+ @Override
+ public boolean isNodeTitle(final TreeTable.Node node, final Object
value) {
+ return (value instanceof NodeSummary);
+ }
+
/**
* Returns a string representation of the tree table.
*/
@@ -511,10 +520,7 @@ public abstract class TreeNode {
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());
- }
+ summary =
NodeSummary.of(child.getValue(TableColumn.VALUE_AS_TEXT));
}
}
if (summary != null) {
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 d68ec03aef..54f27b5786 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
@@ -52,7 +52,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;
+import org.apache.sis.util.internal.shared.TreeTableForGUI;
/**
@@ -150,7 +150,9 @@ check: if (data != null) {
if (columns.contains(TableColumn.NAME)) {
for (final TableColumn<?> value : VALUES_COLUMNS) {
if (columns.contains(value)) {
- ((MetadataTree) getBean()).valueSourceColumn =
value;
+ final var tree = (MetadataTree) getBean();
+ tree.valueSourceColumn = value;
+ tree.formatter.setTree(data);
break check;
}
}
@@ -345,6 +347,11 @@ check: if (data != null) {
private static final class Formatter extends PropertyValueFormatter
implements Callback<CellDataFeatures<TreeTable.Node, String>,
ObservableValue<String>>
{
+ /**
+ * The tree table if it is an instance of {@code TreeTableForGUI}, or
{@code null} otherwise.
+ */
+ private TreeTableForGUI treeIfGUI;
+
/**
* The observable properties created for each tree table node.
* We need to reuse the instances created for each node in order to
keep listeners.
@@ -361,6 +368,13 @@ check: if (data != null) {
observables = new WeakHashMap<>();
}
+ /**
+ * Sets the source of the tree nodes to be formatted.
+ */
+ final void setTree(final TreeTable data) {
+ treeIfGUI = (data instanceof TreeTableForGUI c) ? c : null;
+ }
+
/**
* Returns the value of the metadata property wrapped by the given
argument.
* This method is invoked by JavaFX when a new cell needs to be
rendered.
@@ -379,7 +393,7 @@ check: if (data != null) {
} else {
text = formatUsingStringBuilder(value);
}
- if (value instanceof NodeSummary) {
+ if (treeIfGUI != null && treeIfGUI.isNodeTitle(node, value)) {
final var summary = new SummaryProperty(text);
item.expandedProperty().addListener(new
WeakChangeListener<>(summary));
property = summary;
@@ -394,9 +408,9 @@ check: if (data != null) {
/**
* 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.
+ * This is used for content of node titles. 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> {
/**
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 6dfd5f0b2e..7248bfe09a 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
@@ -27,7 +27,6 @@ import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import org.opengis.metadata.Metadata;
import org.opengis.referencing.IdentifiedObject;
-import org.apache.sis.metadata.AbstractMetadata;
import org.apache.sis.metadata.MetadataStandard;
import org.apache.sis.metadata.ValueExistencePolicy;
import org.apache.sis.xml.XML;
@@ -64,7 +63,7 @@ import org.apache.sis.io.wkt.WKTFormat;
*
* @author Siddhesh Rane (GSoC)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.7
* @since 1.1
*/
public class StandardMetadataTree extends MetadataTree {
@@ -102,11 +101,8 @@ public class StandardMetadataTree extends MetadataTree {
final TreeTable tree;
if (metadata == null) {
tree = null;
- } 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);
+ tree = MetadataStandard.ISO_19115.asTreeTable(metadata,
Metadata.class, ValueExistencePolicy.TITLED);
}
setContent(tree);
}