Skip to content

Commit 34719d5

Browse files
committed
Add support for #@import directive
1 parent 46ca0d0 commit 34719d5

7 files changed

Lines changed: 228 additions & 0 deletions

File tree

src/main/java/org/scijava/module/DefaultModuleService.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import java.util.concurrent.Callable;
4040
import java.util.concurrent.ExecutionException;
4141
import java.util.concurrent.Future;
42+
import java.util.stream.Collectors;
4243

4344
import org.scijava.MenuPath;
4445
import org.scijava.Priority;
@@ -148,6 +149,28 @@ public ModuleInfo getModuleById(final String id) {
148149
return null;
149150
}
150151

152+
@Override
153+
public ModuleInfo getModuleByName(final String name) {
154+
// TODO: Cache names in a hash?
155+
List<ModuleInfo> moduleInfos = getModules().stream().filter(m -> name.equals(m.getName()))
156+
.collect(Collectors.toList());
157+
if (moduleInfos.size() > 1) {
158+
log.warn("Duplicate name " + name + " for modules: " + //
159+
String.join(", ", moduleInfos.stream().map(ModuleInfo::getIdentifier).collect(Collectors.toList())));
160+
log.warn("Using first registered module");
161+
return moduleInfos.get(0);
162+
}
163+
else if (moduleInfos.size() == 1) return moduleInfos.get(0);
164+
return null;
165+
/*
166+
for (final ModuleInfo info : getModules()) {
167+
final String infoName = info.getName();
168+
if (name.equals(infoName)) return info;
169+
}
170+
return null;
171+
*/
172+
}
173+
151174
@Override
152175
public ModuleInfo getModuleForAccelerator(final Accelerator acc) {
153176
for (final ModuleInfo info : getModules()) {

src/main/java/org/scijava/module/ModuleService.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ public interface ModuleService extends SciJavaService {
9797
*/
9898
ModuleInfo getModuleById(String id);
9999

100+
/**
101+
* Gets the module with the given name.
102+
*
103+
* @param name The name of the desired module.
104+
* @return The {@link Identifiable} module with the given name.
105+
*/
106+
ModuleInfo getModuleByName(String name);
107+
100108
/**
101109
* Gets the module for a given keyboard shortcut.
102110
*
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package org.scijava.module.process;
2+
3+
import java.util.Map;
4+
import java.util.concurrent.ExecutionException;
5+
6+
import javax.script.ScriptContext;
7+
8+
import org.scijava.log.LogService;
9+
import org.scijava.module.Module;
10+
import org.scijava.module.ModuleInfo;
11+
import org.scijava.module.ModuleService;
12+
import org.scijava.plugin.Parameter;
13+
import org.scijava.plugin.Plugin;
14+
import org.scijava.script.ScriptImport;
15+
import org.scijava.script.ScriptModule;
16+
17+
@Plugin(type = PreprocessorPlugin.class) // TODO set the priority?
18+
public class ScriptImportPreprocessor extends AbstractPreprocessorPlugin {
19+
20+
@Parameter
21+
private LogService log;
22+
23+
@Parameter
24+
private ModuleService modules;
25+
26+
@Override
27+
public void process(final Module module) {
28+
if (!(module instanceof ScriptModule)) return;
29+
// get import declarations from ScriptInfo
30+
@SuppressWarnings("unchecked")
31+
Map<String, ScriptImport> importMap = (Map<String, ScriptImport>) ((ScriptModule) module).getInfo().getProperty("imports");
32+
if (importMap == null) return;
33+
for (String moduleImport : importMap.keySet()) {
34+
ModuleInfo importModule = modules.getModuleByName(moduleImport);
35+
if (importModule == null) {
36+
log.warn("No module found for import: " + moduleImport);
37+
continue;
38+
}
39+
try {
40+
Module mod = modules.run(importModule, false).get();
41+
Map<String, Object> outputs = mod.getOutputs();
42+
((ScriptModule) module).getEngine().getBindings(ScriptContext.ENGINE_SCOPE).putAll(outputs);
43+
} catch (InterruptedException | ExecutionException exc) {
44+
log.warn("Error running import module: " + moduleImport, exc);
45+
}
46+
}
47+
}
48+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.scijava.script;
2+
3+
public class ScriptImport {
4+
private String scope;
5+
6+
public ScriptImport(String scope) {
7+
this.scope = scope;
8+
}
9+
10+
public String getScope() {
11+
return scope;
12+
}
13+
14+
public boolean hasScope() {
15+
return !(scope == null || scope.isEmpty());
16+
}
17+
}

src/main/java/org/scijava/script/ScriptInfo.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
import java.text.SimpleDateFormat;
4242
import java.util.ArrayList;
4343
import java.util.Date;
44+
import java.util.HashMap;
4445
import java.util.List;
46+
import java.util.Map;
4547

4648
import org.scijava.Context;
4749
import org.scijava.Contextual;
@@ -70,6 +72,7 @@ public class ScriptInfo extends AbstractModuleInfo implements Contextual {
7072
private final URL url;
7173
private final String path;
7274
private final String script;
75+
private Map<String, Object> propertyMap = new HashMap<>();
7376

7477
@Parameter
7578
private Context context;
@@ -273,6 +276,26 @@ public List<ScriptCallback> callbacks() {
273276
if (callbacks == null) callbacks = new ArrayList<>();
274277
return callbacks;
275278
}
279+
280+
/**
281+
* Gets the property object for the given key from the property map.
282+
*
283+
* @param key
284+
* @return property object
285+
*/
286+
public Object getProperty(String key) {
287+
return propertyMap.get(key);
288+
}
289+
290+
/**
291+
* Sets a given property object in the property map
292+
*
293+
* @param key
294+
* @param value
295+
*/
296+
public void setProperty(String key, Object value) {
297+
propertyMap.put(key, value);
298+
}
276299

277300
// -- AbstractModuleInfo methods --
278301

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2017 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
7+
* Institute of Molecular Cell Biology and Genetics, University of
8+
* Konstanz, and KNIME GmbH.
9+
* %%
10+
* Redistribution and use in source and binary forms, with or without
11+
* modification, are permitted provided that the following conditions are met:
12+
*
13+
* 1. Redistributions of source code must retain the above copyright notice,
14+
* this list of conditions and the following disclaimer.
15+
* 2. Redistributions in binary form must reproduce the above copyright notice,
16+
* this list of conditions and the following disclaimer in the documentation
17+
* and/or other materials provided with the distribution.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
23+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29+
* POSSIBILITY OF SUCH DAMAGE.
30+
* #L%
31+
*/
32+
33+
package org.scijava.script.process;
34+
35+
import java.util.LinkedHashMap;
36+
import java.util.Map;
37+
38+
import org.scijava.log.LogService;
39+
import org.scijava.plugin.Parameter;
40+
import org.scijava.plugin.Plugin;
41+
import org.scijava.script.ScriptImport;
42+
43+
/**
44+
* A {@link ScriptProcessor} which parses the {@code #@import} directive.
45+
* <p>
46+
* The syntax is:
47+
* </p>
48+
*
49+
* <pre>
50+
* #@import("myutils", scope="util")
51+
* #@import("utils")
52+
* </pre>
53+
*
54+
* @author Jan Eglinger
55+
*/
56+
@Plugin(type = ScriptProcessor.class)
57+
public class ImportDirectiveScriptProcessor extends DirectiveScriptProcessor {
58+
59+
public ImportDirectiveScriptProcessor() {
60+
super(directive -> "import".equals(directive));
61+
}
62+
63+
@Parameter
64+
private LogService log;
65+
66+
// -- Internal DirectiveScriptProcessor methods --
67+
68+
@Override
69+
protected String process(final String directive, final Map<String, Object> attrs, final String theRest) {
70+
String importItem = null;
71+
String scope = null;
72+
for (final String k : attrs.keySet()) {
73+
if (k == null || is(k, "name"))
74+
importItem = as(attrs.get(k), String.class);
75+
else if (is(k, "scope"))
76+
scope = as(attrs.get(k), String.class);
77+
else
78+
throw new IllegalArgumentException("Wrong import argument");
79+
}
80+
if (importItem != null)
81+
addImport(importItem, scope);
82+
else
83+
throw new IllegalArgumentException("No import name provided");
84+
return "";
85+
}
86+
87+
// -- Helper methods --
88+
89+
private void addImport(String importItem, String scope) {
90+
@SuppressWarnings("unchecked")
91+
Map<String, ScriptImport> map = (Map<String, ScriptImport>) info().getProperty("imports");
92+
if (map==null) map = new LinkedHashMap<>();
93+
map.put(importItem, new ScriptImport(scope));
94+
info().setProperty("imports", map);
95+
}
96+
}

src/test/java/org/scijava/script/ScriptInfoTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,19 @@ public void testScriptDirective() {
337337
assertEquals("bar", info.get("foo"));
338338
}
339339

340+
/** Tests {@code #@import} directives. */
341+
@SuppressWarnings("unchecked")
342+
@Test
343+
public void testImportDirective() {
344+
final String script = "" + //
345+
"#@import(\"myutils\", scope=\"utils\")\n";
346+
ScriptInfo info = null;
347+
info = new ScriptInfo(context, "scriptHeader.bsizes", new StringReader(script));
348+
info.inputs(); // HACK: Force lazy initialization.
349+
350+
assertEquals("utils", ((Map<String, ScriptImport>) info.getProperty("imports")).get("myutils").getScope());
351+
}
352+
340353
/**
341354
* Ensures the ScriptInfos Reader can be reused for multiple executions of the
342355
* script.

0 commit comments

Comments
 (0)