Skip to content

Commit 7b78083

Browse files
committed
Add a base for directive-oriented ScriptProcessors
Now they can all extend the DirectiveScriptProcessor abstract base class, saving a lot of boilerplate code.
1 parent 986cf74 commit 7b78083

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
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.ArrayList;
36+
import java.util.Collections;
37+
import java.util.List;
38+
import java.util.Map;
39+
import java.util.function.Predicate;
40+
import java.util.regex.Matcher;
41+
import java.util.regex.Pattern;
42+
43+
import org.scijava.convert.ConvertService;
44+
import org.scijava.parse.ParseService;
45+
import org.scijava.plugin.Parameter;
46+
import org.scijava.script.ScriptInfo;
47+
48+
/**
49+
* Abstract base class for {@link ScriptProcessor} plugins that parse lines
50+
* of the form {@code #@directive(...) ...}.
51+
*
52+
* @author Curtis Rueden
53+
*/
54+
public abstract class DirectiveScriptProcessor implements ScriptProcessor {
55+
56+
private final Pattern p = //
57+
Pattern.compile("^#@(\\w*)\\s*(\\((.*)\\))?\\s*(.*)$");
58+
59+
@Parameter
60+
private ConvertService convertService;
61+
62+
@Parameter
63+
private ParseService parser;
64+
65+
private ScriptInfo info;
66+
67+
private Predicate<String> directivesToMatch;
68+
69+
public DirectiveScriptProcessor(final Predicate<String> directivesToMatch) {
70+
this.directivesToMatch = directivesToMatch;
71+
}
72+
73+
// -- ScriptProcessor methods --
74+
75+
@Override
76+
public void begin(final ScriptInfo scriptInfo) {
77+
info = scriptInfo;
78+
}
79+
80+
@Override
81+
public String process(final String line) {
82+
// as quickly as possible, verify that this line is a directive
83+
if (!line.startsWith("#@")) return line;
84+
85+
// parse the directive, and ensure it is well-formed
86+
final Matcher m = p.matcher(line);
87+
if (!m.matches()) return line;
88+
89+
// ensure directive is relevant
90+
final String directive = m.group(1);
91+
if (!directivesToMatch.test(directive)) return line;
92+
93+
// parse attributes (inner match without parentheses)
94+
final String attrString = m.group(3);
95+
final Map<String, Object> attrs = attrString == null ? //
96+
Collections.emptyMap() : parser.parse(attrString, false).asMap();
97+
98+
// retain the rest of the string
99+
final String theRest = m.group(4);
100+
101+
return process(directive, attrs, theRest);
102+
}
103+
104+
// -- Internal methods --
105+
106+
/** Processes the given directive. */
107+
protected abstract String process(final String directive,
108+
final Map<String, Object> attrs, final String theRest);
109+
110+
/** Gets the active {@link ScriptInfo} instance. */
111+
protected ScriptInfo info() {
112+
return info;
113+
}
114+
115+
/** Checks whether some key matches the desired value, ignoring case. */
116+
protected boolean is(final String key, final String desired) {
117+
return desired.equalsIgnoreCase(key);
118+
}
119+
120+
/** Coerces some object into another object of the given type. */
121+
protected <T> T as(final Object v, final Class<T> type) {
122+
final T converted = convertService.convert(v, type);
123+
if (converted != null) return converted;
124+
// NB: Attempt to convert via string.
125+
// This is useful in cases where a weird type of object came back
126+
// (e.g., org.scijava.parse.eval.Unresolved), but which happens to have a
127+
// nice string representation which ultimately is expressible as the type.
128+
return convertService.convert(v.toString(), type);
129+
}
130+
131+
/** Coerces some object into a list of objects of the given type. */
132+
protected <T> List<T> asList(final Object v, final Class<T> type) {
133+
final ArrayList<T> result = new ArrayList<>();
134+
final List<?> list = as(v, List.class);
135+
for (final Object item : list) {
136+
result.add(as(item, type));
137+
}
138+
return result;
139+
}
140+
}

0 commit comments

Comments
 (0)