Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/main/java/org/scijava/ops/types/Any.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.scijava.ops.types;

import java.lang.reflect.Type;

/**
* This {@link Type} represents a Type that can be assigned to any other Type.
*
* @author Gabe Selzer
*
*/
public class Any implements Type {
@Override
public String toString() {
return "Any";
}

}
2 changes: 1 addition & 1 deletion src/main/java/org/scijava/ops/types/TypeExtractor.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ default ParameterizedType reify(final T o) {
final Type[] types = new Type[typeVars.length];
for (int i = 0; i < types.length; i++) {
types[i] = reify(o, i);
if (types[i] == null) types[i] = Types.wildcard();
if (types[i] == null) types[i] = new Any();
}
return Types.parameterize(getRawType(), types);
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/scijava/ops/types/TypeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ default Type reify(final Object o) {

// fill in any remaining unresolved type parameters with wildcards
for (final TypeVariable<?> typeVar : typeVars) {
resolved.putIfAbsent(typeVar, Types.wildcard());
resolved.putIfAbsent(typeVar, new Any());
}

// now apply all the type variables we resolved
Expand Down
21 changes: 17 additions & 4 deletions src/main/java/org/scijava/util/Types.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@
import java.util.Set;
import java.util.stream.IntStream;

import org.scijava.ops.types.Any;

/**
* Utility class for working with generic types, fields and methods.
* <p>
Expand Down Expand Up @@ -317,7 +319,7 @@ public static URL location(final Class<?> c, final boolean quietly) {
public static String name(final Type t) {
if (t instanceof Class) {
final Class<?> c = (Class<?>) t;
return c.isArray() ? (name(component(c)) + "[]") : c.getName();
return c.isArray() ? name(component(c)) + "[]" : c.getName();
}
return t.toString();
}
Expand Down Expand Up @@ -831,6 +833,10 @@ public static int isApplicable(final Type[] args, final Type[] params,
for (int i = 0; i < params.length; i++) {
Type arg = args[i];
Type param = params[i];

// if arg is an Any, it must be applicable to param.
if(arg instanceof Any) continue;

// First, check raw type assignability.
if (!isApplicableToRawTypes(arg, param)) return i;

Expand Down Expand Up @@ -894,7 +900,7 @@ private static boolean isApplicableToParameterizedTypes(final Type arg,
// that the type var was contained in a parameterized type. Hence, it will be handled
// like a normal type var which allow more assignability regarding wildcards compared
// to type vars contained in parameterized types
List<Integer> ignoredIndices = new ArrayList<Integer>();
List<Integer> ignoredIndices = new ArrayList<>();
// check to see if any of the Types of this ParameterizedType are
// TypeVariables, if so restrict them to the type parameter of the argument.
for (int i = 0; i < destTypes.length; i++) {
Expand Down Expand Up @@ -1721,7 +1727,7 @@ public String toString() {
@Override
public boolean equals(final Object obj) {
return obj == this || obj instanceof ParameterizedType && TypeUtils
.equals(this, ((ParameterizedType) obj));
.equals(this, (ParameterizedType) obj);
}

@Override
Expand Down Expand Up @@ -1845,6 +1851,9 @@ public static boolean isAssignable(final Type type, final Type toType) {
private static boolean isAssignable(final Type type, final Type toType,
final Map<TypeVariable<?>, Type> typeVarAssigns)
{
if (type instanceof Any) {
return true;
}
if (toType == null || toType instanceof Class) {
return isAssignable(type, (Class<?>) toType);
}
Expand Down Expand Up @@ -2003,6 +2012,7 @@ private static boolean isAssignable(final Type type,
!(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg,
toTypeArg, typeVarAssigns)))
{
if(fromTypeArg instanceof Any) continue;
return false;
}
}
Expand Down Expand Up @@ -3376,6 +3386,9 @@ private static String toString(final Type type, final Set<Type> done) {
if (type instanceof Class) {
return classToString((Class<?>) type, done);
}
if (type instanceof Any) {
return type.toString();
}
if (type instanceof ParameterizedType) {
return parameterizedTypeToString((ParameterizedType) type, done);
}
Expand Down Expand Up @@ -4181,7 +4194,7 @@ public static Type capture(final Type type) {
for (final CaptureTypeImpl captured : toInit) {
captured.init(varMap);
}
final Type ownerType = (pType.getOwnerType() == null) ? null : capture(
final Type ownerType = pType.getOwnerType() == null ? null : capture(
pType.getOwnerType());
return parameterize(clazz, ownerType, capturedArguments);
}
Expand Down
130 changes: 130 additions & 0 deletions src/test/java/org/scijava/ops/types/AnyTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package org.scijava.ops.types;

import java.lang.reflect.Type;
import java.util.function.Function;

import org.junit.Test;
import org.scijava.core.Priority;
import org.scijava.ops.AbstractTestEnvironment;
import org.scijava.ops.core.Op;
import org.scijava.param.Parameter;
import org.scijava.plugin.Plugin;
import org.scijava.struct.ItemIO;

public class AnyTest extends AbstractTestEnvironment {

@Test
public void testAny() {

NestedThing<String, Thing<String>> nthing = new NestedThing<>();
Double e = (Double) ops().run("test.nestedAny", nthing);

Thing<Double> thing = new Thing<>();
Double d = (Double) ops().run("test.any", thing);

assert d == 5.;
assert e == 5.;

}

/**
* NOTE: this is where ops.run() and the Any paradigm fail. However, this can
* easily be avoided by making TypeExtractors for any class for which this kind
* of exception can happen.
*/
@Test(expected = ClassCastException.class)
public void testExceptionalThing() {

ExceptionalThing<Double> ething = new ExceptionalThing<>(0.5);
Double d = (Double) ops().run("test.exceptionalAny", ething);

}

@Plugin(type = TypeExtractor.class, priority = Priority.LOW)
public static class ThingTypeExtractor implements TypeExtractor<Thing<?>> {

@Override
public Type reify(final Thing<?> o, final int n) {
if (n != 0)
throw new IndexOutOfBoundsException();

return new Any();
}

@Override
@SuppressWarnings({ "rawtypes", "unchecked" })
public Class<Thing<?>> getRawType() {
return (Class) Thing.class;
}

}

}

class Thing<U> {

public double create(U u) {
return 5.;
}
}

class ExceptionalThing<U> {

public ExceptionalThing(U u) {
thing = u;
};

U thing;

U getU() {
return thing;
}

public double create(U u) {
thing = u;
return 5.;
}
}

class NestedThing<U, V extends Thing<?>> {
public double create(V u) {
return 5.;
}
}

@Plugin(type = Op.class, name = "test.any")
@Parameter(key = "thing")
@Parameter(key = "output", type = ItemIO.OUTPUT)
class ThingFunction implements Function<Thing<String>, Double> {

@Override
public Double apply(Thing<String> t) {
return t.create("Hello");
}

}

@Plugin(type = Op.class, name = "test.exceptionalAny")
@Parameter(key = "thing")
@Parameter(key = "output", type = ItemIO.OUTPUT)
class ExceptionalThingFunction implements Function<ExceptionalThing<String>, Double> {

@Override
public Double apply(ExceptionalThing<String> t) {
String s = t.getU();
return t.create("Hello");
}

}

@Plugin(type = Op.class, name = "test.nestedAny")
@Parameter(key = "nestedThing")
@Parameter(key = "output", type = ItemIO.OUTPUT)
class NestedThingFunction implements Function<NestedThing<String, Thing<String>>, Double> {

@Override
public Double apply(NestedThing<String, Thing<String>> t) {
return 5.;
}

}