Skip to content

Commit f3089de

Browse files
committed
Add OpMethodInfo
And implement parsing of Op dependencies and parameters.
1 parent b4b0d3b commit f3089de

File tree

3 files changed

+273
-0
lines changed

3 files changed

+273
-0
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* #%L
3+
* ImageJ software for multidimensional image processing and analysis.
4+
* %%
5+
* Copyright (C) 2014 - 2016 Board of Regents of the University of
6+
* Wisconsin-Madison and University of Konstanz.
7+
* %%
8+
* Redistribution and use in source and binary forms, with or without
9+
* modification, are permitted provided that the following conditions are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright notice,
12+
* this list of conditions and the following disclaimer.
13+
* 2. Redistributions in binary form must reproduce the above copyright notice,
14+
* this list of conditions and the following disclaimer in the documentation
15+
* and/or other materials provided with the distribution.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
21+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27+
* POSSIBILITY OF SUCH DAMAGE.
28+
* #L%
29+
*/
30+
31+
package org.scijava.ops;
32+
33+
import java.lang.reflect.Parameter;
34+
import java.lang.reflect.Type;
35+
36+
/**
37+
* @author Marcel Wiedenmann
38+
*/
39+
public class MethodParameterOpDependencyMember<T> extends
40+
AnnotatedOpDependencyMember<T>
41+
{
42+
43+
public MethodParameterOpDependencyMember(Parameter methodParameter,
44+
final Type parameterType, OpDependency annotation)
45+
{
46+
// NB: "Real" parameter name may or may not be available during runtime,
47+
// that is, the keys of instances of this class will likely be of the form:
48+
// arg0, arg1, etc.
49+
super(methodParameter.getName(), parameterType, annotation);
50+
}
51+
}
Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
* #%L
3+
* ImageJ software for multidimensional image processing and analysis.
4+
* %%
5+
* Copyright (C) 2014 - 2016 Board of Regents of the University of
6+
* Wisconsin-Madison and University of Konstanz.
7+
* %%
8+
* Redistribution and use in source and binary forms, with or without
9+
* modification, are permitted provided that the following conditions are met:
10+
*
11+
* 1. Redistributions of source code must retain the above copyright notice,
12+
* this list of conditions and the following disclaimer.
13+
* 2. Redistributions in binary form must reproduce the above copyright notice,
14+
* this list of conditions and the following disclaimer in the documentation
15+
* and/or other materials provided with the distribution.
16+
*
17+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
21+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27+
* POSSIBILITY OF SUCH DAMAGE.
28+
* #L%
29+
*/
30+
31+
package org.scijava.ops.matcher;
32+
33+
import java.lang.reflect.InvocationTargetException;
34+
import java.lang.reflect.Method;
35+
import java.lang.reflect.Modifier;
36+
import java.lang.reflect.Type;
37+
import java.util.ArrayList;
38+
import java.util.List;
39+
import java.util.Objects;
40+
41+
import org.scijava.core.Priority;
42+
import org.scijava.ops.OpMethod;
43+
import org.scijava.ops.OpUtils;
44+
import org.scijava.param.ParameterStructs;
45+
import org.scijava.param.ValidityException;
46+
import org.scijava.param.ValidityProblem;
47+
import org.scijava.struct.Struct;
48+
import org.scijava.struct.StructInstance;
49+
50+
/**
51+
* @author Marcel Wiedenmann
52+
*/
53+
public class OpMethodInfo implements OpInfo {
54+
55+
private final Method method;
56+
private Struct struct;
57+
private final ValidityException validityException;
58+
private Object instance;
59+
60+
public OpMethodInfo(final Method method) {
61+
final List<ValidityProblem> problems = new ArrayList<>();
62+
// Reject all non public methods
63+
if (!Modifier.isPublic(method.getModifiers())) {
64+
problems.add(new ValidityProblem("Method to parse: " + method +
65+
" must be public."));
66+
}
67+
if (Modifier.isStatic(method.getModifiers())) {
68+
// TODO: We can't properly infer the generic types of static methods at
69+
// the moment. This might be a Java limitation.
70+
problems.add(new ValidityProblem("Method to parse: " + method +
71+
" must not be static."));
72+
}
73+
this.method = method;
74+
try {
75+
struct = ParameterStructs.structOf(method.getDeclaringClass(), method);
76+
instance = method.getDeclaringClass().getDeclaredConstructor()
77+
.newInstance();
78+
}
79+
catch (final ValidityException e) {
80+
problems.addAll(e.problems());
81+
}
82+
catch (NoSuchMethodException | InstantiationException
83+
| IllegalAccessException | InvocationTargetException e)
84+
{
85+
problems.add(new ValidityProblem("Could not instantiate method's class.",
86+
e));
87+
}
88+
validityException = problems.isEmpty() ? null : new ValidityException(
89+
problems);
90+
}
91+
92+
// -- OpInfo methods --
93+
94+
@Override
95+
public Type opType() {
96+
return method.getGenericReturnType();
97+
}
98+
99+
@Override
100+
public Struct struct() {
101+
return struct;
102+
}
103+
104+
@Override
105+
public double priority() {
106+
final OpMethod opMethod = method.getAnnotation(OpMethod.class);
107+
return opMethod == null ? Priority.NORMAL : opMethod.priority();
108+
}
109+
110+
@Override
111+
public String implementationName() {
112+
// TODO: This includes all of the modifiers etc. of the method which are not
113+
// necessary to identify it. Use something custom? We need to be careful
114+
// because of method overloading, so just using the name is not sufficient.
115+
return method.toGenericString();
116+
}
117+
118+
@Override
119+
public StructInstance<?> createOpInstance(
120+
final List<? extends Object> dependencies)
121+
{
122+
try {
123+
method.setAccessible(true);
124+
return struct.createInstance(method.invoke(instance, dependencies
125+
.toArray()));
126+
}
127+
catch (final IllegalAccessException | IllegalArgumentException
128+
| InvocationTargetException ex)
129+
{
130+
throw new IllegalStateException("Failed to invoke Op method: " + method +
131+
". Provided Op dependencies were: " + Objects.toString(dependencies),
132+
ex);
133+
}
134+
}
135+
136+
@Override
137+
public boolean isValid() {
138+
return validityException == null;
139+
}
140+
141+
@Override
142+
public ValidityException getValidityException() {
143+
return validityException;
144+
}
145+
146+
// -- Object methods --
147+
148+
@Override
149+
public boolean equals(final Object o) {
150+
if (!(o instanceof OpMethodInfo)) return false;
151+
final OpInfo that = (OpInfo) o;
152+
return struct().equals(that.struct());
153+
}
154+
155+
@Override
156+
public int hashCode() {
157+
return struct().hashCode();
158+
}
159+
160+
@Override
161+
public String toString() {
162+
return OpUtils.opString(this);
163+
}
164+
}

