From 4990fc071b8e9b269920ec8cf176f51f23cf1c5c Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 26 Apr 2017 15:38:59 -0500 Subject: [PATCH 001/754] Fix the Travis configuration Without this, failed builds of master trigger another "mvn install". See: https://gist.github.com/ctrueden/ae0f024a0cdf2cb53c915d75b0759553 --- .travis/build.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.travis/build.sh b/.travis/build.sh index 4c2f8d2e4..8cddb5f18 100755 --- a/.travis/build.sh +++ b/.travis/build.sh @@ -1,7 +1,10 @@ #!/bin/sh dir="$(dirname "$0")" -test "$TRAVIS_SECURE_ENV_VARS" = true \ +if [ "$TRAVIS_SECURE_ENV_VARS" = true \ -a "$TRAVIS_PULL_REQUEST" = false \ - -a "$TRAVIS_BRANCH" = master && - mvn -Pdeploy-to-imagej deploy --settings "$dir/settings.xml" || + -a "$TRAVIS_BRANCH" = master ] +then + mvn -Pdeploy-to-imagej deploy --settings "$dir/settings.xml" +else mvn install +fi From 2b24dafa013e4abb09a966c8598b390e196a893b Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Thu, 27 Apr 2017 14:58:43 -0500 Subject: [PATCH 002/754] Fix javadoc errors --- src/main/java/org/scijava/Context.java | 11 +- src/main/java/org/scijava/Identifiable.java | 2 +- src/main/java/org/scijava/SciJava.java | 5 +- .../java/org/scijava/annotations/Index.java | 15 +- .../scijava/convert/ConversionRequest.java | 18 +- .../org/scijava/convert/ConvertService.java | 16 +- .../java/org/scijava/convert/Converter.java | 5 +- src/main/java/org/scijava/input/KeyCode.java | 2 +- .../java/org/scijava/menu/ShadowMenu.java | 3 +- src/main/java/org/scijava/plugin/Plugin.java | 8 +- .../java/org/scijava/script/ScriptInfo.java | 17 +- src/main/java/org/scijava/test/TestUtils.java | 20 +- .../java/org/scijava/util/CheckSezpoz.java | 22 +- .../org/scijava/util/ConversionUtils.java | 4 + src/main/java/org/scijava/util/FileUtils.java | 3 +- .../org/scijava/util/MersenneTwisterFast.java | 286 +++++++++--------- src/main/java/org/scijava/util/Timing.java | 27 +- 17 files changed, 231 insertions(+), 233 deletions(-) diff --git a/src/main/java/org/scijava/Context.java b/src/main/java/org/scijava/Context.java index 4813fdeab..e74971f46 100644 --- a/src/main/java/org/scijava/Context.java +++ b/src/main/java/org/scijava/Context.java @@ -141,8 +141,9 @@ public Context(final boolean noServices, final boolean noPlugins) { * {@code new Context(DisplayService.class)}) yield the potentially confusing * warning: *

- *
Type safety: A generic array of Class is - * created for a varargs parameter
+ *
Type safety: A generic array of + * {@code Class} is created for a varargs + * parameter
*

