Skip to content

Commit a240a7c

Browse files
Treiblesschorlectrueden
authored andcommitted
Add possibility to declare ops as fields in op collections
1 parent a2b839e commit a240a7c

4 files changed

Lines changed: 194 additions & 0 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.scijava.ops;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
import org.scijava.core.Priority;
9+
import org.scijava.ops.core.OpCollection;
10+
11+
/** Annotates an op declared as a field in an {@link OpCollection}. */
12+
@Retention(RetentionPolicy.RUNTIME)
13+
@Target(ElementType.FIELD)
14+
public @interface OpField {
15+
16+
String names();
17+
18+
double priority() default Priority.NORMAL;
19+
20+
}

src/main/java/org/scijava/ops/OpService.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,26 @@
2828
*/
2929
package org.scijava.ops;
3030

31+
import java.lang.reflect.Field;
3132
import java.lang.reflect.Type;
3233
import java.util.ArrayList;
3334
import java.util.Arrays;
3435
import java.util.HashMap;
3536
import java.util.Iterator;
3637
import java.util.LinkedList;
38+
import java.util.List;
3739
import java.util.Map;
3840
import java.util.Map.Entry;
3941

4042
import org.scijava.InstantiableException;
4143
import org.scijava.log.LogService;
4244
import org.scijava.ops.Ops.OpIdentifier;
4345
import org.scijava.ops.core.Op;
46+
import org.scijava.ops.core.OpCollection;
4447
import org.scijava.ops.matcher.MatchingResult;
4548
import org.scijava.ops.matcher.OpCandidate;
4649
import org.scijava.ops.matcher.OpClassInfo;
50+
import org.scijava.ops.matcher.OpFieldInfo;
4751
import org.scijava.ops.matcher.OpInfo;
4852
import org.scijava.ops.matcher.OpRef;
4953
import org.scijava.ops.matcher.OpTypeMatchingService;
@@ -57,6 +61,7 @@
5761
import org.scijava.service.Service;
5862
import org.scijava.struct.StructInstance;
5963
import org.scijava.ops.types.Nil;
64+
import org.scijava.util.ClassUtils;
6065

