sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Connect MetadataSummary to MetadataTree. The tree view is provided in a separated tab.
Date Mon, 04 Nov 2019 14:48:36 GMT
This is an automated email from the ASF dual-hosted git repository.

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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 042c7e9  Connect MetadataSummary to MetadataTree. The tree view is provided in a
separated tab.
042c7e9 is described below

commit 042c7e9be1aa5c420c09e9220a62785255278c89
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Mon Nov 4 15:48:02 2019 +0100

    Connect MetadataSummary to MetadataTree. The tree view is provided in a separated tab.
---
 .../apache/sis/gui/dataset/ResourceExplorer.java   |  12 +-
 .../org/apache/sis/gui/dataset/ResourceTree.java   |   4 +-
 .../apache/sis/gui/metadata/MetadataSummary.java   | 100 ++++--
 .../org/apache/sis/gui/metadata/MetadataTree.java  | 379 ++++++++++++---------
 .../main/java/org/apache/sis/gui/package-info.java |   5 -
 .../org/apache/sis/internal/gui/Resources.java     |   5 +
 .../apache/sis/internal/gui/Resources.properties   |   1 +
 .../sis/internal/gui/Resources_fr.properties       |   1 +
 .../org/apache/sis/metadata/TreeNodeChildren.java  |   3 +-
 9 files changed, 316 insertions(+), 194 deletions(-)

diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
index 0aac24b..22a6b63 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
@@ -16,7 +16,6 @@
  */
 package org.apache.sis.gui.dataset;
 
-import java.util.Locale;
 import java.util.Collection;
 import javafx.collections.ListChangeListener;
 import javafx.scene.layout.Region;
@@ -26,6 +25,7 @@ import javafx.scene.control.TabPane;
 import javafx.scene.control.TreeItem;
 import org.apache.sis.storage.Resource;
 import org.apache.sis.gui.metadata.MetadataSummary;
+import org.apache.sis.gui.metadata.MetadataTree;
 import org.apache.sis.internal.gui.Resources;
 
 
