You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2021/10/27 16:40:12 UTC
[sis] 02/04: Load native metadata in a background thread separated
from standard metadata. This allows loading those metadata only if the
"native metadata" tab is selected.
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit d517cda884a5d80fb43b96ac163fa2e4f9bdf28a
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Oct 27 15:16:08 2021 +0200
Load native metadata in a background thread separated from standard metadata.
This allows loading those metadata only if the "native metadata" tab is selected.
---
.../apache/sis/gui/dataset/ResourceExplorer.java | 156 +++++++++++++++++----
.../apache/sis/gui/metadata/MetadataSummary.java | 28 +---
.../org/apache/sis/gui/metadata/MetadataTree.java | 7 +-
.../org/apache/sis/gui/metadata/package-info.java | 2 +-
4 files changed, 132 insertions(+), 61 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 aed5ecf..d0936a4 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
@@ -20,6 +20,7 @@ import java.util.Objects;
import java.util.Collection;
import java.util.logging.Level;
import java.util.logging.LogRecord;
+import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ReadOnlyProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.collections.ListChangeListener;
@@ -39,16 +40,19 @@ import org.apache.sis.storage.Aggregate;
import org.apache.sis.storage.DataSet;
import org.apache.sis.storage.FeatureSet;
import org.apache.sis.storage.GridCoverageResource;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.DataStoreProvider;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.gui.metadata.MetadataSummary;
import org.apache.sis.gui.metadata.MetadataTree;
import org.apache.sis.gui.metadata.StandardMetadataTree;
import org.apache.sis.gui.coverage.ImageRequest;
import org.apache.sis.gui.coverage.CoverageExplorer;
-import org.apache.sis.util.collection.TableColumn;
+import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.util.resources.Vocabulary;
import org.apache.sis.internal.gui.Resources;
import org.apache.sis.internal.gui.BackgroundThreads;
+import org.apache.sis.internal.gui.ExceptionReporter;
import org.apache.sis.internal.gui.LogHandler;
@@ -76,10 +80,28 @@ public class ResourceExplorer extends WindowManager {
/**
* The widget showing metadata about a selected resource.
+ * Its content will be updated only when the tab is visible.
*/
private final MetadataSummary metadata;
/**
+ * The widget showing native metadata about a selected resource.
+ * Its content will be updated only when the tab is visible.
+ */
+ private final MetadataTree nativeMetadata;
+
+ /**
+ * The tab containing {@link #nativeMetadata}.
+ * The table title will change depending on the selected resource.
+ */
+ private final Tab nativeMetadataTab;
+
+ /**
+ * Default label for {@link #nativeMetadataTab} when no resource is selected.
+ */
+ private final String defaultNativeTabLabel;
+
+ /**
* The gridded data as an image or as a table, created when first needed.
*/
private CoverageExplorer coverage;
@@ -126,6 +148,11 @@ public class ResourceExplorer extends WindowManager {
private boolean isDataTabSet;
/**
+ * Whether one of the standard metadata tab (either "summary" or "metadata") is selected.
+ */
+ private final BooleanBinding metadataShown;
+
+ /**
* Last divider position as a fraction between 0 and 1, or {@code NaN} if undefined.
* This is used for keeping the position constant when adding and removing controls.
*/
@@ -142,42 +169,48 @@ public class ResourceExplorer extends WindowManager {
resources.getSelectionModel().getSelectedItems().addListener(this::onResourceSelected);
resources.setPrefWidth(400);
selectedResource = new ReadOnlyObjectWrapper<>(this, "selectedResource");
+ final Vocabulary vocabulary = Vocabulary.getResources(resources.locale);
+ /*
+ * "Summary" tab showing a summary of resource metadata.
+ */
metadata = new MetadataSummary();
+ final Tab summaryTab = new Tab(vocabulary.getString(Vocabulary.Keys.Summary), metadata.getView());
/*
- * Build the tabs.
+ * "Visual" tab showing the raster data as an image.
+ *
+ * TODO: add contextual menu for creating a window showing directly the visual.
*/
- final Vocabulary vocabulary = Vocabulary.getResources(resources.locale);
viewTab = new Tab(vocabulary.getString(Vocabulary.Keys.Visual));
- // TODO: add contextual menu for window showing directly the visual.
-
+ /*
+ * "Data" tab showing raster data as a table.
+ */
tableTab = new Tab(vocabulary.getString(Vocabulary.Keys.Data));
tableTab.setContextMenu(new ContextMenu(SelectedData.setTabularView(createNewWindowMenu())));
+ /*
+ * "Metadata" tab showing ISO 19115 metadata as a tree.
+ */
+ final Tab metadataTab = new Tab(vocabulary.getString(Vocabulary.Keys.Metadata), new StandardMetadataTree(metadata));
+ /*
+ * "Native metadata" tab showing metadata in their "raw" form (specific to the format).
+ */
+ nativeMetadata = new MetadataTree(metadata);
+ defaultNativeTabLabel = vocabulary.getString(Vocabulary.Keys.Format);
+ nativeMetadataTab = new Tab(defaultNativeTabLabel, nativeMetadata);
+ nativeMetadataTab.setDisable(true);
+ /*
+ * "Logging" tab showing log records specific to the selected resource
+ * (as opposed to the application menu showing all loggings regardless their source).
+ */
final LogViewer logging = new LogViewer(vocabulary);
logging.source.bind(selectedResource);
-
- final String nativeTabText = vocabulary.getString(Vocabulary.Keys.Format);
- final MetadataTree nativeMetadata = new MetadataTree(metadata);
- final Tab nativeTab = new Tab(nativeTabText, nativeMetadata);
- nativeTab.setDisable(true);
- nativeMetadata.contentProperty.addListener((p,o,n) -> {
- nativeTab.setDisable(n == null);
- Object label = (n != null) ? n.getRoot().getValue(TableColumn.NAME) : null;
- nativeTab.setText(Objects.toString(label, nativeTabText));
- });
-
final Tab loggingTab = new Tab(vocabulary.getString(Vocabulary.Keys.Logs), logging.getView());
loggingTab.disableProperty().bind(logging.isEmptyProperty());
-
- final TabPane tabs = new TabPane(
- new Tab(vocabulary.getString(Vocabulary.Keys.Summary), metadata.getView()), viewTab, tableTab,
- new Tab(vocabulary.getString(Vocabulary.Keys.Metadata), new StandardMetadataTree(metadata)),
- nativeTab, loggingTab);
-
- tabs.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
- tabs.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
/*
* Build the main pane which put everything together.
*/
+ final TabPane tabs = new TabPane(summaryTab, viewTab, tableTab, metadataTab, nativeMetadataTab, loggingTab);
+ tabs.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE);
+ tabs.setTabDragPolicy(TabPane.TabDragPolicy.REORDER);
controls = new SplitPane(resources);
controls.setOrientation(Orientation.VERTICAL);
content = new SplitPane(controls, tabs);
@@ -187,9 +220,22 @@ public class ResourceExplorer extends WindowManager {
SplitPane.setResizableWithParent(tabs, Boolean.TRUE);
/*
* Register listeners last, for making sure we don't have undesired event.
+ * Those listeners trig loading of various objects (data, standard metadata,
+ * native metadata) when the corresponding tab become visible.
*/
viewTab .selectedProperty().addListener((p,o,n) -> dataTabShown(n, true));
tableTab.selectedProperty().addListener((p,o,n) -> dataTabShown(n, false));
+ metadataShown = summaryTab.selectedProperty().or(metadataTab.selectedProperty());
+ metadataShown.addListener((p,o,n) -> {
+ if (Boolean.FALSE.equals(o) && Boolean.TRUE.equals(n)) {
+ metadata.setMetadata(getSelectedResource());
+ }
+ });
+ nativeMetadataTab.selectedProperty().addListener((p,o,n) -> {
+ if (Boolean.FALSE.equals(o) && Boolean.TRUE.equals(n)) {
+ loadNativeMetadata();
+ }
+ });
}
/**
@@ -279,20 +325,72 @@ public class ResourceExplorer extends WindowManager {
if (resource != null) break;
}
}
+ /*
+ * Fetch metadata immediately if one of the two ISO 19115 metadata tabs is selected.
+ * Otherwise metadata will be fetched when one of those tabs will become selected
+ * (listener registered in the constructor). A similar policy is applied for data.
+ */
selectedResource.set(resource);
- metadata.setMetadata(resource);
- isDataTabSet = isDataTabSelected();
+ metadata.setMetadata(metadataShown.get() ? resource : null);
+ isDataTabSet = viewTab.isSelected() || tableTab.isSelected();
updateDataTab(isDataTabSet ? resource : null, true);
if (!isDataTabSet) {
setNewWindowDisabled(!(resource instanceof GridCoverageResource || resource instanceof FeatureSet));
}
+ /*
+ * Update the label is disabled state of the native metadata tab. We do not have a reliable way
+ * to know if metadata are present without trying to fetch them, so current implementation only
+ * checks if the data store implementation override the `getNativeMetadata()` method.
+ */
+ String label = null;
+ boolean disabled = true;
+ if (resource instanceof DataStore) {
+ final DataStore store = (DataStore) resource;
+ final DataStoreProvider provider = store.getProvider();
+ if (provider != null) {
+ label = provider.getShortName();
+ }
+ try {
+ disabled = resource.getClass().getMethod("getNativeMetadata").getDeclaringClass() == DataStore.class;
+ } catch (NoSuchMethodException e) {
+ // Should never happen.
+ }
+ }
+ nativeMetadataTab.setText(Objects.toString(label, defaultNativeTabLabel));
+ nativeMetadataTab.setDisable(disabled);
+ nativeMetadata.setPlaceholder(null);
+ nativeMetadata.setContent(null);
+ if (nativeMetadataTab.isSelected()) {
+ loadNativeMetadata();
+ }
}
/**
- * Returns whether the currently selected tab is {@link #viewTab} or {@link #tableTab}.
+ * Loads native metadata in a background thread and shows them in the "native metadata" tab.
*/
- private boolean isDataTabSelected() {
- return viewTab.isSelected() || tableTab.isSelected();
+ private final void loadNativeMetadata() {
+ final Resource resource = getSelectedResource();
+ if (resource instanceof DataStore) {
+ final DataStore store = (DataStore) resource;
+ BackgroundThreads.execute(new Task<TreeTable>() {
+ /** Invoked in a background thread for fetching metadata. */
+ @Override protected TreeTable call() throws DataStoreException {
+ return store.getNativeMetadata().orElse(null);
+ }
+
+ /** Shows the result in JavaFX thread. */
+ @Override protected void succeeded() {
+ if (resource == getSelectedResource()) {
+ nativeMetadata.setContent(getValue());
+ }
+ }
+
+ /** Invoked in JavaFX thread if metadata loading failed. */
+ @Override protected void failed() {
+ nativeMetadata.setPlaceholder(new ExceptionReporter(getException()).getView());
+ }
+ });
+ }
}
/**
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 8bc8a0d..dee04f1 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
@@ -20,7 +20,6 @@ import java.util.Locale;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.util.Collection;
-import java.util.ArrayList;
import java.util.StringJoiner;
import javafx.application.Platform;
import javafx.beans.DefaultProperty;
@@ -42,13 +41,11 @@ import org.apache.sis.internal.gui.BackgroundThreads;
import org.apache.sis.internal.gui.ExceptionReporter;
import org.apache.sis.internal.gui.Styles;
import org.apache.sis.internal.util.Strings;
-import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.Resource;
import org.apache.sis.storage.Aggregate;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.resources.Vocabulary;
-import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.util.iso.Types;
import org.apache.sis.gui.Widget;
@@ -58,7 +55,7 @@ import org.apache.sis.gui.Widget;
*
* @author Smaniotto Enzo (GSoC)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
* @since 1.1
* @module
*/
@@ -133,13 +130,6 @@ public class MetadataSummary extends Widget {
private final TitledPane[] information;
/**
- * The listeners to notify about native metadata. We define those listeners in this {@link MetadataSummary}
- * for taking advantage of the background loading mechanism. We do not provide public API for such listeners
- * because a future version may want to provide those listeners in a more appropriate (not yet defined) class.
- */
- final ArrayList<MetadataTree> nativeMetadataViews;
-
- /**
* Creates an initially empty metadata overview.
*/
public MetadataSummary() {
@@ -151,7 +141,6 @@ public class MetadataSummary extends Widget {
};
content = new ScrollPane(new VBox());
content.setFitToWidth(true);
- nativeMetadataViews = new ArrayList<>();
metadataProperty = new SimpleObjectProperty<>(this, "metadata");
metadataProperty.addListener(MetadataSummary::applyChange);
}
@@ -230,9 +219,6 @@ public class MetadataSummary extends Widget {
/** The resource from which to load metadata. */
private final Resource resource;
- /** The native metadata, or {@code null} if none or not requested. */
- TreeTable nativeMetadata;
-
/** Creates a new metadata getter. */
Getter(final Resource resource) {
this.resource = resource;
@@ -240,9 +226,6 @@ public class MetadataSummary extends Widget {
/** Invoked in a background thread for fetching metadata. */
@Override protected Metadata call() throws DataStoreException {
- if (resource instanceof DataStore && !nativeMetadataViews.isEmpty()) {
- nativeMetadata = ((DataStore) resource).getNativeMetadata().orElse(null);
- }
return resource.getMetadata();
}
@@ -328,15 +311,6 @@ public class MetadataSummary extends Widget {
s.getter = null; // In case this method is invoked before `Getter` completed.
s.error = null;
if (metadata != oldValue) {
- /*
- * The native metadata are handled in a special way by `setMetadata(Resource)`.
- * Since we have no public API for setting native metadata in `MetadataSummary`,
- * we set the value to null if this method is not invoked from `Getter.succeeded()`.
- */
- final TreeTable nativeMetadata = (getter != null && getter.isDone()) ? getter.nativeMetadata : null;
- for (final MetadataTree view : s.nativeMetadataViews) {
- view.setContent(nativeMetadata);
- }
final ObservableList<Node> children = s.getChildren();
if (!children.isEmpty() && !(children.get(0) instanceof Section)) {
children.clear(); // If we were previously showing an error, clear all.
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 a1a725b..3d322fa 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
@@ -68,7 +68,7 @@ import org.apache.sis.util.logging.Logging;
*
* @author Siddhesh Rane (GSoC)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.2
* @since 1.1
* @module
*/
@@ -185,9 +185,6 @@ check: if (data != null) {
contentProperty.addListener(MetadataTree::applyChange);
if (!standard) {
setShowRoot(false);
- if (controller != null) {
- controller.nativeMetadataViews.add(this);
- }
}
}
@@ -231,6 +228,8 @@ check: if (data != null) {
/**
* Invoked when {@link #contentProperty} value changed.
+ * This method invokes {@link TreeTable#getRoot()} and
+ * wraps the value as the root node of this control.
*
* @param property the property which has been modified.
* @param oldValue the old tree table.
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/package-info.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/package-info.java
index 413a219..3d94a52 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/package-info.java
+++ b/application/sis-javafx/src/main/java/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.1
+ * @version 1.2
* @since 1.1
* @module
*/