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 a5993b3 commit b7285c5
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

1 comment on commit b7285c5

@imagesc-bot
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commit has been mentioned on Image.sc Forum. There might be relevant details there:

https://forum.image.sc/t/unsupportedoperationexception-defaulttableioservice/54197/3

Please sign in to comment.