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 2020/01/28 12:05:17 UTC
[sis] 03/03: Give a way to export metadata information,
through clipboard for now.
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 bfd2d3c80b379d890ec314f82b7ada7b0ab7cc97
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Tue Jan 28 13:04:33 2020 +0100
Give a way to export metadata information, through clipboard for now.
---
application/sis-javafx/pom.xml | 9 ++
.../org/apache/sis/gui/metadata/MetadataTree.java | 154 +++++++++++++++++++--
.../org/apache/sis/internal/gui/DataFormats.java | 47 +++++++
.../org/apache/sis/internal/gui/Resources.java | 15 ++
.../apache/sis/internal/gui/Resources.properties | 3 +
.../sis/internal/gui/Resources_fr.properties | 3 +
6 files changed, 220 insertions(+), 11 deletions(-)
diff --git a/application/sis-javafx/pom.xml b/application/sis-javafx/pom.xml
index f5765ef..21f99c7 100644
--- a/application/sis-javafx/pom.xml
+++ b/application/sis-javafx/pom.xml
@@ -142,6 +142,15 @@
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
+ <dependency>
+ <groupId>jakarta.xml.bind</groupId>
+ <artifactId>jakarta.xml.bind-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.glassfish.jaxb</groupId>
+ <artifactId>jaxb-runtime</artifactId>
+ <scope>runtime</scope>
+ </dependency>
<!-- Test dependencies -->
<dependency>
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 e923015..ccce919 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
@@ -20,6 +20,10 @@ import java.util.Locale;
import java.util.List;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.io.StringWriter;
+import javax.xml.bind.JAXBException;
+import javax.xml.transform.stream.StreamResult;
import javafx.beans.DefaultProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
@@ -27,10 +31,18 @@ import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.ObservableList;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.control.ContextMenu;
+import javafx.scene.control.Menu;
+import javafx.scene.control.MenuItem;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableView;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableColumn.CellDataFeatures;
+import javafx.scene.control.TreeTableRow;
+import javafx.scene.input.Clipboard;
+import javafx.scene.input.ClipboardContent;
import org.opengis.metadata.Metadata;
import org.opengis.util.InternationalString;
import org.opengis.util.ControlledVocabulary;
@@ -39,9 +51,14 @@ 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.internal.xml.LegacyNamespaces;
+import org.apache.sis.internal.gui.ExceptionReporter;
+import org.apache.sis.internal.gui.DataFormats;
+import org.apache.sis.internal.gui.Resources;
import org.apache.sis.util.collection.TreeTable;
import org.apache.sis.util.collection.TableColumn;
import org.apache.sis.util.iso.Types;
+import org.apache.sis.xml.XML;
/**
@@ -63,8 +80,7 @@ import org.apache.sis.util.iso.Types;
* For changing content, use the {@link #contentProperty} instead.</li>
* </ul>
*
- * @todo Add contextual menu for saving or copying in clipboard the XML starting from the selected node.
- * Add contextual menu for showing a node in the summary pane (we would store in memory the path,
+ * @todo Add contextual menu for showing a node in the summary pane (we would store in memory the path,
* including sequence number for multi-values property, and apply it to all opened resources).
* Add a panel for controlling the number/date/angle format pattern.
*
@@ -130,6 +146,11 @@ public class MetadataTree extends TreeTableView<TreeTable.Node> {
private final Locale dataLocale;
/**
+ * The "copy" and "copy as" localized string, used for contextual menus.
+ */
+ private final String copy, copyAs;
+
+ /**
* Creates a new initially empty metadata tree.
*/
public MetadataTree() {
@@ -152,11 +173,16 @@ public class MetadataTree extends TreeTableView<TreeTable.Node> {
textLocale = Locale.getDefault(Locale.Category.DISPLAY);
dataLocale = Locale.getDefault(Locale.Category.FORMAT);
}
+ final Resources localized = Resources.forLocale(textLocale);
+ copy = localized.getString(Resources.Keys.Copy);
+ copyAs = localized.getString(Resources.Keys.CopyAs);
+
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);
+ setRowFactory(Row::new);
setColumnResizePolicy(CONSTRAINED_RESIZE_POLICY);
getColumns().setAll(nameColumn, valueColumn);
@@ -167,21 +193,24 @@ public class MetadataTree extends TreeTableView<TreeTable.Node> {
}
/**
+ * Returns the given metadata as a tree table.
+ */
+ private static TreeTable toTree(final Object metadata) {
+ if (metadata instanceof AbstractMetadata) {
+ return ((AbstractMetadata) metadata).asTreeTable();
+ } else {
+ return MetadataStandard.ISO_19115.asTreeTable(metadata, null, ValueExistencePolicy.COMPACT);
+ }
+ }
+
+ /**
* 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);
+ setContent(metadata == null ? null : toTree(metadata));
}
/**
@@ -234,6 +263,109 @@ public class MetadataTree extends TreeTableView<TreeTable.Node> {
}
/**
+ * A row in a metadata tree view, used for adding contextual menu on a row-by-row basis.
+ */
+ private static final class Row extends TreeTableRow<TreeTable.Node> implements EventHandler<ActionEvent> {
+ /**
+ * The context menu, to be added only if this row is non-empty.
+ */
+ private final ContextMenu menu;
+
+ /**
+ * The menu items for legacy or current XML formats.
+ */
+ private final MenuItem iso1, iso2;
+
+ /**
+ * The menu items for copying in XML formats, to be disabled if we can not do this export.
+ */
+ private final Menu copyAs;
+
+ /**
+ * Creates a new row for the given tree table.
+ */
+ @SuppressWarnings("ThisEscapedInObjectConstruction")
+ Row(final TreeTableView<TreeTable.Node> view) {
+ final MetadataTree md = (MetadataTree) view;
+ final MenuItem copy;
+ copy = new MenuItem(md.copy);
+ iso1 = new MenuItem("XML — ISO 19139:2007");
+ iso2 = new MenuItem("XML — ISO 19115-3:2016");
+ copyAs = new Menu(md.copyAs, null, iso2, iso1);
+ menu = new ContextMenu(copy, copyAs);
+ iso1.setOnAction(this);
+ iso2.setOnAction(this);
+ copy.setOnAction(this);
+ }
+
+ /**
+ * Invoked when a new row is selected. This method enable or disable the "copy as" menu
+ * depending on whether or not we can format XML document for currently selected row.
+ */
+ @Override
+ protected void updateItem(final TreeTable.Node item, final boolean empty) {
+ super.updateItem(item, empty);
+ copyAs.setDisable(empty || getMetadata() == null);
+ setContextMenu(empty ? null : menu);
+ }
+
+ /**
+ * If the currently selected row is a metadata object, returns that object.
+ * Otherwise returns {@code null}.
+ */
+ private Object getMetadata() {
+ final TreeTable.Node node = getItem();
+ if (node != null) {
+ final Object md = node.getUserObject();
+ if (md != null && MetadataStandard.ISO_19115.isMetadata(md.getClass())) {
+ return md;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Invoked when user requested to copy metadata. The requested format (ISO 19115 versus ISO 19139)
+ * will be determined by comparing the event source with {@link #iso1} and {@link #iso2} menu items.
+ */
+ @Override
+ public void handle(final ActionEvent event) {
+ final ClipboardContent content = new ClipboardContent();
+ final Object md = getMetadata();
+ if (md == null) {
+ final TreeTable.Node node = getItem();
+ if (node != null) {
+ final Object value = node.getValue(TableColumn.VALUE);
+ if (value != null) content.putString(value.toString());
+ }
+ } else {
+ final Object source = event.getSource();
+ if (source != iso1 && source != iso2) {
+ content.putString(toTree(md).toString());
+ } else try {
+ if (source == iso2) {
+ final String xml = XML.marshal(md);
+ content.put(DataFormats.XML, xml);
+ content.putString(xml);
+ } else {
+ final StringWriter output = new StringWriter();
+ XML.marshal(md, new StreamResult(output),
+ Collections.singletonMap(XML.METADATA_VERSION, LegacyNamespaces.VERSION_2007));
+ final String xml = output.toString();
+ content.put(DataFormats.ISO_19139, xml);
+ content.putString(xml);
+ }
+ } catch (JAXBException e) {
+ final Resources localized = Resources.forLocale(((MetadataTree) getTreeTableView()).textLocale);
+ ExceptionReporter.show(localized.getString(Resources.Keys.ErrorExportingData),
+ localized.getString(Resources.Keys.CanNotCreateXML), e);
+ }
+ }
+ Clipboard.getSystemClipboard().setContent(content);
+ }
+ }
+
+ /**
* A simple node encapsulating a {@link TreeTable.Node} in a view.
* The list of children is fetched when first needed.
*/
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/DataFormats.java b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/DataFormats.java
new file mode 100644
index 0000000..f1bd133
--- /dev/null
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/DataFormats.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.gui;
+
+import javafx.scene.input.DataFormat;
+import org.apache.sis.internal.storage.xml.AbstractProvider;
+
+
+/**
+ * A central place where to declare data formats used by SIS application.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+public final class DataFormats {
+ /**
+ * The data format for generic XML.
+ */
+ public static final DataFormat XML = new DataFormat(AbstractProvider.MIME_TYPE);
+
+ /**
+ * The data format for legacy ISO 19139:2007.
+ */
+ public static final DataFormat ISO_19139 = new DataFormat("application/vnd.iso.19139+xml");
+
+ /**
+ * Do not allow instantiation of this class.
+ */
+ private DataFormats() {
+ }
+}
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 c864ce9..f5e3199 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
@@ -81,6 +81,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short CanNotCreateCRS_1 = 35;
/**
+ * Can not create XML document.
+ */
+ public static final short CanNotCreateXML = 47;
+
+ /**
* Can not fetch tile ({0}, {1}).
*/
public static final short CanNotFetchTile_2 = 45;
@@ -106,6 +111,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short Copy = 31;
/**
+ * Copy as
+ */
+ public static final short CopyAs = 46;
+
+ /**
* Creation date:
*/
public static final short CreationDate = 16;
@@ -151,6 +161,11 @@ public final class Resources extends IndexedResourceBundle {
public static final short ErrorDataAccess = 40;
/**
+ * Error exporting data
+ */
+ public static final short ErrorExportingData = 48;
+
+ /**
* Error opening file
*/
public static final short ErrorOpeningFile = 6;
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 db5d34f..8328640 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
@@ -26,9 +26,11 @@ CanNotFetchTile_2 = Can not fetch tile ({0}, {1}).
CanNotReadFile_1 = Can not open \u201c{0}\u201d.
CanNotClose_1 = Can not close \u201c{0}\u201d. Data may be lost.
CanNotCreateCRS_1 = Can not create reference system \u201c{0}\u201d.
+CanNotCreateXML = Can not create XML document.
CellGeometry = Cell geometry:
Close = Close
Copy = Copy
+CopyAs = Copy as
CreationDate = Creation date:
Credit = Credit:
CRSs = Coordinate Reference Systems
@@ -36,6 +38,7 @@ Data = Data
Date = Date:
Dimensions = Dimensions:
Display = Display
+ErrorExportingData = Error exporting data
ErrorOpeningFile = Error opening file
ErrorClosingFile = Error closing file
ErrorCreatingCRS = Error creating reference system
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 2699e37..a23fb87 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
@@ -31,9 +31,11 @@ CanNotFetchTile_2 = Ne peut pas obtenir la tuile ({0}, {1}).
CanNotReadFile_1 = Ne peut pas ouvrir \u00ab\u202f{0}\u202f\u00bb.
CanNotClose_1 = Ne peut pas fermer \u00ab\u202f{0}\u202f\u00bb. Il pourrait y avoir une perte de donn\u00e9es.
CanNotCreateCRS_1 = Ne peut pas cr\u00e9er le syst\u00e8me de r\u00e9f\u00e9rence \u00ab\u202f{0}\u202f\u00bb.
+CanNotCreateXML = Ne peut pas cr\u00e9er le document XML.
CellGeometry = G\u00e9om\u00e9trie des cellules\u00a0:
Close = Fermer
Copy = Copier
+CopyAs = Copier comme
CreationDate = Date de cr\u00e9ation\u00a0:
Credit = Cr\u00e9dit\u00a0:
CRSs = Syst\u00e8mes de r\u00e9f\u00e9rence des coordonn\u00e9es
@@ -41,6 +43,7 @@ Data = Donn\u00e9es
Date = Date\u00a0:
Dimensions = Dimensions\u00a0:
Display = Affichage
+ErrorExportingData = Erreur \u00e0 l\u2019exportation de donn\u00e9es
ErrorOpeningFile = Erreur \u00e0 l\u2019ouverture du fichier
ErrorClosingFile = Erreur \u00e0 la fermeture du fichier
ErrorCreatingCRS = Erreur \u00e0 la cr\u00e9ation du syst\u00e8me de r\u00e9f\u00e9rence