6166
/**
6267
* Service to provide a list of available ops structured in a prefix tree and to
@@ -101,6 +106,19 @@ public void initOpCache() {
101106
log.error("Can't load class from plugin info: " + pluginInfo.toString(), exc);
102107
}
103108
}
109+
// Add Ops contained in an OpCollection
110+
for (final PluginInfo<OpCollection> pluginInfo : pluginService.getPluginsOfType(OpCollection.class)) {
111+
try {
112+
final List<Field> fields = ClassUtils.getAnnotatedFields(pluginInfo.loadClass(),
113+
OpField.class);
114+
for (Field field : fields) {
115+
OpInfo opInfo = new OpFieldInfo(field);
116+
addToCache(opInfo, field.getAnnotation(OpField.class).names());
117+
}
118+
} catch (InstantiableException exc) {
119+
log.error("Can't load class from plugin info: " + pluginInfo.toString(), exc);
120+
}
121+
}
104122
}
105123

106124
private void addToCache(OpInfo opInfo, String opNames) {
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.scijava.ops.core;
2+
3+
import org.scijava.plugin.SciJavaPlugin;
4+
5+
/** A plugin marking a collection of ops defined as instance fields. */
6+
public interface OpCollection extends SciJavaPlugin {
7+
// Marker interface.
8+
}
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
* #%L
3+
* ImageJ software for multidimensional image processing and analysis.
4+
* %%
5+
* Copyright (C) 2014 - 2018 ImageJ developers.
6+
* %%
7+
* Redistribution and use in source and binary forms, with or without
8+
* modification, are permitted provided that the following conditions are met:
9+
*
10+
* 1. Redistributions of source code must retain the above copyright notice,
11+
* this list of conditions and the following disclaimer.
12+
* 2. Redistributions in binary form must reproduce the above copyright notice,
13+
* this list of conditions and the following disclaimer in the documentation
14+
* and/or other materials provided with the distribution.
15+
*
16+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
20+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26+
* POSSIBILITY OF SUCH DAMAGE.
27+
* #L%
28+
*/
29+
30+
package org.scijava.ops.matcher;
31+
32+
import java.lang.reflect.Field;
33+
import java.lang.reflect.Modifier;
34+
import java.lang.reflect.Type;
35+
import java.util.ArrayList;
36+
import java.util.List;
37+
38+
import org.scijava.core.Priority;
39+
import org.scijava.ops.OpField;
40+
import org.scijava.ops.OpUtils;
41+
import org.scijava.param.ParameterStructs;
42+
import org.scijava.param.ValidityException;
43+
import org.scijava.param.ValidityProblem;
44+
import org.scijava.struct.Struct;
45+
import org.scijava.struct.StructInstance;
46+
47+
/**
48+
* Metadata about an op implementation defined as a field.
49+
*
50+
* @author Curtis Rueden
51+
*/
52+
public class OpFieldInfo implements OpInfo {
53+
54+
private final Field field;
55+
private Struct struct;
56+
private ValidityException validityException;
57+
58+
public OpFieldInfo(final Field field) {
59+
60+
List<ValidityProblem> problems = new ArrayList<>();
61+
// Reject all non public fields
62+
if (!Modifier.isPublic(field.getModifiers())) {
63+
problems.add(new ValidityProblem("Field to parse: " + field + " must be public."));
64+
}
65+
66+
// NB: Subclassing a collection and inheriting its fields is NOT
67+
// ALLOWED!
68+
this.field = field;
69+
try {
70+
struct = ParameterStructs.structOf(field.getDeclaringClass(), field);
71+
} catch (ValidityException e) {
72+
problems.addAll(e.problems());
73+
}
74+
if (!problems.isEmpty()) {
75+
validityException = new ValidityException(problems);
76+
}
77+
}
78+
79+
// -- OpInfo methods --
80+
81+
@Override
82+
public Type opType() {
83+
return field.getGenericType();
84+
// return Types.fieldType(field, subClass);
85+
}
86+
87+
@Override
88+
public Struct struct() {
89+
return struct;
90+
}
91+
92+
@Override
93+
public double priority() {
94+
final OpField opField = field.getAnnotation(OpField.class);
95+
return opField == null ? Priority.NORMAL : opField.priority();
96+
}
97+
98+
@Override
99+
public String implementationName() {
100+
return field.getDeclaringClass().getName() + "." + field.getName();
101+
}
102+
103+
@Override
104+
public StructInstance<?> createOp() {
105+
// 1. Can we create another instance of the same function by calling
106+
// clone()?
107+
// 2. _SHOULD_ we do that? Or can we simply reuse the same function
108+
// instance every time?
109+
try {
110+
final Object object = field.get(null); // NB: value of static field!
111+
return struct().createInstance(object);
112+
} catch (final IllegalAccessException exc) {
113+
// FIXME
114+
exc.printStackTrace();
115+
throw new RuntimeException(exc);
116+
}
117+
}
118+
119+
@Override
120+
public ValidityException getValidityException() {
121+
return validityException;
122+
}
123+
124+
@Override
125+
public boolean isValid() {
126+
return validityException == null;
127+
}
128+
129+
// -- Object methods --
130+
131+
@Override
132+
public boolean equals(final Object o) {
133+
if (!(o instanceof OpFieldInfo))
134+
return false;
135+
final OpInfo that = (OpInfo) o;
136+
return struct().equals(that.struct());
137+
}
138+
139+
@Override
140+
public int hashCode() {
141+
return struct().hashCode();
142+
}
143+
144+
@Override
145+
public String toString() {
146+
return OpUtils.opString(this);
147+
}
148+
}

0 commit comments

Comments
 (0)