src/main/java/org/scijava/param/ParameterStructs.java

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.stream.Collectors;
1919

2020
import org.scijava.ops.FieldOpDependencyMember;
21+
import org.scijava.ops.MethodParameterOpDependencyMember;
2122
import org.scijava.ops.OpDependency;
2223
import org.scijava.ops.OpDependencyMember;
2324
import org.scijava.struct.ItemIO;
@@ -54,6 +55,13 @@ public static Struct structOf(final Class<?> c, final Field f)
5455
return () -> items;
5556
}
5657

58+
public static Struct structOf(final Class<?> c, final Method m)
59+
throws ValidityException
60+
{
61+
final List<Member<?>> items = parse(c, m);
62+
return () -> items;
63+
}
64+
5765
// TODO parse methods need to be way more dry
5866
public static List<Member<?>> parse(final Class<?> type)
5967
throws ValidityException
@@ -106,6 +114,30 @@ public static List<Member<?>> parse(final Class<?> c, final Field field) throws
106114
return items;
107115
}
108116

117+
public static List<Member<?>> parse(final Class<?> c, final Method method)
118+
throws ValidityException
119+
{
120+
if (c == null || method == null) return null;
121+
122+
method.setAccessible(true);
123+
124+
final ArrayList<Member<?>> items = new ArrayList<>();
125+
final ArrayList<ValidityProblem> problems = new ArrayList<>();
126+
final Set<String> names = new HashSet<>();
127+
final Type methodReturnType = Types.methodReturnType(method, c);
128+
129+
// Parse method level @Parameter annotations.
130+
parseFunctionalParameters(items, names, problems, method, methodReturnType);
131+
132+
// Parse method level @OpDependency annotations.
133+
parseMethodOpDependencies(items, problems, c, method);
134+
135+
// Fail if there were any problems.
136+
if (!problems.isEmpty()) throw new ValidityException(problems);
137+
138+
return items;
139+
}
140+
109141
public static <T> Field field(final Member<T> item) {
110142
if (item instanceof FieldParameterMember) {
111143
final FieldParameterMember<T> fpItem = (FieldParameterMember<T>) item;
@@ -228,6 +260,32 @@ private static void parseFieldOpDependencies(final List<Member<?>> items,
228260
}
229261
}
230262

263+
private static void parseMethodOpDependencies(final List<Member<?>> items,
264+
final List<ValidityProblem> problems, final Class<?> enclosingclass,
265+
final Method annotatedMethod)
266+
{
267+
final OpDependency[] dependencies = annotatedMethod.getAnnotationsByType(
268+
OpDependency.class);
269+
final java.lang.reflect.Parameter[] methodParams = annotatedMethod
270+
.getParameters();
271+
if (dependencies.length != methodParams.length) {
272+
problems.add(new ValidityProblem(
273+
"The number of annotated Op dependencies does not correspond to the number of parameters of Op method: " +
274+
annotatedMethod));
275+
return;
276+
}
277+
final Type[] methodParamTypes = Types.methodParamTypes(annotatedMethod,
278+
enclosingclass);
279+
for (int i = 0; i < dependencies.length; i++) {
280+
final OpDependency dependency = dependencies[i];
281+
final java.lang.reflect.Parameter methodParam = methodParams[i];
282+
final Type methodParamType = methodParamTypes[i];
283+
final Member<?> item = new MethodParameterOpDependencyMember<>(
284+
methodParam, methodParamType, dependency);
285+
items.add(item);
286+
}
287+
}
288+
231289
private static boolean isImmutable(final Class<?> type) {
232290
// NB: All eight primitive types, as well as the boxed primitive
233291
// wrapper classes, as well as strings, are immutable objects.

0 commit comments

Comments
 (0)