Skip to content

Commit 3f89bd0

Browse files
committed
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.
1 parent e57683b commit 3f89bd0

File tree

6 files changed

+57
-35
lines changed

6 files changed

+57
-35
lines changed

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ public class ScriptInfo extends AbstractModuleInfo implements Contextual {
8484
@Parameter
8585
private ScriptProcessorService scriptProcessorService;
8686

87+
/** Final version of the script, after script processing. */
88+
private String processedScript;
89+
8790
/** True iff the return value should be appended as an output. */
8891
private boolean appendReturnValue;
8992

@@ -216,6 +219,16 @@ public BufferedReader getReader() {
216219
return new BufferedReader(new StringReader(script), PARAM_CHAR_MAX);
217220
}
218221

222+
/**
223+
* Gets the script contents <em>after</em> script processing.
224+
*
225+
* @return The processed script.
226+
* @see ScriptProcessorService#process
227+
*/
228+
public String getProcessedScript() {
229+
return processedScript;
230+
}
231+
219232
/** Gets the scripting language of the script. */
220233
public ScriptLanguage getLanguage() {
221234
if (scriptLanguage == null) {
@@ -275,7 +288,7 @@ public List<ScriptCallback> callbacks() {
275288
public void parseParameters() {
276289
clearParameters();
277290
try {
278-
scriptProcessorService.process(this);
291+
processedScript = scriptProcessorService.process(this);
279292
}
280293
catch (final IOException exc) {
281294
// TODO: Consider a better error handling approach.

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,8 @@
3131

3232
package org.scijava.script;
3333

34-
import java.io.FileReader;
3534
import java.io.IOException;
3635
import java.io.PrintWriter;
37-
import java.io.Reader;
3836
import java.io.Writer;
3937

4038
import javax.script.ScriptContext;
@@ -158,9 +156,7 @@ public void run() {
158156
}
159157

160158
// execute script!
161-
final Reader reader = getInfo().getReader();
162-
if (reader == null) returnValue = engine.eval(new FileReader(path));
163-
else returnValue = engine.eval(reader);
159+
returnValue = engine.eval(getInfo().getProcessedScript());
164160
}
165161
catch (Throwable e) {
166162
while (e instanceof ScriptException && e.getCause() != null) {

src/main/java/org/scijava/script/process/ParameterScriptProcessor.java

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,11 @@ public void begin(final ScriptInfo scriptInfo) {
127127
}
128128

129129
@Override
130-
public void process(final String line) {
130+
public String process(final String line) {
131131
// parse new-style parameters starting with @# anywhere in the script.
132132
if (line.matches("^#@.*")) {
133133
final int at = line.indexOf('@');
134-
parseParam(line.substring(at + 1));
135-
return;
134+
return process(line, line.substring(at + 1));
136135
}
137136

138137
// parse old-style parameters in the initial script header
@@ -142,10 +141,12 @@ public void process(final String line) {
142141
// be used as comment line markers.
143142
if (line.matches("^[^\\w]*@.*")) {
144143
final int at = line.indexOf('@');
145-
parseParam(line.substring(at + 1));
144+
return process(line, line.substring(at + 1));
146145
}
147146
else if (line.matches(".*\\w.*")) header = false;
148147
}
148+
149+
return line;
149150
}
150151

151152
@Override
@@ -160,34 +161,40 @@ public void end() {
160161

161162
// -- Helper methods --
162163

163-
private void parseParam(final String param) {
164+
private String process(final String line, final String param) {
165+
if (parseParam(param)) return "";
166+
log.warn("Ignoring invalid parameter: " + param);
167+
return line;
168+
}
169+
170+
private boolean parseParam(final String param) {
164171
final int lParen = param.indexOf("(");
165172
final int rParen = param.lastIndexOf(")");
166-
if (rParen < lParen) { warnInvalid(param); return; }
167-
if (lParen < 0) parseParam(param, parseAttrs("()"));
168-
else {
169-
final String cutParam =
170-
param.substring(0, lParen) + param.substring(rParen + 1);
171-
final String attrs = param.substring(lParen + 1, rParen);
172-
parseParam(cutParam, parseAttrs(attrs));
173-
}
173+
if (rParen < lParen) return false;
174+
if (lParen < 0) return parseParam(param, parseAttrs("()"));
175+
final String cutParam =
176+
param.substring(0, lParen) + param.substring(rParen + 1);
177+
final String attrs = param.substring(lParen + 1, rParen);
178+
return parseParam(cutParam, parseAttrs(attrs));
174179
}
175180

176-
private void parseParam(final String param, final Map<String, Object> attrs) {
181+
private boolean parseParam(final String param,
182+
final Map<String, Object> attrs)
183+
{
177184
final String[] tokens = param.trim().split("[ \t\n]+");
178-
if (tokens.length < 1) { warnInvalid(param); return; }
185+
if (tokens.length < 1) return false;
179186
final String typeName, varName;
180187
final String maybeIOType = tokens[0].toUpperCase();
181188
if (isIOType(maybeIOType)) {
182189
// assume syntax: <IOType> <type> <varName>
183-
if (tokens.length < 3) { warnInvalid(param); return; }
190+
if (tokens.length < 3) return false;
184191
attrs.put("type", maybeIOType);
185192
typeName = tokens[1];
186193
varName = tokens[2];
187194
}
188195
else {
189196
// assume syntax: <type> <varName>
190-
if (tokens.length < 2) { warnInvalid(param); return; }
197+
if (tokens.length < 2) return false;
191198
typeName = tokens[0];
192199
varName = tokens[1];
193200
}
@@ -197,14 +204,16 @@ private void parseParam(final String param, final Map<String, Object> attrs) {
197204
}
198205
catch (final ScriptException exc) {
199206
log.warn("Invalid class: " + typeName, exc);
200-
return;
207+
return false;
201208
}
202209

203210
if (ScriptModule.RETURN_VALUE.equals(varName)) {
204211
// NB: The return value variable is declared as an explicit parameter.
205212
// So we should not append the return value as an extra output.
206213
info.setReturnValueAppended(false);
207214
}
215+
216+
return true;
208217
}
209218

210219
/** Parses a comma-delimited list of {@code key=value} pairs into a map. */
@@ -216,10 +225,6 @@ private boolean isIOType(final String token) {
216225
return convertService.convert(token.toUpperCase(), ItemIO.class) != null;
217226
}
218227

219-
private void warnInvalid(final String param) {
220-
log.warn("Ignoring invalid parameter: " + param);
221-
}
222-
223228
private <T> void addItem(final String name, final Class<T> type,
224229
final Map<String, Object> attrs, final boolean explicit)
225230
{

src/main/java/org/scijava/script/process/ScriptProcessor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
public interface ScriptProcessor extends SingletonPlugin {
4949

5050
void begin(ScriptInfo info);
51-
void process(String line);
51+
String process(String line);
5252
default void end() {}
5353

5454
}

src/main/java/org/scijava/script/process/ScriptProcessorService.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public interface ScriptProcessorService extends
5656
* Invokes all {@link ScriptProcessor} plugins on the given script, line by
5757
* line in sequence.
5858
*/
59-
default void process(final ScriptInfo info) throws IOException {
59+
default String process(final ScriptInfo info) throws IOException {
6060
final List<ScriptProcessor> processors = getPlugins().stream().map(
6161
p -> pluginService().createInstance(p)).collect(Collectors.toList());
6262

@@ -69,19 +69,25 @@ default void process(final ScriptInfo info) throws IOException {
6969
p.begin(info);
7070
}
7171

72+
final StringBuilder sb = new StringBuilder();
73+
7274
try (final BufferedReader in = reader) {
7375
while (true) {
74-
final String line = in.readLine();
76+
String line = in.readLine();
7577
if (line == null) break;
7678
for (final ScriptProcessor p : processors) {
77-
p.process(line);
79+
line = p.process(line);
7880
}
81+
sb.append(line);
82+
sb.append("\n");
7983
}
8084
}
8185

8286
for (final ScriptProcessor p : processors) {
8387
p.end();
8488
}
89+
90+
return sb.toString();
8591
}
8692

8793
// -- PTService methods --

src/main/java/org/scijava/script/process/ShebangScriptProcessor.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,15 +64,17 @@ public void begin(final ScriptInfo scriptInfo) {
6464
}
6565

6666
@Override
67-
public void process(final String line) {
68-
if (!first) return;
67+
public String process(final String line) {
68+
if (!first) return line;
69+
first = false;
6970
if (line.startsWith("#!")) {
7071
// shebang!
7172
final String langName = line.substring(2);
7273
final ScriptLanguage lang = scriptService.getLanguageByName(langName);
7374
if (lang != null) info.setLanguage(lang);
7475
else log.warn("Unknown script language: " + langName);
76+
return "";
7577
}
76-
first = false;
78+
return line;
7779
}
7880
}

0 commit comments

Comments
 (0)