* To avoid this, we have opted to use raw types and suppress the relevant * warnings here instead. @@ -351,7 +352,7 @@ public Service getService(final String className) { /** * Injects the application context into the given object. This does three * distinct things: - *

+ * + * * * @param o The object to which the context should be assigned. * @throws IllegalStateException If the object already has a context. diff --git a/src/main/java/org/scijava/Identifiable.java b/src/main/java/org/scijava/Identifiable.java index e04fbb682..116ee2775 100644 --- a/src/main/java/org/scijava/Identifiable.java +++ b/src/main/java/org/scijava/Identifiable.java @@ -49,7 +49,7 @@ *

* In this way, objects themselves do not need to be {@link Serializable}, nor * do multiple potentially equivalent objects need to be synthesized and then - * compared using {@link #equals}. + * compared using {@link Object#equals}. *

* * @author Curtis Rueden diff --git a/src/main/java/org/scijava/SciJava.java b/src/main/java/org/scijava/SciJava.java index 462b92f72..927cbb224 100644 --- a/src/main/java/org/scijava/SciJava.java +++ b/src/main/java/org/scijava/SciJava.java @@ -74,8 +74,9 @@ public SciJava(final boolean empty) { * {@code new SciJava(LogService.class)}) yield the potentially confusing * warning: *

- *
Type safety: A generic array of Class is - * created for a varargs parameter
+ *
Type safety: A generic array of + * {@code Class} is created for a varargs + * parameter
*

* To avoid this, we have opted to use raw types and suppress the relevant * warning here instead. diff --git a/src/main/java/org/scijava/annotations/Index.java b/src/main/java/org/scijava/annotations/Index.java index 13d8e2d88..84ae391d1 100644 --- a/src/main/java/org/scijava/annotations/Index.java +++ b/src/main/java/org/scijava/annotations/Index.java @@ -45,14 +45,15 @@ /** * Makes the annotation indexes accessible. *

- * You would call it like this:
- * - * for (IndexItem item : Index.load(MyAnnotation.class)) {
- * // do something with item.annotation() and/or item.className()
- * }
- *
+ * You would call it like this: *

* + *
+ * for (IndexItem<MyAnnotation> item : Index.load(MyAnnotation.class)) {
+ *   // do something with item.annotation() and/or item.className()
+ * }
+ * 
+ * * @author Johannes Schindelin */ public class Index implements Iterable> { @@ -74,8 +75,6 @@ public static Index load(final Class annotation) /** * Loads the index of all classes annotated with the specified annotation. - *

- *

* * @param annotation the annotation type * @param loader the class loader to use when loading {@link Class}-type diff --git a/src/main/java/org/scijava/convert/ConversionRequest.java b/src/main/java/org/scijava/convert/ConversionRequest.java index a4397e60f..14c1ae26f 100644 --- a/src/main/java/org/scijava/convert/ConversionRequest.java +++ b/src/main/java/org/scijava/convert/ConversionRequest.java @@ -33,29 +33,27 @@ import java.lang.reflect.Type; +import org.scijava.plugin.HandlerService; import org.scijava.util.GenericUtils; /** - * Currency for use in {@link Converter} and {@link ConvertService} - * methods. + * Currency for use in {@link Converter} and {@link ConvertService} methods. *

* {@link #ConversionRequest} provides a variety of constructors. Note that only * one destination type needs to be set (e.g. either a {@link Type} or a * {@link Class}). *

*

- * Only the {@link Class} source needs to be set for {@link Converter} - * lookup, such as through - * {@link ConvertService#getHandler(ConversionRequest)}. However, to perform - * an actual conversion, e.g. using + * Only the {@link Class} source needs to be set for {@link Converter} lookup, + * such as through {@link HandlerService#getHandler(Object)}. However, to + * perform an actual conversion, e.g. using * {@link Converter#convert(ConversionRequest)}, you must provide an * {@link Object} source. *

*

- * NB: once a {@link Converter} has been acquired, the - * {@code ConversionRequest} used for lookup can be reused to cast to the same - * destination type, simply by updating the source object using the - * {@link #setSourceObject(Object)} method. + * NB: once a {@link Converter} has been acquired, the {@code ConversionRequest} + * used for lookup can be reused to cast to the same destination type, simply by + * updating the source object using the {@link #setSourceObject(Object)} method. *

* * @author Mark Hiner diff --git a/src/main/java/org/scijava/convert/ConvertService.java b/src/main/java/org/scijava/convert/ConvertService.java index cea655f5c..39d950e1e 100644 --- a/src/main/java/org/scijava/convert/ConvertService.java +++ b/src/main/java/org/scijava/convert/ConvertService.java @@ -66,22 +66,22 @@ public interface ConvertService extends Object convert(ConversionRequest request); /** - * @see #getHandler(ConversionRequest) + * @see HandlerService#supports(Object) */ Converter getHandler(Object src, Class dest); /** - * @see #getHandler(ConversionRequest) + * @see HandlerService#supports(Object) */ Converter getHandler(Object src, Type dest); /** - * @see #supports(ConversionRequest) + * @see HandlerService#supports(Object) */ boolean supports(Object src, Class dest); /** - * @see #supports(ConversionRequest) + * @see HandlerService#supports(Object) */ boolean supports(Object src, Type dest); @@ -106,28 +106,28 @@ public interface ConvertService extends // -- Deprecated API -- /** - * @see #getHandler(ConversionRequest) + * @see HandlerService#getHandler(Object) * @deprecated Use {@link #getHandler(Object, Class)} */ @Deprecated Converter getHandler(Class src, Class dest); /** - * @see #getHandler(ConversionRequest) + * @see HandlerService#getHandler(Object) * @deprecated Use {@link #getHandler(Object, Type)} */ @Deprecated Converter getHandler(Class src, Type dest); /** - * @see #supports(ConversionRequest) + * @see HandlerService#supports(Object) * @deprecated Use {@link #supports(Object, Class)} */ @Deprecated boolean supports(Class src, Class dest); /** - * @see #supports(ConversionRequest) + * @see HandlerService#supports(Object) * @deprecated Use {@link #supports(Object, Type)} */ @Deprecated diff --git a/src/main/java/org/scijava/convert/Converter.java b/src/main/java/org/scijava/convert/Converter.java index 325152928..f918d4ee0 100644 --- a/src/main/java/org/scijava/convert/Converter.java +++ b/src/main/java/org/scijava/convert/Converter.java @@ -50,9 +50,8 @@ public interface Converter extends HandlerPlugin { /** - * Checks whether a given {@ConversionRequest} can be - * processed, by converting the desired - * {@link ConversionRequest#sourceClass()} to its + * Checks whether a given {@link ConversionRequest} can be processed, by + * converting the desired {@link ConversionRequest#sourceClass()} to its * {@link ConversionRequest#destClass()} or * {@link ConversionRequest#destType()}. * diff --git a/src/main/java/org/scijava/input/KeyCode.java b/src/main/java/org/scijava/input/KeyCode.java index f6401761f..e70e63222 100644 --- a/src/main/java/org/scijava/input/KeyCode.java +++ b/src/main/java/org/scijava/input/KeyCode.java @@ -442,7 +442,7 @@ public enum KeyCode { /** TODO. */ DEAD_SEMIVOICED_SOUND(0x8f), - /** Ampersand ('&'). */ + /** Ampersand ('&'). */ AMPERSAND(0x96), /** Asterisk ('*'). */ diff --git a/src/main/java/org/scijava/menu/ShadowMenu.java b/src/main/java/org/scijava/menu/ShadowMenu.java index 604492b65..529ba1f81 100644 --- a/src/main/java/org/scijava/menu/ShadowMenu.java +++ b/src/main/java/org/scijava/menu/ShadowMenu.java @@ -172,7 +172,8 @@ public MenuEntry getMenuEntry() { /** * Gets how deep into the menu structure this node is. For example, "File" - * would be at depth 1, whereas "Exit" (of "File>Exit") would be at depth 2. + * would be at depth 1, whereas "Exit" (of "File>Exit") would be at depth + * 2. */ public int getMenuDepth() { return menuDepth; diff --git a/src/main/java/org/scijava/plugin/Plugin.java b/src/main/java/org/scijava/plugin/Plugin.java index 66ac0576d..3a3223574 100644 --- a/src/main/java/org/scijava/plugin/Plugin.java +++ b/src/main/java/org/scijava/plugin/Plugin.java @@ -79,10 +79,10 @@ /** * Abbreviated menu path defining where the plugin is shown in the menu - * structure. Uses greater than signs (>) as a separator; e.g.: - * "Image > Overlay > Properties..." defines a "Properties..." menu item - * within the "Overlay" submenu of the "Image" menu. Use either - * {@link #menuPath} or {@link #menu} but not both. + * structure. Uses greater than signs ({@code >}) as a separator; e.g.: "Image + * > Overlay > Properties..." defines a "Properties..." menu item within + * the "Overlay" submenu of the "Image" menu. Use either {@link #menuPath} or + * {@link #menu} but not both. */ String menuPath() default ""; diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index b93751a31..d22e3902a 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -238,11 +238,11 @@ public BufferedReader getReader() { *

*
    *
  • {@code // @ }
  • - *
  • {@code // @(=, ..., =) }
  • - *
  • {@code // @ }
  • - *
  • - * {@code // @(=, ..., =) } + *
  • {@code // @(=, ..., =) } *
  • + *
  • {@code // @ }
  • + *
  • {@code // @(=, ..., =) + * }
  • *
*

* Where: @@ -267,10 +267,13 @@ public BufferedReader getReader() { *

  • {@code // @Dataset dataset}
  • *
  • {@code // @double(type=OUTPUT) result}
  • *
  • {@code // @BOTH ImageDisplay display}
  • - *
  • {@code // @INPUT(persist=false, visibility=INVISIBLE) boolean verbose}
  • - * parameters will be parsed and filled just like @{@link Parameter} - * -annotated fields in {@link Command}s. + *
  • {@code // @INPUT(persist=false, visibility=INVISIBLE) boolean verbose} + *
  • * + *

    + * Parameters will be parsed and filled just like @{@link Parameter}-annotated + * fields in {@link Command}s. + *

    */ // NB: Widened visibility from AbstractModuleInfo. @Override diff --git a/src/main/java/org/scijava/test/TestUtils.java b/src/main/java/org/scijava/test/TestUtils.java index 9ee2b8932..fd7e48eee 100644 --- a/src/main/java/org/scijava/test/TestUtils.java +++ b/src/main/java/org/scijava/test/TestUtils.java @@ -74,8 +74,8 @@ public static File createPath(final File parent, final String path) * Makes a temporary directory for use with unit tests. *

    * When the unit test runs in a Maven context, the temporary directory will be - * created in the target/ directory corresponding to the calling class - * instead of /tmp/. + * created in the {@code target/} directory corresponding to the calling class + * instead of {@code /tmp/}. *

    * * @param prefix the prefix for the directory's name @@ -91,13 +91,13 @@ public static File createTemporaryDirectory(final String prefix) throws IOExcept * Makes a temporary directory for use with unit tests. *

    * When the unit test runs in a Maven context, the temporary directory will be - * created in the corresponding target/ directory instead of - * /tmp/. + * created in the corresponding {@code target/} directory instead of + * {@code /tmp/}. *

    * * @param prefix the prefix for the directory's name * @param forClass the class for context (to determine whether there's a - * target/ directory) + * {@code target/} directory) * @return the reference to the newly-created temporary directory * @throws IOException */ @@ -113,13 +113,13 @@ public static File createTemporaryDirectory(final String prefix, * Makes a temporary directory for use with unit tests. *

    * When the unit test runs in a Maven context, the temporary directory will be - * created in the corresponding target/ directory instead of - * /tmp/. + * created in the corresponding {@code target/} directory instead of + * {@code /tmp/}. *

    * * @param prefix the prefix for the directory's name * @param forClass the class for context (to determine whether there's a - * target/ directory) + * {@code target/} directory) * @param suffix the suffix for the directory's name * @return the reference to the newly-created temporary directory * @throws IOException @@ -165,7 +165,7 @@ else if (file.exists() && !file.delete()) { * Sometimes it is convenient to determine the caller's context, e.g. to * determine whether running in a maven-surefire-plugin context (in which case * the location of the caller's class would end in - * target/test-classes/). + * {@code target/test-classes/}). *

    * * @param excluding the class to exclude (or null) @@ -181,7 +181,7 @@ public static Class getCallingClass(final Class excluding) { * Sometimes it is convenient to determine the caller's context, e.g. to * determine whether running in a maven-surefire-plugin context (in which case * the location of the caller's class would end in - * target/test-classes/). + * {@code target/test-classes/}). *

    * * @param excluding the class to exclude (or null) diff --git a/src/main/java/org/scijava/util/CheckSezpoz.java b/src/main/java/org/scijava/util/CheckSezpoz.java index b9f61c7e7..1a207272c 100644 --- a/src/main/java/org/scijava/util/CheckSezpoz.java +++ b/src/main/java/org/scijava/util/CheckSezpoz.java @@ -117,15 +117,15 @@ public static boolean checkDirectory(final File classes) throws IOException { /** * Checks whether the annotations are possibly out-of-date. *

    - * This method looks whether there are any .class files older than - * their corresponding .java files, or whether there are .class - * files that were generated since last time we checked. + * This method looks whether there are any {@code .class} files older than + * their corresponding {@code .java} files, or whether there are + * {@code .class} files that were generated since last time we checked. *

    * - * @param classes the classes/ directory where Maven puts the - * .class files - * @param source the src/main/java/ directory where Maven expects the - * .java files + * @param classes the {@code classes/} directory where Maven puts the + * {@code .class} files + * @param source the {@code src/main/java/} directory where Maven expects the + * {@code .java} files * @param youngerThan the date/time when we last checked * @see EclipseHelper */ @@ -137,13 +137,13 @@ public static boolean checkDirectory(final File classes, final File source, } /** - * Checks a .jar file for stale annotations. + * Checks a {@code .jar} file for stale annotations. *

    * This method is broken at the moment since there is no good way to verify - * that SezPoz ran before the .jar file was packaged. + * that SezPoz ran before the {@code .jar} file was packaged. *

    * - * @param file the .jar file + * @param file the {@code .jar} file * @see EclipseHelper */ @Deprecated @@ -170,7 +170,7 @@ public static boolean fix(final File classes, final File sources) { } /** - * Writes out a DOM as .xml file. + * Writes out a DOM as {@code .xml} file. * * @param xml the DOM * @param file the file to write diff --git a/src/main/java/org/scijava/util/ConversionUtils.java b/src/main/java/org/scijava/util/ConversionUtils.java index 0408c6ff2..00439911c 100644 --- a/src/main/java/org/scijava/util/ConversionUtils.java +++ b/src/main/java/org/scijava/util/ConversionUtils.java @@ -146,6 +146,7 @@ public static boolean canCast(final Object src, final Class dest) { * Returns the primitive {@link Class} closest to the given type. *

    * Specifically, the following type conversions are done: + *

    *
      *
    • Boolean.class becomes boolean.class
    • *
    • Byte.class becomes byte.class
    • @@ -157,6 +158,7 @@ public static boolean canCast(final Object src, final Class dest) { *
    • Short.class becomes short.class
    • *
    • Void.class becomes void.class
    • *
    + *

    * All other types are unchanged. *

    */ @@ -181,6 +183,7 @@ public static Class getPrimitiveType(final Class type) { * Returns the non-primitive {@link Class} closest to the given type. *

    * Specifically, the following type conversions are done: + *

    *
      *
    • boolean.class becomes Boolean.class
    • *
    • byte.class becomes Byte.class
    • @@ -192,6 +195,7 @@ public static Class getPrimitiveType(final Class type) { *
    • short.class becomes Short.class
    • *
    • void.class becomes Void.class
    • *
    + *

    * All other types are unchanged. *

    */ diff --git a/src/main/java/org/scijava/util/FileUtils.java b/src/main/java/org/scijava/util/FileUtils.java index d48f3114c..f916dd5db 100644 --- a/src/main/java/org/scijava/util/FileUtils.java +++ b/src/main/java/org/scijava/util/FileUtils.java @@ -668,7 +668,8 @@ public static Map findResources(final String regex, *

    * Each of the given {@link URL}s is recursively scanned using * {@link #listContents(URL)}, and anything matching the given {@code regex} - * pattern is added to the output map. + * pattern is added to the output map. + *

    * * @param regex The regex to use when matching resources, or null to match * everything. diff --git a/src/main/java/org/scijava/util/MersenneTwisterFast.java b/src/main/java/org/scijava/util/MersenneTwisterFast.java index 21a51595f..03b5dca30 100644 --- a/src/main/java/org/scijava/util/MersenneTwisterFast.java +++ b/src/main/java/org/scijava/util/MersenneTwisterFast.java @@ -37,156 +37,146 @@ import java.io.Serializable; import java.util.Random; -/** +/** *

    MersenneTwister and MersenneTwisterFast

    - *

    MersenneTwister is a drop-in subclass replacement - * for java.util.Random. It is properly synchronized and - * can be used in a multithreaded environment. On modern VMs such - * as HotSpot, it is approximately 1/3 slower than java.util.Random. - * - *

    MersenneTwisterFast is not a subclass of java.util.Random. It has - * the same public methods as Random does, however, and it is - * algorithmically identical to MersenneTwister. MersenneTwisterFast - * has hard-code inlined all of its methods directly, and made all of them - * final (well, the ones of consequence anyway). Further, these - * methods are not synchronized, so the same MersenneTwisterFast - * instance cannot be shared by multiple threads. But all this helps - * MersenneTwisterFast achieve well over twice the speed of MersenneTwister. - * java.util.Random is about 1/3 slower than MersenneTwisterFast. - * + *

    + * Version 17, based on version MT199937(99/10/29) of the Mersenne + * Twister algorithm found at + * The Mersenne Twister + * Home Page, with the initialization improved using the new 2002/1/26 + * initialization algorithm By Sean Luke, October 2004. + *

    + * MersenneTwister is a drop-in subclass replacement for + * java.util.Random. It is properly synchronized and can be used in a + * multithreaded environment. On modern VMs such as HotSpot, it is approximately + * 1/3 slower than java.util.Random. + *

    + * MersenneTwisterFast is not a subclass of java.util.Random. It has the + * same public methods as Random does, however, and it is algorithmically + * identical to MersenneTwister. MersenneTwisterFast has hard-code inlined all + * of its methods directly, and made all of them final (well, the ones of + * consequence anyway). Further, these methods are not synchronized, so + * the same MersenneTwisterFast instance cannot be shared by multiple threads. + * But all this helps MersenneTwisterFast achieve well over twice the speed of + * MersenneTwister. java.util.Random is about 1/3 slower than + * MersenneTwisterFast. *

    About the Mersenne Twister

    - *

    This is a Java version of the C-program for MT19937: Integer version. - * The MT19937 algorithm was created by Makoto Matsumoto and Takuji Nishimura, - * who ask: "When you use this, send an email to: matumoto@math.keio.ac.jp - * with an appropriate reference to your work". Indicate that this - * is a translation of their algorithm into Java. - * - *

    Reference. - * Makato Matsumoto and Takuji Nishimura, - * "Mersenne Twister: A 623-Dimensionally Equidistributed Uniform - * Pseudo-Random Number Generator", - * ACM Transactions on Modeling and. Computer Simulation, - * Vol. 8, No. 1, January 1998, pp 3--30. - * + *

    + * This is a Java version of the C-program for MT19937: Integer version. The + * MT19937 algorithm was created by Makoto Matsumoto and Takuji Nishimura, who + * ask: "When you use this, send an email to: matumoto@math.keio.ac.jp with an + * appropriate reference to your work". Indicate that this is a translation of + * their algorithm into Java. + *

    + * Reference. Makato Matsumoto and Takuji Nishimura, "Mersenne Twister: + * A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number Generator", + * ACM Transactions on Modeling and. Computer Simulation, Vol. 8, No. 1, + * January 1998, pp 3--30. *

    About this Version

    - * - *

    Changes since V16: Added nextDouble(includeZero, includeOne) and + *

    + * Changes since V16: Added nextDouble(includeZero, includeOne) and * nextFloat(includeZero, includeOne) to allow for half-open, fully-closed, and * fully-open intervals. - * - *

    Changes Since V15: Added serialVersionUID to quiet compiler warnings + *

    + * Changes Since V15: Added serialVersionUID to quiet compiler warnings * from Sun's overly verbose compilers as of JDK 1.5. - * - *

    Changes Since V14: made strictfp, with StrictMath.log and StrictMath.sqrt - * in nextGaussian instead of Math.log and Math.sqrt. This is largely just to be safe, - * as it presently makes no difference in the speed, correctness, or results of the - * algorithm. - * - *

    Changes Since V13: clone() method CloneNotSupportedException removed. - * - *

    Changes Since V12: clone() method added. - * - *

    Changes Since V11: stateEquals(...) method added. MersenneTwisterFast + *

    + * Changes Since V14: made strictfp, with StrictMath.log and + * StrictMath.sqrt in nextGaussian instead of Math.log and Math.sqrt. This is + * largely just to be safe, as it presently makes no difference in the speed, + * correctness, or results of the algorithm. + *

    + * Changes Since V13: clone() method CloneNotSupportedException removed. + *

    + * Changes Since V12: clone() method added. + *

    + * Changes Since V11: stateEquals(...) method added. MersenneTwisterFast * is equal to other MersenneTwisterFasts with identical state; likewise - * MersenneTwister is equal to other MersenneTwister with identical state. - * This isn't equals(...) because that requires a contract of immutability - * to compare by value. - * - *

    Changes Since V10: A documentation error suggested that - * setSeed(int[]) required an int[] array 624 long. In fact, the array - * can be any non-zero length. The new version also checks for this fact. - * - *

    Changes Since V9: readState(stream) and writeState(stream) - * provided. - * - *

    Changes Since V8: setSeed(int) was only using the first 28 bits - * of the seed; it should have been 32 bits. For small-number seeds the - * behavior is identical. - * - *

    Changes Since V7: A documentation error in MersenneTwisterFast - * (but not MersenneTwister) stated that nextDouble selects uniformly from - * the full-open interval [0,1]. It does not. nextDouble's contract is - * identical across MersenneTwisterFast, MersenneTwister, and java.util.Random, - * namely, selection in the half-open interval [0,1). That is, 1.0 should - * not be returned. A similar contract exists in nextFloat. - * - *

    Changes Since V6: License has changed from LGPL to BSD. - * New timing information to compare against - * java.util.Random. Recent versions of HotSpot have helped Random increase - * in speed to the point where it is faster than MersenneTwister but slower - * than MersenneTwisterFast (which should be the case, as it's a less complex - * algorithm but is synchronized). - * - *

    Changes Since V5: New empty constructor made to work the same - * as java.util.Random -- namely, it seeds based on the current time in + * MersenneTwister is equal to other MersenneTwister with identical state. This + * isn't equals(...) because that requires a contract of immutability to compare + * by value. + *

    + * Changes Since V10: A documentation error suggested that setSeed(int[]) + * required an int[] array 624 long. In fact, the array can be any non-zero + * length. The new version also checks for this fact. + *

    + * Changes Since V9: readState(stream) and writeState(stream) provided. + *

    + * Changes Since V8: setSeed(int) was only using the first 28 bits of the + * seed; it should have been 32 bits. For small-number seeds the behavior is + * identical. + *

    + * Changes Since V7: A documentation error in MersenneTwisterFast (but + * not MersenneTwister) stated that nextDouble selects uniformly from the + * full-open interval [0,1]. It does not. nextDouble's contract is identical + * across MersenneTwisterFast, MersenneTwister, and java.util.Random, namely, + * selection in the half-open interval [0,1). That is, 1.0 should not be + * returned. A similar contract exists in nextFloat. + *

    + * Changes Since V6: License has changed from LGPL to BSD. New timing + * information to compare against java.util.Random. Recent versions of HotSpot + * have helped Random increase in speed to the point where it is faster than + * MersenneTwister but slower than MersenneTwisterFast (which should be the + * case, as it's a less complex algorithm but is synchronized). + *

    + * Changes Since V5: New empty constructor made to work the same as + * java.util.Random -- namely, it seeds based on the current time in * milliseconds. - * - *

    Changes Since V4: New initialization algorithms. See - * (see - * http://www.math.keio.ac.jp/matumoto/MT2002/emt19937ar.html) - * - *

    The MersenneTwister code is based on standard MT19937 C/C++ - * code by Takuji Nishimura, - * with suggestions from Topher Cooper and Marc Rieffel, July 1997. - * The code was originally translated into Java by Michael Lecuyer, - * January 1999, and the original code is Copyright (c) 1999 by Michael Lecuyer. - * + *

    + * Changes Since V4: New initialization algorithms (see + * + * http://www.math.keio.ac.jp/matumoto/MT2002/emt19937ar.html). + *

    + * The MersenneTwister code is based on standard MT19937 C/C++ code by Takuji + * Nishimura, with suggestions from Topher Cooper and Marc Rieffel, July 1997. + * The code was originally translated into Java by Michael Lecuyer, January + * 1999, and the original code is Copyright (c) 1999 by Michael Lecuyer. *

    Java notes

    - * - *

    This implementation implements the bug fixes made - * in Java 1.2's version of Random, which means it can be used with - * earlier versions of Java. See - * - * the JDK 1.2 java.util.Random documentation for further documentation - * on the random-number generation contracts made. Additionally, there's - * an undocumented bug in the JDK java.util.Random.nextBytes() method, - * which this code fixes. - * - *

    Just like java.util.Random, this - * generator accepts a long seed but doesn't use all of it. java.util.Random - * uses 48 bits. The Mersenne Twister instead uses 32 bits (int size). - * So it's best if your seed does not exceed the int range. - * - *

    MersenneTwister can be used reliably - * on JDK version 1.1.5 or above. Earlier Java versions have serious bugs in - * java.util.Random; only MersenneTwisterFast (and not MersenneTwister nor - * java.util.Random) should be used with them. - * - *

    License

    - * - * Copyright (c) 2003 by Sean Luke.
    + *

    + * This implementation implements the bug fixes made in Java 1.2's version of + * Random, which means it can be used with earlier versions of Java. See + * + * the JDK 1.2 java.util.Random documentation for further documentation on + * the random-number generation contracts made. Additionally, there's an + * undocumented bug in the JDK java.util.Random.nextBytes() method, which this + * code fixes. + *

    + * Just like java.util.Random, this generator accepts a long seed but doesn't + * use all of it. java.util.Random uses 48 bits. The Mersenne Twister instead + * uses 32 bits (int size). So it's best if your seed does not exceed the int + * range. + *

    + * MersenneTwister can be used reliably on JDK version 1.1.5 or above. Earlier + * Java versions have serious bugs in java.util.Random; only MersenneTwisterFast + * (and not MersenneTwister nor java.util.Random) should be used with them. + *

    License

    Copyright (c) 2003 by Sean Luke.
    * Portions copyright (c) 1993 by Michael Lecuyer.
    * All rights reserved.
    - * - *

    Redistribution and use in source and binary forms, with or without + *

    + * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: *

      - *
    • Redistributions of source code must retain the above copyright notice, + *
    • Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. - *
    • Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation + *
    • Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - *
    • Neither the name of the copyright owners, their employers, nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. + *
    • Neither the name of the copyright owners, their employers, nor the names + * of its contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. *
    - *

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + *

    + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * @author Sean Luke @@ -795,7 +785,7 @@ public final long nextLong() /** Returns a long drawn uniformly from 0 to n-1. Suffice it to say, - n must be > 0, or an IllegalArgumentException is raised. */ + n must be > 0, or an IllegalArgumentException is raised. */ public final long nextLong(final long n) { if (n<=0) @@ -940,12 +930,12 @@ public final double nextDouble() /** Returns a double in the range from 0.0 to 1.0, possibly inclusive of 0.0 and 1.0 themselves. Thus: -

    -
    ExpressionInterval -
    nextDouble(false, false)(0.0, 1.0) -
    nextDouble(true, false)[0.0, 1.0) -
    nextDouble(false, true)(0.0, 1.0] -
    nextDouble(true, true)[0.0, 1.0] + + + + + +
    ExpressionInterval
    nextDouble(false, false)(0.0, 1.0)
    nextDouble(true, false)[0.0, 1.0)
    nextDouble(false, true)(0.0, 1.0]
    nextDouble(true, true)[0.0, 1.0]

    This version preserves all possible random values in the double range. @@ -1153,12 +1143,12 @@ public final float nextFloat() /** Returns a float in the range from 0.0f to 1.0f, possibly inclusive of 0.0f and 1.0f themselves. Thus: -

    -
    ExpressionInterval -
    nextFloat(false, false)(0.0f, 1.0f) -
    nextFloat(true, false)[0.0f, 1.0f) -
    nextFloat(false, true)(0.0f, 1.0f] -
    nextFloat(true, true)[0.0f, 1.0f] + + + + + +
    ExpressionInterval
    nextFloat(false, false)(0.0f, 1.0f)
    nextFloat(true, false)[0.0f, 1.0f)
    nextFloat(false, true)(0.0f, 1.0f]
    nextFloat(true, true)[0.0f, 1.0f]

    This version preserves all possible random values in the float range. @@ -1179,7 +1169,7 @@ public double nextFloat(boolean includeZero, boolean includeOne) /** Returns an integer drawn uniformly from 0 to n-1. Suffice it to say, - n must be > 0, or an IllegalArgumentException is raised. */ + n must be > 0, or an IllegalArgumentException is raised. */ public final int nextInt(final int n) { if (n<=0) diff --git a/src/main/java/org/scijava/util/Timing.java b/src/main/java/org/scijava/util/Timing.java index a4390222c..15641e7dd 100644 --- a/src/main/java/org/scijava/util/Timing.java +++ b/src/main/java/org/scijava/util/Timing.java @@ -50,20 +50,21 @@ *

    * Use this class as following: *

    - * - * private static Timing timing = new Timing();
    - * ...
    - * private void oneOperation() {
    - *  final long t1 = System.nanoTime();
    - *  ...
    - *  timing.add(System.nanoTime() - t1, "Operation #1");
    - * }
    - * ...
    - * private void atEnd() {
    - *  ...
    - *  timing.report("Operations");
    + * + *
    + * private static Timing timing = new Timing();
    + * ...
    + * private void oneOperation() {
    + *  final long t1 = System.nanoTime();
    + *  ...
    + *  timing.add(System.nanoTime() - t1, "Operation #1");
    + * }
    + * ...
    + * private void atEnd() {
    + *  ...
    + *  timing.report("Operations");
      * }
    - * 
    + * 
    * * @author Johannes Schindelin */ From 7cb92b90e5e8c0455a730ba40d124b867049f95c Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 5 May 2017 14:13:36 -0500 Subject: [PATCH 003/754] StringUtilsTest: add missing license header --- .../org/scijava/util/StringUtilsTest.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/java/org/scijava/util/StringUtilsTest.java b/src/test/java/org/scijava/util/StringUtilsTest.java index 908b59eca..395b23db2 100644 --- a/src/test/java/org/scijava/util/StringUtilsTest.java +++ b/src/test/java/org/scijava/util/StringUtilsTest.java @@ -1,3 +1,33 @@ +/*- + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ package org.scijava.util; From 60ef945e2c310e4e6c88807eed8ce2e01ea48aba Mon Sep 17 00:00:00 2001 From: Leon Yang Date: Fri, 14 Oct 2016 22:30:01 -0500 Subject: [PATCH 004/754] Make Module support Initializable It might be easier for the delegate class inside the module to implement this interface than to specify the initializer inside the Plugin annotation, especially because annotation could not be partially overridden. Now the Initializable interface has higher priority than the initializer annotation, and at most one of them will be called when a module is initialized. Closes #249. Signed-off-by: Curtis Rueden --- .../java/org/scijava/module/AbstractModule.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/scijava/module/AbstractModule.java b/src/main/java/org/scijava/module/AbstractModule.java index 2b63ba440..e50a162e7 100644 --- a/src/main/java/org/scijava/module/AbstractModule.java +++ b/src/main/java/org/scijava/module/AbstractModule.java @@ -35,6 +35,8 @@ import java.util.HashSet; import java.util.Map; +import org.scijava.Initializable; + /** * Abstract superclass of {@link Module} implementations. *

    @@ -76,11 +78,16 @@ public void cancel() { public void initialize() throws MethodCallException { // execute global module initializer final Object delegateObject = getDelegateObject(); - if (initializerRef == null) { - final String initializer = getInfo().getInitializer(); - initializerRef = new MethodRef(delegateObject.getClass(), initializer); + if (delegateObject instanceof Initializable) { + ((Initializable) delegateObject).initialize(); + } + else { + if (initializerRef == null) { + final String initializer = getInfo().getInitializer(); + initializerRef = new MethodRef(delegateObject.getClass(), initializer); + } + initializerRef.execute(delegateObject); } - initializerRef.execute(delegateObject); // execute individual module item initializers for (final ModuleItem item : getInfo().inputs()) { From dbcdc4f97d4cdbb2316b02f500feb83d9e46ade5 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 5 May 2017 14:27:15 -0500 Subject: [PATCH 005/754] CommandModuleTest: test Initializable Command This verifies that the initialize() method of a Command implementing the Initializable interface is called during module preprocessing. --- .../scijava/command/CommandModuleTest.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/test/java/org/scijava/command/CommandModuleTest.java b/src/test/java/org/scijava/command/CommandModuleTest.java index 1021d537c..76ee6bba7 100644 --- a/src/test/java/org/scijava/command/CommandModuleTest.java +++ b/src/test/java/org/scijava/command/CommandModuleTest.java @@ -42,6 +42,7 @@ import org.junit.Test; import org.scijava.Cancelable; import org.scijava.Context; +import org.scijava.Initializable; import org.scijava.ItemIO; import org.scijava.Priority; import org.scijava.log.LogService; @@ -127,6 +128,22 @@ public void testCommandInjection() throws InterruptedException, assertTrue((boolean) module.getOutput("success")); } + @Test + public void testInitializable() throws InterruptedException, + ExecutionException + { + final Context context = new Context(CommandService.class); + final CommandService commandService = context.service(CommandService.class); + + final CommandModule preprocessedModule = // + commandService.run(InitializableCommand.class, true).get(); + assertEquals(42, preprocessedModule.getOutput("output")); + + final CommandModule plainModule = // + commandService.run(InitializableCommand.class, false).get(); + assertEquals(7, plainModule.getOutput("output")); + } + // -- Helper classes -- /** A command which implements {@link Cancelable}. */ @@ -267,4 +284,20 @@ public void run() { } } + @Plugin(type = Command.class) + public static class InitializableCommand implements Command, Initializable { + private int magicNumber = 7; + + @Parameter(type = ItemIO.OUTPUT) + private int output; + + @Override + public void initialize() { + magicNumber = 42; + } + @Override + public void run() { + output = magicNumber; + } + } } From 743deaf4b47f2429f653f4d9cec6b8762a51614c Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 5 May 2017 14:41:03 -0500 Subject: [PATCH 006/754] Remove unused imports --- src/main/java/org/scijava/parse/DefaultParseService.java | 1 - src/test/java/org/scijava/command/CommandInfoTest.java | 1 - 2 files changed, 2 deletions(-) diff --git a/src/main/java/org/scijava/parse/DefaultParseService.java b/src/main/java/org/scijava/parse/DefaultParseService.java index 97ff4eb37..457a19ac9 100644 --- a/src/main/java/org/scijava/parse/DefaultParseService.java +++ b/src/main/java/org/scijava/parse/DefaultParseService.java @@ -36,7 +36,6 @@ import java.util.List; import java.util.Map; -import org.scijava.parse.Variable; import org.scijava.parse.eval.DefaultEvaluator; import org.scijava.plugin.Plugin; import org.scijava.service.AbstractService; diff --git a/src/test/java/org/scijava/command/CommandInfoTest.java b/src/test/java/org/scijava/command/CommandInfoTest.java index 7704f5378..35579a804 100644 --- a/src/test/java/org/scijava/command/CommandInfoTest.java +++ b/src/test/java/org/scijava/command/CommandInfoTest.java @@ -47,7 +47,6 @@ import org.scijava.command.CommandInfoTest.CommandWithEnumParam.Choice; import org.scijava.module.ModuleItem; import org.scijava.plugin.Parameter; -import org.scijava.plugin.Plugin; /** * Tests {@link CommandInfo}. From d90c3679a0c2daceaacd31d4fa409b766d65861a Mon Sep 17 00:00:00 2001 From: Hadrien Mary Date: Mon, 17 Apr 2017 23:18:10 -0400 Subject: [PATCH 007/754] Add autocompletion framework The default autocompletion works by inspecting attributes/methods of an object. Signed-off-by: Curtis Rueden --- .../scijava/script/AbstractAutoCompleter.java | 149 ++++++++++++++++++ .../org/scijava/script/AutoCompleter.java | 50 ++++++ .../scijava/script/AutoCompletionResult.java | 40 +++++ .../scijava/script/DefaultAutoCompleter.java | 44 ++++++ .../org/scijava/script/ScriptLanguage.java | 10 ++ 5 files changed, 293 insertions(+) create mode 100644 src/main/java/org/scijava/script/AbstractAutoCompleter.java create mode 100644 src/main/java/org/scijava/script/AutoCompleter.java create mode 100644 src/main/java/org/scijava/script/AutoCompletionResult.java create mode 100644 src/main/java/org/scijava/script/DefaultAutoCompleter.java diff --git a/src/main/java/org/scijava/script/AbstractAutoCompleter.java b/src/main/java/org/scijava/script/AbstractAutoCompleter.java new file mode 100644 index 000000000..545f4d5a5 --- /dev/null +++ b/src/main/java/org/scijava/script/AbstractAutoCompleter.java @@ -0,0 +1,149 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.script; + +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; + +import javax.script.Bindings; +import javax.script.ScriptContext; +import javax.script.ScriptEngine; + +/** + * Abstract base class for {@link AutoCompleter} implementations. + * + * @author Hadrien Mary + */ +public abstract class AbstractAutoCompleter implements AutoCompleter { + + @SuppressWarnings("unused") + private ScriptLanguage scriptLanguage; + + public AbstractAutoCompleter(final ScriptLanguage scriptLanguage) { + this.scriptLanguage = scriptLanguage; + } + + @Override + public AutoCompletionResult autocomplete(final String code, final int index, + final ScriptEngine engine) + { + + final List matches = new ArrayList<>(); + final int startIndex = 0; + + if (code.endsWith(".")) { + // Autocompletion with all the attributes of the object + matches.addAll(engineAttributesCompleter(code, index, engine)); + + } + else if (code.contains(".")) { + final List codeList = Arrays.asList(code.split("\\.")); + final String objectString = codeList.get(codeList.size() - 2); + final String fieldBeginWith = codeList.get(codeList.size() - 1); + matches.addAll(engineAttributesCompleter(objectString + ".", + fieldBeginWith, index, engine)); + + } + else { + // Autocompletion with variables in the engine scope + matches.addAll(engineVariablesCompleter(code, index, engine)); + } + + // Remove duplicates + matches.stream().distinct().collect(Collectors.toList()); + + // Sort alphabetically, ignoring case + Collections.sort(matches, new Comparator() { + + @Override + public int compare(final Object o1, final Object o2) { + final String s1 = (String) o1; + final String s2 = (String) o2; + return s1.toLowerCase().compareTo(s2.toLowerCase()); + } + }); + + // Return results. For now we ignore index and startIndex. + return new AutoCompletionResult(matches, startIndex); + } + + private List engineVariablesCompleter(final String code, + @SuppressWarnings("unused") final int index, final ScriptEngine engine) + { + final List matches = new ArrayList<>(); + + final Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); + + for (final String key : bindings.keySet()) { + if (key.toLowerCase().startsWith(code.toLowerCase())) { + matches.add(key); + } + } + return matches; + + } + + private List engineAttributesCompleter(final String objectString, + final int index, final ScriptEngine engine) + { + return engineAttributesCompleter(objectString, "", index, engine); + } + + private List engineAttributesCompleter(final String objectString, + final String fieldBeginWith, @SuppressWarnings("unused") final int index, + final ScriptEngine engine) + { + final List matches = new ArrayList<>(); + + final Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); + + for (final String key : bindings.keySet()) { + if (objectString.endsWith(key + ".")) { + final Object obj = bindings.get(key); + for (final Field field : obj.getClass().getDeclaredFields()) { + if (field.getName().toLowerCase().startsWith(fieldBeginWith + .toLowerCase())) + { + matches.add(objectString + field.getName()); + } + } + } + } + + return matches; + } +} diff --git a/src/main/java/org/scijava/script/AutoCompleter.java b/src/main/java/org/scijava/script/AutoCompleter.java new file mode 100644 index 000000000..55590081e --- /dev/null +++ b/src/main/java/org/scijava/script/AutoCompleter.java @@ -0,0 +1,50 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package org.scijava.script; + +import javax.script.ScriptEngine; + +/** + * Helper for generating autocomplete suggestions for code fragments. + * + * @author Hadrien Mary + */ +public interface AutoCompleter { + + default AutoCompletionResult autocomplete(final String code, + final ScriptEngine engine) + { + return autocomplete(code, 0, engine); + } + + AutoCompletionResult autocomplete(String code, int startIndex, + ScriptEngine engine); +} diff --git a/src/main/java/org/scijava/script/AutoCompletionResult.java b/src/main/java/org/scijava/script/AutoCompletionResult.java new file mode 100644 index 000000000..6ea4dbcc3 --- /dev/null +++ b/src/main/java/org/scijava/script/AutoCompletionResult.java @@ -0,0 +1,40 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ + +package org.scijava.script; + +import java.util.List; + +/** + * Data structure housing suggestions given by {@link AutoCompleter} helpers. + * + * @author Hadrien Mary + */ +public class AutoCompletionResult { + + private List matches; + private int startIndex; + + public AutoCompletionResult(final List matches) { + this(matches, 0); + } + + public AutoCompletionResult(final List matches, + final int startIndex) + { + this.matches = matches; + this.startIndex = startIndex; + } + + public int getStartIndex() { + return startIndex; + } + + public List getMatches() { + return matches; + } + +} diff --git a/src/main/java/org/scijava/script/DefaultAutoCompleter.java b/src/main/java/org/scijava/script/DefaultAutoCompleter.java new file mode 100644 index 000000000..9d2681d79 --- /dev/null +++ b/src/main/java/org/scijava/script/DefaultAutoCompleter.java @@ -0,0 +1,44 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.script; + +/** + * Default implementation of {@link AutoCompleter}. + * + * @author Hadrien Mary + */ +public class DefaultAutoCompleter extends AbstractAutoCompleter { + + public DefaultAutoCompleter(final ScriptLanguage scriptLanguage) { + super(scriptLanguage); + } +} diff --git a/src/main/java/org/scijava/script/ScriptLanguage.java b/src/main/java/org/scijava/script/ScriptLanguage.java index 14cf95866..2cdb474e1 100644 --- a/src/main/java/org/scijava/script/ScriptLanguage.java +++ b/src/main/java/org/scijava/script/ScriptLanguage.java @@ -65,6 +65,8 @@ *

    * * @author Johannes Schindelin + * @author Curtis Rueden + * @author Hadrien Mary */ public interface ScriptLanguage extends ScriptEngineFactory, RichPlugin, SingletonPlugin @@ -86,6 +88,14 @@ default Object decode(final Object object) { return object; } + /** + * Gets a helper object capable of generating autocomplete suggestions for a + * code fragment. + */ + default AutoCompleter getAutoCompleter() { + return new DefaultAutoCompleter(this); + } + // -- ScriptEngineFactory methods -- @Override From b24bd0d1f01b9dd0019f49f3d5326489bf354e99 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 5 May 2017 15:02:58 -0500 Subject: [PATCH 008/754] ScriptEngineTest: dispose contexts properly --- .../org/scijava/script/ScriptEngineTest.java | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/scijava/script/ScriptEngineTest.java b/src/test/java/org/scijava/script/ScriptEngineTest.java index bce9fe6f2..c21150c05 100644 --- a/src/test/java/org/scijava/script/ScriptEngineTest.java +++ b/src/test/java/org/scijava/script/ScriptEngineTest.java @@ -46,6 +46,8 @@ import javax.script.ScriptEngine; import javax.script.ScriptException; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.scijava.Context; import org.scijava.plugin.Plugin; @@ -54,13 +56,28 @@ * Basic tests for the {@link ScriptService}. * * @author Johannes Schindelin + * @author Curtis Rueden */ public class ScriptEngineTest { + private Context context; + private ScriptService scriptService; + + @Before + public void setUp() { + context = new Context(ScriptService.class); + scriptService = context.getService(ScriptService.class); + } + + @After + public void tearDown() { + context.dispose(); + context = null; + scriptService = null; + } + @Test public void testRot13() throws Exception { - final Context context = new Context(ScriptService.class); - final ScriptService scriptService = context.getService(ScriptService.class); final ScriptLanguage hello = scriptService.getLanguageByName("Hello"); assertNotNull(hello); final ScriptLanguage rot13 = scriptService.getLanguageByName("Rot13"); @@ -70,8 +87,6 @@ public void testRot13() throws Exception { @Test public void testScriptModuleValue() throws Exception { - final Context context = new Context(ScriptService.class); - final ScriptService scriptService = context.getService(ScriptService.class); final ScriptModule module = scriptService.run("test.rot13", ScriptModule.class.getName(), false, (Map) null).get(); From 4e30352e3a3f9b3993fa2bb1ddd4a9aaac02ddc5 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 5 May 2017 15:05:09 -0500 Subject: [PATCH 009/754] Autocomplete public fields only --- src/main/java/org/scijava/script/AbstractAutoCompleter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/script/AbstractAutoCompleter.java b/src/main/java/org/scijava/script/AbstractAutoCompleter.java index 545f4d5a5..4f21f77ad 100644 --- a/src/main/java/org/scijava/script/AbstractAutoCompleter.java +++ b/src/main/java/org/scijava/script/AbstractAutoCompleter.java @@ -134,7 +134,8 @@ private List engineAttributesCompleter(final String objectString, for (final String key : bindings.keySet()) { if (objectString.endsWith(key + ".")) { final Object obj = bindings.get(key); - for (final Field field : obj.getClass().getDeclaredFields()) { + // check for public field completions + for (final Field field : obj.getClass().getFields()) { if (field.getName().toLowerCase().startsWith(fieldBeginWith .toLowerCase())) { From 5e05056f5ff55d59e66b33583228cacdacce8962 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 5 May 2017 15:06:38 -0500 Subject: [PATCH 010/754] Refactor variable name to be more general It's not just a field name prefix -- it could be a prefix for something else. --- .../org/scijava/script/AbstractAutoCompleter.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/scijava/script/AbstractAutoCompleter.java b/src/main/java/org/scijava/script/AbstractAutoCompleter.java index 4f21f77ad..96db1ddf0 100644 --- a/src/main/java/org/scijava/script/AbstractAutoCompleter.java +++ b/src/main/java/org/scijava/script/AbstractAutoCompleter.java @@ -73,9 +73,9 @@ public AutoCompletionResult autocomplete(final String code, final int index, else if (code.contains(".")) { final List codeList = Arrays.asList(code.split("\\.")); final String objectString = codeList.get(codeList.size() - 2); - final String fieldBeginWith = codeList.get(codeList.size() - 1); + final String prefix = codeList.get(codeList.size() - 1); matches.addAll(engineAttributesCompleter(objectString + ".", - fieldBeginWith, index, engine)); + prefix, index, engine)); } else { @@ -124,10 +124,11 @@ private List engineAttributesCompleter(final String objectString, } private List engineAttributesCompleter(final String objectString, - final String fieldBeginWith, @SuppressWarnings("unused") final int index, + final String prefix, @SuppressWarnings("unused") final int index, final ScriptEngine engine) { final List matches = new ArrayList<>(); + final String lPrefix = prefix.toLowerCase(); final Bindings bindings = engine.getBindings(ScriptContext.ENGINE_SCOPE); @@ -136,9 +137,7 @@ private List engineAttributesCompleter(final String objectString, final Object obj = bindings.get(key); // check for public field completions for (final Field field : obj.getClass().getFields()) { - if (field.getName().toLowerCase().startsWith(fieldBeginWith - .toLowerCase())) - { + if (field.getName().toLowerCase().startsWith(lPrefix)) { matches.add(objectString + field.getName()); } } From 2ecfbef18a330cce6160c5131fb4447f8b896d37 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 5 May 2017 15:07:50 -0500 Subject: [PATCH 011/754] Autocomplete public methods, too --- .../java/org/scijava/script/AbstractAutoCompleter.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/org/scijava/script/AbstractAutoCompleter.java b/src/main/java/org/scijava/script/AbstractAutoCompleter.java index 96db1ddf0..b69bfca70 100644 --- a/src/main/java/org/scijava/script/AbstractAutoCompleter.java +++ b/src/main/java/org/scijava/script/AbstractAutoCompleter.java @@ -32,6 +32,7 @@ package org.scijava.script; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -141,6 +142,12 @@ private List engineAttributesCompleter(final String objectString, matches.add(objectString + field.getName()); } } + // check for public method completions + for (final Method method : obj.getClass().getMethods()) { + if (method.getName().toLowerCase().startsWith(lPrefix)) { + matches.add(objectString + method.getName() + "("); + } + } } } From cbe709755ce3053d592869e92bd2f811ea34f698 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 5 May 2017 20:46:33 -0500 Subject: [PATCH 012/754] AbstractAutoCompleter: fix duplicate removal logic --- .../java/org/scijava/script/AbstractAutoCompleter.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/scijava/script/AbstractAutoCompleter.java b/src/main/java/org/scijava/script/AbstractAutoCompleter.java index b69bfca70..5155ba784 100644 --- a/src/main/java/org/scijava/script/AbstractAutoCompleter.java +++ b/src/main/java/org/scijava/script/AbstractAutoCompleter.java @@ -85,10 +85,11 @@ else if (code.contains(".")) { } // Remove duplicates - matches.stream().distinct().collect(Collectors.toList()); + final List unique = // + matches.stream().distinct().collect(Collectors.toList()); // Sort alphabetically, ignoring case - Collections.sort(matches, new Comparator() { + Collections.sort(unique, new Comparator() { @Override public int compare(final Object o1, final Object o2) { @@ -99,7 +100,7 @@ public int compare(final Object o1, final Object o2) { }); // Return results. For now we ignore index and startIndex. - return new AutoCompletionResult(matches, startIndex); + return new AutoCompletionResult(unique, startIndex); } private List engineVariablesCompleter(final String code, From 514797099c95ce763a3edd9a0dc3761388863f44 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 5 May 2017 20:46:53 -0500 Subject: [PATCH 013/754] ScriptEngineTest: test the AutoCompleter logic --- .../org/scijava/script/ScriptEngineTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/test/java/org/scijava/script/ScriptEngineTest.java b/src/test/java/org/scijava/script/ScriptEngineTest.java index c21150c05..1f75dcfa6 100644 --- a/src/test/java/org/scijava/script/ScriptEngineTest.java +++ b/src/test/java/org/scijava/script/ScriptEngineTest.java @@ -97,6 +97,35 @@ public void testScriptModuleValue() throws Exception { assertEquals(context, info.context()); } + @Test + public void testAutoCompleter() { + final ScriptLanguage hello = scriptService.getLanguageByName("Hello"); + final ScriptEngine engine = hello.getScriptEngine(); + final AutoCompleter ac = hello.getAutoCompleter(); + + // test all matches + engine.put("thing", new Object()); + final AutoCompletionResult result = ac.autocomplete("thing.", engine); + assertEquals(0, result.getStartIndex()); + final List matches = result.getMatches(); + final List expected = Arrays.asList("thing.equals(", + "thing.getClass(", "thing.hashCode(", "thing.notify(", "thing.notifyAll(", + "thing.toString(", "thing.wait("); + assertEquals(matches, expected); + + // test prefix + engine.put("hello", "world"); + final AutoCompletionResult cWords = ac.autocomplete("hello.c", engine); + assertEquals(0, cWords.getStartIndex()); + final List cMatches = cWords.getMatches(); + final List cExpected = Arrays.asList("hello.CASE_INSENSITIVE_ORDER", + "hello.charAt(", "hello.chars(", "hello.codePointAt(", + "hello.codePointBefore(", "hello.codePointCount(", "hello.codePoints(", + "hello.compareTo(", "hello.compareToIgnoreCase(", "hello.concat(", + "hello.contains(", "hello.contentEquals(", "hello.copyValueOf("); + assertEquals(cMatches, cExpected); + } + @Plugin(type = ScriptLanguage.class) public static class Rot13 extends AbstractScriptLanguage { From 49c055b05f5d46340eeade887744ab72bbba7525 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 17 May 2017 08:08:25 -0500 Subject: [PATCH 014/754] Fix author tags --- src/main/java/org/scijava/event/ContextDisposingEvent.java | 2 +- src/test/java/org/scijava/util/ProcessUtilsTest.java | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/scijava/event/ContextDisposingEvent.java b/src/main/java/org/scijava/event/ContextDisposingEvent.java index 0dc1aaf34..bbe3fba40 100644 --- a/src/main/java/org/scijava/event/ContextDisposingEvent.java +++ b/src/main/java/org/scijava/event/ContextDisposingEvent.java @@ -34,6 +34,6 @@ /** * Event to be published just before disposing a context. * - * @author Johannes Schindein + * @author Johannes Schindelin */ public class ContextDisposingEvent extends SciJavaEvent { } diff --git a/src/test/java/org/scijava/util/ProcessUtilsTest.java b/src/test/java/org/scijava/util/ProcessUtilsTest.java index c2a4ae0b2..227703ca9 100644 --- a/src/test/java/org/scijava/util/ProcessUtilsTest.java +++ b/src/test/java/org/scijava/util/ProcessUtilsTest.java @@ -71,11 +71,7 @@ private void assumePOSIX() { assumeTrue(PlatformUtils.isPOSIX()); } - /** - * A class executing a 'sleep' call, to be interrupted. - * - * @author Johannes Schindelin - */ + /** A class executing a 'sleep' call, to be interrupted. */ private static class SleepThread extends Thread { private int seconds; private Throwable result; From 96ae9bed1c67b3832a8ac47aed3db6ffe037b2a7 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 17 May 2017 09:13:01 -0500 Subject: [PATCH 015/754] Add missing contributors All of these folks wrote code which ended up here in SJC. --- pom.xml | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index fd1a7ddea..c04cd8bcc 100644 --- a/pom.xml +++ b/pom.xml @@ -55,16 +55,15 @@ http://imagej.net/User:Schindelin dscho + + Chris Allan + callan + Barry DeZonia http://imagej.net/User:Bdezonia bdezonia - - Lee Kamentsky - http://imagej.net/User:Leek - LeeKamentsky - Christian Dietz http://imagej.net/User:Dietzc @@ -80,15 +79,49 @@ http://imagej.net/User:Gab1one gab1one + + Aivar Grislis + http://imagej.net/User:Grislis + grislis + Jonathan Hale Squareys + + Grant Harris + http://imagej.net/User:Harris + tnargsirrah + + + Lee Kamentsky + http://imagej.net/User:Leek + LeeKamentsky + + + Rick Lentz + http://imagej.net/User:Lentz + + + Melissa Linkert + http://imagej.net/User:Linkert + melissalinkert + Kevin Mader http://imagej.net/User:Ksmader kmader + + Hadrien Mary + http://imagej.net/User:Hadim + hadim + + + Alison Walter + http://imagej.net/User:Awalter2 + awalter17 + Jay Warrick jaywarrick From 418de99580412c34ab8afc0d50e637c9a6f7ef58 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Thu, 18 May 2017 10:31:19 -0500 Subject: [PATCH 016/754] ThreadService: improve thread safety We now use double-checked locking for initialization. Otherwise, conceivably, two threads could trigger simultaneous creation of competing ExecutorServices. And we also synchronize the disposal, so that the ExecutorService cannot possibly be created after dispose() is called. --- .../org/scijava/thread/DefaultThreadService.java | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/scijava/thread/DefaultThreadService.java b/src/main/java/org/scijava/thread/DefaultThreadService.java index 945a6fe43..1de7899fe 100644 --- a/src/main/java/org/scijava/thread/DefaultThreadService.java +++ b/src/main/java/org/scijava/thread/DefaultThreadService.java @@ -141,9 +141,12 @@ public ThreadContext getThreadContext(final Thread thread) { // -- Disposable methods -- @Override - public void dispose() { + public synchronized void dispose() { disposed = true; - if (executor != null) executor.shutdown(); + if (executor != null) { + executor.shutdown(); + executor = null; + } } // -- ThreadFactory methods -- @@ -157,12 +160,15 @@ public Thread newThread(final Runnable r) { // -- Helper methods -- private ExecutorService executor() { - if (executor == null) { - executor = Executors.newCachedThreadPool(this); - } + if (executor == null) initExecutor(); return executor; } + private synchronized void initExecutor() { + if (executor != null) return; + executor = Executors.newCachedThreadPool(this); + } + private Runnable wrap(final Runnable r) { final Thread parent = Thread.currentThread(); return new Runnable() { From 35ee2f494f9869bed55883181986a9af696b267c Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Thu, 18 May 2017 10:32:10 -0500 Subject: [PATCH 017/754] ThreadService: clarify javadoc --- src/main/java/org/scijava/thread/ThreadService.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/scijava/thread/ThreadService.java b/src/main/java/org/scijava/thread/ThreadService.java index 7be845d97..bbc03acfd 100644 --- a/src/main/java/org/scijava/thread/ThreadService.java +++ b/src/main/java/org/scijava/thread/ThreadService.java @@ -112,7 +112,7 @@ public enum ThreadContext { /** * Gets whether the current thread is a dispatch thread for use with - * {@link #invoke} and {@link #queue}. + * {@link #invoke(Runnable)} and {@link #queue(Runnable)}. *

    * In the case of AWT-based applications (e.g., Java on the desktop), this is * typically the AWT Event Dispatch Thread (EDT). However, ultimately the @@ -141,7 +141,8 @@ void invoke(Runnable code) throws InterruptedException, InvocationTargetException; /** - * Queues the given code for later execution in a special dispatch thread. + * Queues the given code for later execution in a special dispatch thread, + * returning immediately. *

    * In the case of AWT-based applications (e.g., Java on the desktop), this is * typically the AWT Event Dispatch Thread (EDT). However, ultimately the From ec4f6e0336148bd10f0fd3538d9b1cac216a9f82 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Thu, 18 May 2017 10:50:00 -0500 Subject: [PATCH 018/754] ThreadService: add ability to queue jobs flexibly Previously, you could only queue a job to the special dispatch thread (typically the EDT). But in some scenarios, the EDT is exactly the wrong one to use. This commit adds the capability to queue jobs to any arbitrarily named queue. Essentially, each ID corresponds to a different single thread processing its jobs one at a time. This commit is dedicated to Hadrien Mary. --- .../scijava/thread/DefaultThreadService.java | 30 +++++++++++++++++++ .../org/scijava/thread/ThreadService.java | 25 ++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/main/java/org/scijava/thread/DefaultThreadService.java b/src/main/java/org/scijava/thread/DefaultThreadService.java index 1de7899fe..88f36f893 100644 --- a/src/main/java/org/scijava/thread/DefaultThreadService.java +++ b/src/main/java/org/scijava/thread/DefaultThreadService.java @@ -33,6 +33,8 @@ import java.awt.EventQueue; import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; @@ -65,6 +67,9 @@ public final class DefaultThreadService extends AbstractService implements private ExecutorService executor; + /** Mapping from ID to single-thread {@link ExecutorService} queue. */ + private Map queues; + private int nextThread = 0; private boolean disposed; @@ -117,6 +122,16 @@ public void queue(final Runnable code) { EventQueue.invokeLater(wrap(code)); } + @Override + public Future queue(final String id, final Runnable code) { + return executor(id).submit(wrap(code)); + } + + @Override + public Future queue(final String id, final Callable code) { + return executor(id).submit(wrap(code)); + } + @Override public Thread getParent(final Thread thread) { return parents.get(thread != null ? thread : Thread.currentThread()); @@ -147,6 +162,11 @@ public synchronized void dispose() { executor.shutdown(); executor = null; } + if (queues != null) { + for (final ExecutorService queue : queues.values()) { + queue.shutdown(); + } + } } // -- ThreadFactory methods -- @@ -164,6 +184,16 @@ private ExecutorService executor() { return executor; } + private synchronized ExecutorService executor(final String id) { + if (disposed) return null; + if (queues == null) queues = new HashMap<>(); + if (!queues.containsKey(id)) { + final ExecutorService queue = Executors.newSingleThreadExecutor(); + queues.put(id, queue); + } + return queues.get(id); + } + private synchronized void initExecutor() { if (executor != null) return; executor = Executors.newCachedThreadPool(this); diff --git a/src/main/java/org/scijava/thread/ThreadService.java b/src/main/java/org/scijava/thread/ThreadService.java index bbc03acfd..9ae680fc2 100644 --- a/src/main/java/org/scijava/thread/ThreadService.java +++ b/src/main/java/org/scijava/thread/ThreadService.java @@ -153,6 +153,31 @@ void invoke(Runnable code) throws InterruptedException, */ void queue(Runnable code); + /** + * Queues the given code for later execution in a dispatch thread associated + * with the specified ID, returning immediately. + * + * @param id The ID designating which dispatch thread will execute the code. + * @param code The code to execute. + * @return A {@link Future} whose {@link Future#get()} method blocks until the + * queued code has completed executing and returns {@code null}. + * @see ExecutorService#submit(Runnable) + */ + Future queue(String id, Runnable code); + + /** + * Queues the given code for later execution in a dispatch thread associated + * with the specified ID, returning immediately. + * + * @param id The ID designating which dispatch thread will execute the code. + * @param code The code to execute. + * @return A {@link Future} whose {@link Future#get()} method blocks until the + * queued code has completed executing and returns the result of the + * execution. + * @see ExecutorService#submit(Callable) + */ + Future queue(String id, Callable code); + /** * Returns the thread that called the specified thread. *

    From 02b59b0398e408c18a3025b365e7bbc1c698ea72 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Thu, 18 May 2017 11:40:12 -0500 Subject: [PATCH 019/754] POM: bump the minor version The ThreadService has new API. --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index c04cd8bcc..d3e2e0eab 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ scijava-common - 2.62.2-SNAPSHOT + 2.63.0-SNAPSHOT SciJava Common SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO. From 6fa4c8e68d89c42c89ef3ed10fa70740903ddd62 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Thu, 18 May 2017 21:15:57 -0500 Subject: [PATCH 020/754] ThreadService: give job queue threads better names Now the thread names use the same convention as ThreadService#run, but instead of thread number suffixes, the suffix is the ID given. --- .../org/scijava/thread/DefaultThreadService.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/thread/DefaultThreadService.java b/src/main/java/org/scijava/thread/DefaultThreadService.java index 88f36f893..f16ec6beb 100644 --- a/src/main/java/org/scijava/thread/DefaultThreadService.java +++ b/src/main/java/org/scijava/thread/DefaultThreadService.java @@ -40,6 +40,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; +import java.util.concurrent.ThreadFactory; import org.scijava.log.LogService; import org.scijava.plugin.Parameter; @@ -188,7 +189,16 @@ private synchronized ExecutorService executor(final String id) { if (disposed) return null; if (queues == null) queues = new HashMap<>(); if (!queues.containsKey(id)) { - final ExecutorService queue = Executors.newSingleThreadExecutor(); + final ThreadFactory factory = new ThreadFactory() { + + @Override + public Thread newThread(final Runnable r) { + final String threadName = contextThreadPrefix() + id; + return new Thread(r, threadName); + } + + }; + final ExecutorService queue = Executors.newSingleThreadExecutor(factory); queues.put(id, queue); } return queues.get(id); From 0856f790d987b2c88861f78263b8f7175f65b441 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 17 May 2017 10:28:39 -0500 Subject: [PATCH 021/754] Deprecate CodeGenerator classes --- src/main/java/org/scijava/script/CodeGenerator.java | 2 ++ src/main/java/org/scijava/script/CodeGeneratorJava.java | 2 ++ src/main/java/org/scijava/script/InvocationObject.java | 2 ++ src/main/java/org/scijava/script/ParameterObject.java | 2 ++ 4 files changed, 8 insertions(+) diff --git a/src/main/java/org/scijava/script/CodeGenerator.java b/src/main/java/org/scijava/script/CodeGenerator.java index d79c7f6c4..12591841a 100644 --- a/src/main/java/org/scijava/script/CodeGenerator.java +++ b/src/main/java/org/scijava/script/CodeGenerator.java @@ -35,7 +35,9 @@ * Code Generator Interface * * @author Grant Harris + * @deprecated To be removed in SciJava Common 3.0.0. */ +@Deprecated public interface CodeGenerator { /** Adds delimiter character between arguments (typically a ','). */ diff --git a/src/main/java/org/scijava/script/CodeGeneratorJava.java b/src/main/java/org/scijava/script/CodeGeneratorJava.java index 750aa55b3..1dba40118 100644 --- a/src/main/java/org/scijava/script/CodeGeneratorJava.java +++ b/src/main/java/org/scijava/script/CodeGeneratorJava.java @@ -35,7 +35,9 @@ * {@link CodeGenerator} for Java. * * @author Grant Harris + * @deprecated To be removed in SciJava Common 3.0.0. */ +@Deprecated public class CodeGeneratorJava implements CodeGenerator { static final String lsep = System.getProperty("line.separator"); diff --git a/src/main/java/org/scijava/script/InvocationObject.java b/src/main/java/org/scijava/script/InvocationObject.java index 4ace6b083..639c33833 100644 --- a/src/main/java/org/scijava/script/InvocationObject.java +++ b/src/main/java/org/scijava/script/InvocationObject.java @@ -38,7 +38,9 @@ * the parameters that were passed to it. * * @author Grant Harris + * @deprecated To be removed in SciJava Common 3.0.0. */ +@Deprecated public class InvocationObject { public String moduleCalled; diff --git a/src/main/java/org/scijava/script/ParameterObject.java b/src/main/java/org/scijava/script/ParameterObject.java index 7f02b1d39..5073e147c 100644 --- a/src/main/java/org/scijava/script/ParameterObject.java +++ b/src/main/java/org/scijava/script/ParameterObject.java @@ -35,7 +35,9 @@ * Holds a parameter, its type and value, for a recorded macro. * * @author Grant Harris + * @deprecated To be removed in SciJava Common 3.0.0. */ +@Deprecated public class ParameterObject { public ParameterObject(final String param, final Class type, From 8c84c5660eadd1ec7539bcc0848590effefed47b Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 17 May 2017 10:32:41 -0500 Subject: [PATCH 022/754] Move internal class into DefaultScriptInterpreter --- .../script/DefaultScriptInterpreter.java | 110 +++++++++++++ src/main/java/org/scijava/script/History.java | 145 ------------------ 2 files changed, 110 insertions(+), 145 deletions(-) delete mode 100644 src/main/java/org/scijava/script/History.java diff --git a/src/main/java/org/scijava/script/DefaultScriptInterpreter.java b/src/main/java/org/scijava/script/DefaultScriptInterpreter.java index 543ea506a..4a2599713 100644 --- a/src/main/java/org/scijava/script/DefaultScriptInterpreter.java +++ b/src/main/java/org/scijava/script/DefaultScriptInterpreter.java @@ -42,6 +42,7 @@ import org.scijava.log.LogService; import org.scijava.plugin.Parameter; import org.scijava.prefs.PrefService; +import org.scijava.util.LastRecentlyUsed; /** * The default implementation of a {@link ScriptInterpreter}. @@ -363,4 +364,113 @@ private static T callMethod(final Object object, final String methodName, return null; } + // -- Helper classes -- + + /** Container for a script language's interpreter history. */ + private static class History { + + @SuppressWarnings("unused") + protected static final long serialVersionUID = 2L; + + private static final String PREFIX = "History."; + private final int MAX_ENTRIES = 1000; + + private final PrefService prefs; + private final String name; + private final LastRecentlyUsed entries = + new LastRecentlyUsed<>(MAX_ENTRIES); + private String currentCommand = ""; + private int position = -1; + + /** + * Constructs a history object for a given scripting language. + * + * @param name the name of the scripting language + */ + public History(final PrefService prefs, final String name) { + this.prefs = prefs; + this.name = name; + } + + /** + * Read back a persisted history. + */ + public void read() { + entries.clear(); + for (final String item : prefs.getIterable(getClass(), PREFIX + name)) { + entries.addToEnd(item); + } + } + + /** + * Persist the history. + * + * @see PrefService + */ + public void write() { + prefs.putIterable(getClass(), entries, PREFIX + name); + } + + /** + * Adds the most recently issued command. + * + * @param command the most recent command to add to the history + */ + public void add(final String command) { + entries.add(command); + position = -1; + currentCommand = ""; + } + + public boolean replace(final String command) { + if (position < 0) { + currentCommand = command; + return false; + } + return entries.replace(position, command); + } + + /** + * Navigates to the next (more recent) command. + *

    + * This method wraps around, i.e. it returns {@code null} when there is no + * more-recent command in the history. + *

    + * + * @return the next command + */ + public String next() { + position = entries.next(position); + return position < 0 ? currentCommand : entries.get(position); + } + + /** + * Navigates to the previous (i.e less recent) command. + *

    + * This method wraps around, i.e. it returns {@code null} when there is no + * less-recent command in the history. + *

    + * + * @return the previous command + */ + public String previous() { + position = entries.previous(position); + return position < 0 ? currentCommand : entries.get(position); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + int pos = -1; + for (;;) { + pos = entries.previous(pos); + if (pos < 0) break; + if (builder.length() > 0) builder.append(" -> "); + if (this.position == pos) builder.append("["); + builder.append(entries.get(pos)); + if (this.position == pos) builder.append("]"); + } + return builder.toString(); + } + } } diff --git a/src/main/java/org/scijava/script/History.java b/src/main/java/org/scijava/script/History.java deleted file mode 100644 index 648a466c9..000000000 --- a/src/main/java/org/scijava/script/History.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * #%L - * SciJava Common shared library for SciJava software. - * %% - * Copyright (C) 2009 - 2017 Board of Regents of the University of - * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck - * Institute of Molecular Cell Biology and Genetics. - * %% - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * #L% - */ - -package org.scijava.script; - -import org.scijava.prefs.PrefService; -import org.scijava.util.LastRecentlyUsed; - -/** - * Container for a script language's interpreter history. - * - * @author Johannes Schindelin - */ -class History { - - protected static final long serialVersionUID = 1L; - - private static final String PREFIX = "History."; - private final int MAX_ENTRIES = 1000; - - private final PrefService prefs; - private final String name; - private final LastRecentlyUsed entries = new LastRecentlyUsed<>(MAX_ENTRIES); - private String currentCommand = ""; - private int position = -1; - - /** - * Constructs a history object for a given scripting language. - * - * @param name the name of the scripting language - */ - public History(final PrefService prefs, final String name) { - this.prefs = prefs; - this.name = name; - } - - /** - * Read back a persisted history. - */ - public void read() { - entries.clear(); - for (final String item : prefs.getIterable(getClass(), PREFIX + name)) { - entries.addToEnd(item); - } - } - - /** - * Persist the history. - * - * @see PrefService - */ - public void write() { - prefs.putIterable(getClass(), entries, PREFIX + name); - } - - /** - * Adds the most recently issued command. - * - * @param command the most recent command to add to the history - */ - public void add(final String command) { - entries.add(command); - position = -1; - currentCommand = ""; - } - - public boolean replace(final String command) { - if (position < 0) { - currentCommand = command; - return false; - } - return entries.replace(position, command); - } - - /** - * Navigates to the next (more recent) command. - *

    - * This method wraps around, i.e. it returns {@code null} when there is no - * more-recent command in the history. - *

    - * - * @return the next command - */ - public String next() { - position = entries.next(position); - return position < 0 ? currentCommand : entries.get(position); - } - - /** - * Navigates to the previous (i.e less recent) command. - *

    - * This method wraps around, i.e. it returns {@code null} when there is no - * less-recent command in the history. - *

    - * - * @return the previous command - */ - public String previous() { - position = entries.previous(position); - return position < 0 ? currentCommand : entries.get(position); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - int pos = -1; - for (;;) { - pos = entries.previous(pos); - if (pos < 0) break; - if (builder.length() > 0) builder.append(" -> "); - if (this.position == pos) builder.append("["); - builder.append(entries.get(pos)); - if (this.position == pos) builder.append("]"); - } - return builder.toString(); - } -} From 2faeac5d4380196fd0545b83b443254f3ccb12c5 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 17 May 2017 10:33:42 -0500 Subject: [PATCH 023/754] Add missing license headers --- .../scijava/script/AbstractAutoCompleter.java | 4 +-- .../scijava/script/AutoCompletionResult.java | 33 ++++++++++++++++--- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/scijava/script/AbstractAutoCompleter.java b/src/main/java/org/scijava/script/AbstractAutoCompleter.java index 5155ba784..05956001a 100644 --- a/src/main/java/org/scijava/script/AbstractAutoCompleter.java +++ b/src/main/java/org/scijava/script/AbstractAutoCompleter.java @@ -8,13 +8,13 @@ * %% * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE diff --git a/src/main/java/org/scijava/script/AutoCompletionResult.java b/src/main/java/org/scijava/script/AutoCompletionResult.java index 6ea4dbcc3..ec48da96c 100644 --- a/src/main/java/org/scijava/script/AutoCompletionResult.java +++ b/src/main/java/org/scijava/script/AutoCompletionResult.java @@ -1,7 +1,32 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. +/*- + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% */ package org.scijava.script; From 55518586f39d9b58d2686bd8064b51c9d5f492da Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Thu, 18 May 2017 23:03:28 -0500 Subject: [PATCH 024/754] ScriptInfo: fix big bug in the return value logic The actual return value never got assigned to the implicit "result" output, because addReturnValue called addItem to add "result" as an output, which then mistakenly disabled the appendReturnValue flag. That flag should only be disabled when an _explicit_ output exists. --- src/main/java/org/scijava/script/ScriptInfo.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index d22e3902a..1b3d7f62e 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -432,7 +432,7 @@ private void parseParam(final String param, varName = tokens[1]; } final Class type = scriptService.lookupClass(typeName); - addItem(varName, type, attrs); + addItem(varName, type, attrs, true); if (ScriptModule.RETURN_VALUE.equals(varName)) { // NB: The return value variable is declared as an explicit OUTPUT. @@ -460,11 +460,11 @@ private void checkValid(final boolean valid, final String param) private void addReturnValue() { final HashMap attrs = new HashMap<>(); attrs.put("type", "OUTPUT"); - addItem(ScriptModule.RETURN_VALUE, Object.class, attrs); + addItem(ScriptModule.RETURN_VALUE, Object.class, attrs, false); } private void addItem(final String name, final Class type, - final Map attrs) + final Map attrs, final boolean explicit) { final DefaultMutableModuleItem item = new DefaultMutableModuleItem<>(this, name, type); @@ -477,7 +477,7 @@ private void addItem(final String name, final Class type, registerOutput(item); // NB: Only append the return value as an extra // output when no explicit outputs are declared. - appendReturnValue = false; + if (explicit) appendReturnValue = false; } } From 059e6993df31deb0270ea81185894861da98ec08 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 19 May 2017 19:02:02 -0500 Subject: [PATCH 025/754] Bump to next development cycle Signed-off-by: Curtis Rueden --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d3e2e0eab..08815add8 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ scijava-common - 2.63.0-SNAPSHOT + 2.63.1-SNAPSHOT SciJava Common SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO. From 15e232db8dd82cb0b7a81faad30805638bb1ac62 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Tue, 23 May 2017 07:18:17 -0500 Subject: [PATCH 026/754] PTService: fix javadoc The examples given are all from SciJava Common, not ImageJ. --- src/main/java/org/scijava/plugin/PTService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/plugin/PTService.java b/src/main/java/org/scijava/plugin/PTService.java index b131a8600..36a128498 100644 --- a/src/main/java/org/scijava/plugin/PTService.java +++ b/src/main/java/org/scijava/plugin/PTService.java @@ -40,7 +40,7 @@ *

    * There are many kinds of services, but most of them share one common * characteristic: they provide API specific to a particular type of plugin. A - * few examples from ImageJ: + * few examples: *

    *
      *
    • The {@link org.scijava.command.CommandService} works with From ed9cb477312cb9bee4c2ba5bc9ca5d4c25817ca8 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 22 May 2017 13:52:09 -0500 Subject: [PATCH 027/754] Move ScriptModule#getLanguage() into ScriptInfo For a given script, which language to use is a constant. It should be part of the script _metadata_, not the script module _instance_. --- pom.xml | 2 +- .../java/org/scijava/script/ScriptInfo.java | 19 ++++++++++ .../java/org/scijava/script/ScriptModule.java | 37 ++++++++----------- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/pom.xml b/pom.xml index 08815add8..9a0b01354 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ scijava-common - 2.63.1-SNAPSHOT + 2.64.0-SNAPSHOT SciJava Common SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO. diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index 1b3d7f62e..c3668767e 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -101,6 +101,9 @@ public class ScriptInfo extends AbstractModuleInfo implements Contextual { /** True iff the return value should be appended as an output. */ private boolean appendReturnValue; + /** Script language in which the script should be executed. */ + private ScriptLanguage scriptLanguage; + /** * Creates a script metadata object which describes the given script file. * @@ -224,6 +227,22 @@ public BufferedReader getReader() { return new BufferedReader(new StringReader(script), PARAM_CHAR_MAX); } + /** Gets the scripting language of the script. */ + public ScriptLanguage getLanguage() { + if (scriptLanguage == null) { + // infer the language from the script path's extension + final String scriptPath = getPath(); + final String extension = FileUtils.getExtension(scriptPath); + scriptLanguage = scriptService.getLanguageByExtension(extension); + } + return scriptLanguage; + } + + /** Overrides the script language to use when executing the script. */ + public void setLanguage(final ScriptLanguage scriptLanguage) { + this.scriptLanguage = scriptLanguage; + } + /** * Parses the script's input and output parameters from the script header. *

      diff --git a/src/main/java/org/scijava/script/ScriptModule.java b/src/main/java/org/scijava/script/ScriptModule.java index 92cf3ced9..d00b7ab5e 100644 --- a/src/main/java/org/scijava/script/ScriptModule.java +++ b/src/main/java/org/scijava/script/ScriptModule.java @@ -50,7 +50,6 @@ import org.scijava.module.Module; import org.scijava.module.ModuleItem; import org.scijava.plugin.Parameter; -import org.scijava.util.FileUtils; /** * A {@link Module} which executes a script. @@ -76,9 +75,6 @@ public class ScriptModule extends AbstractModule implements Contextual { @Parameter private LogService log; - /** Script language in which the script should be executed. */ - private ScriptLanguage scriptLanguage; - /** Script engine with which the script should be executed. */ private ScriptEngine scriptEngine; @@ -96,22 +92,6 @@ public ScriptModule(final ScriptInfo info) { // -- ScriptModule methods -- - /** Gets the scripting language of the script. */ - public ScriptLanguage getLanguage() { - if (scriptLanguage == null) { - // infer the language from the script path's extension - final String path = getInfo().getPath(); - final String extension = FileUtils.getExtension(path); - scriptLanguage = scriptService.getLanguageByExtension(extension); - } - return scriptLanguage; - } - - /** Overrides the script language to use when executing the script. */ - public void setLanguage(final ScriptLanguage scriptLanguage) { - this.scriptLanguage = scriptLanguage; - } - /** Sets the writer used to record the standard output stream. */ public void setOutputWriter(final Writer output) { this.output = output; @@ -125,7 +105,7 @@ public void setErrorWriter(final Writer error) { /** Gets the script engine used to execute the script. */ public ScriptEngine getEngine() { if (scriptEngine == null) { - scriptEngine = getLanguage().getScriptEngine(); + scriptEngine = getInfo().getLanguage().getScriptEngine(); } return scriptEngine; } @@ -185,7 +165,7 @@ public void run() { } // populate output values - final ScriptLanguage language = getLanguage(); + final ScriptLanguage language = getInfo().getLanguage(); for (final ModuleItem item : getInfo().outputs()) { final String name = item.getName(); final Object value; @@ -230,4 +210,17 @@ public void setContext(final Context context) { context.inject(this); } + // -- Deprecated methods -- + + /** @deprecated Use {@link ScriptInfo#getLanguage()} instead. */ + @Deprecated + public ScriptLanguage getLanguage() { + return getInfo().getLanguage(); + } + + /** @deprecated Use {@link ScriptInfo#setLanguage(ScriptLanguage)} instead. */ + @Deprecated + public void setLanguage(final ScriptLanguage scriptLanguage) { + getInfo().setLanguage(scriptLanguage); + } } From e467c5d30435c5614974afabd2fc0819aac6b09d Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 22 May 2017 13:53:06 -0500 Subject: [PATCH 028/754] ScriptInfo: add fallback code for null url & path Now, the URL and (psuedo-)path can both be null. If that happens, the getURL() and getPath() methods will both return null. And the ScriptLanguage will be detected as the highest priority plugin available (typically Groovy, but depends on runtime classpath). --- .../java/org/scijava/script/ScriptInfo.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index c3668767e..768a9ee23 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -232,8 +232,16 @@ public ScriptLanguage getLanguage() { if (scriptLanguage == null) { // infer the language from the script path's extension final String scriptPath = getPath(); - final String extension = FileUtils.getExtension(scriptPath); - scriptLanguage = scriptService.getLanguageByExtension(extension); + if (scriptPath != null) { + // use language associated with the script path extension + final String extension = FileUtils.getExtension(scriptPath); + scriptLanguage = scriptService.getLanguageByExtension(extension); + } + else { + // use the highest priority language + final List langs = scriptService.getLanguages(); + if (langs != null && !langs.isEmpty()) scriptLanguage = langs.get(0); + } } return scriptLanguage; } @@ -371,7 +379,7 @@ public void setContext(final Context context) { @Override public String getIdentifier() { - return "script:" + path; + return "script:" + (path == null ? "" : path); } // -- Locatable methods -- @@ -403,6 +411,7 @@ public String getVersion() { private URL url(final URL u, final String p) { if (u != null) return u; + if (p == null) return null; try { return new File(p).toURI().toURL(); } @@ -413,7 +422,8 @@ private URL url(final URL u, final String p) { } private String path(final URL u, final String p) { - return p == null ? u.getPath() : p; + if (p != null) return p; + return u == null ? null : u.getPath(); } private void parseParam(final String param) throws ScriptException { From bebaf6b179ae77402bfd2d6212d5407511901a9f Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 22 May 2017 15:18:24 -0500 Subject: [PATCH 029/754] ScriptInfo: relocate isReturnValueAppended method See: https://imagej.net/Coding_style#Ordering_of_code_blocks --- src/main/java/org/scijava/script/ScriptInfo.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index 768a9ee23..b5f4ccdbc 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -251,6 +251,13 @@ public void setLanguage(final ScriptLanguage scriptLanguage) { this.scriptLanguage = scriptLanguage; } + /** Gets whether the return value is appended as an additional output. */ + public boolean isReturnValueAppended() { + return appendReturnValue; + } + + // -- AbstractModuleInfo methods -- + /** * Parses the script's input and output parameters from the script header. *

      @@ -335,11 +342,6 @@ public void parseParameters() { } } - /** Gets whether the return value is appended as an additional output. */ - public boolean isReturnValueAppended() { - return appendReturnValue; - } - // -- ModuleInfo methods -- @Override From 012b00d4fe92587973e304c264081a7749b93c31 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 22 May 2017 15:19:04 -0500 Subject: [PATCH 030/754] ScriptInfo: add setter for appendReturnValue This matches the getter isReturnValueAppended. It will be useful when we externalize the parameter parsing logic. --- src/main/java/org/scijava/script/ScriptInfo.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index b5f4ccdbc..1e36139c2 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -256,6 +256,11 @@ public boolean isReturnValueAppended() { return appendReturnValue; } + /** Gets whether the return value is appended as an additional output. */ + public void setReturnValueAppended(final boolean appendReturnValue) { + this.appendReturnValue = appendReturnValue; + } + // -- AbstractModuleInfo methods -- /** From 27423a8874f5d2d4c55ec7bf28cb7228c5ca2ea9 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 22 May 2017 15:20:41 -0500 Subject: [PATCH 031/754] ScriptInfo: widen visibility of parameter methods We will need to be able to manipulate a ScriptInfo's inputs and outputs from external code, as part of the parameter parsing externalization. --- .../java/org/scijava/script/ScriptInfo.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index 1e36139c2..844f6d912 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -60,6 +60,7 @@ import org.scijava.module.AbstractModuleInfo; import org.scijava.module.DefaultMutableModuleItem; import org.scijava.module.ModuleException; +import org.scijava.module.ModuleItem; import org.scijava.parse.ParseService; import org.scijava.plugin.Parameter; import org.scijava.util.DigestUtils; @@ -347,6 +348,24 @@ public void parseParameters() { } } + // NB: Widened visibility from AbstractModuleInfo. + @Override + public void clearParameters() { + super.clearParameters(); + } + + // NB: Widened visibility from AbstractModuleInfo. + @Override + public void registerInput(final ModuleItem input) { + super.registerInput(input); + } + + // NB: Widened visibility from AbstractModuleInfo. + @Override + public void registerOutput(final ModuleItem output) { + super.registerOutput(output); + } + // -- ModuleInfo methods -- @Override From bcb24546ac3aba8db49801141f70b778f8ec6425 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 22 May 2017 13:27:19 -0500 Subject: [PATCH 032/754] Add service and plugin type to process scripts This will be useful for implementing script processing directives in an extensible way. Here is one example, for dependency declaration: #@repository('https://maven.imagej.net/content/groups/public') #@dependency('net.imagej:imagej:2.0.0-rc-60') And here is another, for defining the intended script language: #!clojure We will also migrate the script parameter syntax to this scheme: #@input String name #@input int age #@output String greeting Or with the familiar sloppy shorthand: #@String name #@int age #@output String greeting See #265 See https://github.com/hadim/scijava-jupyter-kernel/issues/51#issuecomment-301816226 --- .../scijava/script/DefaultScriptService.java | 1 + .../DefaultScriptProcessorService.java | 49 ++++++++++ .../script/process/ScriptProcessor.java | 54 +++++++++++ .../process/ScriptProcessorService.java | 93 +++++++++++++++++++ .../java/org/scijava/ContextCreationTest.java | 1 + 5 files changed, 198 insertions(+) create mode 100644 src/main/java/org/scijava/script/process/DefaultScriptProcessorService.java create mode 100644 src/main/java/org/scijava/script/process/ScriptProcessor.java create mode 100644 src/main/java/org/scijava/script/process/ScriptProcessorService.java diff --git a/src/main/java/org/scijava/script/DefaultScriptService.java b/src/main/java/org/scijava/script/DefaultScriptService.java index 41ec47dab..9480cf863 100644 --- a/src/main/java/org/scijava/script/DefaultScriptService.java +++ b/src/main/java/org/scijava/script/DefaultScriptService.java @@ -62,6 +62,7 @@ import org.scijava.plugin.Plugin; import org.scijava.plugin.PluginService; import org.scijava.plugin.SciJavaPlugin; +import org.scijava.script.process.ScriptProcessorService; import org.scijava.service.Service; import org.scijava.util.ClassUtils; import org.scijava.util.ColorRGB; diff --git a/src/main/java/org/scijava/script/process/DefaultScriptProcessorService.java b/src/main/java/org/scijava/script/process/DefaultScriptProcessorService.java new file mode 100644 index 000000000..3cbce02e3 --- /dev/null +++ b/src/main/java/org/scijava/script/process/DefaultScriptProcessorService.java @@ -0,0 +1,49 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.script.process; + +import org.scijava.plugin.AbstractSingletonService; +import org.scijava.plugin.Plugin; +import org.scijava.service.Service; + +/** + * Default implementation of {@link ScriptProcessorService}. + * + * @author Curtis Rueden + */ +@Plugin(type = Service.class) +public class DefaultScriptProcessorService extends + AbstractSingletonService implements + ScriptProcessorService +{ + // NB: No implementation needed. +} diff --git a/src/main/java/org/scijava/script/process/ScriptProcessor.java b/src/main/java/org/scijava/script/process/ScriptProcessor.java new file mode 100644 index 000000000..3d412d074 --- /dev/null +++ b/src/main/java/org/scijava/script/process/ScriptProcessor.java @@ -0,0 +1,54 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.script.process; + +import org.scijava.plugin.SingletonPlugin; +import org.scijava.script.ScriptInfo; + +/** + * A script processor defines some sort of processing that primes a particular + * script for execution. + *

      + * Typically, these plugins look for special directives in the script itself + * beginning with distinctive character sequences like {@code #@}, and then + * perform some action in response. + *

      + * + * @author Curtis Rueden + */ +public interface ScriptProcessor extends SingletonPlugin { + + void begin(ScriptInfo info); + void process(String line); + default void end() {} + +} diff --git a/src/main/java/org/scijava/script/process/ScriptProcessorService.java b/src/main/java/org/scijava/script/process/ScriptProcessorService.java new file mode 100644 index 000000000..2f1d431cf --- /dev/null +++ b/src/main/java/org/scijava/script/process/ScriptProcessorService.java @@ -0,0 +1,93 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.script.process; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +import org.scijava.plugin.PTService; +import org.scijava.script.ScriptInfo; +import org.scijava.service.SciJavaService; + +/** + * Interface for service that processes scripts. This service discovers + * available {@link ScriptProcessor} plugins, and provides convenience methods + * to interact with them. + * + * @author Curtis Rueden + */ +public interface ScriptProcessorService extends + PTService, SciJavaService +{ + + /** + * Invokes all {@link ScriptProcessor} plugins on the given script, line by + * line in sequence. + */ + default void process(final ScriptInfo info) throws IOException { + final List processors = getPlugins().stream().map( + p -> pluginService().createInstance(p)).collect(Collectors.toList()); + + BufferedReader reader = info.getReader(); + if (reader == null) { + reader = new BufferedReader(new FileReader(info.getPath())); + } + + for (final ScriptProcessor p : processors) { + p.begin(info); + } + + try (final BufferedReader in = reader) { + while (true) { + final String line = in.readLine(); + if (line == null) break; + for (final ScriptProcessor p : processors) { + p.process(line); + } + } + } + + for (final ScriptProcessor p : processors) { + p.end(); + } + } + + // -- PTService methods -- + + @Override + default Class getPluginType() { + return ScriptProcessor.class; + } +} diff --git a/src/test/java/org/scijava/ContextCreationTest.java b/src/test/java/org/scijava/ContextCreationTest.java index 8de76276e..5dc0ed1b4 100644 --- a/src/test/java/org/scijava/ContextCreationTest.java +++ b/src/test/java/org/scijava/ContextCreationTest.java @@ -109,6 +109,7 @@ public void testFull() { org.scijava.prefs.DefaultPrefService.class, org.scijava.run.DefaultRunService.class, org.scijava.script.DefaultScriptHeaderService.class, + org.scijava.script.process.DefaultScriptProcessorService.class, org.scijava.text.DefaultTextService.class, org.scijava.thread.DefaultThreadService.class, org.scijava.tool.DefaultToolService.class, From 50d4997c3bae8a437a6aa691bcfc8c01c4093a78 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 22 May 2017 15:21:56 -0500 Subject: [PATCH 033/754] Externalize the script parameter parsing logic It now lives in a new ParameterScriptProcessor. For the moment, script processing is triggered internally by the ScriptInfo's parseParameters method, which is a bit hacky. --- .../scijava/script/DefaultScriptService.java | 3 + .../java/org/scijava/script/ScriptInfo.java | 241 +-------------- .../process/ParameterScriptProcessor.java | 286 ++++++++++++++++++ 3 files changed, 300 insertions(+), 230 deletions(-) create mode 100644 src/main/java/org/scijava/script/process/ParameterScriptProcessor.java diff --git a/src/main/java/org/scijava/script/DefaultScriptService.java b/src/main/java/org/scijava/script/DefaultScriptService.java index 9480cf863..f63eb7938 100644 --- a/src/main/java/org/scijava/script/DefaultScriptService.java +++ b/src/main/java/org/scijava/script/DefaultScriptService.java @@ -91,6 +91,9 @@ public class DefaultScriptService extends @Parameter private AppService appService; + @Parameter + private ScriptProcessorService scriptProcessorService; + @Parameter private ParseService parser; diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index 844f6d912..fb3859b2b 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -33,7 +33,6 @@ import java.io.BufferedReader; import java.io.File; -import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; @@ -41,37 +40,24 @@ import java.net.MalformedURLException; import java.net.URL; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Date; -import java.util.HashMap; import java.util.List; -import java.util.Map; - -import javax.script.ScriptException; import org.scijava.Context; import org.scijava.Contextual; -import org.scijava.ItemIO; -import org.scijava.ItemVisibility; import org.scijava.NullContextException; -import org.scijava.command.Command; -import org.scijava.convert.ConvertService; import org.scijava.log.LogService; import org.scijava.module.AbstractModuleInfo; -import org.scijava.module.DefaultMutableModuleItem; import org.scijava.module.ModuleException; import org.scijava.module.ModuleItem; -import org.scijava.parse.ParseService; import org.scijava.plugin.Parameter; +import org.scijava.script.process.ParameterScriptProcessor; +import org.scijava.script.process.ScriptProcessorService; import org.scijava.util.DigestUtils; import org.scijava.util.FileUtils; /** * Metadata about a script. - *

      - * This class is responsible for parsing the script for parameters. See - * {@link #parseParameters()} for details. - *

      * * @author Curtis Rueden * @author Johannes Schindelin @@ -94,10 +80,7 @@ public class ScriptInfo extends AbstractModuleInfo implements Contextual { private ScriptService scriptService; @Parameter - private ParseService parser; - - @Parameter - private ConvertService convertService; + private ScriptProcessorService scriptProcessorService; /** True iff the return value should be appended as an output. */ private boolean appendReturnValue; @@ -265,86 +248,21 @@ public void setReturnValueAppended(final boolean appendReturnValue) { // -- AbstractModuleInfo methods -- /** - * Parses the script's input and output parameters from the script header. - *

      - * This method is called automatically the first time any parameter accessor - * method is called ({@link #getInput}, {@link #getOutput}, {@link #inputs()}, - * {@link #outputs()}, etc.). Subsequent calls will reparse the parameters. - *

      - * SciJava's scripting framework supports specifying @{@link Parameter}-style - * inputs and outputs in a preamble. The format is a simplified version of the - * Java @{@link Parameter} annotation syntax. The following syntaxes are - * supported: - *

      - *
        - *
      • {@code // @ }
      • - *
      • {@code // @(=, ..., =) } - *
      • - *
      • {@code // @ }
      • - *
      • {@code // @(=, ..., =) - * }
      • - *
      - *

      - * Where: - *

      - *
        - *
      • {@code //} = the comment style of the scripting language, so that the - * parameter line is ignored by the script engine itself.
      • - *
      • {@code } = one of {@code INPUT}, {@code OUTPUT}, or - * {@code BOTH}.
      • - *
      • {@code } = the name of the input or output variable.
      • - *
      • {@code } = the Java {@link Class} of the variable.
      • - *
      • {@code } = an attribute key.
      • - *
      • {@code } = an attribute value.
      • - *
      - *

      - * See the @{@link Parameter} annotation for a list of valid attributes. - *

      - *

      - * Here are a few examples: - *

      - *
        - *
      • {@code // @Dataset dataset}
      • - *
      • {@code // @double(type=OUTPUT) result}
      • - *
      • {@code // @BOTH ImageDisplay display}
      • - *
      • {@code // @INPUT(persist=false, visibility=INVISIBLE) boolean verbose} - *
      • - *
      - *

      - * Parameters will be parsed and filled just like @{@link Parameter}-annotated - * fields in {@link Command}s. - *

      + * Performs script processing. In particular, parses the script parameters. + * + * @see ParameterScriptProcessor + * @see ScriptProcessorService#process */ // NB: Widened visibility from AbstractModuleInfo. @Override public void parseParameters() { clearParameters(); - appendReturnValue = true; - - try (final BufferedReader in = script == null ? // - new BufferedReader(new FileReader(getPath())) : getReader()) // - { - while (true) { - final String line = in.readLine(); - if (line == null) break; - - // NB: Scan for lines containing an '@' with no prior alphameric - // characters. This assumes that only non-alphanumeric characters can - // be used as comment line markers. - if (line.matches("^[^\\w]*@.*")) { - final int at = line.indexOf('@'); - parseParam(line.substring(at + 1)); - } - else if (line.matches(".*\\w.*")) break; - } - - if (appendReturnValue) addReturnValue(); + try { + scriptProcessorService.process(this); } catch (final IOException exc) { - log.error("Error reading script: " + path, exc); - } - catch (final ScriptException exc) { - log.error("Invalid parameter syntax for script: " + path, exc); + // TODO: Consider a better error handling approach. + throw new RuntimeException(exc); } } @@ -452,143 +370,6 @@ private String path(final URL u, final String p) { return u == null ? null : u.getPath(); } - private void parseParam(final String param) throws ScriptException { - final int lParen = param.indexOf("("); - final int rParen = param.lastIndexOf(")"); - if (rParen < lParen) { - throw new ScriptException("Invalid parameter: " + param); - } - if (lParen < 0) parseParam(param, parseAttrs("()")); - else { - final String cutParam = - param.substring(0, lParen) + param.substring(rParen + 1); - final String attrs = param.substring(lParen + 1, rParen); - parseParam(cutParam, parseAttrs(attrs)); - } - } - - private void parseParam(final String param, - final Map attrs) throws ScriptException - { - final String[] tokens = param.trim().split("[ \t\n]+"); - checkValid(tokens.length >= 1, param); - final String typeName, varName; - if (isIOType(tokens[0])) { - // assume syntax: - checkValid(tokens.length >= 3, param); - attrs.put("type", tokens[0]); - typeName = tokens[1]; - varName = tokens[2]; - } - else { - // assume syntax: - checkValid(tokens.length >= 2, param); - typeName = tokens[0]; - varName = tokens[1]; - } - final Class type = scriptService.lookupClass(typeName); - addItem(varName, type, attrs, true); - - if (ScriptModule.RETURN_VALUE.equals(varName)) { - // NB: The return value variable is declared as an explicit OUTPUT. - // So we should not append the return value as an extra output. - appendReturnValue = false; - } - } - - /** Parses a comma-delimited list of {@code key=value} pairs into a map. */ - private Map parseAttrs(final String attrs) { - return parser.parse(attrs, false).asMap(); - } - - private boolean isIOType(final String token) { - return convertService.convert(token, ItemIO.class) != null; - } - - private void checkValid(final boolean valid, final String param) - throws ScriptException - { - if (!valid) throw new ScriptException("Invalid parameter: " + param); - } - - /** Adds an output for the value returned by the script itself. */ - private void addReturnValue() { - final HashMap attrs = new HashMap<>(); - attrs.put("type", "OUTPUT"); - addItem(ScriptModule.RETURN_VALUE, Object.class, attrs, false); - } - - private void addItem(final String name, final Class type, - final Map attrs, final boolean explicit) - { - final DefaultMutableModuleItem item = - new DefaultMutableModuleItem<>(this, name, type); - for (final String key : attrs.keySet()) { - final Object value = attrs.get(key); - assignAttribute(item, key, value); - } - if (item.isInput()) registerInput(item); - if (item.isOutput()) { - registerOutput(item); - // NB: Only append the return value as an extra - // output when no explicit outputs are declared. - if (explicit) appendReturnValue = false; - } - } - - private void assignAttribute(final DefaultMutableModuleItem item, - final String k, final Object v) - { - // CTR: There must be an easier way to do this. - // Just compile the thing using javac? Or parse via javascript, maybe? - if (is(k, "callback")) item.setCallback(as(v, String.class)); - else if (is(k, "choices")) item.setChoices(asList(v, item.getType())); - else if (is(k, "columns")) item.setColumnCount(as(v, int.class)); - else if (is(k, "description")) item.setDescription(as(v, String.class)); - else if (is(k, "initializer")) item.setInitializer(as(v, String.class)); - else if (is(k, "validater")) item.setValidater(as(v, String.class)); - else if (is(k, "type")) item.setIOType(as(v, ItemIO.class)); - else if (is(k, "label")) item.setLabel(as(v, String.class)); - else if (is(k, "max")) item.setMaximumValue(as(v, item.getType())); - else if (is(k, "min")) item.setMinimumValue(as(v, item.getType())); - else if (is(k, "name")) item.setName(as(v, String.class)); - else if (is(k, "persist")) item.setPersisted(as(v, boolean.class)); - else if (is(k, "persistKey")) item.setPersistKey(as(v, String.class)); - else if (is(k, "required")) item.setRequired(as(v, boolean.class)); - else if (is(k, "softMax")) item.setSoftMaximum(as(v, item.getType())); - else if (is(k, "softMin")) item.setSoftMinimum(as(v, item.getType())); - else if (is(k, "stepSize")) item.setStepSize(as(v, double.class)); - else if (is(k, "style")) item.setWidgetStyle(as(v, String.class)); - else if (is(k, "visibility")) item.setVisibility(as(v, ItemVisibility.class)); - else if (is(k, "value")) item.setDefaultValue(as(v, item.getType())); - else item.set(k, v.toString()); - } - - /** Super terse comparison helper method. */ - private boolean is(final String key, final String desired) { - return desired.equalsIgnoreCase(key); - } - - /** Super terse conversion helper method. */ - private T as(final Object v, final Class type) { - final T converted = convertService.convert(v, type); - if (converted != null) return converted; - // NB: Attempt to convert via string. - // This is useful in cases where a weird type of object came back - // (e.g., org.scijava.parse.eval.Unresolved), but which happens to have a - // nice string representation which ultimately is expressible as the type. - return convertService.convert(v.toString(), type); - } - - private List asList(final Object v, final Class type) { - final ArrayList result = new ArrayList<>(); - final List list = as(v, List.class); - for (final Object item : list) { - result.add(as(item, type)); - } - return result; - } - /** * Read entire contents of a Reader and return as String. * diff --git a/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java b/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java new file mode 100644 index 000000000..9652e3f77 --- /dev/null +++ b/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java @@ -0,0 +1,286 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.script.process; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.script.ScriptException; + +import org.scijava.ItemIO; +import org.scijava.ItemVisibility; +import org.scijava.command.Command; +import org.scijava.convert.ConvertService; +import org.scijava.log.LogService; +import org.scijava.module.DefaultMutableModuleItem; +import org.scijava.parse.ParseService; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; +import org.scijava.script.ScriptInfo; +import org.scijava.script.ScriptModule; +import org.scijava.script.ScriptService; + +/** + * A {@link ScriptProcessor} which parses the script's input and output + * parameters from the script header. + *

      + * SciJava's scripting framework supports specifying @{@link Parameter}-style + * inputs and outputs in a preamble. The format is a simplified version of the + * Java @{@link Parameter} annotation syntax. The following syntaxes are + * supported: + *

      + *
        + *
      • {@code // @ }
      • + *
      • {@code // @(=, ..., =) } + *
      • + *
      • {@code // @ }
      • + *
      • {@code // @(=, ..., =) + * }
      • + *
      + *

      + * Where: + *

      + *
        + *
      • {@code //} = the comment style of the scripting language, so that the + * parameter line is ignored by the script engine itself.
      • + *
      • {@code } = one of {@code INPUT}, {@code OUTPUT}, or {@code BOTH}. + *
      • + *
      • {@code } = the name of the input or output variable.
      • + *
      • {@code } = the Java {@link Class} of the variable.
      • + *
      • {@code } = an attribute key.
      • + *
      • {@code } = an attribute value.
      • + *
      + *

      + * See the @{@link Parameter} annotation for a list of valid attributes. + *

      + *

      + * Here are a few examples: + *

      + *
        + *
      • {@code // @Dataset dataset}
      • + *
      • {@code // @double(type=OUTPUT) result}
      • + *
      • {@code // @BOTH ImageDisplay display}
      • + *
      • {@code // @INPUT(persist=false, visibility=INVISIBLE) boolean verbose} + *
      • + *
      + *

      + * Parameters will be parsed and filled just like @{@link Parameter}-annotated + * fields in {@link Command}s. + *

      + * + * @author Curtis Rueden + */ +@Plugin(type = ScriptProcessor.class) +public class ParameterScriptProcessor implements ScriptProcessor { + + @Parameter + private ScriptService scriptService; + + @Parameter + private ConvertService convertService; + + @Parameter + private ParseService parser; + + @Parameter + private LogService log; + + private ScriptInfo info; + private boolean header = true; + + // -- ScriptProcessor methods -- + + @Override + public void begin(final ScriptInfo scriptInfo) { + info = scriptInfo; + info.setReturnValueAppended(true); + } + + @Override + public void process(final String line) { + if (header) { + // NB: Check if line contains an '@' with no prior alphameric + // characters. This assumes that only non-alphanumeric characters can + // be used as comment line markers. + if (line.matches("^[^\\w]*@.*")) { + final int at = line.indexOf('@'); + parseParam(line.substring(at + 1)); + } + else if (line.matches(".*\\w.*")) header = false; + } + } + + @Override + public void end() { + if (info.isReturnValueAppended()) { + // add an output for the value returned by the script itself + final HashMap attrs = new HashMap<>(); + attrs.put("type", "OUTPUT"); + addItem(ScriptModule.RETURN_VALUE, Object.class, attrs, false); + } + } + + // -- Helper methods -- + + private void parseParam(final String param) { + final int lParen = param.indexOf("("); + final int rParen = param.lastIndexOf(")"); + if (rParen < lParen) { warnInvalid(param); return; } + if (lParen < 0) parseParam(param, parseAttrs("()")); + else { + final String cutParam = + param.substring(0, lParen) + param.substring(rParen + 1); + final String attrs = param.substring(lParen + 1, rParen); + parseParam(cutParam, parseAttrs(attrs)); + } + } + + private void parseParam(final String param, final Map attrs) { + final String[] tokens = param.trim().split("[ \t\n]+"); + if (tokens.length < 1) { warnInvalid(param); return; } + final String typeName, varName; + if (isIOType(tokens[0])) { + // assume syntax: + if (tokens.length < 3) { warnInvalid(param); return; } + attrs.put("type", tokens[0]); + typeName = tokens[1]; + varName = tokens[2]; + } + else { + // assume syntax: + if (tokens.length < 2) { warnInvalid(param); return; } + typeName = tokens[0]; + varName = tokens[1]; + } + try { + final Class type = scriptService.lookupClass(typeName); + addItem(varName, type, attrs, true); + } + catch (final ScriptException exc) { + log.warn("Invalid class: " + typeName, exc); + return; + } + + if (ScriptModule.RETURN_VALUE.equals(varName)) { + // NB: The return value variable is declared as an explicit parameter. + // So we should not append the return value as an extra output. + info.setReturnValueAppended(false); + } + } + + /** Parses a comma-delimited list of {@code key=value} pairs into a map. */ + private Map parseAttrs(final String attrs) { + return parser.parse(attrs, false).asMap(); + } + + private boolean isIOType(final String token) { + return convertService.convert(token, ItemIO.class) != null; + } + + private void warnInvalid(final String param) { + log.warn("Ignoring invalid parameter: " + param); + } + + private void addItem(final String name, final Class type, + final Map attrs, final boolean explicit) + { + final DefaultMutableModuleItem item = + new DefaultMutableModuleItem<>(info, name, type); + for (final String key : attrs.keySet()) { + final Object value = attrs.get(key); + assignAttribute(item, key, value); + } + if (item.isInput()) info.registerInput(item); + if (item.isOutput()) { + info.registerOutput(item); + // NB: Only append the return value as an extra + // output when no explicit outputs are declared. + if (explicit) info.setReturnValueAppended(false); + } + } + + private void assignAttribute(final DefaultMutableModuleItem item, + final String k, final Object v) + { + // CTR: There must be an easier way to do this. + // Just compile the thing using javac? Or parse via javascript, maybe? + if (is(k, "callback")) item.setCallback(as(v, String.class)); + else if (is(k, "choices")) item.setChoices(asList(v, item.getType())); + else if (is(k, "columns")) item.setColumnCount(as(v, int.class)); + else if (is(k, "description")) item.setDescription(as(v, String.class)); + else if (is(k, "initializer")) item.setInitializer(as(v, String.class)); + else if (is(k, "validater")) item.setValidater(as(v, String.class)); + else if (is(k, "type")) item.setIOType(as(v, ItemIO.class)); + else if (is(k, "label")) item.setLabel(as(v, String.class)); + else if (is(k, "max")) item.setMaximumValue(as(v, item.getType())); + else if (is(k, "min")) item.setMinimumValue(as(v, item.getType())); + else if (is(k, "name")) item.setName(as(v, String.class)); + else if (is(k, "persist")) item.setPersisted(as(v, boolean.class)); + else if (is(k, "persistKey")) item.setPersistKey(as(v, String.class)); + else if (is(k, "required")) item.setRequired(as(v, boolean.class)); + else if (is(k, "softMax")) item.setSoftMaximum(as(v, item.getType())); + else if (is(k, "softMin")) item.setSoftMinimum(as(v, item.getType())); + else if (is(k, "stepSize")) item.setStepSize(as(v, double.class)); + else if (is(k, "style")) item.setWidgetStyle(as(v, String.class)); + else if (is(k, "visibility")) item.setVisibility(as(v, ItemVisibility.class)); + else if (is(k, "value")) item.setDefaultValue(as(v, item.getType())); + else item.set(k, v.toString()); + } + + /** Super terse comparison helper method. */ + private boolean is(final String key, final String desired) { + return desired.equalsIgnoreCase(key); + } + + /** Super terse conversion helper method. */ + private T as(final Object v, final Class type) { + final T converted = convertService.convert(v, type); + if (converted != null) return converted; + // NB: Attempt to convert via string. + // This is useful in cases where a weird type of object came back + // (e.g., org.scijava.parse.eval.Unresolved), but which happens to have a + // nice string representation which ultimately is expressible as the type. + return convertService.convert(v.toString(), type); + } + + private List asList(final Object v, final Class type) { + final ArrayList result = new ArrayList<>(); + final List list = as(v, List.class); + for (final Object item : list) { + result.add(as(item, type)); + } + return result; + } + +} From 347b484a79f4d080ab224cd2c89dd14e0cd04bdf Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Tue, 23 May 2017 19:04:39 -0500 Subject: [PATCH 034/754] Add script processor for shebang syntax This lets a script declare its own intended language. See: https://github.com/hadim/scijava-jupyter-kernel/issues/51#issuecomment-301816226 --- .../process/ShebangScriptProcessor.java | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 src/main/java/org/scijava/script/process/ShebangScriptProcessor.java diff --git a/src/main/java/org/scijava/script/process/ShebangScriptProcessor.java b/src/main/java/org/scijava/script/process/ShebangScriptProcessor.java new file mode 100644 index 000000000..63dc09db3 --- /dev/null +++ b/src/main/java/org/scijava/script/process/ShebangScriptProcessor.java @@ -0,0 +1,78 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.script.process; + +import org.scijava.log.LogService; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; +import org.scijava.script.ScriptInfo; +import org.scijava.script.ScriptLanguage; +import org.scijava.script.ScriptService; + +/** + * A {@link ScriptProcessor} which looks for a {@code #!} at the beginning of a + * script, and set the language accordingly. + * + * @author Curtis Rueden + */ +@Plugin(type = ScriptProcessor.class) +public class ShebangScriptProcessor implements ScriptProcessor { + + @Parameter + private ScriptService scriptService; + + @Parameter + private LogService log; + + private ScriptInfo info; + private boolean first = true; + + // -- ScriptProcessor methods -- + + @Override + public void begin(final ScriptInfo scriptInfo) { + info = scriptInfo; + } + + @Override + public void process(final String line) { + if (!first) return; + if (line.startsWith("#!")) { + // shebang! + final String langName = line.substring(2); + final ScriptLanguage lang = scriptService.getLanguageByName(langName); + if (lang != null) info.setLanguage(lang); + else log.warn("Unknown script language: " + langName); + } + first = false; + } +} \ No newline at end of file From 1541cf1075c6ec826e4e95ca447e6d005f257cae Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Tue, 23 May 2017 20:44:33 -0500 Subject: [PATCH 035/754] Parse a new style of parameter syntax See: https://github.com/scijava/scijava-common/pull/265#issuecomment-302124612 --- .../process/ParameterScriptProcessor.java | 44 +++++++++++-------- .../org/scijava/script/ScriptInfoTest.java | 36 ++++++++++++++- 2 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java b/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java index 9652e3f77..c65e713c0 100644 --- a/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java +++ b/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java @@ -61,25 +61,24 @@ * supported: *

      *
        - *
      • {@code // @ }
      • - *
      • {@code // @(=, ..., =) } - *
      • - *
      • {@code // @ }
      • - *
      • {@code // @(=, ..., =) + *
      • {@code #@ }
      • + *
      • {@code #@(=, ..., =) }
      • + *
      • {@code #@ }
      • + *
      • {@code #@(=, ..., =) * }
      • *
      *

      * Where: *

      *
        - *
      • {@code //} = the comment style of the scripting language, so that the + *
      • {@code #@} - signals a special script processing instruction, so that the * parameter line is ignored by the script engine itself.
      • - *
      • {@code } = one of {@code INPUT}, {@code OUTPUT}, or {@code BOTH}. + *
      • {@code } - one of {@code INPUT}, {@code OUTPUT}, or {@code BOTH}. *
      • - *
      • {@code } = the name of the input or output variable.
      • - *
      • {@code } = the Java {@link Class} of the variable.
      • - *
      • {@code } = an attribute key.
      • - *
      • {@code } = an attribute value.
      • + *
      • {@code } - the name of the input or output variable.
      • + *
      • {@code } - the Java {@link Class} of the variable.
      • + *
      • {@code } - an attribute key.
      • + *
      • {@code } - an attribute value.
      • *
      *

      * See the @{@link Parameter} annotation for a list of valid attributes. @@ -88,10 +87,10 @@ * Here are a few examples: *

      *
        - *
      • {@code // @Dataset dataset}
      • - *
      • {@code // @double(type=OUTPUT) result}
      • - *
      • {@code // @BOTH ImageDisplay display}
      • - *
      • {@code // @INPUT(persist=false, visibility=INVISIBLE) boolean verbose} + *
      • {@code #@Dataset dataset}
      • + *
      • {@code #@double(type=OUTPUT) result}
      • + *
      • {@code #@BOTH ImageDisplay display}
      • + *
      • {@code #@INPUT(persist=false, visibility=INVISIBLE) boolean verbose} *
      • *
      *

      @@ -129,6 +128,14 @@ public void begin(final ScriptInfo scriptInfo) { @Override public void process(final String line) { + // parse new-style parameters starting with @# anywhere in the script. + if (line.matches("^#@.*")) { + final int at = line.indexOf('@'); + parseParam(line.substring(at + 1)); + return; + } + + // parse old-style parameters in the initial script header if (header) { // NB: Check if line contains an '@' with no prior alphameric // characters. This assumes that only non-alphanumeric characters can @@ -170,10 +177,11 @@ private void parseParam(final String param, final Map attrs) { final String[] tokens = param.trim().split("[ \t\n]+"); if (tokens.length < 1) { warnInvalid(param); return; } final String typeName, varName; - if (isIOType(tokens[0])) { + final String maybeIOType = tokens[0].toUpperCase(); + if (isIOType(maybeIOType)) { // assume syntax: if (tokens.length < 3) { warnInvalid(param); return; } - attrs.put("type", tokens[0]); + attrs.put("type", maybeIOType); typeName = tokens[1]; varName = tokens[2]; } @@ -205,7 +213,7 @@ private Map parseAttrs(final String attrs) { } private boolean isIOType(final String token) { - return convertService.convert(token, ItemIO.class) != null; + return convertService.convert(token.toUpperCase(), ItemIO.class) != null; } private void warnInvalid(final String param) { diff --git a/src/test/java/org/scijava/script/ScriptInfoTest.java b/src/test/java/org/scijava/script/ScriptInfoTest.java index 8d022f748..7e99b439e 100644 --- a/src/test/java/org/scijava/script/ScriptInfoTest.java +++ b/src/test/java/org/scijava/script/ScriptInfoTest.java @@ -87,6 +87,41 @@ public static void tearDown() { // -- Tests -- + /** Tests whether new-style parameter syntax are parsed correctly. */ + @Test + public void testNewStyle() throws Exception { + final String script = "" + // + "##########\n" + // + "# Inputs #\n" + // + "##########\n" + // + "#@input int stuff\n" + // + "#@input int things\n" + // + "\n" + // + "###########\n" + // + "# Credits #\n" + // + "###########\n" + // + "Brought to you by:\n" + // + "person@example.com\n" + // + "\n" + // + "###########\n" + // + "# Outputs #\n" + // + "###########\n" + // + "#@output String blackHoles\n" + + "#@output String revelations\n" + + "\n" + // + "THE END!\n"; + final ScriptModule scriptModule = + scriptService.run("newStyle.bsizes", script, true).get(); + + final Object output = scriptModule.getReturnValue(); + + if (output == null) fail("null result"); + else if (!(output instanceof Integer)) { + fail("result is a " + output.getClass().getName()); + } + else assertEquals(4, ((Integer) output).intValue()); + } + /** * Tests that the return value is appended as an extra output when no * explicit outputs were declared. @@ -122,7 +157,6 @@ public void testReturnValueExcluded() throws Exception { assertFalse(outputs.containsKey(ScriptModule.RETURN_VALUE)); } - /** * Ensures parameters are parsed correctly from scripts, even in the presence * of noise like e-mail addresses. From e57683baf15a106f65ce68c197adc55f1cecd07d Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 24 May 2017 07:35:19 -0500 Subject: [PATCH 036/754] Add a callback mechanism for script execution The intended use case is for ScriptProcessor plugins to use it when they need to do something every time (or deferred until the first time) a script executes. Initially, this will be useful for dependency grabbing. --- .../java/org/scijava/script/ScriptInfo.java | 17 ++++++ .../java/org/scijava/script/ScriptModule.java | 8 ++- .../script/process/ScriptCallback.java | 52 +++++++++++++++++++ 3 files changed, 76 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/scijava/script/process/ScriptCallback.java diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index fb3859b2b..954a6594e 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -40,6 +40,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Date; import java.util.List; @@ -52,6 +53,7 @@ import org.scijava.module.ModuleItem; import org.scijava.plugin.Parameter; import org.scijava.script.process.ParameterScriptProcessor; +import org.scijava.script.process.ScriptCallback; import org.scijava.script.process.ScriptProcessorService; import org.scijava.util.DigestUtils; import org.scijava.util.FileUtils; @@ -88,6 +90,9 @@ public class ScriptInfo extends AbstractModuleInfo implements Contextual { /** Script language in which the script should be executed. */ private ScriptLanguage scriptLanguage; + /** Routines to be invoked prior to script execution. */ + private ArrayList callbacks; + /** * Creates a script metadata object which describes the given script file. * @@ -245,6 +250,18 @@ public void setReturnValueAppended(final boolean appendReturnValue) { this.appendReturnValue = appendReturnValue; } + /** + * Gets the list of routines which should be invoked each time the script is + * about to execute. + * + * @return Reference to the mutable list of {@link Runnable} objects which the + * {@link ScriptModule} will run prior to executing the script itself. + */ + public List callbacks() { + if (callbacks == null) callbacks = new ArrayList<>(); + return callbacks; + } + // -- AbstractModuleInfo methods -- /** diff --git a/src/main/java/org/scijava/script/ScriptModule.java b/src/main/java/org/scijava/script/ScriptModule.java index d00b7ab5e..80a5d928a 100644 --- a/src/main/java/org/scijava/script/ScriptModule.java +++ b/src/main/java/org/scijava/script/ScriptModule.java @@ -50,6 +50,7 @@ import org.scijava.module.Module; import org.scijava.module.ModuleItem; import org.scijava.plugin.Parameter; +import org.scijava.script.process.ScriptCallback; /** * A {@link Module} which executes a script. @@ -149,9 +150,14 @@ public void run() { engine.put(name, getInput(name)); } - // execute script! returnValue = null; try { + // invoke the callbacks + for (final ScriptCallback c : getInfo().callbacks()) { + c.invoke(this); + } + + // execute script! final Reader reader = getInfo().getReader(); if (reader == null) returnValue = engine.eval(new FileReader(path)); else returnValue = engine.eval(reader); diff --git a/src/main/java/org/scijava/script/process/ScriptCallback.java b/src/main/java/org/scijava/script/process/ScriptCallback.java new file mode 100644 index 000000000..fa6f8f0ed --- /dev/null +++ b/src/main/java/org/scijava/script/process/ScriptCallback.java @@ -0,0 +1,52 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.script.process; + +import javax.script.ScriptException; + +import org.scijava.script.ScriptModule; + +/** + * A routine which will be invoked just prior to script execution. + * + * @author Curtis Rueden + */ +public interface ScriptCallback { + + /** + * Invokes the callback routine. + * + * @param module The {@link ScriptModule} instance which will + * execute the script. + */ + void invoke(final ScriptModule module) throws ScriptException; +} From 3f89bd015244b2078bf06b8198f14673a5f40d77 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 24 May 2017 09:55:29 -0500 Subject: [PATCH 037/754] Allow script processors to modify the script In particular, we want the #@ processing directives to disappear from the executed version of the script, since they are invalid syntax for many of the script languages. This change gives ScriptProcessor plugins the flexibility to modify each line of the script however they choose, although our initial use case here will simply blank out lines which have been handled. What processors should not do is change the number of lines; we want the line numbers in error messages to match those of the original script. --- .../java/org/scijava/script/ScriptInfo.java | 15 +++++- .../java/org/scijava/script/ScriptModule.java | 6 +-- .../process/ParameterScriptProcessor.java | 49 ++++++++++--------- .../script/process/ScriptProcessor.java | 2 +- .../process/ScriptProcessorService.java | 12 +++-- .../process/ShebangScriptProcessor.java | 8 +-- 6 files changed, 57 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/scijava/script/ScriptInfo.java b/src/main/java/org/scijava/script/ScriptInfo.java index 954a6594e..bee3b1c06 100644 --- a/src/main/java/org/scijava/script/ScriptInfo.java +++ b/src/main/java/org/scijava/script/ScriptInfo.java @@ -84,6 +84,9 @@ public class ScriptInfo extends AbstractModuleInfo implements Contextual { @Parameter private ScriptProcessorService scriptProcessorService; + /** Final version of the script, after script processing. */ + private String processedScript; + /** True iff the return value should be appended as an output. */ private boolean appendReturnValue; @@ -216,6 +219,16 @@ public BufferedReader getReader() { return new BufferedReader(new StringReader(script), PARAM_CHAR_MAX); } + /** + * Gets the script contents after script processing. + * + * @return The processed script. + * @see ScriptProcessorService#process + */ + public String getProcessedScript() { + return processedScript; + } + /** Gets the scripting language of the script. */ public ScriptLanguage getLanguage() { if (scriptLanguage == null) { @@ -275,7 +288,7 @@ public List callbacks() { public void parseParameters() { clearParameters(); try { - scriptProcessorService.process(this); + processedScript = scriptProcessorService.process(this); } catch (final IOException exc) { // TODO: Consider a better error handling approach. diff --git a/src/main/java/org/scijava/script/ScriptModule.java b/src/main/java/org/scijava/script/ScriptModule.java index 80a5d928a..5ed0f39a6 100644 --- a/src/main/java/org/scijava/script/ScriptModule.java +++ b/src/main/java/org/scijava/script/ScriptModule.java @@ -31,10 +31,8 @@ package org.scijava.script; -import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; -import java.io.Reader; import java.io.Writer; import javax.script.ScriptContext; @@ -158,9 +156,7 @@ public void run() { } // execute script! - final Reader reader = getInfo().getReader(); - if (reader == null) returnValue = engine.eval(new FileReader(path)); - else returnValue = engine.eval(reader); + returnValue = engine.eval(getInfo().getProcessedScript()); } catch (Throwable e) { while (e instanceof ScriptException && e.getCause() != null) { diff --git a/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java b/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java index c65e713c0..d7c50d2b0 100644 --- a/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java +++ b/src/main/java/org/scijava/script/process/ParameterScriptProcessor.java @@ -127,12 +127,11 @@ public void begin(final ScriptInfo scriptInfo) { } @Override - public void process(final String line) { + public String process(final String line) { // parse new-style parameters starting with @# anywhere in the script. if (line.matches("^#@.*")) { final int at = line.indexOf('@'); - parseParam(line.substring(at + 1)); - return; + return process(line, line.substring(at + 1)); } // parse old-style parameters in the initial script header @@ -142,10 +141,12 @@ public void process(final String line) { // be used as comment line markers. if (line.matches("^[^\\w]*@.*")) { final int at = line.indexOf('@'); - parseParam(line.substring(at + 1)); + return process(line, line.substring(at + 1)); } else if (line.matches(".*\\w.*")) header = false; } + + return line; } @Override @@ -160,34 +161,40 @@ public void end() { // -- Helper methods -- - private void parseParam(final String param) { + private String process(final String line, final String param) { + if (parseParam(param)) return ""; + log.warn("Ignoring invalid parameter: " + param); + return line; + } + + private boolean parseParam(final String param) { final int lParen = param.indexOf("("); final int rParen = param.lastIndexOf(")"); - if (rParen < lParen) { warnInvalid(param); return; } - if (lParen < 0) parseParam(param, parseAttrs("()")); - else { - final String cutParam = - param.substring(0, lParen) + param.substring(rParen + 1); - final String attrs = param.substring(lParen + 1, rParen); - parseParam(cutParam, parseAttrs(attrs)); - } + if (rParen < lParen) return false; + if (lParen < 0) return parseParam(param, parseAttrs("()")); + final String cutParam = + param.substring(0, lParen) + param.substring(rParen + 1); + final String attrs = param.substring(lParen + 1, rParen); + return parseParam(cutParam, parseAttrs(attrs)); } - private void parseParam(final String param, final Map attrs) { + private boolean parseParam(final String param, + final Map attrs) + { final String[] tokens = param.trim().split("[ \t\n]+"); - if (tokens.length < 1) { warnInvalid(param); return; } + if (tokens.length < 1) return false; final String typeName, varName; final String maybeIOType = tokens[0].toUpperCase(); if (isIOType(maybeIOType)) { // assume syntax: - if (tokens.length < 3) { warnInvalid(param); return; } + if (tokens.length < 3) return false; attrs.put("type", maybeIOType); typeName = tokens[1]; varName = tokens[2]; } else { // assume syntax: - if (tokens.length < 2) { warnInvalid(param); return; } + if (tokens.length < 2) return false; typeName = tokens[0]; varName = tokens[1]; } @@ -197,7 +204,7 @@ private void parseParam(final String param, final Map attrs) { } catch (final ScriptException exc) { log.warn("Invalid class: " + typeName, exc); - return; + return false; } if (ScriptModule.RETURN_VALUE.equals(varName)) { @@ -205,6 +212,8 @@ private void parseParam(final String param, final Map attrs) { // So we should not append the return value as an extra output. info.setReturnValueAppended(false); } + + return true; } /** Parses a comma-delimited list of {@code key=value} pairs into a map. */ @@ -216,10 +225,6 @@ private boolean isIOType(final String token) { return convertService.convert(token.toUpperCase(), ItemIO.class) != null; } - private void warnInvalid(final String param) { - log.warn("Ignoring invalid parameter: " + param); - } - private void addItem(final String name, final Class type, final Map attrs, final boolean explicit) { diff --git a/src/main/java/org/scijava/script/process/ScriptProcessor.java b/src/main/java/org/scijava/script/process/ScriptProcessor.java index 3d412d074..e89ca7150 100644 --- a/src/main/java/org/scijava/script/process/ScriptProcessor.java +++ b/src/main/java/org/scijava/script/process/ScriptProcessor.java @@ -48,7 +48,7 @@ public interface ScriptProcessor extends SingletonPlugin { void begin(ScriptInfo info); - void process(String line); + String process(String line); default void end() {} } diff --git a/src/main/java/org/scijava/script/process/ScriptProcessorService.java b/src/main/java/org/scijava/script/process/ScriptProcessorService.java index 2f1d431cf..202071f62 100644 --- a/src/main/java/org/scijava/script/process/ScriptProcessorService.java +++ b/src/main/java/org/scijava/script/process/ScriptProcessorService.java @@ -56,7 +56,7 @@ public interface ScriptProcessorService extends * Invokes all {@link ScriptProcessor} plugins on the given script, line by * line in sequence. */ - default void process(final ScriptInfo info) throws IOException { + default String process(final ScriptInfo info) throws IOException { final List processors = getPlugins().stream().map( p -> pluginService().createInstance(p)).collect(Collectors.toList()); @@ -69,19 +69,25 @@ default void process(final ScriptInfo info) throws IOException { p.begin(info); } + final StringBuilder sb = new StringBuilder(); + try (final BufferedReader in = reader) { while (true) { - final String line = in.readLine(); + String line = in.readLine(); if (line == null) break; for (final ScriptProcessor p : processors) { - p.process(line); + line = p.process(line); } + sb.append(line); + sb.append("\n"); } } for (final ScriptProcessor p : processors) { p.end(); } + + return sb.toString(); } // -- PTService methods -- diff --git a/src/main/java/org/scijava/script/process/ShebangScriptProcessor.java b/src/main/java/org/scijava/script/process/ShebangScriptProcessor.java index 63dc09db3..f1b75d546 100644 --- a/src/main/java/org/scijava/script/process/ShebangScriptProcessor.java +++ b/src/main/java/org/scijava/script/process/ShebangScriptProcessor.java @@ -64,15 +64,17 @@ public void begin(final ScriptInfo scriptInfo) { } @Override - public void process(final String line) { - if (!first) return; + public String process(final String line) { + if (!first) return line; + first = false; if (line.startsWith("#!")) { // shebang! final String langName = line.substring(2); final ScriptLanguage lang = scriptService.getLanguageByName(langName); if (lang != null) info.setLanguage(lang); else log.warn("Unknown script language: " + langName); + return ""; } - first = false; + return line; } } \ No newline at end of file From e5e496d297b9a3d7e32a772dd1cda03a3663edf0 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 24 May 2017 10:09:40 -0500 Subject: [PATCH 038/754] ScriptInfoTest: test script modification --- src/test/java/org/scijava/script/ScriptInfoTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/test/java/org/scijava/script/ScriptInfoTest.java b/src/test/java/org/scijava/script/ScriptInfoTest.java index 7e99b439e..a5b0e6029 100644 --- a/src/test/java/org/scijava/script/ScriptInfoTest.java +++ b/src/test/java/org/scijava/script/ScriptInfoTest.java @@ -113,6 +113,10 @@ public void testNewStyle() throws Exception { final ScriptModule scriptModule = scriptService.run("newStyle.bsizes", script, true).get(); + final String expectedProcessed = script.replaceAll("#@.*", ""); + final String actualProcessed = scriptModule.getInfo().getProcessedScript(); + assertEquals(expectedProcessed, actualProcessed); + final Object output = scriptModule.getReturnValue(); if (output == null) fail("null result"); From 8896894ea52465c77d8816a321c4aa1e5e634b19 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 24 May 2017 12:56:53 -0500 Subject: [PATCH 039/754] Bump to next development cycle Signed-off-by: Curtis Rueden --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9a0b01354..3700d7ab0 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ scijava-common - 2.64.0-SNAPSHOT + 2.64.1-SNAPSHOT SciJava Common SciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO. From cc99b9ba3f2e8bb556055ac808b8f4a7500d5132 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 2 Jun 2017 21:05:44 -0500 Subject: [PATCH 040/754] POM: fix Chris Allan ID --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 3700d7ab0..e0beb5f14 100644 --- a/pom.xml +++ b/pom.xml @@ -57,7 +57,7 @@ Chris Allan - callan + chris-allan Barry DeZonia From 7f9bcccd4b55cf1eb1765639ab5cbbfcb92e18e1 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 12 Jun 2017 12:42:02 -0500 Subject: [PATCH 041/754] Parameter: deprecate columns() method --- src/main/java/org/scijava/plugin/Parameter.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/scijava/plugin/Parameter.java b/src/main/java/org/scijava/plugin/Parameter.java index c8591c48a..21edaa358 100644 --- a/src/main/java/org/scijava/plugin/Parameter.java +++ b/src/main/java/org/scijava/plugin/Parameter.java @@ -158,12 +158,6 @@ /** Defines the step size to use (numeric parameters only). */ String stepSize() default ""; - /** - * Defines the width of the input field in characters (text field parameters - * only). - */ - int columns() default 6; - /** Defines the list of possible values (multiple choice text fields only). */ String[] choices() default {}; @@ -173,4 +167,7 @@ */ Attr[] attrs() default {}; + /** @deprecated Replaced by {@link #style()}. */ + @Deprecated + int columns() default 6; } From 75382b65c9517814332541d1c9b9ac5dde17f9b9 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Mon, 19 Jun 2017 15:30:54 +0200 Subject: [PATCH 042/754] PrefService: never let key or value be null The Java Preferences API hates nulls: public void put(String key, String value) { if (key==null || value==null) throw new NullPointerException(); ... --- src/main/java/org/scijava/prefs/DefaultPrefService.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/org/scijava/prefs/DefaultPrefService.java b/src/main/java/org/scijava/prefs/DefaultPrefService.java index 288d2f2dc..d03423e55 100644 --- a/src/main/java/org/scijava/prefs/DefaultPrefService.java +++ b/src/main/java/org/scijava/prefs/DefaultPrefService.java @@ -653,6 +653,7 @@ private String safeName(final String name) { *

    */ private String makeSafe(final String s, final int max) { + if (s == null) return ""; // Java Preferences API hates nulls. final int len = s.length(); if (len < max) return s; return "..." + s.substring(len - max + 3, len); From 526b7163b9ae7442c74cfe184be7eae2798bb2ad Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 14 Dec 2016 02:50:36 +0100 Subject: [PATCH 043/754] AbstractLogService: fix typo in javadoc --- src/main/java/org/scijava/log/AbstractLogService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/log/AbstractLogService.java b/src/main/java/org/scijava/log/AbstractLogService.java index 69eddd8c0..748cac6a2 100644 --- a/src/main/java/org/scijava/log/AbstractLogService.java +++ b/src/main/java/org/scijava/log/AbstractLogService.java @@ -38,7 +38,7 @@ import org.scijava.service.AbstractService; /** - * Base class for {@link LogService} implementationst. + * Base class for {@link LogService} implementations. * * @author Johannes Schindelin */ From 294f37c5500437701a4e4a7dcc1eb9e16c0b5355 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Wed, 14 Dec 2016 02:53:18 +0100 Subject: [PATCH 044/754] AbstractLogService: fix incorrect comment The default level went back and forth between INFO and WARN a couple of times, but it is currently INFO. --- src/main/java/org/scijava/log/AbstractLogService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/log/AbstractLogService.java b/src/main/java/org/scijava/log/AbstractLogService.java index 748cac6a2..037975724 100644 --- a/src/main/java/org/scijava/log/AbstractLogService.java +++ b/src/main/java/org/scijava/log/AbstractLogService.java @@ -78,7 +78,7 @@ public AbstractLogService() { if (level >= 0) setLevel(level); if (getLevel() == 0) { - // use the default, which is WARN unless the DEBUG env. variable is set + // use the default, which is INFO unless the DEBUG env. variable is set setLevel(System.getenv("DEBUG") == null ? INFO : DEBUG); } From 8138f114485ab4bd3743abfdb3d9c86f981d39ad Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Tue, 13 Dec 2016 16:59:05 +0100 Subject: [PATCH 045/754] AbstractLogService: refactor duplicate code --- .../java/org/scijava/log/AbstractLogService.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/scijava/log/AbstractLogService.java b/src/main/java/org/scijava/log/AbstractLogService.java index 037975724..44f9fea29 100644 --- a/src/main/java/org/scijava/log/AbstractLogService.java +++ b/src/main/java/org/scijava/log/AbstractLogService.java @@ -46,7 +46,7 @@ public abstract class AbstractLogService extends AbstractService implements LogService { - private int currentLevel = System.getenv("DEBUG") == null ? INFO : DEBUG; + private int currentLevel = levelFromEnvironment(); private final Map classAndPackageLevels = new HashMap<>(); @@ -77,10 +77,8 @@ public AbstractLogService() { final int level = level(logProp); if (level >= 0) setLevel(level); - if (getLevel() == 0) { - // use the default, which is INFO unless the DEBUG env. variable is set - setLevel(System.getenv("DEBUG") == null ? INFO : DEBUG); - } + if (getLevel() == 0) + setLevel(levelFromEnvironment()); // populate custom class- and package-specific log level properties final String logLevelPrefix = LOG_LEVEL_PROPERTY + ":"; @@ -297,4 +295,9 @@ private String parentPackage(final String classOrPackageName) { return classOrPackageName.substring(0, dot); } + private int levelFromEnvironment() { + // use the default, which is INFO unless the DEBUG env. variable is set + return System.getenv("DEBUG") == null ? INFO : DEBUG; + } + } From 7cccfac373d7f34fc5288ccaeccf0f3030ee2fe7 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Thu, 22 Jun 2017 17:35:05 +0200 Subject: [PATCH 046/754] AnnotationProcessor: it works with Java 7 & 8, too The SupportedSourceVersion indicates the _newest_ release of Java supported by the processor. SciJava supports Java 8 and earlier. --- .../java/org/scijava/annotations/AnnotationProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/scijava/annotations/AnnotationProcessor.java b/src/main/java/org/scijava/annotations/AnnotationProcessor.java index 0f96bab4f..472f57335 100644 --- a/src/main/java/org/scijava/annotations/AnnotationProcessor.java +++ b/src/main/java/org/scijava/annotations/AnnotationProcessor.java @@ -70,11 +70,11 @@ import org.scijava.annotations.AbstractIndexWriter.StreamFactory; /** - * The annotation processor for use with Java 6 and above. + * The annotation processor for use with Java 8 and earlier. * * @author Johannes Schindelin */ -@SupportedSourceVersion(SourceVersion.RELEASE_6) +@SupportedSourceVersion(SourceVersion.RELEASE_8) @SupportedAnnotationTypes("*") public class AnnotationProcessor extends AbstractProcessor { From 596813c198d3f6eb77efac458a4d3dd71cfcfaf6 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Sun, 3 Jul 2016 10:34:04 -0500 Subject: [PATCH 047/754] Do runtime type checking in Typed.supports method There are situations where we cannot solely rely on the compiler. For example, with DataHandle plugins, the generic parameter L is heterogeneous, so we need to actually check the type of the data. I tried to make this the default implementation of the Typed interface itself, but ran into problems with calling super.supports in downstream classes; apparently, you cannot directly reference default interface methods by writing e.g. Typed.super.supports(...)? Strange. --- .../java/org/scijava/plugin/AbstractTypedPlugin.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/plugin/AbstractTypedPlugin.java b/src/main/java/org/scijava/plugin/AbstractTypedPlugin.java index e8b3ff338..ac29ee129 100644 --- a/src/main/java/org/scijava/plugin/AbstractTypedPlugin.java +++ b/src/main/java/org/scijava/plugin/AbstractTypedPlugin.java @@ -43,5 +43,15 @@ public abstract class AbstractTypedPlugin extends AbstractRichPlugin implements TypedPlugin { - // NB: No implementation needed. + // -- Typed methods -- + + @Override + public boolean supports(final D data) { + // NB: Even though the compiler will often guarantee that only data + // of type T is provided here, we still need the runtime check + // for cases where the exact type is not known to compiler -- + // e.g., if the object was manufactured by reflection. + return getType().isInstance(data); + } + } From 28700f4676fda5d8a676ae2bc2c8bee31b0698e0 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 1 Jul 2016 23:27:43 -0500 Subject: [PATCH 048/754] Add NIOService for working with NIO classes Migrated from the NIOService of SCIFIO, which was previously adapted from the NIOByteBufferProvider class of Bio-Formats. --- .../org/scijava/io/nio/DefaultNIOService.java | 119 ++++++++++++++++++ .../java/org/scijava/io/nio/NIOService.java | 73 +++++++++++ .../java/org/scijava/ContextCreationTest.java | 1 + 3 files changed, 193 insertions(+) create mode 100644 src/main/java/org/scijava/io/nio/DefaultNIOService.java create mode 100644 src/main/java/org/scijava/io/nio/NIOService.java diff --git a/src/main/java/org/scijava/io/nio/DefaultNIOService.java b/src/main/java/org/scijava/io/nio/DefaultNIOService.java new file mode 100644 index 000000000..9a631daf6 --- /dev/null +++ b/src/main/java/org/scijava/io/nio/DefaultNIOService.java @@ -0,0 +1,119 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2016 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.io.nio; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; + +import org.scijava.log.LogService; +import org.scijava.plugin.Parameter; +import org.scijava.plugin.Plugin; +import org.scijava.service.AbstractService; +import org.scijava.service.Service; + +/** + * Default service for working with the {@link java.nio} package, particularly + * NIO {@link ByteBuffer} objects. + * + * @author Chris Allan + * @author Curtis Rueden + */ +@Plugin(type = Service.class) +public class DefaultNIOService extends AbstractService implements NIOService { + + // -- Fields -- + + @Parameter + private LogService log; + + /** Whether or not we are to use memory mapped I/O. */ + private final boolean useMappedByteBuffer = Boolean.parseBoolean(System + .getProperty("mappedBuffers")); + + // -- NIOService API methods -- + + @Override + public ByteBuffer allocate(final FileChannel channel, final MapMode mapMode, + final long bufferStartPosition, final int newSize) throws IOException + { + log.debug("NIO: allocate: mapped=" + useMappedByteBuffer + ", start=" + + bufferStartPosition + ", size=" + newSize); + if (useMappedByteBuffer) { + return allocateMappedByteBuffer(channel, mapMode, bufferStartPosition, + newSize); + } + return allocateDirect(channel, bufferStartPosition, newSize); + } + + // -- Helper methods -- + + /** + * Allocates memory and copies the desired file data into it. + * + * @param channel File channel to allocate or map byte buffers from. + * @param bufferStartPosition The absolute position of the start of the + * buffer. + * @param newSize The buffer size. + * @return A newly allocated NIO byte buffer. + * @throws IOException If there is an issue aligning or allocating the buffer. + */ + private ByteBuffer allocateDirect(final FileChannel channel, + final long bufferStartPosition, final int newSize) throws IOException + { + final ByteBuffer buffer = ByteBuffer.allocate(newSize); + channel.read(buffer, bufferStartPosition); + return buffer; + } + + /** + * Memory maps the desired file data into memory. + * + * @param channel File channel to allocate or map byte buffers from. + * @param mapMode The map mode. Required but only used if memory mapped I/O is + * to occur. + * @param bufferStartPosition The absolute position of the start of the + * buffer. + * @param newSize The buffer size. + * @return A newly mapped NIO byte buffer. + * @throws IOException If there is an issue mapping, aligning or allocating + * the buffer. + */ + private ByteBuffer allocateMappedByteBuffer(final FileChannel channel, + final MapMode mapMode, final long bufferStartPosition, final int newSize) + throws IOException + { + return channel.map(mapMode, bufferStartPosition, newSize); + } + +} diff --git a/src/main/java/org/scijava/io/nio/NIOService.java b/src/main/java/org/scijava/io/nio/NIOService.java new file mode 100644 index 000000000..0c6865045 --- /dev/null +++ b/src/main/java/org/scijava/io/nio/NIOService.java @@ -0,0 +1,73 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2016 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.io.nio; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; + +import org.scijava.service.SciJavaService; + +/** + * Interface for services that work with the {@link java.nio} package, + * particularly NIO {@link ByteBuffer} objects. + * + * @author Chris Allan + * @author Curtis Rueden + */ +public interface NIOService extends SciJavaService { + + /** + * Allocates or maps the desired file data into memory. + *

    + * This method provides a facade to byte buffer allocation that enables + * FileChannel.map() usage on platforms where it's unlikely to + * give us problems and heap allocation where it is. + *

    + * + * @param channel File channel to allocate or map byte buffers from. + * @param mapMode The map mode. Required but only used if memory mapped I/O is + * to occur. + * @param bufferStartPosition The absolute position of the start of the + * buffer. + * @param newSize The buffer size. + * @return A newly allocated or mapped NIO byte buffer. + * @see "http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5092131" + * @see "http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6417205" + * @throws IOException If there is an issue mapping, aligning or allocating + * the buffer. + */ + ByteBuffer allocate(FileChannel channel, MapMode mapMode, + long bufferStartPosition, int newSize) throws IOException; + +} diff --git a/src/test/java/org/scijava/ContextCreationTest.java b/src/test/java/org/scijava/ContextCreationTest.java index 5dc0ed1b4..449fcc26f 100644 --- a/src/test/java/org/scijava/ContextCreationTest.java +++ b/src/test/java/org/scijava/ContextCreationTest.java @@ -98,6 +98,7 @@ public void testFull() { org.scijava.io.DefaultDataHandleService.class, org.scijava.io.DefaultIOService.class, org.scijava.io.DefaultRecentFileService.class, + org.scijava.io.nio.DefaultNIOService.class, org.scijava.main.DefaultMainService.class, org.scijava.menu.DefaultMenuService.class, org.scijava.module.DefaultModuleService.class, From ac6a54ae9a00a379fd3539dba130fc8da844d24f Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 7 Jul 2017 15:18:13 -0500 Subject: [PATCH 049/754] Reorganize the org.scijava.io classes This moves Location stuff to org.scijava.io.location, and DataHandle stuff to org.scijava.io.handle. It breaks backwards compatibility, but the Location API was not being used by any SJC-based systems yet. We make the change now to minimize future damage. --- src/main/java/org/scijava/io/IOService.java | 2 -- .../org/scijava/io/{ => handle}/AbstractDataHandle.java | 3 ++- src/main/java/org/scijava/io/{ => handle}/DataHandle.java | 3 ++- .../org/scijava/io/{ => handle}/DataHandleInputStream.java | 4 +++- .../scijava/io/{ => handle}/DataHandleOutputStream.java | 4 +++- .../org/scijava/io/{ => handle}/DataHandleService.java | 4 +++- .../scijava/io/{ => handle}/DefaultDataHandleService.java | 3 ++- src/main/java/org/scijava/io/{ => handle}/FileHandle.java | 3 ++- .../org/scijava/io/{ => location}/AbstractLocation.java | 2 +- .../java/org/scijava/io/{ => location}/BytesLocation.java | 2 +- .../java/org/scijava/io/{ => location}/FileLocation.java | 2 +- src/main/java/org/scijava/io/{ => location}/Location.java | 4 +++- .../java/org/scijava/io/{ => location}/URILocation.java | 2 +- .../java/org/scijava/io/{ => location}/URLLocation.java | 2 +- src/test/java/org/scijava/ContextCreationTest.java | 2 +- .../java/org/scijava/io/{ => handle}/DataHandleTest.java | 5 ++++- .../java/org/scijava/io/{ => handle}/FileHandleTest.java | 7 ++++++- .../org/scijava/io/{ => location}/BytesLocationTest.java | 3 ++- .../org/scijava/io/{ => location}/FileLocationTest.java | 3 ++- .../org/scijava/io/{ => location}/URILocationTest.java | 3 ++- .../org/scijava/io/{ => location}/URLLocationTest.java | 3 ++- 21 files changed, 44 insertions(+), 22 deletions(-) rename src/main/java/org/scijava/io/{ => handle}/AbstractDataHandle.java (96%) rename src/main/java/org/scijava/io/{ => handle}/DataHandle.java (99%) rename src/main/java/org/scijava/io/{ => handle}/DataHandleInputStream.java (97%) rename src/main/java/org/scijava/io/{ => handle}/DataHandleOutputStream.java (97%) rename src/main/java/org/scijava/io/{ => handle}/DataHandleService.java (95%) rename src/main/java/org/scijava/io/{ => handle}/DefaultDataHandleService.java (96%) rename src/main/java/org/scijava/io/{ => handle}/FileHandle.java (98%) rename src/main/java/org/scijava/io/{ => location}/AbstractLocation.java (98%) rename src/main/java/org/scijava/io/{ => location}/BytesLocation.java (98%) rename src/main/java/org/scijava/io/{ => location}/FileLocation.java (98%) rename src/main/java/org/scijava/io/{ => location}/Location.java (96%) rename src/main/java/org/scijava/io/{ => location}/URILocation.java (99%) rename src/main/java/org/scijava/io/{ => location}/URLLocation.java (98%) rename src/test/java/org/scijava/io/{ => handle}/DataHandleTest.java (97%) rename src/test/java/org/scijava/io/{ => handle}/FileHandleTest.java (91%) rename src/test/java/org/scijava/io/{ => location}/BytesLocationTest.java (97%) rename src/test/java/org/scijava/io/{ => location}/FileLocationTest.java (96%) rename src/test/java/org/scijava/io/{ => location}/URILocationTest.java (96%) rename src/test/java/org/scijava/io/{ => location}/URLLocationTest.java (96%) diff --git a/src/main/java/org/scijava/io/IOService.java b/src/main/java/org/scijava/io/IOService.java index a4f284680..3774de692 100644 --- a/src/main/java/org/scijava/io/IOService.java +++ b/src/main/java/org/scijava/io/IOService.java @@ -40,8 +40,6 @@ * Interface for high-level data I/O: opening and saving data. * * @author Curtis Rueden - * @see DataHandleService - * @see Location */ public interface IOService extends HandlerService>, SciJavaService diff --git a/src/main/java/org/scijava/io/AbstractDataHandle.java b/src/main/java/org/scijava/io/handle/AbstractDataHandle.java similarity index 96% rename from src/main/java/org/scijava/io/AbstractDataHandle.java rename to src/main/java/org/scijava/io/handle/AbstractDataHandle.java index 7a30a6d3b..b072df9ad 100644 --- a/src/main/java/org/scijava/io/AbstractDataHandle.java +++ b/src/main/java/org/scijava/io/handle/AbstractDataHandle.java @@ -29,10 +29,11 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.handle; import java.nio.ByteOrder; +import org.scijava.io.location.Location; import org.scijava.plugin.AbstractWrapperPlugin; /** diff --git a/src/main/java/org/scijava/io/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java similarity index 99% rename from src/main/java/org/scijava/io/DataHandle.java rename to src/main/java/org/scijava/io/handle/DataHandle.java index f058d9241..9f6d1e3dc 100644 --- a/src/main/java/org/scijava/io/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -29,7 +29,7 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.handle; import java.io.Closeable; import java.io.DataInput; @@ -39,6 +39,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; +import org.scijava.io.location.Location; import org.scijava.plugin.WrapperPlugin; /** diff --git a/src/main/java/org/scijava/io/DataHandleInputStream.java b/src/main/java/org/scijava/io/handle/DataHandleInputStream.java similarity index 97% rename from src/main/java/org/scijava/io/DataHandleInputStream.java rename to src/main/java/org/scijava/io/handle/DataHandleInputStream.java index 6b2e21e4a..2a215c69a 100644 --- a/src/main/java/org/scijava/io/DataHandleInputStream.java +++ b/src/main/java/org/scijava/io/handle/DataHandleInputStream.java @@ -29,11 +29,13 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.handle; import java.io.IOException; import java.io.InputStream; +import org.scijava.io.location.Location; + /** * {@link InputStream} backed by a {@link DataHandle}. * diff --git a/src/main/java/org/scijava/io/DataHandleOutputStream.java b/src/main/java/org/scijava/io/handle/DataHandleOutputStream.java similarity index 97% rename from src/main/java/org/scijava/io/DataHandleOutputStream.java rename to src/main/java/org/scijava/io/handle/DataHandleOutputStream.java index 6f876694d..c2320281f 100644 --- a/src/main/java/org/scijava/io/DataHandleOutputStream.java +++ b/src/main/java/org/scijava/io/handle/DataHandleOutputStream.java @@ -29,11 +29,13 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.handle; import java.io.IOException; import java.io.OutputStream; +import org.scijava.io.location.Location; + /** * {@link OutputStream} backed by a {@link DataHandle}. * diff --git a/src/main/java/org/scijava/io/DataHandleService.java b/src/main/java/org/scijava/io/handle/DataHandleService.java similarity index 95% rename from src/main/java/org/scijava/io/DataHandleService.java rename to src/main/java/org/scijava/io/handle/DataHandleService.java index 723461870..5ed20cfc0 100644 --- a/src/main/java/org/scijava/io/DataHandleService.java +++ b/src/main/java/org/scijava/io/handle/DataHandleService.java @@ -29,8 +29,10 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.handle; +import org.scijava.io.IOService; +import org.scijava.io.location.Location; import org.scijava.plugin.WrapperService; import org.scijava.service.SciJavaService; diff --git a/src/main/java/org/scijava/io/DefaultDataHandleService.java b/src/main/java/org/scijava/io/handle/DefaultDataHandleService.java similarity index 96% rename from src/main/java/org/scijava/io/DefaultDataHandleService.java rename to src/main/java/org/scijava/io/handle/DefaultDataHandleService.java index dc98ce28f..02123344a 100644 --- a/src/main/java/org/scijava/io/DefaultDataHandleService.java +++ b/src/main/java/org/scijava/io/handle/DefaultDataHandleService.java @@ -29,8 +29,9 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.handle; +import org.scijava.io.location.Location; import org.scijava.plugin.AbstractWrapperService; import org.scijava.plugin.Plugin; import org.scijava.service.Service; diff --git a/src/main/java/org/scijava/io/FileHandle.java b/src/main/java/org/scijava/io/handle/FileHandle.java similarity index 98% rename from src/main/java/org/scijava/io/FileHandle.java rename to src/main/java/org/scijava/io/handle/FileHandle.java index f3ac1f422..8d60a27ee 100644 --- a/src/main/java/org/scijava/io/FileHandle.java +++ b/src/main/java/org/scijava/io/handle/FileHandle.java @@ -29,11 +29,12 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.handle; import java.io.IOException; import java.io.RandomAccessFile; +import org.scijava.io.location.FileLocation; import org.scijava.plugin.Plugin; /** diff --git a/src/main/java/org/scijava/io/AbstractLocation.java b/src/main/java/org/scijava/io/location/AbstractLocation.java similarity index 98% rename from src/main/java/org/scijava/io/AbstractLocation.java rename to src/main/java/org/scijava/io/location/AbstractLocation.java index 8937d3847..89d1402e3 100644 --- a/src/main/java/org/scijava/io/AbstractLocation.java +++ b/src/main/java/org/scijava/io/location/AbstractLocation.java @@ -29,7 +29,7 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.location; /** * Abstract base class for {@link Location} implementations. diff --git a/src/main/java/org/scijava/io/BytesLocation.java b/src/main/java/org/scijava/io/location/BytesLocation.java similarity index 98% rename from src/main/java/org/scijava/io/BytesLocation.java rename to src/main/java/org/scijava/io/location/BytesLocation.java index 8070dc225..dc41fbade 100644 --- a/src/main/java/org/scijava/io/BytesLocation.java +++ b/src/main/java/org/scijava/io/location/BytesLocation.java @@ -29,7 +29,7 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.location; import java.nio.ByteBuffer; diff --git a/src/main/java/org/scijava/io/FileLocation.java b/src/main/java/org/scijava/io/location/FileLocation.java similarity index 98% rename from src/main/java/org/scijava/io/FileLocation.java rename to src/main/java/org/scijava/io/location/FileLocation.java index 3d93ee12c..31a331bd8 100644 --- a/src/main/java/org/scijava/io/FileLocation.java +++ b/src/main/java/org/scijava/io/location/FileLocation.java @@ -29,7 +29,7 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.location; import java.io.File; import java.net.URI; diff --git a/src/main/java/org/scijava/io/Location.java b/src/main/java/org/scijava/io/location/Location.java similarity index 96% rename from src/main/java/org/scijava/io/Location.java rename to src/main/java/org/scijava/io/location/Location.java index 34b79f63b..ec2e7c363 100644 --- a/src/main/java/org/scijava/io/Location.java +++ b/src/main/java/org/scijava/io/location/Location.java @@ -29,10 +29,12 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.location; import java.net.URI; +import org.scijava.io.handle.DataHandle; + /** * A location is a data descriptor, such as a file on disk, a remote * URL, or a database connection. diff --git a/src/main/java/org/scijava/io/URILocation.java b/src/main/java/org/scijava/io/location/URILocation.java similarity index 99% rename from src/main/java/org/scijava/io/URILocation.java rename to src/main/java/org/scijava/io/location/URILocation.java index 819fb2282..1c4045955 100644 --- a/src/main/java/org/scijava/io/URILocation.java +++ b/src/main/java/org/scijava/io/location/URILocation.java @@ -29,7 +29,7 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.location; import java.io.UnsupportedEncodingException; import java.net.URI; diff --git a/src/main/java/org/scijava/io/URLLocation.java b/src/main/java/org/scijava/io/location/URLLocation.java similarity index 98% rename from src/main/java/org/scijava/io/URLLocation.java rename to src/main/java/org/scijava/io/location/URLLocation.java index 1b7490894..24789ff14 100644 --- a/src/main/java/org/scijava/io/URLLocation.java +++ b/src/main/java/org/scijava/io/location/URLLocation.java @@ -29,7 +29,7 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.location; import java.net.URI; import java.net.URISyntaxException; diff --git a/src/test/java/org/scijava/ContextCreationTest.java b/src/test/java/org/scijava/ContextCreationTest.java index 449fcc26f..6f364ddfc 100644 --- a/src/test/java/org/scijava/ContextCreationTest.java +++ b/src/test/java/org/scijava/ContextCreationTest.java @@ -95,9 +95,9 @@ public void testFull() { org.scijava.display.DefaultDisplayService.class, org.scijava.event.DefaultEventHistory.class, org.scijava.input.DefaultInputService.class, - org.scijava.io.DefaultDataHandleService.class, org.scijava.io.DefaultIOService.class, org.scijava.io.DefaultRecentFileService.class, + org.scijava.io.handle.DefaultDataHandleService.class, org.scijava.io.nio.DefaultNIOService.class, org.scijava.main.DefaultMainService.class, org.scijava.menu.DefaultMenuService.class, diff --git a/src/test/java/org/scijava/io/DataHandleTest.java b/src/test/java/org/scijava/io/handle/DataHandleTest.java similarity index 97% rename from src/test/java/org/scijava/io/DataHandleTest.java rename to src/test/java/org/scijava/io/handle/DataHandleTest.java index 185f47881..c91b7c4ea 100644 --- a/src/test/java/org/scijava/io/DataHandleTest.java +++ b/src/test/java/org/scijava/io/handle/DataHandleTest.java @@ -29,7 +29,7 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.handle; import static org.junit.Assert.assertEquals; @@ -41,6 +41,9 @@ import org.junit.Test; import org.scijava.Context; +import org.scijava.io.handle.DataHandle; +import org.scijava.io.handle.DataHandleService; +import org.scijava.io.location.Location; import org.scijava.util.Bytes; /** diff --git a/src/test/java/org/scijava/io/FileHandleTest.java b/src/test/java/org/scijava/io/handle/FileHandleTest.java similarity index 91% rename from src/test/java/org/scijava/io/FileHandleTest.java rename to src/test/java/org/scijava/io/handle/FileHandleTest.java index ed8effa58..fd754a673 100644 --- a/src/test/java/org/scijava/io/FileHandleTest.java +++ b/src/test/java/org/scijava/io/handle/FileHandleTest.java @@ -29,12 +29,17 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.handle; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import org.scijava.io.handle.DataHandle; +import org.scijava.io.handle.FileHandle; +import org.scijava.io.location.FileLocation; +import org.scijava.io.location.Location; + /** * Tests {@link FileHandle}. * diff --git a/src/test/java/org/scijava/io/BytesLocationTest.java b/src/test/java/org/scijava/io/location/BytesLocationTest.java similarity index 97% rename from src/test/java/org/scijava/io/BytesLocationTest.java rename to src/test/java/org/scijava/io/location/BytesLocationTest.java index 014dff813..b406e76eb 100644 --- a/src/test/java/org/scijava/io/BytesLocationTest.java +++ b/src/test/java/org/scijava/io/location/BytesLocationTest.java @@ -29,12 +29,13 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.location; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import org.junit.Test; +import org.scijava.io.location.BytesLocation; /** * Tests {@link BytesLocation}. diff --git a/src/test/java/org/scijava/io/FileLocationTest.java b/src/test/java/org/scijava/io/location/FileLocationTest.java similarity index 96% rename from src/test/java/org/scijava/io/FileLocationTest.java rename to src/test/java/org/scijava/io/location/FileLocationTest.java index 60b95e1b0..1c68568f6 100644 --- a/src/test/java/org/scijava/io/FileLocationTest.java +++ b/src/test/java/org/scijava/io/location/FileLocationTest.java @@ -29,13 +29,14 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.location; import static org.junit.Assert.assertEquals; import java.io.File; import org.junit.Test; +import org.scijava.io.location.FileLocation; /** * Tests {@link FileLocation}. diff --git a/src/test/java/org/scijava/io/URILocationTest.java b/src/test/java/org/scijava/io/location/URILocationTest.java similarity index 96% rename from src/test/java/org/scijava/io/URILocationTest.java rename to src/test/java/org/scijava/io/location/URILocationTest.java index 866837b04..251242f3e 100644 --- a/src/test/java/org/scijava/io/URILocationTest.java +++ b/src/test/java/org/scijava/io/location/URILocationTest.java @@ -29,7 +29,7 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.location; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; @@ -39,6 +39,7 @@ import java.util.Map; import org.junit.Test; +import org.scijava.io.location.URILocation; /** * Tests {@link URILocation}. diff --git a/src/test/java/org/scijava/io/URLLocationTest.java b/src/test/java/org/scijava/io/location/URLLocationTest.java similarity index 96% rename from src/test/java/org/scijava/io/URLLocationTest.java rename to src/test/java/org/scijava/io/location/URLLocationTest.java index 0fde7d087..191102fe4 100644 --- a/src/test/java/org/scijava/io/URLLocationTest.java +++ b/src/test/java/org/scijava/io/location/URLLocationTest.java @@ -29,7 +29,7 @@ * #L% */ -package org.scijava.io; +package org.scijava.io.location; import static org.junit.Assert.assertSame; @@ -37,6 +37,7 @@ import java.net.URL; import org.junit.Test; +import org.scijava.io.location.URLLocation; /** * Tests {@link URLLocation}. From 195438da5fa9ce4c2da7283925af3473f9ff555e Mon Sep 17 00:00:00 2001 From: Gabriel Einsdorf Date: Tue, 14 Mar 2017 13:48:43 +0100 Subject: [PATCH 050/754] Location: tweak javadoc Clarify that locations can be read and/or write. --- src/main/java/org/scijava/io/location/Location.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/io/location/Location.java b/src/main/java/org/scijava/io/location/Location.java index ec2e7c363..7dc000855 100644 --- a/src/main/java/org/scijava/io/location/Location.java +++ b/src/main/java/org/scijava/io/location/Location.java @@ -44,7 +44,7 @@ * resource identifier ({@link URI}), a location identifies where * the data resides, without necessarily specifying how to access that * data. The {@link DataHandle} interface defines a plugin that knows how to - * provide a stream of bytes for a particular kind of location. + * read and/or write bytes for a particular kind of location. *

    * * @author Curtis Rueden From b87dd694c31ca4a779de731d7cc2034bb2fc4071 Mon Sep 17 00:00:00 2001 From: Gabriel Einsdorf Date: Tue, 14 Mar 2017 17:04:12 +0100 Subject: [PATCH 051/754] Location: add getName() method The name returned is meant as an analogue to a file name and might be used for meta-data purposes. Signed-off-by: Curtis Rueden --- src/main/java/org/scijava/io/location/Location.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/org/scijava/io/location/Location.java b/src/main/java/org/scijava/io/location/Location.java index 7dc000855..da96a1f98 100644 --- a/src/main/java/org/scijava/io/location/Location.java +++ b/src/main/java/org/scijava/io/location/Location.java @@ -48,6 +48,7 @@ *

    * * @author Curtis Rueden + * @author Gabriel Einsdorf */ public interface Location { @@ -59,4 +60,13 @@ default URI getURI() { return null; } + /** + * Gets the name of the object addressed by this location, or an empty string + * if it has no name. + */ + default String getName() { + final URI uri = getURI(); + return uri == null ? "" : uri.toString(); + } + } From 64aa6873fb38b3e994c0e901d659f89ddb07e87b Mon Sep 17 00:00:00 2001 From: Gabriel Einsdorf Date: Wed, 15 Mar 2017 15:22:30 +0100 Subject: [PATCH 052/754] AbstractLocation: add hashCode() and equals() Locations need to be properly distinguishable. This change makes it feasible to put Location objects into a HashMap and to compare them using the equals() method. --- .../scijava/io/location/AbstractLocation.java | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/io/location/AbstractLocation.java b/src/main/java/org/scijava/io/location/AbstractLocation.java index 89d1402e3..2d9d238e2 100644 --- a/src/main/java/org/scijava/io/location/AbstractLocation.java +++ b/src/main/java/org/scijava/io/location/AbstractLocation.java @@ -31,11 +31,30 @@ package org.scijava.io.location; +import java.util.Objects; + /** * Abstract base class for {@link Location} implementations. * * @author Curtis Rueden */ public abstract class AbstractLocation implements Location { - // NB: No implementation needed. + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((getURI() == null) ? 0 : getURI().hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + final Location other = (Location) obj; + return Objects.equals(getURI(), other.getURI()); + } + } From fd5bf70618eed48fce1cf0fc780e98921f2d86fa Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Sun, 3 Jul 2016 11:10:10 -0500 Subject: [PATCH 053/754] DataHandle: update some javadoc We avoid the term "pointer" (especially "file pointer"). DataHandle plugins are more general than just files. --- .../java/org/scijava/io/handle/DataHandle.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 9f6d1e3dc..768b2801e 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -126,8 +126,8 @@ default int read(final ByteBuffer buf, final int len) throws IOException { } /** - * Sets the stream pointer offset, measured from the beginning of the stream, - * at which the next read or write occurs. + * Sets the stream offset, measured from the beginning of the stream, at which + * the next read or write occurs. */ void seek(long pos) throws IOException; @@ -203,9 +203,8 @@ default String findString(final String... terminators) throws IOException { * Reads or skips a string ending with one of the given terminating * substrings. * - * @param saveString Whether to collect the string from the current file - * pointer to the terminating bytes, and return it. If false, returns - * null. + * @param saveString Whether to collect the string from the current offset to + * the terminating bytes, and return it. If false, returns null. * @param terminators The strings for which to search. * @throws IOException If saveString flag is set and the maximum search length * (512 MB) is exceeded. @@ -239,9 +238,8 @@ default String findString(final int blockSize, final String... terminators) * Reads or skips a string ending with one of the given terminating * substrings, using the specified block size for buffering. * - * @param saveString Whether to collect the string from the current file - * pointer to the terminating bytes, and return it. If false, returns - * null. + * @param saveString Whether to collect the string from the current offset + * to the terminating bytes, and return it. If false, returns null. * @param blockSize The block size to use when reading bytes in chunks. * @param terminators The strings for which to search. * @throws IOException If saveString flag is set and the maximum search length From c134f4afe31deed764fd5d6d22982f3034b1cd66 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 1 Jul 2016 23:04:28 -0500 Subject: [PATCH 054/754] DataHandle: provide some default implementations --- .../org/scijava/io/handle/DataHandle.java | 102 +++++++++++++++++- 1 file changed, 101 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 768b2801e..3fae84f1b 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -331,7 +331,9 @@ default String findString(final boolean saveString, final int blockSize, * @return the next byte of data, or -1 if the end of the stream is reached. * @throws IOException - if an I/O error occurs. */ - int read() throws IOException; + default int read() throws IOException { + return offset() < length() ? readByte() & 0xff : -1; + } /** * Reads up to b.length bytes of data from the stream into an array of bytes. @@ -369,4 +371,102 @@ default long skip(final long n) throws IOException { return num; } + // -- DataInput methods -- + + @Override + default boolean readBoolean() throws IOException { + return readByte() != 0; + } + + @Override + default void readFully(final byte[] b) throws IOException { + readFully(b, 0, b.length); + } + + @Override + default int readUnsignedByte() throws IOException { + return readByte() & 0xff; + } + + @Override + default int readUnsignedShort() throws IOException { + return readShort() & 0xffff; + } + + @Override + default String readLine() throws IOException { + // NB: Code adapted from java.io.RandomAccessFile.readLine(). + + final StringBuffer input = new StringBuffer(); + int c = -1; + boolean eol = false; + + while (!eol) { + switch (c = read()) { + case -1: + case '\n': + eol = true; + break; + case '\r': + eol = true; + long cur = offset(); + if (read() != '\n') seek(cur); + break; + default: + input.append((char)c); + break; + } + } + + if (c == -1 && input.length() == 0) { + return null; + } + return input.toString(); + } + + @Override + default String readUTF() throws IOException { + final int length = readUnsignedShort(); + final byte[] b = new byte[length]; + read(b); + return new String(b, "UTF-8"); + } + + @Override + default int skipBytes(final int n) throws IOException { + final int skipped = (int) Math.min(n, length() - offset()); + if (skipped < 0) return 0; + seek(offset() + skipped); + return skipped; + } + + // -- DataOutput methods -- + + @Override + default void write(final byte[] b) throws IOException { + write(b, 0, b.length); + } + + @Override + default void writeBoolean(final boolean v) throws IOException { + write(v ? 1 : 0); + } + + @Override + default void writeByte(final int v) throws IOException { + write(v); + } + + @Override + default void writeBytes(final String s) throws IOException { + write(s.getBytes("UTF-8")); + } + + @Override + default void writeUTF(final String str) throws IOException { + final byte[] b = str.getBytes("UTF-8"); + writeShort(b.length); + write(b); + } + } From 05a0d2bed4a7dfe4804403a136f5a20a0a16659c Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 1 Jul 2016 23:12:43 -0500 Subject: [PATCH 055/754] DataHandle: add a mutator for the length In SCIFIO and Bio-Formats, this was a feature of the AbstractNIOHandle. But there is no reason to limit it to NIO-flavored handles only. --- src/main/java/org/scijava/io/handle/DataHandle.java | 8 ++++++++ src/main/java/org/scijava/io/handle/FileHandle.java | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 3fae84f1b..adb88b394 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -66,6 +66,14 @@ public interface DataHandle extends WrapperPlugin, /** Returns the length of the stream. */ long length() throws IOException; + /** + * Sets the new length of the handle. + * + * @param length New length. + * @throws IOException If there is an error changing the handle's length. + */ + void setLength(long length) throws IOException; + /** * Returns the current order of the stream. * diff --git a/src/main/java/org/scijava/io/handle/FileHandle.java b/src/main/java/org/scijava/io/handle/FileHandle.java index 8d60a27ee..944e384d5 100644 --- a/src/main/java/org/scijava/io/handle/FileHandle.java +++ b/src/main/java/org/scijava/io/handle/FileHandle.java @@ -83,6 +83,11 @@ public long length() throws IOException { return raf().length(); } + @Override + public void setLength(final long length) throws IOException { + raf().setLength(length); + } + @Override public int read() throws IOException { return raf().read(); From efec596db50a5715326082bd654dca745f17b2ed Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 1 Jul 2016 23:15:24 -0500 Subject: [PATCH 056/754] DataHandle: add ensureWritable method In SCIFIO and Bio-Formats, this was present as the method AbstractNIOHandle#validateLength. But there is no reason to limit it to NIO-flavored handles only. We use the name ensureWritable for clarity, and for symmetry with a future ensureReadable method. --- .../org/scijava/io/handle/DataHandle.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index adb88b394..3f52f1705 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -74,6 +74,25 @@ public interface DataHandle extends WrapperPlugin, */ void setLength(long length) throws IOException; + /** + * Ensures that the handle has the correct length to be written to and extends + * it as required. + * + * @param count Number of bytes to write. + * @return {@code true} if the handle's length was sufficient, or + * {@code false} if the handle's length required an extension. + * @throws IOException If something goes wrong with the check, or there is an + * error changing the handle's length. + */ + default boolean ensureWritable(final long count) throws IOException { + final long minLength = offset() + count; + if (length() < minLength) { + setLength(minLength); + return false; + } + return true; + } + /** * Returns the current order of the stream. * From e36a8fa44ae4ad3be080b4e1e4a02fd657ab4382 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 20:56:55 -0500 Subject: [PATCH 057/754] DataHandle: add available and ensureReadable --- .../org/scijava/io/handle/DataHandle.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 3f52f1705..efdb804ae 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -34,6 +34,7 @@ import java.io.Closeable; import java.io.DataInput; import java.io.DataOutput; +import java.io.EOFException; import java.io.IOException; import java.io.InputStreamReader; import java.nio.ByteBuffer; @@ -74,6 +75,32 @@ public interface DataHandle extends WrapperPlugin, */ void setLength(long length) throws IOException; + /** + * Verifies that the handle has sufficient bytes available to read, returning + * the actual number of bytes which will be possible to read, which might + * be less than the requested value. + * + * @param count Number of bytes to read. + * @return The actual number of bytes available to be read. + * @throws IOException If something goes wrong with the check. + */ + default long available(final long count) throws IOException { + final long remain = length() - offset(); + return remain < count ? remain : count; + } + + /** + * Ensures that the handle has sufficient bytes available to read. + * + * @param count Number of bytes to read. + * @see #available(long) + * @throws EOFException If there are insufficient bytes available. + * @throws IOException If something goes wrong with the check. + */ + default void ensureReadable(final long count) throws IOException { + if (available(count) < count) throw new EOFException(); + } + /** * Ensures that the handle has the correct length to be written to and extends * it as required. From 6c87eb06d5417e91c3eef92540c39f54318a17ae Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 15:49:50 -0500 Subject: [PATCH 058/754] DataHandle: add more default method impls --- .../org/scijava/io/handle/DataHandle.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index efdb804ae..52d27fe35 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -442,11 +442,68 @@ default int readUnsignedByte() throws IOException { return readByte() & 0xff; } + @Override + default short readShort() throws IOException { + final int ch1 = read(); + final int ch2 = read(); + if ((ch1 | ch2) < 0) throw new EOFException(); + return (short) ((ch1 << 8) + (ch2 << 0)); + } + @Override default int readUnsignedShort() throws IOException { return readShort() & 0xffff; } + @Override + default char readChar() throws IOException { + return (char) readShort(); + } + + @Override + default int readInt() throws IOException { + int ch1 = read(); + int ch2 = read(); + int ch3 = read(); + int ch4 = read(); + if ((ch1 | ch2 | ch3 | ch4) < 0) throw new EOFException(); + return ((ch1 << 24) + (ch2 << 16) + (ch3 << 8) + (ch4 << 0)); + } + + @Override + default long readLong() throws IOException { + int ch1 = read(); + int ch2 = read(); + int ch3 = read(); + int ch4 = read(); + int ch5 = read(); + int ch6 = read(); + int ch7 = read(); + int ch8 = read(); + if ((ch1 | ch2 | ch3 | ch4 | ch5 | ch6 | ch7 | ch8) < 0) { + throw new EOFException(); + } + // TODO: Double check this inconsistent code. + return ((long) ch1 << 56) + // + ((long) (ch2 & 255) << 48) + // + ((long) (ch3 & 255) << 40) + // + ((long) (ch4 & 255) << 32) + // + ((long) (ch5 & 255) << 24) + // + ((ch6 & 255) << 16) + // + ((ch7 & 255) << 8) + // + ((ch8 & 255) << 0); + } + + @Override + default float readFloat() throws IOException { + return Float.intBitsToFloat(readInt()); + } + + @Override + default double readDouble() throws IOException { + return Double.longBitsToDouble(readLong()); + } + @Override default String readLine() throws IOException { // NB: Code adapted from java.io.RandomAccessFile.readLine(). @@ -511,6 +568,42 @@ default void writeByte(final int v) throws IOException { write(v); } + @Override + default void writeChar(final int v) throws IOException { + write((v >>> 8) & 0xFF); + write((v >>> 0) & 0xFF); + } + + @Override + default void writeInt(final int v) throws IOException { + write((v >>> 24) & 0xFF); + write((v >>> 16) & 0xFF); + write((v >>> 8) & 0xFF); + write((v >>> 0) & 0xFF); + } + + @Override + default void writeLong(final long v) throws IOException { + write((byte) (v >>> 56)); + write((byte) (v >>> 48)); + write((byte) (v >>> 40)); + write((byte) (v >>> 32)); + write((byte) (v >>> 24)); + write((byte) (v >>> 16)); + write((byte) (v >>> 8)); + write((byte) (v >>> 0)); + } + + @Override + default void writeFloat(final float v) throws IOException { + writeInt(Float.floatToIntBits(v)); + } + + @Override + default void writeDouble(final double v) throws IOException { + writeLong(Double.doubleToLongBits(v)); + } + @Override default void writeBytes(final String s) throws IOException { write(s.getBytes("UTF-8")); From fc853ff1acb6f15b7348ec9aaa503c44fb955dcb Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 15:51:10 -0500 Subject: [PATCH 059/754] DataHandle: improve class javadoc --- src/main/java/org/scijava/io/handle/DataHandle.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 52d27fe35..ae0143c26 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -44,8 +44,8 @@ import org.scijava.plugin.WrapperPlugin; /** - * A data handle is a plugin which provides access to bytes in a data - * stream (e.g., files or arrays), identified by a {@link Location}. + * A data handle is a plugin which provides both streaming and random + * access to bytes at a {@link Location} (e.g., files or arrays). * * @author Curtis Rueden * @see DataHandleInputStream From b948c95431cdd15ecd2dcf31db357d83ac749442 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 15:51:54 -0500 Subject: [PATCH 060/754] DataHandle: tweak getOrder() javadoc --- src/main/java/org/scijava/io/handle/DataHandle.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index ae0143c26..e6809d90f 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -120,11 +120,7 @@ default boolean ensureWritable(final long count) throws IOException { return true; } - /** - * Returns the current order of the stream. - * - * @return See above. - */ + /** Returns the byte order of the stream. */ ByteOrder getOrder(); /** Gets the endianness of the stream. */ From 9dabafa5be25ca92922378478e48998eeacb18f5 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 15:53:41 -0500 Subject: [PATCH 061/754] DataHandle: relocate the seek method It is the mutator which goes with offset(), so should be adjacent to it. --- src/main/java/org/scijava/io/handle/DataHandle.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index e6809d90f..545580da0 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -64,6 +64,12 @@ public interface DataHandle extends WrapperPlugin, /** Returns the current offset in the stream. */ long offset() throws IOException; + /** + * Sets the stream offset, measured from the beginning of the stream, at which + * the next read or write occurs. + */ + void seek(long pos) throws IOException; + /** Returns the length of the stream. */ long length() throws IOException; @@ -175,12 +181,6 @@ default int read(final ByteBuffer buf, final int len) throws IOException { return n; } - /** - * Sets the stream offset, measured from the beginning of the stream, at which - * the next read or write occurs. - */ - void seek(long pos) throws IOException; - /** * Writes up to {@code buf.remaining()} bytes of data from the given * {@link ByteBuffer} to the stream. From 90f2d418f76d1b6dcaa87d967f8cc5ffb4ad3604 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 15:55:07 -0500 Subject: [PATCH 062/754] DataHandle: relocate the isLittleEndian() method It belongs immediately before the (to be renamed) setOrder(boolean). --- src/main/java/org/scijava/io/handle/DataHandle.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 545580da0..779923800 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -129,11 +129,6 @@ default boolean ensureWritable(final long count) throws IOException { /** Returns the byte order of the stream. */ ByteOrder getOrder(); - /** Gets the endianness of the stream. */ - default boolean isLittleEndian() { - return getOrder() == ByteOrder.LITTLE_ENDIAN; - } - /** * Sets the byte order of the stream. * @@ -141,6 +136,11 @@ default boolean isLittleEndian() { */ void setOrder(ByteOrder order); + /** Gets the endianness of the stream. */ + default boolean isLittleEndian() { + return getOrder() == ByteOrder.LITTLE_ENDIAN; + } + /** Sets the endianness of the stream. */ default void setOrder(final boolean little) { setOrder(little ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); From d6a4271687c02d2d2132d9f2097b0a8400fe3606 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 15:56:23 -0500 Subject: [PATCH 063/754] DataHandle: fix up the endianness API Now we have both isLittleEndian() and isBigEndian() accessors, and the setOrder(boolean) method is now setLittleEndian(boolean). --- .../org/scijava/io/handle/DataHandle.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 779923800..dd87f5a67 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -136,13 +136,32 @@ default boolean ensureWritable(final long count) throws IOException { */ void setOrder(ByteOrder order); - /** Gets the endianness of the stream. */ + /** + * Returns true iff the stream's order is {@link ByteOrder#BIG_ENDIAN}. + * + * @see #getOrder() + */ + default boolean isBigEndian() { + return getOrder() == ByteOrder.BIG_ENDIAN; + } + + /** + * Returns true iff the stream's order is {@link ByteOrder#LITTLE_ENDIAN}. + * + * @see #getOrder() + */ default boolean isLittleEndian() { return getOrder() == ByteOrder.LITTLE_ENDIAN; } - /** Sets the endianness of the stream. */ - default void setOrder(final boolean little) { + /** + * Sets the endianness of the stream. + * + * @param little If true, sets the order to {@link ByteOrder#LITTLE_ENDIAN}; + * otherwise, sets the order to {@link ByteOrder#BIG_ENDIAN}. + * @see #setOrder(ByteOrder) + */ + default void setLittleEndian(final boolean little) { setOrder(little ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN); } From aa6f96237927bec4ba678c016ca423e7c6332712 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 16:07:13 -0500 Subject: [PATCH 064/754] DataHandle: tweak comment --- src/main/java/org/scijava/io/handle/DataHandle.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index dd87f5a67..52c8c1971 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -521,7 +521,7 @@ default double readDouble() throws IOException { @Override default String readLine() throws IOException { - // NB: Code adapted from java.io.RandomAccessFile.readLine(). + // NB: Adapted from java.io.RandomAccessFile.readLine(). final StringBuffer input = new StringBuffer(); int c = -1; From b2e9316cfb7e7ebf7cec2e124dffb6cf8f2a9668 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 16:07:43 -0500 Subject: [PATCH 065/754] DataHandle: tweak readString, skip and skipBytes They can lean on the available(long) method. --- .../org/scijava/io/handle/DataHandle.java | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 52c8c1971..81a217a49 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -234,10 +234,9 @@ default String readCString() throws IOException { } /** Reads a string of up to length n. */ - default String readString(int n) throws IOException { - final long avail = length() - offset(); - if (n > avail) n = (int) avail; - final byte[] b = new byte[n]; + default String readString(final int n) throws IOException { + final int r = (int) available(n); + final byte[] b = new byte[r]; readFully(b); return new String(b, getEncoding()); } @@ -433,11 +432,10 @@ default int read(byte[] b) throws IOException { * @throws IOException - if an I/O error occurs. */ default long skip(final long n) throws IOException { - if (n < 0) return 0; - final long remain = length() - offset(); - final long num = n < remain ? n : remain; - seek(offset() + num); - return num; + final long skip = available(n); + if (skip <= 0) return 0; + seek(offset() + skip); + return skip; } // -- DataInput methods -- @@ -560,10 +558,11 @@ default String readUTF() throws IOException { @Override default int skipBytes(final int n) throws IOException { - final int skipped = (int) Math.min(n, length() - offset()); - if (skipped < 0) return 0; - seek(offset() + skipped); - return skipped; + // NB: Cast here is safe since the value of n bounds the result to an int. + final int skip = (int) available(n); + if (skip < 0) return 0; + seek(offset() + skip); + return skip; } // -- DataOutput methods -- From 6f2994fd7fa4c00ac5a3166708cede1a1d3d5e6a Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 16:10:02 -0500 Subject: [PATCH 066/754] DataHandle: relocate DataInput methods Now they match their declared order in DataInput. --- .../org/scijava/io/handle/DataHandle.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 81a217a49..6cf3147da 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -441,13 +441,22 @@ default long skip(final long n) throws IOException { // -- DataInput methods -- @Override - default boolean readBoolean() throws IOException { - return readByte() != 0; + default void readFully(final byte[] b) throws IOException { + readFully(b, 0, b.length); } @Override - default void readFully(final byte[] b) throws IOException { - readFully(b, 0, b.length); + default int skipBytes(final int n) throws IOException { + // NB: Cast here is safe since the value of n bounds the result to an int. + final int skip = (int) available(n); + if (skip < 0) return 0; + seek(offset() + skip); + return skip; + } + + @Override + default boolean readBoolean() throws IOException { + return readByte() != 0; } @Override @@ -556,15 +565,6 @@ default String readUTF() throws IOException { return new String(b, "UTF-8"); } - @Override - default int skipBytes(final int n) throws IOException { - // NB: Cast here is safe since the value of n bounds the result to an int. - final int skip = (int) available(n); - if (skip < 0) return 0; - seek(offset() + skip); - return skip; - } - // -- DataOutput methods -- @Override From daa914594be635b6d176dd6e412aa53d1e6b17bb Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 16:12:48 -0500 Subject: [PATCH 067/754] DataHandle: add readFully(byte[], int, int) impl --- .../java/org/scijava/io/handle/DataHandle.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 6cf3147da..9b697b90d 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -33,6 +33,7 @@ import java.io.Closeable; import java.io.DataInput; +import java.io.DataInputStream; import java.io.DataOutput; import java.io.EOFException; import java.io.IOException; @@ -445,6 +446,20 @@ default void readFully(final byte[] b) throws IOException { readFully(b, 0, b.length); } + @Override + default void readFully(final byte[] b, final int off, final int len) + throws IOException + { + // NB: Adapted from java.io.DataInputStream.readFully(byte[], int, int). + if (len < 0) throw new IndexOutOfBoundsException(); + int n = 0; + while (n < len) { + int count = read(b, off + n, len - n); + if (count < 0) throw new EOFException(); + n += count; + } + } + @Override default int skipBytes(final int n) throws IOException { // NB: Cast here is safe since the value of n bounds the result to an int. From 2e11aff8993812cb94e450c35d00bfbdcc1eb207 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 16:29:38 -0500 Subject: [PATCH 068/754] DataHandle: improve readUTF implementation Better to lean on the Java standard library here. --- src/main/java/org/scijava/io/handle/DataHandle.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 9b697b90d..0fea60ecc 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -574,10 +574,7 @@ default String readLine() throws IOException { @Override default String readUTF() throws IOException { - final int length = readUnsignedShort(); - final byte[] b = new byte[length]; - read(b); - return new String(b, "UTF-8"); + return DataInputStream.readUTF(this); } // -- DataOutput methods -- From 21881a9f13b62199803ec670b03bb7bc0e73ab1a Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 16:31:11 -0500 Subject: [PATCH 069/754] DataHandle: add more default DataOutput methods --- .../java/org/scijava/io/handle/DataHandle.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 0fea60ecc..c5d0e86cb 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -594,6 +594,12 @@ default void writeByte(final int v) throws IOException { write(v); } + @Override + default void writeShort(final int v) throws IOException { + write((v >>> 8) & 0xFF); + write((v >>> 0) & 0xFF); + } + @Override default void writeChar(final int v) throws IOException { write((v >>> 8) & 0xFF); @@ -635,6 +641,16 @@ default void writeBytes(final String s) throws IOException { write(s.getBytes("UTF-8")); } + @Override + default void writeChars(final String s) throws IOException { + final int len = s.length(); + for (int i = 0 ; i < len ; i++) { + final int v = s.charAt(i); + write((v >>> 8) & 0xFF); + write((v >>> 0) & 0xFF); + } + } + @Override default void writeUTF(final String str) throws IOException { final byte[] b = str.getBytes("UTF-8"); From 02b18b9fd303995c59ccd9579b0091c7c760d5ac Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 16:48:38 -0500 Subject: [PATCH 070/754] Add a utility class which knows how to write UTF The whole thing exists only to work around the fact that DataOutputStream.writeUTF(String, DataOutput), which is the method we really need, has package-protected access. This utility class grabs that method via reflection, makes it accessible, and caches the reference. --- .../org/scijava/io/handle/DataHandles.java | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/main/java/org/scijava/io/handle/DataHandles.java diff --git a/src/main/java/org/scijava/io/handle/DataHandles.java b/src/main/java/org/scijava/io/handle/DataHandles.java new file mode 100644 index 000000000..47be40ebc --- /dev/null +++ b/src/main/java/org/scijava/io/handle/DataHandles.java @@ -0,0 +1,110 @@ +/* + * #%L + * SciJava Common shared library for SciJava software. + * %% + * Copyright (C) 2009 - 2017 Board of Regents of the University of + * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck + * Institute of Molecular Cell Biology and Genetics. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package org.scijava.io.handle; + +import java.io.DataOutput; +import java.io.DataOutputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Utility methods for working with {@link DataHandle}s. + * + * @author Curtis Rueden + */ +public final class DataHandles { + + private static Method utfMethod; + + private DataHandles() { + // Prevent instantiation of utility class. + } + + /** + * Writes a string to the specified DataOutput using modified UTF-8 encoding + * in a machine-independent manner. + *

    + * First, two bytes are written to out as if by the {@code writeShort} method + * giving the number of bytes to follow. This value is the number of bytes + * actually written out, not the length of the string. Following the length, + * each character of the string is output, in sequence, using the modified + * UTF-8 encoding for the character. If no exception is thrown, the counter + * {@code written} is incremented by the total number of bytes written to the + * output stream. This will be at least two plus the length of {@code str}, + * and at most two plus thrice the length of {@code str}. + *

    + * + * @param str a string to be written. + * @param out destination to write to + * @return The number of bytes written out. + * @throws IOException if an I/O error occurs. + */ + public static int writeUTF(final String str, final DataOutput out) + throws IOException + { + // HACK: Strangely, DataOutputStream.writeUTF(String, DataOutput) + // has package-private access. We work around it via reflection. + try { + return (Integer) utfMethod().invoke(null, str, out); + } + catch (final IllegalAccessException | IllegalArgumentException + | InvocationTargetException exc) + { + throw new IllegalStateException( + "Cannot invoke DataOutputStream.writeUTF(String, DataOutput)", exc); + } + } + + // -- Helper methods -- + + /** Gets the {@link #utfMethod} field, initializing if needed. */ + private static Method utfMethod() { + if (utfMethod == null) initUTFMethod(); + return utfMethod; + } + + /** Initializes the {@link #utfMethod} field. */ + private static synchronized void initUTFMethod() { + if (utfMethod != null) return; + try { + final Method m = DataOutputStream.class.getDeclaredMethod("writeUTF", + String.class, DataOutput.class); + m.setAccessible(true); + utfMethod = m; + } + catch (final NoSuchMethodException | SecurityException exc) { + throw new IllegalStateException( + "No usable DataOutputStream.writeUTF(String, DataOutput)", exc); + } + } +} From e702e9195d878477eae011f70fdaff708ecf125c Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 16 Jun 2017 16:52:35 -0500 Subject: [PATCH 071/754] DataHandle: simplify writeUTF method Now it leans on the Java standard library (indirectly). --- src/main/java/org/scijava/io/handle/DataHandle.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index c5d0e86cb..7f7f39f03 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -653,9 +653,7 @@ default void writeChars(final String s) throws IOException { @Override default void writeUTF(final String str) throws IOException { - final byte[] b = str.getBytes("UTF-8"); - writeShort(b.length); - write(b); + DataHandles.writeUTF(str, this); } } From 5b1d44f591b232a8c8560de344af9fbd088f2c04 Mon Sep 17 00:00:00 2001 From: Gabriel Einsdorf Date: Tue, 14 Mar 2017 14:33:27 +0100 Subject: [PATCH 072/754] DataHandle: improve javadoc --- .../org/scijava/io/handle/DataHandle.java | 41 ++++++++++++++----- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 7f7f39f03..7aa5fe255 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -71,7 +71,7 @@ public interface DataHandle extends WrapperPlugin, */ void seek(long pos) throws IOException; - /** Returns the length of the stream. */ + /** Returns the length of the data in bytes. */ long length() throws IOException; /** @@ -83,12 +83,31 @@ public interface DataHandle extends WrapperPlugin, void setLength(long length) throws IOException; /** - * Verifies that the handle has sufficient bytes available to read, returning - * the actual number of bytes which will be possible to read, which might - * be less than the requested value. + * Gets the number of bytes which can be safely read from, or written to, the + * data handle, bounded by the specified number of bytes. + *

    + * In the case of reading, attempting to read the returned number of bytes is + * guaranteed not to throw {@link EOFException}. However, be aware that the + * following methods might still process fewer bytes than indicated + * by this method: + *

    + *
      + *
    • {@link #read(ByteBuffer)}
    • + *
    • {@link #read(ByteBuffer, int)}
    • + *
    • {@link #read(byte[])}
    • + *
    • {@link #read(byte[], int, int)}
    • + *
    • {@link #skip(long)}
    • + *
    • {@link #skipBytes(int)}
    • + *
    + *

    + * In the case of writing, attempting to write the returned number of bytes is + * guaranteed not to expand the length of the handle; i.e., the write will + * only overwrite bytes already within the handle's bounds. + *

    * - * @param count Number of bytes to read. - * @return The actual number of bytes available to be read. + * @param count Desired number of bytes to read/write. + * @return The actual number of bytes which could be safely read/written, + * which might be less than the requested value. * @throws IOException If something goes wrong with the check. */ default long available(final long count) throws IOException { @@ -109,8 +128,8 @@ default void ensureReadable(final long count) throws IOException { } /** - * Ensures that the handle has the correct length to be written to and extends - * it as required. + * Ensures that the handle has the correct length to be written to, and + * extends it as required. * * @param count Number of bytes to write. * @return {@code true} if the handle's length was sufficient, or @@ -202,7 +221,7 @@ default int read(final ByteBuffer buf, final int len) throws IOException { } /** - * Writes up to {@code buf.remaining()} bytes of data from the given + * Writes {@code buf.remaining()} bytes of data from the given * {@link ByteBuffer} to the stream. */ default void write(final ByteBuffer buf) throws IOException { @@ -210,7 +229,8 @@ default void write(final ByteBuffer buf) throws IOException { } /** - * Writes up to len bytes of data from the given ByteBuffer to the stream. + * Writes {@code len} bytes of data from the given {@link ByteBuffer} to the + * stream. */ default void write(final ByteBuffer buf, final int len) throws IOException @@ -227,7 +247,6 @@ default void write(final ByteBuffer buf, final int len) } } - /** Reads a string of arbitrary length, terminated by a null char. */ default String readCString() throws IOException { final String line = findString("\0"); From 795295b64a09c647a24363b2144a4a2c72061948 Mon Sep 17 00:00:00 2001 From: Gabriel Einsdorf Date: Mon, 20 Mar 2017 15:53:50 +0100 Subject: [PATCH 073/754] DataHandle: add writeLine(String) method This is analogous to readLine(). --- src/main/java/org/scijava/io/handle/DataHandle.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 7aa5fe255..f71b3df28 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -411,6 +411,17 @@ default String findString(final boolean saveString, final int blockSize, return saveString ? out.toString() : null; } + /** + * Writes the provided string, followed by a newline character. + * + * @param string The string to write. + * @throws IOException If an I/O error occurs. + */ + default void writeLine(final String string) throws IOException { + writeBytes(string); + writeBytes("\n"); + } + // -- InputStream look-alikes -- /** From d978412359ffabb27d8989e797b77bae0e565637 Mon Sep 17 00:00:00 2001 From: Gabriel Einsdorf Date: Fri, 7 Jul 2017 14:15:48 +0200 Subject: [PATCH 074/754] DataHandle: improve javadoc --- .../org/scijava/io/handle/DataHandle.java | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index f71b3df28..9f2afe5ba 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -83,7 +83,7 @@ public interface DataHandle extends WrapperPlugin, void setLength(long length) throws IOException; /** - * Gets the number of bytes which can be safely read from, or written to, the + * Gets the number of bytes which can be read from, or written to, the * data handle, bounded by the specified number of bytes. *

    * In the case of reading, attempting to read the returned number of bytes is @@ -104,9 +104,9 @@ public interface DataHandle extends WrapperPlugin, * guaranteed not to expand the length of the handle; i.e., the write will * only overwrite bytes already within the handle's bounds. *

    - * + * * @param count Desired number of bytes to read/write. - * @return The actual number of bytes which could be safely read/written, + * @return The actual number of bytes which could be read/written, * which might be less than the requested value. * @throws IOException If something goes wrong with the check. */ @@ -151,14 +151,14 @@ default boolean ensureWritable(final long count) throws IOException { /** * Sets the byte order of the stream. - * + * * @param order Order to set. */ void setOrder(ByteOrder order); /** * Returns true iff the stream's order is {@link ByteOrder#BIG_ENDIAN}. - * + * * @see #getOrder() */ default boolean isBigEndian() { @@ -167,7 +167,7 @@ default boolean isBigEndian() { /** * Returns true iff the stream's order is {@link ByteOrder#LITTLE_ENDIAN}. - * + * * @see #getOrder() */ default boolean isLittleEndian() { @@ -176,7 +176,7 @@ default boolean isLittleEndian() { /** * Sets the endianness of the stream. - * + * * @param little If true, sets the order to {@link ByteOrder#LITTLE_ENDIAN}; * otherwise, sets the order to {@link ByteOrder#BIG_ENDIAN}. * @see #setOrder(ByteOrder) @@ -290,7 +290,7 @@ default String findString(final String... terminators) throws IOException { /** * Reads or skips a string ending with one of the given terminating * substrings. - * + * * @param saveString Whether to collect the string from the current offset to * the terminating bytes, and return it. If false, returns null. * @param terminators The strings for which to search. @@ -309,7 +309,7 @@ default String findString(final boolean saveString, /** * Reads a string ending with one of the given terminating substrings, using * the specified block size for buffering. - * + * * @param blockSize The block size to use when reading bytes in chunks. * @param terminators The strings for which to search. * @return The string from the initial position through the end of the @@ -325,9 +325,9 @@ default String findString(final int blockSize, final String... terminators) /** * Reads or skips a string ending with one of the given terminating * substrings, using the specified block size for buffering. - * - * @param saveString Whether to collect the string from the current offset - * to the terminating bytes, and return it. If false, returns null. + * + * @param saveString Whether to collect the string from the current offset to + * the terminating bytes, and return it. If false, returns null. * @param blockSize The block size to use when reading bytes in chunks. * @param terminators The strings for which to search. * @throws IOException If saveString flag is set and the maximum search length @@ -354,8 +354,8 @@ default String findString(final boolean saveString, final int blockSize, } @SuppressWarnings("resource") - final InputStreamReader in = - new InputStreamReader(new DataHandleInputStream<>(this), getEncoding()); + final InputStreamReader in = new InputStreamReader( + new DataHandleInputStream<>(this), getEncoding()); final char[] buf = new char[blockSize]; long loc = 0; while (loc < maxLen && offset() < length() - 1) { @@ -426,7 +426,7 @@ default void writeLine(final String string) throws IOException { /** * Reads the next byte of data from the stream. - * + * * @return the next byte of data, or -1 if the end of the stream is reached. * @throws IOException - if an I/O error occurs. */ @@ -436,7 +436,7 @@ default int read() throws IOException { /** * Reads up to b.length bytes of data from the stream into an array of bytes. - * + * * @return the total number of bytes read into the buffer. */ default int read(byte[] b) throws IOException { @@ -445,7 +445,7 @@ default int read(byte[] b) throws IOException { /** * Reads up to len bytes of data from the stream into an array of bytes. - * + * * @return the total number of bytes read into the buffer. */ int read(byte[] b, int off, int len) throws IOException; @@ -457,7 +457,7 @@ default int read(byte[] b) throws IOException { * of a number of conditions; reaching end of file before {@code n} bytes have * been skipped is only one possibility. The actual number of bytes skipped is * returned. If {@code n} is negative, no bytes are skipped. - * + * * @param n - the number of bytes to be skipped. * @return the actual number of bytes skipped. * @throws IOException - if an I/O error occurs. @@ -591,7 +591,7 @@ default String readLine() throws IOException { if (read() != '\n') seek(cur); break; default: - input.append((char)c); + input.append((char) c); break; } } @@ -674,7 +674,7 @@ default void writeBytes(final String s) throws IOException { @Override default void writeChars(final String s) throws IOException { final int len = s.length(); - for (int i = 0 ; i < len ; i++) { + for (int i = 0; i < len; i++) { final int v = s.charAt(i); write((v >>> 8) & 0xFF); write((v >>> 0) & 0xFF); From 4251229c24d4f7e05c7be011c5d318b656800383 Mon Sep 17 00:00:00 2001 From: Curtis Rueden Date: Fri, 7 Jul 2017 15:41:28 -0500 Subject: [PATCH 075/754] DataHandle: make some minor style tweaks --- src/main/java/org/scijava/io/handle/DataHandle.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index 9f2afe5ba..e184a5232 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -439,12 +439,13 @@ default int read() throws IOException { * * @return the total number of bytes read into the buffer. */ - default int read(byte[] b) throws IOException { + default int read(final byte[] b) throws IOException { return read(b, 0, b.length); } /** - * Reads up to len bytes of data from the stream into an array of bytes. + * Reads up to {@code len} bytes of data from the stream into an array of + * bytes. * * @return the total number of bytes read into the buffer. */ From 8b065baff3a41c919f6a862f5cef602c12fa06ba Mon Sep 17 00:00:00 2001 From: Gabriel Einsdorf Date: Wed, 5 Jul 2017 17:08:08 +0200 Subject: [PATCH 076/754] DataHandle: remove built-in references to java.nio The java.nio package is not available on all JVMs, so its use must be optional on top of the base API layer. --- .../scijava/io/handle/AbstractDataHandle.java | 2 - .../org/scijava/io/handle/DataHandle.java | 64 ++----------------- .../org/scijava/io/handle/DataHandleTest.java | 9 +-- 3 files changed, 5 insertions(+), 70 deletions(-) diff --git a/src/main/java/org/scijava/io/handle/AbstractDataHandle.java b/src/main/java/org/scijava/io/handle/AbstractDataHandle.java index b072df9ad..ac0e4b7a8 100644 --- a/src/main/java/org/scijava/io/handle/AbstractDataHandle.java +++ b/src/main/java/org/scijava/io/handle/AbstractDataHandle.java @@ -31,8 +31,6 @@ package org.scijava.io.handle; -import java.nio.ByteOrder; - import org.scijava.io.location.Location; import org.scijava.plugin.AbstractWrapperPlugin; diff --git a/src/main/java/org/scijava/io/handle/DataHandle.java b/src/main/java/org/scijava/io/handle/DataHandle.java index e184a5232..4c3b845d8 100644 --- a/src/main/java/org/scijava/io/handle/DataHandle.java +++ b/src/main/java/org/scijava/io/handle/DataHandle.java @@ -38,8 +38,6 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStreamReader; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; import org.scijava.io.location.Location; import org.scijava.plugin.WrapperPlugin; @@ -56,6 +54,10 @@ public interface DataHandle extends WrapperPlugin, DataInput, DataOutput, Closeable { + public enum ByteOrder { + LITTLE_ENDIAN, BIG_ENDIAN + } + /** Default block size to use when searching through the stream. */ int DEFAULT_BLOCK_SIZE = 256 * 1024; // 256 KB @@ -92,8 +94,6 @@ public interface DataHandle extends WrapperPlugin, * by this method: *

    *

    Version 17, based on version MT199937(99/10/29) - * of the Mersenne Twister algorithm found at - * - * The Mersenne Twister Home Page, with the initialization - * improved using the new 2002/1/26 initialization algorithm - * By Sean Luke, October 2004. - * - *