Skip to content

Commit 235abac

Browse files
committed
Add inplace to function transformer
1 parent 94d2dab commit 235abac

3 files changed

Lines changed: 394 additions & 0 deletions

File tree

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
/*
2+
* #%L
3+
* SciJava Operations: a framework for reusable algorithms.
4+
* %%
5+
* Copyright (C) 2018 SciJava 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.transform.functional;
31+
32+
import java.lang.reflect.ParameterizedType;
33+
import java.lang.reflect.Type;
34+
import java.util.Arrays;
35+
import java.util.List;
36+
37+
import org.scijava.ops.OpService;
38+
import org.scijava.ops.OpUtils;
39+
import org.scijava.ops.core.inplace.BiInplaceFirst;
40+
import org.scijava.ops.core.inplace.BiInplaceSecond;
41+
import org.scijava.ops.core.inplace.Inplace;
42+
import org.scijava.ops.core.inplace.Inplace3First;
43+
import org.scijava.ops.core.inplace.Inplace3Second;
44+
import org.scijava.ops.core.inplace.Inplace4First;
45+
import org.scijava.ops.core.inplace.Inplace5First;
46+
import org.scijava.ops.matcher.OpRef;
47+
import org.scijava.ops.transform.OpTransformationException;
48+
import org.scijava.ops.transform.OpTransformer;
49+
import org.scijava.ops.util.Adapt;
50+
import org.scijava.ops.util.Functions;
51+
import org.scijava.ops.util.Inplaces;
52+
import org.scijava.ops.util.Inplaces.InplaceInfo;
53+
import org.scijava.param.ParameterStructs;
54+
import org.scijava.plugin.Plugin;
55+
import org.scijava.util.Types;
56+
57+
/**
58+
* Transforms inplaces into functions using the corresponding adapters in
59+
* {@link org.scijava.ops.util.Adapt.Inplaces}.
60+
*
61+
* @author Marcel Wiedenmann
62+
*/
63+
@Plugin(type = OpTransformer.class)
64+
public class InplaceToFunctionTransformer implements FunctionalTypeTransformer {
65+
66+
@Override
67+
public Object transform(final OpService opService, final Object src, final OpRef targetRef)
68+
throws OpTransformationException
69+
{
70+
final Class<?> targetFunctionalRawType = OpUtils.findFirstImplementedFunctionalInterface(targetRef);
71+
checkCanTransform(src, targetRef, targetFunctionalRawType);
72+
if (src instanceof Inplace) return Adapt.Inplaces.asFunction((Inplace<?>) src);
73+
if (src instanceof BiInplaceFirst) return Adapt.Inplaces.asBiFunction((BiInplaceFirst<?, ?>) src);
74+
if (src instanceof BiInplaceSecond) return Adapt.Inplaces.asBiFunction((BiInplaceSecond<?, ?>) src);
75+
if (src instanceof Inplace3First) return Adapt.Inplaces.asFunction3((Inplace3First<?, ?, ?>) src);
76+
if (src instanceof Inplace3Second) return Adapt.Inplaces.asFunction3((Inplace3Second<?, ?, ?>) src);
77+
if (src instanceof Inplace4First) return Adapt.Inplaces.asFunction4((Inplace4First<?, ?, ?, ?>) src);
78+
if (src instanceof Inplace5First) return Adapt.Inplaces.asFunction5((Inplace5First<?, ?, ?, ?, ?>) src);
79+
throw createCannotTransformException(src, targetRef, "Source does not implement a supported inplace interface.",
80+
null);
81+
}
82+
83+
private void checkCanTransform(final Object src, final OpRef targetRef, final Class<?> targetFunctionalRawType)
84+
throws OpTransformationException
85+
{
86+
String problem = null;
87+
final Class<?> srcFunctionalRawType = ParameterStructs.findFunctionalInterface(src.getClass());
88+
if (srcFunctionalRawType == null) {
89+
problem = "Source does not implement a functional interface.";
90+
}
91+
else if (targetFunctionalRawType == null) {
92+
problem = "Target does not implement a functional interface.";
93+
}
94+
else {
95+
final InplaceInfo srcInfo = Inplaces.ALL_INPLACES.get(srcFunctionalRawType);
96+
if (srcInfo == null) {
97+
problem = "Source does not implement a known inplace interface.";
98+
}
99+
else {
100+
final Integer targetArity = Functions.ALL_FUNCTIONS.get(targetFunctionalRawType);
101+
if (targetArity == null) {
102+
problem = "Target does not implement a known function interface.";
103+
}
104+
else {
105+
final int srcArity = srcInfo.arity();
106+
if (srcArity != targetArity.intValue()) {
107+
problem = "Source and target arities disagree (" + srcArity + " vs. " + targetArity + ").";
108+
}
109+
}
110+
}
111+
}
112+
if (problem != null) {
113+
throw createCannotTransformException(src, targetRef, problem, null);
114+
}
115+
}
116+
117+
@Override
118+
public Integer getTargetArity(final Class<?> targetFunctionalRawType) {
119+
return Functions.ALL_FUNCTIONS.get(targetFunctionalRawType);
120+
}
121+
122+
@Override
123+
public List<Class<?>> getSourceFunctionalInterfaces(final int targetArity) {
124+
return Inplaces.getInplacesOfArity(targetArity);
125+
}
126+
127+
@Override
128+
public Type getSourceOpType(final Type targetOpType, final Class<?> targetFunctionalRawType,
129+
final Class<?> sourceFunctionalRawType)
130+
{
131+
if (targetOpType instanceof ParameterizedType) {
132+
final Type[] targetParamTypes = ((ParameterizedType) targetOpType).getActualTypeArguments();
133+
final int srcMutableParamPosition = Inplaces.ALL_INPLACES.get(sourceFunctionalRawType).mutablePosition();
134+
final int targetOutputParamPosition = targetParamTypes.length - 1;
135+
if (targetParamTypes[srcMutableParamPosition].equals(targetParamTypes[targetOutputParamPosition])) {
136+
// NB: Drop output parameter of the function as it's essentially the
137+
// mutable position of the inplace.
138+
return Types.parameterize(sourceFunctionalRawType, Arrays.copyOf(targetParamTypes, targetOutputParamPosition));
139+
}
140+
}
141+
return null;
142+
}
143+
144+
@Override
145+
public Type[] getSourceInputParameterTypes(final OpRef targetRef, final int targetArity) {
146+
return targetRef.getArgs();
147+
}
148+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* #%L
3+
* SciJava Operations: a framework for reusable algorithms.
4+
* %%
5+
* Copyright (C) 2018 SciJava 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;
31+
32+
import static org.scijava.ops.TestUtils.argsToString;
33+
34+
import java.util.concurrent.atomic.AtomicReference;
35+
import java.util.function.BiFunction;
36+
import java.util.function.Function;
37+
38+
import org.junit.Assert;
39+
import org.junit.Test;
40+
import org.scijava.ops.core.function.Function3;
41+
import org.scijava.ops.core.function.Function4;
42+
import org.scijava.ops.core.function.Function5;
43+
import org.scijava.ops.types.Nil;
44+
import org.scijava.ops.util.Functions;
45+
46+
/**
47+
* @author Marcel Wiedenmann
48+
*/
49+
public class InplaceToFunctionTransformTest extends AbstractTestEnvironment {
50+
51+
private static final Nil<Byte> byteNil = new Nil<Byte>() {};
52+
53+
private static final Nil<Double> doubleNil = new Nil<Double>() {};
54+
55+
private static final Nil<Float> floatNil = new Nil<Float>() {};
56+
57+
private static final Nil<Integer> integerNil = new Nil<Integer>() {};
58+
59+
private static final Nil<AtomicReference<String>> atomicStringNil = new Nil<AtomicReference<String>>() {};
60+
61+
private static final String hello = "hello";
62+
63+
@Test
64+
public void testInplaceToFunction() {
65+
final Function<AtomicReference<String>, AtomicReference<String>> f = Functions.unary(ops,
66+
"test.inplaceToFunctionTestOp", atomicStringNil, atomicStringNil);
67+
final AtomicReference<String> io = new AtomicReference<>(hello);
68+
final AtomicReference<String> out = f.apply(io);
69+
assert io == out;
70+
assertOutEquals(hello + " inplace", out);
71+
}
72+
73+
@Test
74+
public void testBiInplaceFirstToBiFunction() {
75+
final BiFunction<AtomicReference<String>, Byte, AtomicReference<String>> f = Functions.binary(ops,
76+
"test.biInplaceFirstToBiFunctionTestOp", atomicStringNil, byteNil, atomicStringNil);
77+
final AtomicReference<String> io = new AtomicReference<>(hello);
78+
final byte in2 = 22;
79+
final AtomicReference<String> out = f.apply(io, in2);
80+
assert io == out;
81+
assertOutEquals(argsToString(hello, in2), out);
82+
}
83+
84+
@Test
85+
public void testBiInplaceSecondToBiFunction() {
86+
final BiFunction<Byte, AtomicReference<String>, AtomicReference<String>> f = Functions.binary(ops,
87+
"test.biInplaceSecondToBiFunctionTestOp", byteNil, atomicStringNil, atomicStringNil);
88+
final byte in1 = 11;
89+
final AtomicReference<String> io = new AtomicReference<>(hello);
90+
final AtomicReference<String> out = f.apply(in1, io);
91+
assert io == out;
92+
assertOutEquals(argsToString(in1, hello), out);
93+
}
94+
95+
@Test
96+
public void testInplace3FirstToFunction3() {
97+
final Function3<AtomicReference<String>, Byte, Double, AtomicReference<String>> f = Functions.ternary(ops,
98+
"test.inplace3FirstToFunction3TestOp", atomicStringNil, byteNil, doubleNil, atomicStringNil);
99+
final AtomicReference<String> io = new AtomicReference<>(hello);
100+
final byte in2 = 22;
101+
final double in3 = 3.33;
102+
final AtomicReference<String> out = f.apply(io, in2, in3);
103+
assert io == out;
104+
assertOutEquals(argsToString(hello, in2, in3), out);
105+
}
106+
107+
@Test
108+
public void testInplace3SecondToFunction3() {
109+
final Function3<Byte, AtomicReference<String>, Double, AtomicReference<String>> f = Functions.ternary(ops,
110+
"test.inplace3SecondToFunction3TestOp", byteNil, atomicStringNil, doubleNil, atomicStringNil);
111+
final byte in1 = 111;
112+
final AtomicReference<String> io = new AtomicReference<>(hello);
113+
final double in3 = 3.33;
114+
final AtomicReference<String> out = f.apply(in1, io, in3);
115+
assert io == out;
116+
assertOutEquals(argsToString(in1, hello, in3), out);
117+
}
118+
119+
@Test
120+
public void testInplace4FirstToFunction4() {
121+
final Function4<AtomicReference<String>, Byte, Double, Float, AtomicReference<String>> f = Functions.quaternary(ops,
122+
"test.inplace4FirstToFunction4TestOp", atomicStringNil, byteNil, doubleNil, floatNil, atomicStringNil);
123+
final AtomicReference<String> io = new AtomicReference<>(hello);
124+
final byte in2 = 22;
125+
final double in3 = 3.33;
126+
final float in4 = 44f;
127+
final AtomicReference<String> out = f.apply(io, in2, in3, in4);
128+
assert io == out;
129+
assertOutEquals(argsToString(hello, in2, in3, in4), out);
130+
}
131+
132+
@Test
133+
public void testInplace5FirstToFunction5() {
134+
final Function5<AtomicReference<String>, Byte, Double, Float, Integer, AtomicReference<String>> f = Functions
135+
.quinary(ops, "test.inplace5FirstToFunction5TestOp", atomicStringNil, byteNil, doubleNil, floatNil, integerNil,
136+
atomicStringNil);
137+
final AtomicReference<String> io = new AtomicReference<>(hello);
138+
final byte in2 = 22;
139+
final double in3 = 3.33;
140+
final float in4 = 44f;
141+
final int in5 = 555555;
142+
final AtomicReference<String> out = f.apply(io, in2, in3, in4, in5);
143+
assert io == out;
144+
assertOutEquals(argsToString(hello, in2, in3, in4, in5), out);
145+
}
146+
147+
private static void assertOutEquals(final String expected, final AtomicReference<String> actual) {
148+
Assert.assertEquals(expected, actual.get());
149+
}
150+
}

0 commit comments

Comments
 (0)