Skip to content

Commit

Permalink
Use Location, not String, in the I/O API
Browse files Browse the repository at this point in the history
This is much more elegant. It is also a very breaking change.

Co-authored-by: Deborah Schmidt <[email protected]>
  • Loading branch information
ctrueden and frauzufall committed Aug 12, 2020
1 parent dcb568d commit 06021e0
Show file tree
Hide file tree
Showing 14 changed files with 256 additions and 57 deletions.
41 changes: 38 additions & 3 deletions src/main/java/org/scijava/io/AbstractIOPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<D> extends AbstractHandlerPlugin<String>
implements IOPlugin<D>
public abstract class AbstractIOPlugin<D> extends
AbstractHandlerPlugin<Location> implements IOPlugin<D>
{
// 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);
}
}
}
30 changes: 27 additions & 3 deletions src/main/java/org/scijava/io/DefaultIOService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -47,7 +50,7 @@
*/
@Plugin(type = Service.class)
public final class DefaultIOService
extends AbstractHandlerService<String, IOPlugin<?>> implements IOService
extends AbstractHandlerService<Location, IOPlugin<?>> implements IOService
{

@Parameter
Expand All @@ -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 + ".");
Expand All @@ -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<Object> saver = getSaver(data, destination);
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/org/scijava/io/DefaultRecentFileService.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
import org.scijava.event.EventHandler;
import org.scijava.event.EventService;
import org.scijava.io.event.IOEvent;
import org.scijava.io.location.FileLocation;
import org.scijava.io.location.Location;
import org.scijava.menu.MenuConstants;
import org.scijava.module.ModuleInfo;
import org.scijava.module.ModuleService;
Expand Down Expand Up @@ -181,7 +183,10 @@ public void dispose() {

@EventHandler
protected void onEvent(final IOEvent event) {
add(event.getDescriptor());
final Location loc = event.getLocation();
if (!(loc instanceof FileLocation)) return;
final FileLocation fileLoc = (FileLocation) loc;
add(fileLoc.getFile().getPath());
}

// -- Helper methods --
Expand Down
34 changes: 29 additions & 5 deletions src/main/java/org/scijava/io/IOPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

import java.io.IOException;

import org.scijava.io.location.FileLocation;
import org.scijava.io.location.Location;
import org.scijava.plugin.HandlerPlugin;
import org.scijava.plugin.Plugin;

Expand All @@ -48,52 +50,74 @@
* @see Plugin
* @see IOService
*/
public interface IOPlugin<D> extends HandlerPlugin<String> {
public interface IOPlugin<D> extends HandlerPlugin<Location> {

/** The type of data opened and/or saved by the plugin. */
Class<D> getDataType();

/** Checks whether the I/O plugin can open data from the given source. */
@SuppressWarnings("unused")
default boolean supportsOpen(final String source) {
return supportsOpen(new FileLocation(source));
}

/** Checks whether the I/O plugin can open data from the given location. */
default boolean supportsOpen(Location source) {
return false;
}

/** Checks whether the I/O plugin can save data to the given destination. */
@SuppressWarnings("unused")
default boolean supportsSave(final String destination) {
return supportsSave(new FileLocation(destination));
}

/** Checks whether the I/O plugin can save data to the given location. */
default boolean supportsSave(Location destination) {
return false;
}

/**
* Checks whether the I/O plugin can save the given data to the specified
* destination.
* location.
*/
default boolean supportsSave(final Object data, final String destination) {
return supportsSave(destination) && getDataType().isInstance(data);
}

default boolean supportsSave(Object data, Location destination) {
return supportsSave(destination) && getDataType().isInstance(data);
}

/** Opens data from the given source. */
@SuppressWarnings("unused")
default D open(final String source) throws IOException {
throw new UnsupportedOperationException();
}

/** Opens data from the given location. */
default D open(Location source) throws IOException {
throw new UnsupportedOperationException();
}
/** Saves the given data to the specified destination. */
@SuppressWarnings("unused")
default void save(final D data, final String destination) throws IOException {
save(data, new FileLocation(destination));
}

/** Saves the given data to the specified location. */
default void save(D data, Location destination) throws IOException {
throw new UnsupportedOperationException();
}

// -- Typed methods --

@Override
default boolean supports(final String descriptor) {
return supportsOpen(descriptor) || supportsSave(descriptor);
}

@Override
default Class<String> getType() {
return String.class;
default Class<Location> getType() {
return Location.class;
}
}
59 changes: 52 additions & 7 deletions src/main/java/org/scijava/io/IOService.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

import java.io.IOException;

import org.scijava.io.location.FileLocation;
import org.scijava.io.location.Location;
import org.scijava.plugin.HandlerService;
import org.scijava.service.SciJavaService;

Expand All @@ -39,15 +41,23 @@
*
* @author Curtis Rueden
*/
public interface IOService extends HandlerService<String, IOPlugin<?>>,
public interface IOService extends HandlerService<Location, IOPlugin<?>>,
SciJavaService
{

/**
* Gets the most appropriate {@link IOPlugin} for opening data from the given
* source.
* location.
*/
default IOPlugin<?> getOpener(final String source) {
return getOpener(new FileLocation(source));
}

/**
* Gets the most appropriate {@link IOPlugin} for opening data from the given
* location.
*/
default IOPlugin<?> getOpener(Location source) {
for (final IOPlugin<?> handler : getInstances()) {
if (handler.supportsOpen(source)) return handler;
}
Expand All @@ -56,9 +66,17 @@ default IOPlugin<?> getOpener(final String source) {

/**
* Gets the most appropriate {@link IOPlugin} for saving data to the given
* destination.
* location.
*/
default <D> IOPlugin<D> getSaver(final D data, final String destination) {
return getSaver(data, new FileLocation(destination));
}

/**
* Gets the most appropriate {@link IOPlugin} for saving data to the given
* location.
*/
default <D> IOPlugin<D> getSaver(D data, Location destination) {
for (final IOPlugin<?> handler : getInstances()) {
if (handler.supportsSave(data, destination)) {
@SuppressWarnings("unchecked")
Expand All @@ -77,7 +95,7 @@ default <D> IOPlugin<D> getSaver(final D data, final String destination) {
* The opener to use is automatically determined based on available
* {@link IOPlugin}s; see {@link #getOpener(String)}.
* </p>
*
*
* @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
Expand All @@ -86,21 +104,48 @@ default <D> IOPlugin<D> getSaver(final D data, final String destination) {
*/
Object open(String source) throws IOException;

/**
* Loads data from the given location.
* <p>
* The opener to use is automatically determined based on available
* {@link IOPlugin}s; see {@link #getOpener(Location)}.
* </p>
*
* @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.
* <p>
* The saver to use is automatically determined based on available
* {@link IOPlugin}s; see {@link #getSaver(Object, String)}.
* </p>
*
*
* @param data The data to be saved to the destination.
* @param destination The destination (e.g., file path) to which data should
* be saved.
* @throws IOException if something goes wrong saving the data.
*/
void save(Object data, String destination) throws IOException;

/**
* Saves data to the given location.
* <p>
* The saver to use is automatically determined based on available
* {@link IOPlugin}s; see {@link #getSaver(Object, Location)}.
* </p>
*
* @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
Expand All @@ -110,7 +155,7 @@ default Class<IOPlugin<?>> getPluginType() {
}

@Override
default Class<String> getType() {
return String.class;
default Class<Location> getType() {
return Location.class;
}
}
16 changes: 6 additions & 10 deletions src/main/java/org/scijava/io/event/DataOpenedEvent.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

}
Loading

0 comments on commit 06021e0

Please sign in to comment.