2929
3030package org .scijava .ops .parser ;
3131
32+ import com .google .common .base .Strings ;
3233import com .google .common .collect .Multimap ;
3334import com .google .common .collect .MultimapBuilder ;
3435import org .yaml .snakeyaml .Yaml ;
6162public 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 }
0 commit comments