@@ -60,11 +60,15 @@ public class ResourceExplorer {
      */
     public ResourceExplorer() {
         resources = new ResourceTree();
-        metadata  = new MetadataSummary(resources.getLocale(), Locale.getDefault(Locale.Category.FORMAT));
+        metadata  = new MetadataSummary();
         pane      = new SplitPane();
 
-        final Tab mdTab = new Tab(resources.localized.getString(Resources.Keys.Summary),
metadata.getView());
-        final TabPane tabs = new TabPane(mdTab);
+        final MetadataTree metadataTree = new MetadataTree();
+        metadata.metadataProperty.addListener((p,o,n) -> metadataTree.setContent(n));
+
+        final Tab summaryTab = new Tab(resources.localized.getString(Resources.Keys.Summary),
 metadata.getView());
+        final Tab metadatTab = new Tab(resources.localized.getString(Resources.Keys.Metadata),
metadataTree);
+        final TabPane tabs = new TabPane(summaryTab, metadatTab);
         tabs.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
         tabs.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
 
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
index 6fc59bb..9aa3290 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
@@ -61,8 +61,8 @@ import org.apache.sis.internal.gui.Styles;
 
 
 /**
- * Tree viewer displaying a {@link Resource} hierarchy.
- * This viewer can be used for showing the content of one or many {@link DataStore}s.
+ * A view of data {@link Resource}s organized as a tree.
+ * This view can be used for showing the content of one or many {@link DataStore}s.
  * A resource can be added by a call to {@link #addResource(Resource)} or loaded from
  * a file by {@link #loadResource(Object)}.
  *
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
index b0ac119..beded69 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataSummary.java
@@ -24,6 +24,10 @@ import java.util.Collection;
 import java.util.Locale;
 import java.util.StringJoiner;
 import javafx.application.Platform;
+import javafx.beans.DefaultProperty;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ObservableValue;
 import javafx.collections.ObservableList;
 import javafx.concurrent.Task;
 import javafx.concurrent.Worker;
@@ -45,7 +49,6 @@ import org.apache.sis.util.iso.Types;
 import org.apache.sis.util.logging.Logging;
 
 
-
 /**
  * A panel showing a summary of metadata.
  *
@@ -55,6 +58,7 @@ import org.apache.sis.util.logging.Logging;
  * @since   1.1
  * @module
  */
+@DefaultProperty("metadata")
 public class MetadataSummary {
     /**
      * Titles panes for different metadata sections (identification info, spatial information,
<i>etc</i>).
@@ -70,6 +74,7 @@ public class MetadataSummary {
 
     /**
      * The locale to use for date/number formatters.
+     * This is often the same than {@link #localized}.
      */
     private final Locale formatLocale;
 
@@ -108,6 +113,14 @@ public class MetadataSummary {
     private Worker<Metadata> loader;
 
     /**
+     * The metadata shown in this pane.
+     *
+     * @see #getMetadata()
+     * @see #setMetadata(Metadata)
+     */
+    public final ObjectProperty<Metadata> metadataProperty;
+
+    /**
      * If the metadata or the grid geometry can not be obtained, the reason.
      *
      * @todo show in this control.
@@ -123,20 +136,18 @@ public class MetadataSummary {
 
     /**
      * Creates an initially empty metadata overview.
-     *
-     * @param  textLocale  the locale for the text.
-     * @param  dataLocale  the locale for formatting numbers and dates.
-     *                     This is often the same than {@code textLocale}.
      */
-    public MetadataSummary(final Locale textLocale, final Locale dataLocale) {
-        localized    = Resources.forLocale(textLocale);
-        formatLocale = dataLocale;
+    public MetadataSummary() {
+        localized    = Resources.forLocale(Locale.getDefault(Locale.Category.DISPLAY));
+        formatLocale = Locale.getDefault(Locale.Category.FORMAT);
         information  = new TitledPane[] {
             new TitledPane(localized.getString(Resources.Keys.ResourceIdentification), new
IdentificationInfo(this)),
             new TitledPane(localized.getString(Resources.Keys.SpatialRepresentation),  new
RepresentationInfo(this))
         };
         content = new ScrollPane(new VBox());
         content.setFitToWidth(true);
+        metadataProperty = new SimpleObjectProperty<>(this, "metadata");
+        metadataProperty.addListener(MetadataSummary::applyChange);
     }
 
     /**
@@ -218,32 +229,65 @@ public class MetadataSummary {
 
     /**
      * Sets the content of this pane to the given metadata.
+     * This is a convenience method for setting {@link #metadataProperty} value.
      *
      * @param  metadata  the metadata to show, or {@code null}.
+     *
+     * @see #metadataProperty
+     * @see #setMetadata(Resource)
      */
-    public void setMetadata(final Metadata metadata) {
+    public final void setMetadata(final Metadata metadata) {
         assert Platform.isFxApplicationThread();
-        error = null;
-        final ObservableList<Node> children = ((VBox) content.getContent()).getChildren();
-        /*
-         * We want to include only the non-empty panes in the children list. But instead
of
-         * removing everything and adding back non-empty panes, we check case-by-case if
a
-         * child should be added or removed. It will often result in no modification at all.
-         */
-        int i = 0;
-        for (TitledPane pane : information) {
-            final Section<?> info = (Section<?>) pane.getContent();
-            info.setInformation(metadata);
-            final boolean isEmpty   = info.isEmpty();
-            final boolean isPresent = (i < children.size()) && children.get(i)
== pane;
-            if (isEmpty == isPresent) {     // Should not be present if empty, or should
be present if non-empty.
-                if (isEmpty) {
-                    children.remove(i);
-                } else {
-                    children.add(i, pane);
+        metadataProperty.setValue(metadata);
+    }
+
+    /**
+     * Returns the metadata currently shown, or {@code null} if none.
+     * This is a convenience method for fetching {@link #metadataProperty} value.
+     *
+     * @return the metadata currently shown, or {@code null} if none.
+     *
+     * @see #metadataProperty
+     * @see #setMetadata(Metadata)
+     */
+    public final Metadata getMetadata() {
+        return metadataProperty.getValue();
+    }
+
+    /**
+     * Invoked when {@link #metadataProperty} value changed.
+     *
+     * @param  property  the property which has been modified.
+     * @param  oldValue  the old metadata.
+     * @param  metadata  the metadata to use for building new content.
+     */
+    private static void applyChange(final ObservableValue<? extends Metadata> property,
+                                    final Metadata oldValue, final Metadata metadata)
+    {
+        final MetadataSummary s = (MetadataSummary) ((SimpleObjectProperty<?>) property).getBean();
+        s.error = null;
+        if (metadata != oldValue) {
+            final ObservableList<Node> children = ((VBox) s.content.getContent()).getChildren();
+            /*
+             * We want to include only the non-empty panes in the children list. But instead
of
+             * removing everything and adding back non-empty panes, we check case-by-case
if a
+             * child should be added or removed. It will often result in no modification
at all.
+             */
+            int i = 0;
+            for (TitledPane pane : s.information) {
+                final Section<?> info = (Section<?>) pane.getContent();
+                info.setInformation(metadata);
+                final boolean isEmpty   = info.isEmpty();
+                final boolean isPresent = (i < children.size()) && children.get(i)
== pane;
+                if (isEmpty == isPresent) {     // Should not be present if empty, or should
be present if non-empty.
+                    if (isEmpty) {
+                        children.remove(i);
+                    } else {
+                        children.add(i, pane);
+                    }
                 }
+                if (!isEmpty) i++;
             }
-            if (!isEmpty) i++;
         }
     }
 
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
index 94e3840..eb5f379 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java
@@ -16,213 +16,284 @@
  */
 package org.apache.sis.gui.metadata;
 
-import java.util.Collection;
-import java.util.Comparator;
+import java.util.Locale;
 import java.util.List;
-import java.util.Objects;
-import java.util.function.Predicate;
+import java.util.ArrayList;
+import java.util.Collection;
+import javafx.beans.DefaultProperty;
 import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.ReadOnlyObjectWrapper;
+import javafx.beans.property.ReadOnlyStringWrapper;
 import javafx.beans.property.SimpleObjectProperty;
-import javafx.beans.property.SimpleStringProperty;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.ObservableList;
 import javafx.scene.control.TreeItem;
-import javafx.scene.control.TreeTableColumn;
 import javafx.scene.control.TreeTableView;
-import org.apache.sis.util.collection.TableColumn;
+import javafx.scene.control.TreeTableColumn;
+import javafx.scene.control.TreeTableColumn.CellDataFeatures;
+import org.opengis.metadata.Metadata;
+import org.opengis.util.InternationalString;
+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.referencing.IdentifiedObjects;
 import org.apache.sis.util.collection.TreeTable;
-import org.opengis.referencing.ReferenceSystem;
+import org.apache.sis.util.collection.TableColumn;
 
 
 /**
+ * A view of {@link Metadata} properties organized as a tree table.
+ * The content of each row in this tree table is represented by a {@link TreeTable.Node}.
+ * The tree table shows the following columns:
+ *
+ * <ul>
+ *   <li>{@link TableColumn#NAME}  — a name for the metadata property, e.g. "Title".</li>
+ *   <li>{@link TableColumn#VALUE} — the property value typically as a string, number
or date.</li>
+ * </ul>
+ *
+ * <p>While this view is designed mostly for metadata, it can actually be used
+ * for other kinds of data provided by the {@link TreeTable} interface.</p>
  *
- * @author Siddhesh Rane (GSoC)
+ * @author  Siddhesh Rane (GSoC)
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
  */
-final class MetadataTree extends TreeTableView<TreeTable.Node> {
-
-    private static final Predicate<TreeTable.Node> HIDE_EMPTY_LEAF = t -> !t.isLeaf()
|| t.getValue(TableColumn.VALUE) != null;
-    private static final Predicate<TreeTable.Node> EXPAND_SINGLE_CHILD = node ->
node.getChildren().size() == 1 || node.getParent() == null;
-
-    private TreeTable treeTable;
-    private TreeTableColumn<TreeTable.Node, String> nameColumn;
-    private TreeTableColumn<TreeTable.Node, TreeTable.Node> valueColumn;
-    private TreeTableColumn<TreeTable.Node, String> textColumn;
+@DefaultProperty("content")
+public class MetadataTree extends TreeTableView<TreeTable.Node> {
+    /**
+     * The column for metadata property name.
+     */
+    private final TreeTableColumn<TreeTable.Node, String> nameColumn;
 
-    private final ObjectProperty<Predicate<TreeTable.Node>> createTreeItemForNodeProperty
= new SimpleObjectProperty<>(HIDE_EMPTY_LEAF);
+    /**
+     * The column for metadata property value.
+     * Values are typically {@link InternationalString}, {@link Number} or dates.
+     */
+    private final TreeTableColumn<TreeTable.Node, Object> valueColumn;
 
     /**
-     * A property containing predicate that returns true if the given
-     * {@link TreeTable.Node} must be shown as a {@link TreeItem} in the
-     * {@link TreeTableView}. By default return true for all nodes unless
-     * preferences are loaded
+     * The data shown in this tree table. The {@link ObjectProperty#set(Object)} method requires
+     * that the given value obey to the constraints documented in {@link #setContent(TreeTable)}.
      *
-     * @return The property containing the expansion predicate
+     * @see #getContent()
+     * @see #setContent(TreeTable)
      */
-    private ObjectProperty<Predicate<TreeTable.Node>> createTreeItemForNodeProperty()
{
-        return createTreeItemForNodeProperty;
-    }
+    public final ObjectProperty<TreeTable> contentProperty;
 
-    private Predicate<TreeTable.Node> getCreateTreeItemForNode() {
-        return createTreeItemForNodeProperty.get();
-    }
+    /**
+     * Implementation of {@link MetadataTree#contentProperty} as a named class for more readable
stack trace.
+     * This class verifies the constraints documented in {@link MetadataTree#setContent(TreeTable)}.
+     */
+    private static final class ContentProperty extends SimpleObjectProperty<TreeTable>
{
+        /** Creates a new property. */
+        ContentProperty(final MetadataTree bean) {
+            super(bean, "content");
+        }
 
-    private void setCreateTreeItemForNode(Predicate<TreeTable.Node> createTreeItemForNode)
{
-        createTreeItemForNodeProperty.set(createTreeItemForNode);
+        /** Invoked when the user wants to set new data. */
+        @Override public void set(final TreeTable data) {
+            if (data != null) {
+                final List<TableColumn<?>> columns = data.getColumns();
+                if (!(columns.contains(TableColumn.NAME) && columns.contains(TableColumn.VALUE)))
{
+                    throw new IllegalArgumentException();
+                }
+            }
+            super.set(data);
+        }
     }
 
-    private final SimpleObjectProperty<Predicate<TreeTable.Node>> expandNodeProperty
= new SimpleObjectProperty<>(EXPAND_SINGLE_CHILD);
+    /**
+     * The locale to use for texts.
+     */
+    private final Locale textLocale;
 
     /**
-     * A property containing predicate that returns true if the given
-     * {@link TreeTable.Node} must be expanded in the {@link TreeTableView} to
-     * show its children by default.
-     *
-     * @return The property containing the expansion predicate
+     * The locale to use for dates/numbers.
+     * This is often the same than {@link #textLocale}.
      */
-    private ObjectProperty<Predicate<TreeTable.Node>> expandNodeProperty() {
-        return expandNodeProperty;
-    }
+    private final Locale dataLocale;
 
-    private Predicate<TreeTable.Node> getExpandNode() {
-        return expandNodeProperty.get();
-    }
+    /**
+     * Creates a new initially empty metadata tree.
+     */
+    public MetadataTree() {
+        textLocale      = Locale.getDefault(Locale.Category.DISPLAY);
+        dataLocale      = Locale.getDefault(Locale.Category.FORMAT);
+        contentProperty = new ContentProperty(this);
+        nameColumn      = new TreeTableColumn<>(TableColumn.NAME .getHeader().toString(textLocale));
+        valueColumn     = new TreeTableColumn<>(TableColumn.VALUE.getHeader().toString(textLocale));
+        nameColumn .setCellValueFactory(MetadataTree::getPropertyName);
+        valueColumn.setCellValueFactory(MetadataTree::getPropertyValue);
 
-    private void setExpandNode(Predicate<TreeTable.Node> expandNode) {
-        expandNodeProperty.set(expandNode);
+        setColumnResizePolicy(CONSTRAINED_RESIZE_POLICY);
+        getColumns().setAll(nameColumn, valueColumn);
+        contentProperty.addListener(MetadataTree::applyChange);
     }
 
-    private void expandNodes(TreeItem<TreeTable.Node> root) {
-        if (root == null || root.isLeaf()) {
-            return;
-        }
-        root.setExpanded(getExpandNode().test(root.getValue()));
-        for (TreeItem<TreeTable.Node> child : root.getChildren()) {
-            expandNodes(child);
+    /**
+     * Sets the metadata to show in this tree table. This method gets a {@link TreeTable}
view
+     * of the given metadata, then delegates to {@link #setContent(TreeTable)}.
+     *
+     * @param  metadata  the metadata to show in this tree table view, or {@code null} if
none.
+     */
+    public void setContent(final Metadata metadata) {
+        TreeTable content = null;
+        if (metadata != null) {
+            if (metadata instanceof AbstractMetadata) {
+                content = ((AbstractMetadata) metadata).asTreeTable();
+            } else {
+                content = MetadataStandard.ISO_19115.asTreeTable(metadata, null, ValueExistencePolicy.COMPACT);
+            }
         }
+        setContent(content);
     }
 
-    ObjectProperty<Comparator<TreeTable.Node>> order = new SimpleObjectProperty<>(Comparator.comparingInt(n
-> 0));
-
     /**
-     * the default order of sorting for rows of the tree table.
+     * Sets the data to show.
+     * This is a convenience method for setting {@link #contentProperty} value.
+     * The given {@link TreeTable} shall contain at least the following columns:
      *
-     * @return
+     * <ul>
+     *   <li>{@link TableColumn#NAME}</li>
+     *   <li>{@link TableColumn#VALUE}</li>
+     * </ul>
+     *
+     * @param  data  the data to show, or {@code null} if none.
+     * @throws IllegalArgumentException if the data is non-null but does not contains the
required columns.
      */
-    private ObjectProperty<Comparator<TreeTable.Node>> orderProperty() {
-        return order;
+    public final void setContent(final TreeTable data) {
+        contentProperty.setValue(data);
     }
 
-    private void setOrder(Comparator c) {
-        order.set(c);
-    }
-
-    private Comparator<TreeTable.Node> getOrder() {
-        return order.get();
-    }
-
-    MetadataTree(TreeTable treeTable) {
-        this();
-        setTreeTable(treeTable);
-    }
-
-    private MetadataTree() {
-        createTableColumns();
-        setShowRoot(false);
-        setColumnResizePolicy(CONSTRAINED_RESIZE_POLICY);
-        expandNodeProperty.addListener(ob -> expandNodes(getRoot()));
+    /**
+     * Returns the data currently shown, or {@code null} if none.
+     * This is a convenience method for fetching {@link #contentProperty} value.
+     *
+     * @return the table currently shown, or {@code null} if none.
+     *
+     * @see #contentProperty
+     * @see #setContent(TreeTable)
+     */
+    public final TreeTable getContent() {
+        return contentProperty.getValue();
     }
 
     /**
-     * Creates {@link TableColumn}s for name, identifier, value valueastext, but
-     * doesnt add them to the table. These columns can later be added according
-     * what all is contained by the TreeTable.
+     * Invoked when {@link #contentProperty} value changed.
+     *
+     * @param  property  the property which has been modified.
+     * @param  oldValue  the old tree table.
+     * @param  content   the tree table to use for building new content.
      */
-    private void createTableColumns() {
-        //NAME column
-        nameColumn = new TreeTableColumn<>(TableColumn.NAME.getHeader().toString());
-        nameColumn.setCellValueFactory((param) -> {
-            CharSequence value = param.getValue().getValue().getValue(TableColumn.NAME);
-            return new SimpleStringProperty(Objects.toString(value, ""));
-        });
-
-        //TEXT column
-        textColumn = new TreeTableColumn<>(TableColumn.VALUE_AS_TEXT.getHeader().toString());
-        textColumn.setCellValueFactory((param) -> {
-            CharSequence value = param.getValue().getValue().getValue(TableColumn.VALUE_AS_TEXT);
-            return new SimpleStringProperty(Objects.toString(value, ""));
-        });
-
-        //VALUE column
-        valueColumn = new TreeTableColumn<>(TableColumn.VALUE.getHeader().toString());
-        valueColumn.setCellValueFactory((param) -> {
-            TreeTable.Node value = param.getValue().getValue();
-            SimpleObjectProperty ret = new SimpleObjectProperty(value.getValue(TableColumn.VALUE));
-            if (value.getValue(TableColumn.NAME).toString().equals("Reference system info"))
{
-                ReferenceSystem newVal = (ReferenceSystem) ret.getValue();
-                ret.setValue(newVal.getName());
-            }
-            return ret;
-        });
+    private static void applyChange(final ObservableValue<? extends TreeTable> property,
+                                    final TreeTable oldValue, final TreeTable  content)
+    {
+        final MetadataTree s = (MetadataTree) ((ContentProperty) property).getBean();
+        TreeItem<TreeTable.Node> root = null;
+        if (content != null) {
+            root = new Item(content.getRoot());
+            root.setExpanded(true);
+        }
+        s.setRoot(root);
     }
 
-    private TreeTable getTreeTable() {
-        return treeTable;
-    }
+    /**
+     * A simple node encapsulating a {@link TreeTable.Node} in a view.
+     * The list of children is fetched when first needed.
+     */
+    private static final class Item extends TreeItem<TreeTable.Node> {
+        /**
+         * +1 if this item is a leaf, +2 if it has children, or 0 if not yet determined.
+         */
+        private byte isLeaf;
 
-    private void setTreeTable(TreeTable treeTable) {
-        this.treeTable = treeTable;
-        if (treeTable == null) {
-            setRoot(null);
-            return;
+        /**
+         * Creates a new node.
+         */
+        Item(final TreeTable.Node node) {
+            super(node);
         }
 
-        List<TableColumn<?>> columns = treeTable.getColumns();
-        if (columns.contains(TableColumn.NAME)) {
-            if (nameColumn.getTreeTableView() == null) {
-                getColumns().add(nameColumn);
-            }
-        } else {
-            getColumns().remove(nameColumn);
-        }
-        if (columns.contains(TableColumn.VALUE)) {
-            if (valueColumn.getTreeTableView() == null) {
-                getColumns().add(valueColumn);
+        /**
+         * Returns whether the node can not have children.
+         */
+        @Override
+        public boolean isLeaf() {
+            if (isLeaf == 0) {
+                final TreeTable.Node node = getValue();
+                isLeaf = (node.isLeaf() || node.getChildren().isEmpty()) ? (byte) 1 : 2;
             }
-        } else {
-            getColumns().remove(valueColumn);
+            return isLeaf == 1;
         }
-        if (columns.contains(TableColumn.VALUE_AS_TEXT)) {
-            if (textColumn.getTreeTableView() == null) {
-                getColumns().add(textColumn);
+
+        /**
+         * Returns the items for all sub-nodes contained in this node.
+         */
+        @Override
+        public ObservableList<TreeItem<TreeTable.Node>> getChildren() {
+            final ObservableList<TreeItem<TreeTable.Node>> children = super.getChildren();
+            if (children.isEmpty()) {
+                final Collection<TreeTable.Node> data = getValue().getChildren();
+                final List<Item> wrappers = new ArrayList<>(data.size());
+                for (final TreeTable.Node child : data) {
+                    wrappers.add(new Item(child));
+                }
+                children.setAll(wrappers);      // Fire a single event instead of multiple
`add`.
             }
-        } else {
-            getColumns().remove(textColumn);
+            return children;
         }
-        TreeItem<TreeTable.Node> rootItem = new TreeItem<>(treeTable.getRoot());
-        setRoot(rootItem);
-        rootItem.setExpanded(true);
-        updateRoot();
     }
 
-    private void updateRoot() {
-        getRoot().getChildren().clear();
-        createTreeItems(getRoot(), treeTable.getRoot().getChildren());
+    /**
+     * 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, ? extends
T> cell, final TableColumn<T> column) {
+        final TreeTable.Node node = cell.getValue().getValue();
+        return node.getValue(column);
     }
 
-    private void createTreeItems(TreeItem<TreeTable.Node> rootItem, Collection<TreeTable.Node>
children) {
-        for (TreeTable.Node node : children) {
-            TreeItem parent = rootItem;
-            //include this node in the tree table view?
-            if (getCreateTreeItemForNode().test(node)) {
-                parent = new TreeItem(node);
-                parent.setExpanded(getExpandNode().test(node));
-                rootItem.getChildren().add(parent);
-            }
-            if (!node.isLeaf()) {
-                createTreeItems(parent, node.getChildren());
-            }
-            if (parent.getChildren().size() > 1) {
-                Comparator<TreeItem<TreeTable.Node>> cp = Comparator.comparing(TreeItem<TreeTable.Node>::getValue,
getOrder());
-                parent.getChildren().sort(cp);
+    /**
+     * 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.
+     */
+    private static ObservableValue<String> getPropertyName(final CellDataFeatures<TreeTable.Node,
String> cell) {
+        final CharSequence value = getValue(cell, TableColumn.NAME);
+        final String text;
+        if (value instanceof InternationalString) {
+            final MetadataTree view = (MetadataTree) cell.getTreeTableView();
+            text = ((InternationalString) value).toString(view.textLocale);
+        } else {
+            text = (value != null) ? value.toString() : null;
+        }
+        return new ReadOnlyStringWrapper(text);
+    }
+
+    /**
+     * 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.
+     */
+    private static ObservableValue<Object> getPropertyValue(final CellDataFeatures<TreeTable.Node,
Object> cell) {
+        Object value = getValue(cell, TableColumn.VALUE);
+        if (value instanceof IdentifiedObject) {
+            String name = IdentifiedObjects.getName((IdentifiedObject) value, null);
+            if (name == null) {
+                name = IdentifiedObjects.getIdentifierOrName((IdentifiedObject) value);
             }
+            value = name;
+        }
+        if (value instanceof InternationalString) {
+            final MetadataTree view = (MetadataTree) cell.getTreeTableView();
+            value = ((InternationalString) value).toString(view.textLocale);
         }
+        return new ReadOnlyObjectWrapper<>(value);
     }
 }
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/package-info.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/package-info.java
index 9b4825d..8901cca 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/package-info.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/package-info.java
@@ -17,11 +17,6 @@
 
 /**
  * JavaFX application for Apache SIS.
- * All classes in this package extend the JavaFX {@link javafx.application.Application} class
- * (for controls reusable in other applications, see sub-packages).
- * Those applications may be instantiated and modified programmatically
- * (for example by adding new menu items),
- * but compatibility with future Apache SIS versions is not guaranteed.
  *
  * @author  Smaniotto Enzo (GSoC)
  * @author  Johann Sorel (Geomatys)
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
index 59cbb10..ab69265 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
@@ -146,6 +146,11 @@ public final class Resources extends IndexedResourceBundle {
         public static final short Loading = 7;
 
         /**
+         * Metadata
+         */
+        public static final short Metadata = 30;
+
+        /**
          * Number of dimensions:
          */
         public static final short NumberOfDimensions = 27;
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
index eeb6167..13f1571 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
@@ -38,6 +38,7 @@ Extent                 = Extent:
 File                   = File
 GeospatialFiles        = Geospatial data files
 Loading                = Loading\u2026
+Metadata               = Metadata
 NumberOfDimensions     = Number of dimensions:
 Open                   = Open\u2026
 OpenDataFile           = Open data file
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
index 2b4225a..697962f 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
@@ -43,6 +43,7 @@ Extent                 = \u00c9tendue\u00a0:
 File                   = Fichier
 GeospatialFiles        = Fichiers de donn\u00e9es g\u00e9ospatiales
 Loading                = Chargement\u2026
+Metadata               = Metadonn\u00e9es
 NumberOfDimensions     = Nombre de dimensions\u00a0:
 Open                   = Ouvrir\u2026
 OpenDataFile           = Ouvrir un fichier de donn\u00e9es
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNodeChildren.java
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNodeChildren.java
index 1c475d9..f4b0a24 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNodeChildren.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/TreeNodeChildren.java
@@ -47,7 +47,7 @@ import org.apache.sis.util.resources.Errors;
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.1
  * @since   0.3
  * @module
  */
@@ -317,6 +317,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.
         return accessor.count(metadata, parent.table.valuePolicy, PropertyAccessor.COUNT_FIRST)
== 0;
     }
 


Mime
View raw message