diff --git a/docs/environment.yml b/docs/environment.yml
index 539f61801..946aa495a 100644
--- a/docs/environment.yml
+++ b/docs/environment.yml
@@ -1,7 +1,6 @@
name: scijava-docs
channels:
- conda-forge
- - defaults
dependencies:
- myst-nb
- openjdk=11
diff --git a/docs/ops/doc/WritingYourOwnOpPackage.md b/docs/ops/doc/WritingYourOwnOpPackage.md
index 757fc6432..98f04db50 100644
--- a/docs/ops/doc/WritingYourOwnOpPackage.md
+++ b/docs/ops/doc/WritingYourOwnOpPackage.md
@@ -467,6 +467,7 @@ Assuming your project is following Maven's [standard directory layout](https://m
```yaml
- op:
names: [your.op.name1, your.op.name2, ...] # Array of Strings
+ hints: [op.hint1, op.hint2, ...] # Array of Strings
description: 'your Op description' # String
source: yourOpSource # String - see below
priority: 0.0 # Number
@@ -493,6 +494,17 @@ Assuming your project is following Maven's [standard directory layout](https://m
Of particular note are the following sections:
+#### Hints
+
+Each Op can define a set of hints (i.e. flags to the matcher) that can enable/disable particular aspects of the matcher.
+
+Some of the most useful Op hints are described below:
+
+| Hint | Use Case |
+|------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `Adaptation.FORBIDDEN` | Some algorithms like a [Fast Fourier Transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform) require their outputs be
of a particular size (not equivalent to the input size). If they are a `Computer`,
adaptation to `Function`s may cause errors. |
+| `Conversion.FORBIDDEN` | `engine.convert` Ops often require this hint to avoid infinite loops in
converted Op matching. |
+
#### Source
For **java objects**, the `source` will start with one of the following prefix, followed by a `:/`, followed by a [percent-encoded](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding) stringification of the Op (i.e. calling `toString()` on the object). Examples are shown below:
diff --git a/scijava-ops-api/src/main/java/org/scijava/ops/api/Hints.java b/scijava-ops-api/src/main/java/org/scijava/ops/api/Hints.java
index 487e2ef06..459ddae2a 100644
--- a/scijava-ops-api/src/main/java/org/scijava/ops/api/Hints.java
+++ b/scijava-ops-api/src/main/java/org/scijava/ops/api/Hints.java
@@ -62,7 +62,7 @@ public Hints(final String... startingHints) {
this(Arrays.asList(startingHints));
}
- private Hints(final Collection hints) {
+ public Hints(final Collection hints) {
this.hints = new HashSet<>(hints);
}
diff --git a/scijava-ops-engine/src/main/java/org/scijava/ops/engine/yaml/impl/AbstractYAMLOpInfo.java b/scijava-ops-engine/src/main/java/org/scijava/ops/engine/yaml/impl/AbstractYAMLOpInfo.java
index 40c7d04be..5fdefc865 100644
--- a/scijava-ops-engine/src/main/java/org/scijava/ops/engine/yaml/impl/AbstractYAMLOpInfo.java
+++ b/scijava-ops-engine/src/main/java/org/scijava/ops/engine/yaml/impl/AbstractYAMLOpInfo.java
@@ -35,8 +35,8 @@
import org.scijava.priority.Priority;
import org.scijava.struct.Struct;
-import java.lang.reflect.Type;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -59,7 +59,10 @@ public AbstractYAMLOpInfo(final Map yaml,
this.priority = parsePriority();
this.description = yaml.getOrDefault("description", "").toString();
this.version = (String) yaml.get("version");
- this.hints = new Hints();
+ this.hints = new Hints((List) yaml.getOrDefault( //
+ "hints", //
+ Collections.emptyList() //
+ ));
}
/**
diff --git a/scijava-ops-engine/src/test/java/org/scijava/ops/engine/yaml/impl/YAMLOpTest.java b/scijava-ops-engine/src/test/java/org/scijava/ops/engine/yaml/impl/YAMLOpTest.java
index a1ed22395..63961b235 100644
--- a/scijava-ops-engine/src/test/java/org/scijava/ops/engine/yaml/impl/YAMLOpTest.java
+++ b/scijava-ops-engine/src/test/java/org/scijava/ops/engine/yaml/impl/YAMLOpTest.java
@@ -121,4 +121,16 @@ public void testYAMLDescription() {
Assertions.assertEquals(expected, actual);
}
+ @Test
+ public void testYAMLHints() {
+ var infos = ops.infos("example.xor");
+ Assertions.assertEquals(1, infos.size());
+ var info = infos.iterator().next();
+ var hints = info.declaredHints();
+ Assertions.assertTrue(hints.containsAll( //
+ "Adaptation.FORBIDDEN", //
+ "Conversion.FORBIDDEN" //
+ ));
+ }
+
}
diff --git a/scijava-ops-engine/src/test/java/org/scijava/ops/engine/yaml/impl/ops/YAMLMethodOp.java b/scijava-ops-engine/src/test/java/org/scijava/ops/engine/yaml/impl/ops/YAMLMethodOp.java
index e5a15fe90..9852a6744 100644
--- a/scijava-ops-engine/src/test/java/org/scijava/ops/engine/yaml/impl/ops/YAMLMethodOp.java
+++ b/scijava-ops-engine/src/test/java/org/scijava/ops/engine/yaml/impl/ops/YAMLMethodOp.java
@@ -57,7 +57,7 @@ public static Double subtract(N aDouble, N aDouble2) {
/**
* Another example Op, implemented by a {@link Method}
*
- * @implNote op name=example.xor, type=Inplace1
+ * @implNote op name=example.xor, type=Inplace1, hints="Adaptation.FORBIDDEN,Conversion.FORBIDDEN"
* @param aList the first integer {@link List}
* @param aList2 the second integer {@link List}
*/
diff --git a/scijava-ops-indexer/src/main/java/org/scijava/ops/indexer/OpImplData.java b/scijava-ops-indexer/src/main/java/org/scijava/ops/indexer/OpImplData.java
index 816b81ace..0ac2ad176 100644
--- a/scijava-ops-indexer/src/main/java/org/scijava/ops/indexer/OpImplData.java
+++ b/scijava-ops-indexer/src/main/java/org/scijava/ops/indexer/OpImplData.java
@@ -29,9 +29,6 @@
package org.scijava.ops.indexer;
-import static org.scijava.ops.indexer.ProcessingUtils.blockSeparator;
-import static org.scijava.ops.indexer.ProcessingUtils.tagElementSeparator;
-
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;
@@ -39,6 +36,8 @@
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element;
+import static org.scijava.ops.indexer.ProcessingUtils.*;
+
/**
* A data structure containing all the metadata needed to define an Op
*
@@ -89,6 +88,11 @@ abstract class OpImplData {
*/
protected final List authors = new ArrayList<>();
+ /**
+ * A {@link List} of the hints declared by this Op
+ */
+ protected final List hints = new ArrayList<>();
+
protected final ProcessingEnvironment env;
/**
@@ -186,6 +190,10 @@ private void parseImplNote(String implTag) {
else if ("names".equals(kv[0]) || "name".equals(kv[0])) {
names.addAll(Arrays.asList(value.split("\\s*,\\s*")));
}
+ else if ("hints".equals(kv[0])) {
+
+ hints.addAll(Arrays.asList(value.split("\\s*,\\s*")));
+ }
else {
if (value.contains(",")) {
tags.put(kv[0], value.split(","));
@@ -216,6 +224,7 @@ public Map dumpData() {
map.put("description", description);
map.put("priority", priority);
map.put("authors", authors.toArray(String[]::new));
+ map.put("hints", hints.toArray(String[]::new));
var foo = params.stream() //
.map(OpParameter::data) //
.collect(Collectors.toList());
diff --git a/scijava-ops-indexer/src/main/java/org/scijava/ops/indexer/ProcessingUtils.java b/scijava-ops-indexer/src/main/java/org/scijava/ops/indexer/ProcessingUtils.java
index e585eb3d6..18dc26b18 100644
--- a/scijava-ops-indexer/src/main/java/org/scijava/ops/indexer/ProcessingUtils.java
+++ b/scijava-ops-indexer/src/main/java/org/scijava/ops/indexer/ProcessingUtils.java
@@ -65,7 +65,7 @@ final class ProcessingUtils {
* tags.
*/
public static final Pattern tagElementSeparator = Pattern.compile(
- "\\s*[,\\s]+(?=(?:[^']*'[^']*')*[^']*$)");
+ "\\s*[,\\s]+(?=(?:[^'\"]*['\"][^'\"]*['\"])*[^'\"]*$)");
private ProcessingUtils() {
throw new AssertionError("not instantiable");