From b7285c5f807891097b5405b270952fcd32f2253c Mon Sep 17 00:00:00 2001
From: Curtis Rueden
Date: Sat, 7 Jun 2014 13:19:45 -0500
Subject: [PATCH] Use Location, not String, in the I/O API
This is much more elegant. It is also a very breaking change.
Co-authored-by: Deborah Schmidt
---
.../java/org/scijava/io/AbstractIOPlugin.java | 41 ++++++++++++-
.../java/org/scijava/io/DefaultIOService.java | 30 +++++++++-
.../scijava/io/DefaultRecentFileService.java | 7 ++-
src/main/java/org/scijava/io/IOPlugin.java | 34 +++++++++--
src/main/java/org/scijava/io/IOService.java | 59 ++++++++++++++++---
.../org/scijava/io/event/DataOpenedEvent.java | 16 ++---
.../org/scijava/io/event/DataSavedEvent.java | 12 ++--
.../java/org/scijava/io/event/IOEvent.java | 18 +++---
.../org/scijava/script/io/ScriptIOPlugin.java | 11 +++-
.../org/scijava/text/io/TextIOPlugin.java | 15 +++--
.../ui/dnd/FileDragAndDropHandler.java | 9 +--
.../java/org/scijava/io/DummyTextFormat.java | 22 +++++++
.../java/org/scijava/io/IOServiceTest.java | 38 ++++++++++++
src/test/resources/org/scijava/io/test.txt | 1 +
14 files changed, 256 insertions(+), 57 deletions(-)
create mode 100644 src/test/java/org/scijava/io/DummyTextFormat.java
create mode 100644 src/test/java/org/scijava/io/IOServiceTest.java
create mode 100644 src/test/resources/org/scijava/io/test.txt
diff --git a/src/main/java/org/scijava/io/AbstractIOPlugin.java b/src/main/java/org/scijava/io/AbstractIOPlugin.java
index 6ccd92f2c..9d314f8a3 100644
--- a/src/main/java/org/scijava/io/AbstractIOPlugin.java
+++ b/src/main/java/org/scijava/io/AbstractIOPlugin.java
@@ -29,15 +29,50 @@
package org.scijava.io;
+import org.scijava.io.location.Location;
+import org.scijava.io.location.LocationService;
import org.scijava.plugin.AbstractHandlerPlugin;
+import org.scijava.plugin.Parameter;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
/**
* Abstract base class for {@link IOPlugin}s.
*
* @author Curtis Rueden
*/
-public abstract class AbstractIOPlugin extends AbstractHandlerPlugin
- implements IOPlugin
+public abstract class AbstractIOPlugin extends
+ AbstractHandlerPlugin implements IOPlugin
{
- // NB: No implementation needed.
+
+ @Parameter
+ private LocationService locationService;
+
+ @Override
+ public boolean supportsOpen(final String source) {
+ try {
+ return supportsOpen(locationService.resolve(source));
+ } catch (URISyntaxException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean supportsSave(final String destination) {
+ try {
+ return supportsSave(locationService.resolve(destination));
+ } catch (URISyntaxException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public void save(final D data, final String destination) throws IOException {
+ try {
+ save(data, locationService.resolve(destination));
+ } catch (URISyntaxException e) {
+ throw new IOException(e);
+ }
+ }
}
diff --git a/src/main/java/org/scijava/io/DefaultIOService.java b/src/main/java/org/scijava/io/DefaultIOService.java
index c71896453..bfdaf35a2 100644
--- a/src/main/java/org/scijava/io/DefaultIOService.java
+++ b/src/main/java/org/scijava/io/DefaultIOService.java
@@ -30,10 +30,13 @@
package org.scijava.io;
import java.io.IOException;
+import java.net.URISyntaxException;
import org.scijava.event.EventService;
import org.scijava.io.event.DataOpenedEvent;
import org.scijava.io.event.DataSavedEvent;
+import org.scijava.io.location.Location;
+import org.scijava.io.location.LocationService;
import org.scijava.log.LogService;
import org.scijava.plugin.AbstractHandlerService;
import org.scijava.plugin.Parameter;
@@ -47,7 +50,7 @@
*/
@Plugin(type = Service.class)
public final class DefaultIOService
- extends AbstractHandlerService> implements IOService
+ extends AbstractHandlerService> implements IOService
{
@Parameter
@@ -56,10 +59,31 @@ public final class DefaultIOService
@Parameter
private EventService eventService;
- // -- IOService methods --
+ @Parameter
+ private LocationService locationService;
@Override
public Object open(final String source) throws IOException {
+ try {
+ return open(locationService.resolve(source));
+ } catch (URISyntaxException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public void save(final Object data, final String destination)
+ throws IOException
+ {
+ try {
+ save(data, locationService.resolve(destination));
+ } catch (URISyntaxException e) {
+ throw new IOException(e);
+ }
+ }
+
+ @Override
+ public Object open(final Location source) throws IOException {
final IOPlugin> opener = getOpener(source);
if (opener == null) {
log.error("No opener IOPlugin found for " + source + ".");
@@ -77,7 +101,7 @@ public Object open(final String source) throws IOException {
}
@Override
- public void save(final Object data, final String destination)
+ public void save(final Object data, final Location destination)
throws IOException
{
final IOPlugin
- *
+ *
* @param source The source (e.g., file path) from which to data should be
* loaded.
* @return An object representing the loaded data, or null if the source is
@@ -86,6 +104,20 @@ default IOPlugin getSaver(final D data, final String destination) {
*/
Object open(String source) throws IOException;
+ /**
+ * Loads data from the given location.
+ *
+ * The opener to use is automatically determined based on available
+ * {@link IOPlugin}s; see {@link #getOpener(Location)}.
+ *
+ *
+ * @param source The location from which to data should be loaded.
+ * @return An object representing the loaded data, or null if the source is
+ * not supported.
+ * @throws IOException if something goes wrong loading the data.
+ */
+ Object open(Location source) throws IOException;
+
/**
* Saves data to the given destination. The nature of the destination is left
* intentionally general, but the most common example is a file path.
@@ -93,7 +125,7 @@ default IOPlugin getSaver(final D data, final String destination) {
* The saver to use is automatically determined based on available
* {@link IOPlugin}s; see {@link #getSaver(Object, String)}.
*
- *
+ *
* @param data The data to be saved to the destination.
* @param destination The destination (e.g., file path) to which data should
* be saved.
@@ -101,6 +133,19 @@ default IOPlugin getSaver(final D data, final String destination) {
*/
void save(Object data, String destination) throws IOException;
+ /**
+ * Saves data to the given location.
+ *
+ * The saver to use is automatically determined based on available
+ * {@link IOPlugin}s; see {@link #getSaver(Object, Location)}.
+ *
+ *
+ * @param data The data to be saved to the destination.
+ * @param destination The destination location to which data should be saved.
+ * @throws IOException if something goes wrong saving the data.
+ */
+ void save(Object data, Location destination) throws IOException;
+
// -- HandlerService methods --
@Override
@@ -110,7 +155,7 @@ default Class> getPluginType() {
}
@Override
- default Class getType() {
- return String.class;
+ default Class getType() {
+ return Location.class;
}
}
diff --git a/src/main/java/org/scijava/io/event/DataOpenedEvent.java b/src/main/java/org/scijava/io/event/DataOpenedEvent.java
index 7af006c5a..4cf613856 100644
--- a/src/main/java/org/scijava/io/event/DataOpenedEvent.java
+++ b/src/main/java/org/scijava/io/event/DataOpenedEvent.java
@@ -29,22 +29,18 @@
package org.scijava.io.event;
+
+import org.scijava.io.location.Location;
+
/**
- * An event indicating that data has been opened from a source.
+ * An event indicating that data has been opened from a location.
*
* @author Curtis Rueden
*/
public class DataOpenedEvent extends IOEvent {
- public DataOpenedEvent(final String source, final Object data) {
- super(source, data);
- }
-
- // -- DataOpenedEvent methods --
-
- /** Gets the source from which data was opened. */
- public String getSource() {
- return getDescriptor();
+ public DataOpenedEvent(final Location location, final Object data) {
+ super(location, data);
}
}
diff --git a/src/main/java/org/scijava/io/event/DataSavedEvent.java b/src/main/java/org/scijava/io/event/DataSavedEvent.java
index cd6d22439..fe4b7abc3 100644
--- a/src/main/java/org/scijava/io/event/DataSavedEvent.java
+++ b/src/main/java/org/scijava/io/event/DataSavedEvent.java
@@ -29,6 +29,9 @@
package org.scijava.io.event;
+
+import org.scijava.io.location.Location;
+
/**
* An event indicating that data has been saved to a destination.
*
@@ -36,15 +39,8 @@
*/
public class DataSavedEvent extends IOEvent {
- public DataSavedEvent(final String destination, final Object data) {
+ public DataSavedEvent(final Location destination, final Object data) {
super(destination, data);
}
- // -- DataSavedEvent methods --
-
- /** Gets the destination to which data was saved. */
- public String getDestination() {
- return getDescriptor();
- }
-
}
diff --git a/src/main/java/org/scijava/io/event/IOEvent.java b/src/main/java/org/scijava/io/event/IOEvent.java
index 1a62e6fca..9228c94a4 100644
--- a/src/main/java/org/scijava/io/event/IOEvent.java
+++ b/src/main/java/org/scijava/io/event/IOEvent.java
@@ -30,6 +30,7 @@
package org.scijava.io.event;
import org.scijava.event.SciJavaEvent;
+import org.scijava.io.location.Location;
/**
* An event indicating that I/O (e.g., opening or saving) has occurred.
@@ -38,20 +39,20 @@
*/
public abstract class IOEvent extends SciJavaEvent {
- /** The data descriptor (source or destination). */
- private final String descriptor;
+ /** The data location (source or destination). */
+ private final Location location;
/** The data for which I/O took place. */
private final Object data;
- public IOEvent(final String descriptor, final Object data) {
- this.descriptor = descriptor;
+ public IOEvent(final Location location, final Object data) {
+ this.location = location;
this.data = data;
}
- /** Gets the data descriptor (source or destination). */
- public String getDescriptor() {
- return descriptor;
+ /** Gets the data location (source or destination). */
+ public Location getLocation() {
+ return location;
}
/** Gets the data for which I/O took place. */
@@ -63,7 +64,8 @@ public Object getData() {
@Override
public String toString() {
- return super.toString() + "\n\tdescriptor = " + data + "\n\tdata = " + data;
+ return super.toString() + "\n\tlocation = " + location + "\n\tdata = " +
+ data;
}
}
diff --git a/src/main/java/org/scijava/script/io/ScriptIOPlugin.java b/src/main/java/org/scijava/script/io/ScriptIOPlugin.java
index 56d201770..f881dc429 100644
--- a/src/main/java/org/scijava/script/io/ScriptIOPlugin.java
+++ b/src/main/java/org/scijava/script/io/ScriptIOPlugin.java
@@ -33,6 +33,8 @@
import org.scijava.io.AbstractIOPlugin;
import org.scijava.io.IOPlugin;
+import org.scijava.io.location.FileLocation;
+import org.scijava.io.location.Location;
import org.scijava.plugin.Parameter;
import org.scijava.script.ScriptService;
@@ -55,13 +57,16 @@ public Class getDataType() {
}
@Override
- public boolean supportsOpen(final String source) {
+ public boolean supportsOpen(final Location source) {
if (scriptService == null) return false; // no service for opening scripts
- return scriptService.canHandleFile(source);
+ // TODO: Update ScriptService to use Location instead of File.
+ if (!(source instanceof FileLocation)) return false;
+ final FileLocation loc = (FileLocation) source;
+ return scriptService.canHandleFile(loc.getFile());
}
@Override
- public String open(final String source) throws IOException {
+ public String open(final Location source) throws IOException {
if (scriptService == null) return null; // no service for opening scripts
// TODO: Use the script service to open the file in the script editor.
return null;
diff --git a/src/main/java/org/scijava/text/io/TextIOPlugin.java b/src/main/java/org/scijava/text/io/TextIOPlugin.java
index 3d2c64172..523ff2c34 100644
--- a/src/main/java/org/scijava/text/io/TextIOPlugin.java
+++ b/src/main/java/org/scijava/text/io/TextIOPlugin.java
@@ -29,12 +29,13 @@
package org.scijava.text.io;
-import java.io.File;
import java.io.IOException;
import org.scijava.Priority;
import org.scijava.io.AbstractIOPlugin;
import org.scijava.io.IOPlugin;
+import org.scijava.io.location.FileLocation;
+import org.scijava.io.location.Location;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.text.TextService;
@@ -59,15 +60,19 @@ public Class getDataType() {
}
@Override
- public boolean supportsOpen(final String source) {
+ public boolean supportsOpen(final Location source) {
if (textService == null) return false; // no service for opening text files
- return textService.supports(new File(source));
+ if (!(source instanceof FileLocation)) return false;
+ final FileLocation loc = (FileLocation) source;
+ return textService.supports(loc.getFile());
}
@Override
- public String open(final String source) throws IOException {
+ public String open(final Location source) throws IOException {
if (textService == null) return null; // no service for opening text files
- return textService.asHTML(new File(source));
+ if (!(source instanceof FileLocation)) throw new IllegalArgumentException();
+ final FileLocation loc = (FileLocation) source;
+ return textService.asHTML(loc.getFile());
}
}
diff --git a/src/main/java/org/scijava/ui/dnd/FileDragAndDropHandler.java b/src/main/java/org/scijava/ui/dnd/FileDragAndDropHandler.java
index 2076c6a20..02289b6e4 100644
--- a/src/main/java/org/scijava/ui/dnd/FileDragAndDropHandler.java
+++ b/src/main/java/org/scijava/ui/dnd/FileDragAndDropHandler.java
@@ -36,6 +36,7 @@
import org.scijava.display.Display;
import org.scijava.display.DisplayService;
import org.scijava.io.IOService;
+import org.scijava.io.location.FileLocation;
import org.scijava.log.LogService;
import org.scijava.plugin.Parameter;
import org.scijava.plugin.Plugin;
@@ -68,7 +69,8 @@ public boolean supports(final File file) {
if (!super.supports(file)) return false;
// verify that the file can be opened somehow
- return ioService.getOpener(file.getAbsolutePath()) != null;
+ final FileLocation loc = new FileLocation(file);
+ return ioService.getOpener(loc) != null;
}
@Override
@@ -78,13 +80,12 @@ public boolean drop(final File file, final Display> display) {
if (file == null) return true; // trivial case
// load the data
- final String filename = file.getAbsolutePath();
final Object data;
try {
- data = ioService.open(filename);
+ data = ioService.open(new FileLocation(file));
}
catch (final IOException exc) {
- if (log != null) log.error("Error opening file: " + filename, exc);
+ if (log != null) log.error("Error opening file: " + file, exc);
return false;
}
diff --git a/src/test/java/org/scijava/io/DummyTextFormat.java b/src/test/java/org/scijava/io/DummyTextFormat.java
new file mode 100644
index 000000000..21729843f
--- /dev/null
+++ b/src/test/java/org/scijava/io/DummyTextFormat.java
@@ -0,0 +1,22 @@
+package org.scijava.io;
+
+import org.scijava.plugin.Plugin;
+import org.scijava.text.AbstractTextFormat;
+import org.scijava.text.TextFormat;
+
+import java.util.Collections;
+import java.util.List;
+
+@Plugin(type = TextFormat.class)
+public class DummyTextFormat extends AbstractTextFormat {
+
+ @Override
+ public List getExtensions() {
+ return Collections.singletonList("txt");
+ }
+
+ @Override
+ public String asHTML(String text) {
+ return text;
+ }
+}
diff --git a/src/test/java/org/scijava/io/IOServiceTest.java b/src/test/java/org/scijava/io/IOServiceTest.java
new file mode 100644
index 000000000..55ebcf774
--- /dev/null
+++ b/src/test/java/org/scijava/io/IOServiceTest.java
@@ -0,0 +1,38 @@
+package org.scijava.io;
+
+import org.junit.Test;
+import org.scijava.Context;
+import org.scijava.io.location.FileLocation;
+import org.scijava.plugin.PluginInfo;
+import org.scijava.text.TextFormat;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class IOServiceTest {
+
+ @Test
+ public void testTextFile() throws IOException {
+ // create context, add dummy text format
+ final Context ctx = new Context();
+ ctx.getPluginIndex().add(new PluginInfo<>(DummyTextFormat.class, TextFormat.class));
+ final IOService io = ctx.getService(IOService.class);
+
+ // open text file from resources as String
+ String localFile = getClass().getResource("test.txt").getPath();
+ Object obj = io.open(localFile);
+ assertNotNull(obj);
+ String content = obj.toString();
+ assertTrue(content.contains("content"));
+
+ // open text file from resources as FileLocation
+ obj = io.open(new FileLocation(localFile));
+ assertNotNull(obj);
+ assertEquals(content, obj.toString());
+ }
+}
diff --git a/src/test/resources/org/scijava/io/test.txt b/src/test/resources/org/scijava/io/test.txt
new file mode 100644
index 000000000..d95f3ad14
--- /dev/null
+++ b/src/test/resources/org/scijava/io/test.txt
@@ -0,0 +1 @@
+content