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/09/03 13:51:10 UTC
[sis] 01/04: Refactor the "Copy file path" action in a separated
class with better management of File/URI conversions.
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 7d270e20b517fb2c1aa40983b68f9752cd261a81
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu Sep 3 11:43:41 2020 +0200
Refactor the "Copy file path" action in a separated class with better management of File/URI conversions.
---
.../org/apache/sis/gui/dataset/CopyAction.java | 137 +++++++++++++++++++++
.../org/apache/sis/gui/dataset/ResourceTree.java | 59 ++-------
.../sis/internal/storage/io/IOUtilities.java | 10 +-
3 files changed, 152 insertions(+), 54 deletions(-)
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/CopyAction.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/CopyAction.java
new file mode 100644
index 0000000..acdc442
--- /dev/null
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/CopyAction.java
@@ -0,0 +1,137 @@
+/*
+ * 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.gui.dataset;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.nio.file.Path;
+import java.io.File;
+import java.net.URL;
+import java.net.URI;
+import java.net.URISyntaxException;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.scene.control.TreeCell;
+import javafx.scene.input.Clipboard;
+import javafx.scene.input.ClipboardContent;
+import org.apache.sis.internal.gui.ExceptionReporter;
+import org.apache.sis.internal.storage.ResourceOnFileSystem;
+import org.apache.sis.internal.storage.URIDataStore;
+import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.Resource;
+
+
+/**
+ * The "Copy file path" action. This class gets the file path of a resource and copies it in the clipboard.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+final class CopyAction implements EventHandler<ActionEvent> {
+ /**
+ * The cell for which to provide a copy action.
+ */
+ private final TreeCell<Resource> cell;
+
+ /**
+ * Creates a new instance.
+ */
+ CopyAction(final TreeCell<Resource> source) {
+ this.cell = source;
+ }
+
+ /**
+ * Invoked when the user selected "Copy file path" item in the contextual menu of a {@link ResourceTree} cell.
+ * This method copies the path of the selected resource to the clipboard.
+ */
+ @Override
+ public void handle(final ActionEvent event) {
+ final Resource resource = cell.getItem();
+ final Object path;
+ try {
+ path = URIDataStore.location(resource);
+ } catch (DataStoreException e) {
+ ExceptionReporter.show(null, null, e);
+ return;
+ }
+ /*
+ * The file path can be given in two forms: as an URI or as a text (named "file" below).
+ * The textual form is the one that will usually be pasted. We try to provide paths on
+ * the local file system if possible, converting "file:///" URI if needed. Only if URI
+ * can not be converted to a local file path, we keep the URI form.
+ *
+ * The `uri` is determined in a way opposite to `file`: we convert local path to URI.
+ * That form is provided in case the path is pasted in applications expecting URI.
+ */
+ Object file = path;
+ Object uri = null;
+ try {
+ if (path instanceof URI) {
+ uri = path; // Must be first because next line may fail.
+ file = new File((URI) path);
+ } else if (path instanceof URL) {
+ uri = path; // Must be first because next line may fail.
+ file = new File(((URL) path).toURI());
+ } else if (path instanceof File) {
+ uri = ((File) path).toURI();
+ } else if (path instanceof Path) {
+ uri = ((Path) path).toUri(); // Must be first because next line may fail.
+ file = ((Path) path).toFile();
+ } else if (path instanceof CharSequence) {
+ uri = new URI(path.toString()); // Stay null if this operation fail.
+ } else {
+ file = "";
+ }
+ } catch (URISyntaxException | IllegalArgumentException | UnsupportedOperationException e) {
+ // Ignore. The `uri` or `text` field that we failed to assign keep its original value.
+ }
+ /*
+ * Above code obtained a single path, considered the main one. But a resource may also be
+ * associated with many files (some kinds of data are actually provided as a group of files).
+ * We put in the clipboard all `java.io.File` instances that we can get from the resource.
+ * This list of files will usually be ignored and only the `file` text will be pasted,
+ * but it depends on the application where files will be pasted.
+ */
+ List<File> files = null;
+ if (resource instanceof ResourceOnFileSystem) try {
+ final Path[] components = ((ResourceOnFileSystem) resource).getComponentFiles();
+ if (components != null) {
+ files = new ArrayList<>(components.length);
+ for (final Path p : components) try {
+ if (p != null) files.add(p.toFile());
+ } catch (UnsupportedOperationException e) {
+ // Ignore and try to add other components.
+ }
+ }
+ } catch (DataStoreException e) {
+ ResourceTree.unexpectedException("copy", e);
+ } else if (file instanceof File) {
+ files = Collections.singletonList((File) file);
+ }
+ /*
+ * Put in the clipboard all information that we could get.
+ */
+ final ClipboardContent content = new ClipboardContent();
+ content.putString(file.toString());
+ if (files != null) content.putFiles(files);
+ if (uri != null) content.putUrl(uri.toString());
+ Clipboard.getSystemClipboard().setContent(content);
+ }
+}
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 12c1e1b..cc923b7 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
@@ -17,8 +17,6 @@
package org.apache.sis.gui.dataset;
import java.io.File;
-import java.nio.file.Path;
-import java.net.URI;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.AbstractList;
@@ -40,8 +38,6 @@ import javafx.scene.control.TreeView;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
-import javafx.scene.input.Clipboard;
-import javafx.scene.input.ClipboardContent;
import javafx.scene.paint.Color;
import org.opengis.util.GenericName;
import org.opengis.util.InternationalString;
@@ -260,46 +256,6 @@ public class ResourceTree extends TreeView<Resource> {
}
/**
- * Performs a "copy" action on the given resource. Current implementation performs only
- * a "copy file path" action, but future versions may add more kinds of copy actions.
- *
- * @param resource the resource to copy.
- */
- private static void copy(final Resource resource) {
- Object path;
- try {
- path = URIDataStore.location(resource);
- } catch (DataStoreException e) {
- ExceptionReporter.show(null, null, e);
- return;
- }
- final ClipboardContent content = new ClipboardContent();
- final boolean isKindOfPath = IOUtilities.isKindOfPath(path);
- if (isKindOfPath || path instanceof CharSequence) {
- String uri = path.toString();
- String text = uri;
- try {
- if (path instanceof URI) {
- path = new File((URI) path);
- } else if (path instanceof Path) {
- path = ((Path) path).toFile();
- }
- } catch (IllegalArgumentException | UnsupportedOperationException e) {
- // Ignore
- }
- if (path instanceof File) {
- content.putFiles(Collections.singletonList((File) path));
- text = path.toString();
- }
- if (isKindOfPath) {
- content.putUrl(uri);
- }
- content.putString(text);
- }
- Clipboard.getSystemClipboard().setContent(content);
- }
-
- /**
* Removes the given resource from the tree and closes it if it is a {@link DataStore}.
* It is caller's responsibility to ensure that the given resource is not used anymore.
* A resource can be removed only if it is a root. If the given resource is not in this
@@ -468,6 +424,13 @@ public class ResourceTree extends TreeView<Resource> {
}
/**
+ * Reports an unexpected but non-fatal exception in the given method.
+ */
+ static void unexpectedException(final String method, final Exception e) {
+ Logging.unexpectedException(Logging.getLogger(Modules.APPLICATION), ResourceTree.class, method, e);
+ }
+
+ /**
* The visual appearance of an {@link Item} in a tree. This call gets the cell text from a resource
* by a call to {@link ResourceTree#getTitle(Resource, boolean)}. Cells are initially empty;
* their content will be specified by {@link TreeView} after construction.
@@ -542,9 +505,7 @@ public class ResourceTree extends TreeView<Resource> {
menu = new ContextMenu();
final Resources localized = tree.localized();
final MenuItem[] items = new MenuItem[CLOSE + 1];
- items[COPY_PATH] = localized.menu(Resources.Keys.CopyFilePath, (e) -> {
- copy(getItem());
- });
+ items[COPY_PATH] = localized.menu(Resources.Keys.CopyFilePath, new CopyAction(this));
items[CLOSE] = localized.menu(Resources.Keys.Close, (e) -> {
((ResourceTree) getTreeView()).removeAndClose(getItem());
});
@@ -559,9 +520,9 @@ public class ResourceTree extends TreeView<Resource> {
path = URIDataStore.location(resource);
} catch (DataStoreException e) {
path = null;
- Logging.unexpectedException(Logging.getLogger(Modules.APPLICATION), URIDataStore.class, "location", e);
+ unexpectedException("updateItem", e);
}
- menu.getItems().get(COPY_PATH).setDisable(!(IOUtilities.isKindOfPath(path) || path instanceof CharSequence));
+ menu.getItems().get(COPY_PATH).setDisable(!IOUtilities.isKindOfPath(path));
}
setContextMenu(menu);
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
index 7ce731b..fae94a7 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
@@ -71,9 +71,8 @@ public final class IOUtilities extends Static {
}
/**
- * Returns {@code true} if the given object is a {@link Path}, {@link File}, {@link URL} or {@link URI}.
- * Note that this method does not returns {@code true} for {@link CharSequence}; that type needs to be
- * verified by the caller if desired.
+ * Returns {@code true} if the given object is a {@link Path}, {@link File}, {@link URL}, {@link URI}
+ * or {@link CharSequence}. They are the types accepted by methods such as {@link #filename(Object)}.
*
* @param path the object to verify.
* @return whether the given object is of known type.
@@ -81,8 +80,9 @@ public final class IOUtilities extends Static {
* @since 1.1
*/
public static boolean isKindOfPath(final Object path) {
- return (path instanceof URI) || (path instanceof URL) || // Test final classes first.
- (path instanceof Path) || (path instanceof File);
+ return (path instanceof URI) || (path instanceof URL) || // Test final classes first.
+ (path instanceof Path) || (path instanceof File) ||
+ (path instanceof CharSequence);
}
/**