Skip to content

Commit c69c919

Browse files
committed
ops-ext-parser: expand metadata fields
Use priority, description, type and alias keys in the shorthand YAML (removing need for an explicit containers specification).
1 parent f1467b9 commit c69c919

5 files changed

Lines changed: 286 additions & 112 deletions

File tree

scijava-ops-ext-parser/src/main/java/org/scijava/ops/parser/OpData.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,12 @@ public class OpData {
7171
/**
7272
* The priority of this Op.
7373
*/
74-
protected double priority = 0.0;
74+
private final double priority;
7575

7676
/**
7777
* A description of the functionality provided by this Op.
7878
*/
79-
protected String description = "";
79+
private final String description;
8080

8181
/**
8282
* A {@link List} of the authors of this Op
@@ -85,14 +85,17 @@ public class OpData {
8585

8686
public OpData(final String source, final String version,
8787
final List<String> names, final List<OpParameter> params,
88-
final Map<String, Object> tags, List<String> authors)
88+
final Map<String, Object> tags, List<String> authors, double priority,
89+
String description)
8990
{
9091
this.source = source;
9192
this.version = version;
9293
this.names = names;
9394
this.params = params;
9495
this.tags = tags;
9596
this.authors = authors;
97+
this.priority = priority;
98+
this.description = description;
9699

97100
validateOpData();
98101
}

scijava-ops-ext-parser/src/main/java/org/scijava/ops/parser/OpParser.java

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
package org.scijava.ops.parser;
3131

32+
import com.google.common.base.Strings;
3233
import com.google.common.collect.Multimap;
3334
import com.google.common.collect.MultimapBuilder;
3435
import org.yaml.snakeyaml.Yaml;
@@ -61,9 +62,12 @@
6162
public final class OpParser {
6263

6364
private static final String NS_KEY = "namespace";
64-
private static final String CONTAINER_KEY = "containers";
6565
private static final String VERSION_KEY = "version";
6666
private static final String AUTHOR_KEY = "authors";
67+
private static final String PRIORITY_KEY = "priority";
68+
private static final String ALIAS_KEY = "alias";
69+
private static final String TYPE_KEY = "type";
70+
private static final String DESCRIPTION_KEY = "description";
6771

6872
/**
6973
* @param args One argument is required: path to a YAML file containing Op
@@ -103,7 +107,6 @@ public static String parseOpDocument(String inputYamlPath)
103107
Yaml configYaml = new Yaml();
104108
String namespace = null;
105109
List<OpData> ops = new ArrayList<>();
106-
Set<String> containerClasses = new HashSet<>();
107110
List<String> authors = new ArrayList<>();
108111
String version = "unknown";
109112
Map<String, Object> opsYaml;
@@ -120,10 +123,6 @@ public static String parseOpDocument(String inputYamlPath)
120123
if (opsYaml.containsKey(NS_KEY)) {
121124
namespace = (String) opsYaml.remove(NS_KEY);
122125
}
123-
if (opsYaml.containsKey(CONTAINER_KEY)) {
124-
containerClasses = new HashSet<>((List<String>) opsYaml.remove(
125-
CONTAINER_KEY));
126-
}
127126
if (opsYaml.containsKey(VERSION_KEY)) {
128127
version = (String) opsYaml.remove(VERSION_KEY);
129128
}
@@ -139,29 +138,48 @@ public static String parseOpDocument(String inputYamlPath)
139138
// As our YAML specification for desired method names, we want all
140139
// overloaded implementations of those methods.
141140
Multimap<String, Method> methods = makeMultimap(clazz);
142-
final Map<String, String> opMethods = (Map<String, String>) opDeclaration
143-
.getValue();
144-
for (Map.Entry<String, String> opMethod : opMethods.entrySet()) {
141+
final Map<String, Map<String, Object>> opMethods =
142+
(Map<String, Map<String, Object>>) opDeclaration.getValue();
143+
for (Map.Entry<String, Map<String, Object>> opMethod : opMethods
144+
.entrySet())
145+
{
145146
final String methodName = opMethod.getKey();
146-
List<String> opNames = new ArrayList<>();
147-
// The value for each op method is the "SciJava Ops-style" name
148-
opNames.add(opMethod.getValue());
147+
final List<String> opNames = new ArrayList<>();
148+
final Map<String, Object> opMetadata = opMethod.getValue();
149+
final String opType = (String) opMetadata.getOrDefault(TYPE_KEY, "");
150+
final String description = (String) opMetadata.getOrDefault(
151+
DESCRIPTION_KEY, "");
152+
final double priority = Double.parseDouble((String) opMetadata
153+
.getOrDefault(PRIORITY_KEY, "0.0"));
154+
155+
opNames.add((String) opMetadata.getOrDefault(ALIAS_KEY, "ext" +
156+
methodName));
157+
158+
List<String> opAuthors = authors;
159+
if (opMetadata.containsKey(AUTHOR_KEY)) {
160+
opAuthors = (List<String>) opMetadata.get(AUTHOR_KEY);
161+
}
149162

150-
// If a namespace is specified, we also alias the Op by its method name
163+
// If a global namespace is specified, we also alias the Op by its
164+
// method name
151165
// for a "classic" path to calling the op
152166
if (namespace != null) {
153167
opNames.add(namespace + "." + methodName);
154168
}
155169

156170
// For each overloaded method we create one OpData instance
171+
if (!methods.containsKey(methodName)) {
172+
throw new InvalidOpException("No method named " + methodName +
173+
" in class " + className);
174+
}
157175
for (Method method : methods.get(methodName)) {
158176
Map<String, Object> tags = new HashMap<>();
159177
List<OpParameter> params = new ArrayList<>();
160178
String opSource = parseOpSource(className, methodName, method
161179
.getParameterTypes());
162-
parseParams(method, params, tags, containerClasses);
180+
parseParams(method, params, tags, opType);
163181
OpData data = new OpData(opSource, version, opNames, params, tags,
164-
authors);
182+
opAuthors, priority, description);
165183
ops.add(data);
166184
}
167185
}
@@ -183,13 +201,12 @@ public static String parseOpDocument(String inputYamlPath)
183201
private static String parseOpSource(String className, String methodName,
184202
Class<?>[] parameterTypes)
185203
{
186-
String opSource = "javaMethod:/" //
204+
return "javaMethod:/" //
187205
+ URLEncoder.encode(className //
188206
+ "." //
189207
+ methodName //
190208
+ Arrays.stream(parameterTypes).map(Class::getName).collect(Collectors
191-
.joining(",", "(", ")")));
192-
return opSource;
209+
.joining(",", "(", ")")), UTF_8);
193210
}
194211

195212
/**
@@ -203,31 +220,26 @@ private static String parseOpSource(String className, String methodName,
203220
* @param method The {@link Method} being wrapped to an Op
204221
* @param params Empty list of {@link OpParameter}s to be populated
205222
* @param tags Empty {@link Map} of tags to be populated
206-
* @param containerClasses Known parameter classes that should be considered
207-
* potential {@code IO_TYPE#CONTAINER}s
223+
* @param type Empty string if an {@code ItemIO.FUNCTION}, otherwise
224+
* "ComputerN" where N is the parameter index of the container.
208225
*/
209-
private static void parseParams(Method method, List<OpParameter> params,
210-
Map<String, Object> tags, Set<String> containerClasses)
226+
private static void parseParams(final Method method,
227+
final List<OpParameter> params, Map<String, Object> tags, final String type)
211228
{
212-
int containersSeen = 0;
213229
int containerIndex = -1;
230+
if (!Strings.isNullOrEmpty(type)) {
231+
containerIndex = Integer.parseInt(type.substring(type.length() - 1)) - 1;
232+
}
214233

215234
// Iterate over each parameter
216235
Class<?>[] types = method.getParameterTypes();
217236
for (int i = 0; i < types.length; i++) {
218237
String className = types[i].getName();
219238
OpParameter.IO_TYPE ioType = OpParameter.IO_TYPE.INPUT;
220-
String paramName = "in" + (i + 1 - (containersSeen > 1 ? 1 : 0));
221-
// If this the second containerClass seen, mark it as the output type and
222-
// include a Computer tag
223-
if (containerClasses.contains(className)) {
224-
if (containersSeen == 1) {
225-
ioType = OpParameter.IO_TYPE.CONTAINER;
226-
paramName = "out";
227-
containerIndex = i + 1;
228-
tags.put("type", "Computer" + containerIndex);
229-
}
230-
containersSeen++;
239+
String paramName = method.getParameters()[i].getName();
240+
if (i == containerIndex) {
241+
ioType = OpParameter.IO_TYPE.CONTAINER;
242+
tags.put("type", type);
231243
}
232244
params.add(new OpParameter(paramName, className, ioType, ""));
233245
}

scijava-ops-ext-parser/src/test/java/org/scijava/ops/parser/TestOpParser.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,29 @@ public void validateParsing() throws ClassNotFoundException {
5757
+ "\n version: '1'" //
5858
+ "\n parameters:" //
5959
+
60-
"\n - {parameter type: INPUT, name: in1, description: '', type: '[Ljava.lang.Object;'}" //
60+
"\n - {parameter type: INPUT, name: arg0, description: '', type: '[Ljava.lang.Object;'}" //
6161
+ "\n authors: [Nobody, Everybody]" //
6262
+ "\n tags: {}" //
63+
+ "\n- op:" //
64+
+ "\n names: [system.arraycopy, test.arraycopy]" //
65+
+ "\n description: a useful op" //
66+
+
67+
"\n source: javaMethod:/java.lang.System.arraycopy%28java.lang.Object%2Cint%2Cjava.lang.Object%2Cint%2Cint%29" //
68+
+ "\n priority: 50.0" //
69+
+ "\n version: '1'" //
70+
+ "\n parameters:" //
71+
+
72+
"\n - {parameter type: INPUT, name: arg0, description: '', type: java.lang.Object}" //
73+
+
74+
"\n - {parameter type: CONTAINER, name: arg1, description: '', type: int}" //
75+
+
76+
"\n - {parameter type: INPUT, name: arg2, description: '', type: java.lang.Object}" //
77+
+
78+
"\n - {parameter type: INPUT, name: arg3, description: '', type: int}" //
79+
+
80+
"\n - {parameter type: INPUT, name: arg4, description: '', type: int}" //
81+
+ "\n authors: [You-know-who]" //
82+
+ "\n tags: {type: Computer2}" //
6383
+ "\n";
6484
Assertions.assertEquals(expected, actual);
6585
}
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
11
namespace: "test"
22
version: "1"
3-
containers:
4-
- "int"
53
authors:
64
- "Nobody"
75
- "Everybody"
86

97
java.util.Arrays:
10-
deepToString: "arrays.toStringDeep"
8+
deepToString:
9+
alias: "arrays.toStringDeep"
10+
11+
java.lang.System:
12+
arraycopy:
13+
alias: "system.arraycopy"
14+
priority: "50"
15+
description: "a useful op"
16+
authors:
17+
- "You-know-who"
18+
type: "Computer2"
19+

0 commit comments

Comments
 (0)