sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 01/04: Refactor the "Copy file path" action in a separated class with better management of File/URI conversions.
Date Thu, 03 Sep 2020 13:51:10 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

commit 7d270e20b517fb2c1aa40983b68f9752cd261a81
Author: Martin Desruisseaux <martin.desruisseaux@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);
     }
 
     /**


Mime
View raw message