From 31e6c1b34179928578d4f55f3d2b07395cf5ea3e Mon Sep 17 00:00:00 2001
From: Gabriel Einsdorf
Date: Mon, 27 Nov 2017 15:46:37 +0100
Subject: [PATCH 001/500] BytesLocation: improve javadoc
- Adds note about size of handles created using BytesLocation(int).
- Adds explanation of BytesLocation(byte[], int, int)
---
.../java/org/scijava/io/location/BytesLocation.java | 12 +++++++++---
1 file changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/main/java/org/scijava/io/location/BytesLocation.java b/src/main/java/org/scijava/io/location/BytesLocation.java
index 49c1b07cd..43c08e659 100644
--- a/src/main/java/org/scijava/io/location/BytesLocation.java
+++ b/src/main/java/org/scijava/io/location/BytesLocation.java
@@ -34,6 +34,7 @@
import org.scijava.io.ByteArrayByteBank;
import org.scijava.io.ByteBank;
+import org.scijava.io.handle.DataHandle;
import org.scijava.util.ByteArray;
/**
@@ -57,8 +58,9 @@ public BytesLocation(final ByteBank bytes) {
}
/**
- * Creates a {@link BytesLocation} backed by a {@link ByteArrayByteBank}
- * with the specified initial capacity.
+ * Creates a {@link BytesLocation} backed by a {@link ByteArrayByteBank} with
+ * the specified initial capacity, but with a reported size of 0. This method
+ * can be used to avoid needing to grow the underlying {@link ByteBank}.
*/
public BytesLocation(final int initialCapacity) {
this.bytes = new ByteArrayByteBank(initialCapacity);
@@ -84,7 +86,11 @@ public BytesLocation(final byte[] bytes) {
/**
* Creates a {@link BytesLocation} backed by a {@link ByteArrayByteBank} with
- * the specified initial capacity.
+ * the specified initial capacity and the provided data.
+ *
+ * @param bytes the bytes to copy into the new {@link BytesLocation}
+ * @param offset the offset in the bytes array to start copying from
+ * @param length the number of bytes to copy, starting from the offset
*/
public BytesLocation(final byte[] bytes, final int offset,
final int length)
From 8b9f05c42d038973d710f9491e750e0acdf91e7c Mon Sep 17 00:00:00 2001
From: Gabriel Einsdorf
Date: Thu, 28 Dec 2017 23:32:46 +0100
Subject: [PATCH 002/500] Add LocationService framework
The LocationService manages LocationResolver plugins that provide
translation from URI to Location. The LocationService itself
contains convenience methods to translate from String to Location.
---
.../io/location/AbstractLocationResolver.java | 72 ++++++++++++++++
.../io/location/DefaultLocationService.java | 72 ++++++++++++++++
.../scijava/io/location/LocationResolver.java | 56 ++++++++++++
.../scijava/io/location/LocationService.java | 86 +++++++++++++++++++
.../java/org/scijava/ContextCreationTest.java | 1 +
5 files changed, 287 insertions(+)
create mode 100644 src/main/java/org/scijava/io/location/AbstractLocationResolver.java
create mode 100644 src/main/java/org/scijava/io/location/DefaultLocationService.java
create mode 100644 src/main/java/org/scijava/io/location/LocationResolver.java
create mode 100644 src/main/java/org/scijava/io/location/LocationService.java
diff --git a/src/main/java/org/scijava/io/location/AbstractLocationResolver.java b/src/main/java/org/scijava/io/location/AbstractLocationResolver.java
new file mode 100644
index 000000000..3ab3c8e75
--- /dev/null
+++ b/src/main/java/org/scijava/io/location/AbstractLocationResolver.java
@@ -0,0 +1,72 @@
+/*
+ * #%L
+ * SciJava Common shared library for SciJava software.
+ * %%
+ * Copyright (C) 2009 - 2017 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
+ * Institute of Molecular Cell Biology and Genetics, University of
+ * Konstanz, and KNIME GmbH.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.io.location;
+
+import java.net.URI;
+
+import org.scijava.plugin.AbstractHandlerPlugin;
+
+/**
+ * Abstract super class for {@link LocationResolver} plugins.
+ *
+ * @author Gabriel Einsdorf
+ */
+public abstract class AbstractLocationResolver extends
+ AbstractHandlerPlugin implements LocationResolver
+{
+
+ private final String[] schemes;
+
+ /**
+ * @param schemes the uri schmemes that the implementing sub-type supports
+ */
+ public AbstractLocationResolver(String... schemes) {
+ assert schemes.length > 0;
+ this.schemes = schemes;
+ }
+
+ @Override
+ public boolean supports(URI uri) {
+ boolean supports = false;
+ for (final String scheme : schemes) {
+ supports = supports || scheme.equals(uri.getScheme());
+ }
+ return supports;
+ }
+
+ @Override
+ public Class getType() {
+ return URI.class;
+ }
+
+}
diff --git a/src/main/java/org/scijava/io/location/DefaultLocationService.java b/src/main/java/org/scijava/io/location/DefaultLocationService.java
new file mode 100644
index 000000000..a75d5dbfd
--- /dev/null
+++ b/src/main/java/org/scijava/io/location/DefaultLocationService.java
@@ -0,0 +1,72 @@
+/*
+ * #%L
+ * SciJava Common shared library for SciJava software.
+ * %%
+ * Copyright (C) 2009 - 2017 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
+ * Institute of Molecular Cell Biology and Genetics, University of
+ * Konstanz, and KNIME GmbH.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.io.location;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.scijava.plugin.AbstractHandlerService;
+import org.scijava.plugin.Plugin;
+import org.scijava.service.Service;
+
+/**
+ * Default {@link LocationService} implementation.
+ *
+ * @author Gabriel Einsdorf
+ */
+@Plugin(type = Service.class)
+public class DefaultLocationService extends
+ AbstractHandlerService implements
+ LocationService
+{
+
+ private final Map resolvers = new HashMap<>();
+
+ @Override
+ public Class getPluginType() {
+ return LocationResolver.class;
+ }
+
+ @Override
+ public Class getType() {
+ return URI.class;
+ }
+
+
+ @Override
+ public LocationResolver getResolver(final URI uri) {
+ return resolvers.computeIfAbsent(uri.getScheme(), u -> getHandler(uri));
+ }
+}
diff --git a/src/main/java/org/scijava/io/location/LocationResolver.java b/src/main/java/org/scijava/io/location/LocationResolver.java
new file mode 100644
index 000000000..6e71f617c
--- /dev/null
+++ b/src/main/java/org/scijava/io/location/LocationResolver.java
@@ -0,0 +1,56 @@
+/*
+ * #%L
+ * SciJava Common shared library for SciJava software.
+ * %%
+ * Copyright (C) 2009 - 2017 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
+ * Institute of Molecular Cell Biology and Genetics, University of
+ * Konstanz, and KNIME GmbH.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.io.location;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.scijava.plugin.HandlerPlugin;
+
+/**
+ * {@link LocationResolver} plugins allow resolving an {@link URI} to a
+ * {@link Location}. Extending {@link AbstractLocationResolver} is recommended
+ * for easy implementation.
+ *
+ * @author Gabriel Einsdorf
+ */
+public interface LocationResolver extends HandlerPlugin {
+
+ /**
+ * Resolves the given {@link URI} to a {@link Location}
+ *
+ * @return the resolved Location
+ * @throws URISyntaxException
+ */
+ Location resolve(URI uri) throws URISyntaxException;
+}
diff --git a/src/main/java/org/scijava/io/location/LocationService.java b/src/main/java/org/scijava/io/location/LocationService.java
new file mode 100644
index 000000000..bd62ad7e8
--- /dev/null
+++ b/src/main/java/org/scijava/io/location/LocationService.java
@@ -0,0 +1,86 @@
+/*
+ * #%L
+ * SciJava Common shared library for SciJava software.
+ * %%
+ * Copyright (C) 2009 - 2017 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
+ * Institute of Molecular Cell Biology and Genetics, University of
+ * Konstanz, and KNIME GmbH.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.io.location;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.scijava.plugin.HandlerService;
+import org.scijava.service.SciJavaService;
+
+/**
+ * A service that allows resolving of URIs to Locations, using
+ * {@link LocationResolver} plugins for translation.
+ *
+ * @author Gabriel Einsdorf
+ */
+public interface LocationService extends HandlerService,
+ SciJavaService
+{
+
+ /**
+ * Turns the given string into an {@link URI}, then resolves it to a
+ * {@link Location}
+ *
+ * @param uri the uri to resolve
+ * @return the resolved {@link Location}
+ * @throws URISyntaxException if the URI is malformed
+ */
+ default Location resolve(final String uri) throws URISyntaxException {
+ return resolve(new URI(uri));
+ }
+
+ /**
+ * Resolves the given {@link URI} to a location.
+ *
+ * @param uri the uri to resolve
+ * @return the resolved {@link Location} or null if no resolver
+ * could be found.
+ * @throws URISyntaxException if the URI is malformed
+ */
+ default Location resolve(final URI uri) throws URISyntaxException {
+ final LocationResolver resolver = getResolver(uri);
+ return resolver != null ? resolver.resolve(uri) : null;
+ }
+
+ /**
+ * Returns a {@link LocationResolver} capable of resolving URL like the one
+ * provided to this method. Allows faster repeated resolving of similar URIs
+ * without going through this service.
+ *
+ * @param uri the uri
+ * @return the {@link LocationResolver} for this uri type, or
+ * null if no resolver could be found.
+ */
+ LocationResolver getResolver(URI uri);
+}
diff --git a/src/test/java/org/scijava/ContextCreationTest.java b/src/test/java/org/scijava/ContextCreationTest.java
index 79b80affc..bfcce46e5 100644
--- a/src/test/java/org/scijava/ContextCreationTest.java
+++ b/src/test/java/org/scijava/ContextCreationTest.java
@@ -100,6 +100,7 @@ public void testFull() {
org.scijava.io.DefaultIOService.class,
org.scijava.io.DefaultRecentFileService.class,
org.scijava.io.handle.DefaultDataHandleService.class,
+ org.scijava.io.location.DefaultLocationService.class,
org.scijava.io.nio.DefaultNIOService.class,
org.scijava.main.DefaultMainService.class,
org.scijava.menu.DefaultMenuService.class,
From 76e4e1efee88e58b0daa7892c0d0e858b87232ea Mon Sep 17 00:00:00 2001
From: Gabriel Einsdorf
Date: Thu, 28 Dec 2017 23:41:26 +0100
Subject: [PATCH 003/500] Add FileLocationResolver + LocationService Tests
---
.../io/location/FileLocationResolver.java | 55 ++++++++++++++
.../io/location/FileLocationResolverTest.java | 72 +++++++++++++++++++
.../io/location/LocationServiceTest.java | 66 +++++++++++++++++
3 files changed, 193 insertions(+)
create mode 100644 src/main/java/org/scijava/io/location/FileLocationResolver.java
create mode 100644 src/test/java/org/scijava/io/location/FileLocationResolverTest.java
create mode 100644 src/test/java/org/scijava/io/location/LocationServiceTest.java
diff --git a/src/main/java/org/scijava/io/location/FileLocationResolver.java b/src/main/java/org/scijava/io/location/FileLocationResolver.java
new file mode 100644
index 000000000..b40f0f13e
--- /dev/null
+++ b/src/main/java/org/scijava/io/location/FileLocationResolver.java
@@ -0,0 +1,55 @@
+/*
+ * #%L
+ * SciJava Common shared library for SciJava software.
+ * %%
+ * Copyright (C) 2009 - 2017 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
+ * Institute of Molecular Cell Biology and Genetics, University of
+ * Konstanz, and KNIME GmbH.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.io.location;
+
+import java.net.URI;
+
+import org.scijava.plugin.Plugin;
+
+/**
+ * Implementation of {@link LocationResolver} for {@link FileLocation}.
+ *
+ * @author Gabriel Einsdorf
+ */
+@Plugin(type = LocationResolver.class)
+public class FileLocationResolver extends AbstractLocationResolver {
+
+ public FileLocationResolver() {
+ super("file");
+ }
+
+ @Override
+ public Location resolve(URI uri) {
+ return new FileLocation(uri);
+ }
+}
diff --git a/src/test/java/org/scijava/io/location/FileLocationResolverTest.java b/src/test/java/org/scijava/io/location/FileLocationResolverTest.java
new file mode 100644
index 000000000..9e94b0281
--- /dev/null
+++ b/src/test/java/org/scijava/io/location/FileLocationResolverTest.java
@@ -0,0 +1,72 @@
+/*
+ * #%L
+ * SciJava Common shared library for SciJava software.
+ * %%
+ * Copyright (C) 2009 - 2017 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
+ * Institute of Molecular Cell Biology and Genetics, University of
+ * Konstanz, and KNIME GmbH.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.io.location;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.junit.Test;
+import org.scijava.Context;
+
+/**
+ * Test for {@link FileLocationResolver}.
+ *
+ * @author Gabriel Einsdorf
+ */
+public class FileLocationResolverTest {
+
+ Context ctx = new Context(LocationService.class);
+ LocationService resolver = ctx.getService(LocationService.class);
+
+ @Test
+ public void testStringResolve() throws URISyntaxException {
+ final String uri = new File(new File(".").getAbsolutePath()) //
+ .toURI().toString();
+ final Location loc = resolver.resolve(uri);
+ assertTrue(loc instanceof FileLocation);
+ assertEquals(uri, loc.getURI().toString());
+ }
+
+ @Test
+ public void testURIResolve() throws URISyntaxException {
+ final URI uri = new File(new File(".").getAbsolutePath()).toURI();
+ final Location loc = resolver.resolve(uri);
+ assertTrue(loc instanceof FileLocation);
+ assertEquals(uri, loc.getURI());
+ }
+
+}
diff --git a/src/test/java/org/scijava/io/location/LocationServiceTest.java b/src/test/java/org/scijava/io/location/LocationServiceTest.java
new file mode 100644
index 000000000..5d6d5d9b7
--- /dev/null
+++ b/src/test/java/org/scijava/io/location/LocationServiceTest.java
@@ -0,0 +1,66 @@
+/*
+ * #%L
+ * SciJava Common shared library for SciJava software.
+ * %%
+ * Copyright (C) 2009 - 2017 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, Max Planck
+ * Institute of Molecular Cell Biology and Genetics, University of
+ * Konstanz, and KNIME GmbH.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.io.location;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.junit.Test;
+import org.scijava.Context;
+
+/**
+ * Tests {@link LocationService}.
+ *
+ * @author Gabriel Einsdorf
+ */
+public class LocationServiceTest {
+
+ @Test
+ public void testResolve() throws URISyntaxException {
+ final Context ctx = new Context(LocationService.class);
+ final LocationService loc = ctx.getService(LocationService.class);
+
+ final URI uri = new File(new File(".").getAbsolutePath()).toURI();
+ final LocationResolver res = loc.getResolver(uri);
+
+ assertTrue(res instanceof FileLocationResolver);
+ assertEquals(uri, res.resolve(uri).getURI());
+ assertEquals(uri, loc.resolve(uri).getURI());
+ assertEquals(uri, loc.resolve(uri.toString()).getURI());
+ }
+
+}
From e3babc89c95bbd3a4fe15deaffea017536c851de Mon Sep 17 00:00:00 2001
From: Curtis Rueden
Date: Tue, 20 Feb 2018 09:13:17 -0600
Subject: [PATCH 004/500] LocationService: move get*Type methods to iface
---
.../io/location/DefaultLocationService.java | 12 ------------
.../org/scijava/io/location/LocationService.java | 14 ++++++++++++++
2 files changed, 14 insertions(+), 12 deletions(-)
diff --git a/src/main/java/org/scijava/io/location/DefaultLocationService.java b/src/main/java/org/scijava/io/location/DefaultLocationService.java
index a75d5dbfd..d8c3c037d 100644
--- a/src/main/java/org/scijava/io/location/DefaultLocationService.java
+++ b/src/main/java/org/scijava/io/location/DefaultLocationService.java
@@ -33,7 +33,6 @@
package org.scijava.io.location;
import java.net.URI;
-import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
@@ -54,17 +53,6 @@ public class DefaultLocationService extends
private final Map resolvers = new HashMap<>();
- @Override
- public Class getPluginType() {
- return LocationResolver.class;
- }
-
- @Override
- public Class getType() {
- return URI.class;
- }
-
-
@Override
public LocationResolver getResolver(final URI uri) {
return resolvers.computeIfAbsent(uri.getScheme(), u -> getHandler(uri));
diff --git a/src/main/java/org/scijava/io/location/LocationService.java b/src/main/java/org/scijava/io/location/LocationService.java
index bd62ad7e8..193e03929 100644
--- a/src/main/java/org/scijava/io/location/LocationService.java
+++ b/src/main/java/org/scijava/io/location/LocationService.java
@@ -83,4 +83,18 @@ default Location resolve(final URI uri) throws URISyntaxException {
* null if no resolver could be found.
*/
LocationResolver getResolver(URI uri);
+
+ // -- PTService methods --
+
+ @Override
+ default Class getPluginType() {
+ return LocationResolver.class;
+ }
+
+ // -- Typed methods --
+
+ @Override
+ default Class getType() {
+ return URI.class;
+ }
}
From 353ca568dae8abc7e3059e895aaed7a0f41347a8 Mon Sep 17 00:00:00 2001
From: Curtis Rueden
Date: Thu, 28 Jul 2016 23:26:56 -0500
Subject: [PATCH 005/500] Add a utility class for working with Java types
This Types class will be the central place for utility methods that work
with Java types via reflection, including reasoning about generics,
retrieving Field and Method metadata, and similar activities.
The existing GenericUtils and ClassUtils functionality will be merged
into the Types class and refined.
---
pom.xml | 2 +-
src/main/java/org/scijava/util/Types.java | 114 ++++++++++++++++++++++
2 files changed, 115 insertions(+), 1 deletion(-)
create mode 100644 src/main/java/org/scijava/util/Types.java
diff --git a/pom.xml b/pom.xml
index 7029accf9..2c5fc4cb0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
scijava-common
- 2.69.1-SNAPSHOT
+ 2.70.0-SNAPSHOTSciJava CommonSciJava Common is a shared library for SciJava software. It provides a plugin framework, with an extensible mechanism for service discovery, backed by its own annotation processor, so that plugins can be loaded dynamically. It is used by downstream projects in the SciJava ecosystem, such as ImageJ and SCIFIO.
diff --git a/src/main/java/org/scijava/util/Types.java b/src/main/java/org/scijava/util/Types.java
new file mode 100644
index 000000000..57764592a
--- /dev/null
+++ b/src/main/java/org/scijava/util/Types.java
@@ -0,0 +1,114 @@
+/*
+ * #%L
+ * SciJava Common shared library for SciJava software.
+ * %%
+ * Copyright (C) 2009 - 2016 Board of Regents of the University of
+ * Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
+ * Institute of Molecular Cell Biology and Genetics.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.List;
+
+/**
+ * Utility class for working with generic types, fields and methods.
+ *
+ * Logic and inspiration were drawn from the following excellent libraries:
+ *
+ *
Google Guava's {@code com.google.common.reflect} package.
+ *
Apache Commons Lang 3's {@code org.apache.commons.lang3.reflect} package.
+ *
+ *
GenTyRef (Generic Type
+ * Reflector), a library for runtime generic type introspection.
+ *
+ *
+ *
+ * @author Curtis Rueden
+ */
+public final class Types {
+
+ private Types() {
+ // NB: Prevent instantiation of utility class.
+ }
+
+ // TODO: Migrate all GenericUtils methods here.
+
+ public static String name(final Type t) {
+ // NB: It is annoying that Class.toString() prepends "class " or
+ // "interface "; this method exists to work around that behavior.
+ return t instanceof Class ? ((Class>) t).getName() : t.toString();
+ }
+
+ /**
+ * Gets the (first) raw class of the given type.
+ *
+ *
If the type is a {@code Class} itself, the type itself is returned.
+ *
+ *
If the type is a {@link ParameterizedType}, the raw type of the
+ * parameterized type is returned.
+ *
If the type is a {@link GenericArrayType}, the returned type is the
+ * corresponding array class. For example: {@code List[] => List[]}.
+ *
+ *
If the type is a type variable or wildcard type, the raw type of the
+ * first upper bound is returned. For example:
+ * {@code => Foo}.
+ *
+ *
+ * If you want all raw classes of the given type, use {@link #raws}.
+ *
+ */
+ public static Class> raw(final Type type) {
+ // TODO: Consolidate with GenericUtils.
+ return GenericUtils.getClass(type);
+ }
+
+ /**
+ * Gets all raw classes corresponding to the given type.
+ *
+ * For example, a type parameter {@code A extends Number & Iterable} will
+ * return both {@link Number} and {@link Iterable} as its raw classes.
+ *
+ *
+ * @see #raw
+ */
+ public static List> raws(final Type type) {
+ // TODO: Consolidate with GenericUtils.
+ return GenericUtils.getClasses(type);
+ }
+
+ public static Field field(final Class> c, final String name) {
+ if (c == null) throw new IllegalArgumentException("No such field: " + name);
+ try {
+ return c.getDeclaredField(name);
+ }
+ catch (final NoSuchFieldException e) {}
+ return field(c.getSuperclass(), name);
+ }
+}
From 5f75cada8305ba5711972ee7f2140918feb31911 Mon Sep 17 00:00:00 2001
From: Curtis Rueden
Date: Fri, 29 Jul 2016 16:47:07 -0500
Subject: [PATCH 006/500] Fork Apache Commons Lang 3.4's TypeUtils class
We cannot use it as is, because it has bugs which we will need to fix.
And (at least for now) it is nice to avoid this dependency anyway.
This commit splices in the TypeUtils code from Apache Commons Lang 3.4
with a minimal set of changes which compile and work with SciJava code
style. Subsequent commits will fix bugs as well as clean up various
issues in the code.
---
src/main/java/org/scijava/util/Types.java | 2096 +++++++++++++++++++++
1 file changed, 2096 insertions(+)
diff --git a/src/main/java/org/scijava/util/Types.java b/src/main/java/org/scijava/util/Types.java
index 57764592a..bd4018ff5 100644
--- a/src/main/java/org/scijava/util/Types.java
+++ b/src/main/java/org/scijava/util/Types.java
@@ -31,11 +31,33 @@
package org.scijava.util;
+// Portions of this class were adapted from the
+// org.apache.commons.lang3.reflect.TypeUtils and
+// org.apache.commons.lang3.Validate classes of
+// Apache Commons Lang 3.4, which is distributed
+// under the Apache 2 license.
+// See lines below starting with "BEGIN FORK".
+
+import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+import org.scijava.util.ConversionUtils;
+import org.scijava.util.GenericUtils;
/**
* Utility class for working with generic types, fields and methods.
@@ -111,4 +133,2078 @@ public static Field field(final Class> c, final String name) {
catch (final NoSuchFieldException e) {}
return field(c.getSuperclass(), name);
}
+
+ // -- BEGIN FORK OF APACHE COMMONS LANG 3.4 CODE --
+
+ /*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /**
+ *
+ * Utility methods focusing on type inspection, particularly with regard to
+ * generics.
+ *
+ *
+ * @since 3.0
+ * @version $Id: TypeUtils.java 1606051 2014-06-27 12:22:17Z ggregory $
+ */
+ @SuppressWarnings("unused")
+ private static class TypeUtils {
+
+ /**
+ * {@link WildcardType} builder.
+ *
+ * @since 3.2
+ */
+ public static class WildcardTypeBuilder {
+
+ /**
+ * Constructor
+ */
+ private WildcardTypeBuilder() {}
+
+ private Type[] upperBounds;
+ private Type[] lowerBounds;
+
+ /**
+ * Specify upper bounds of the wildcard type to build.
+ *
+ * @param bounds to set
+ * @return {@code this}
+ */
+ public WildcardTypeBuilder withUpperBounds(final Type... bounds) {
+ this.upperBounds = bounds;
+ return this;
+ }
+
+ /**
+ * Specify lower bounds of the wildcard type to build.
+ *
+ * @param bounds to set
+ * @return {@code this}
+ */
+ public WildcardTypeBuilder withLowerBounds(final Type... bounds) {
+ this.lowerBounds = bounds;
+ return this;
+ }
+
+ public WildcardType build() {
+ return new WildcardTypeImpl(upperBounds, lowerBounds);
+ }
+ }
+
+ /**
+ * GenericArrayType implementation class.
+ *
+ * @since 3.2
+ */
+ private static final class GenericArrayTypeImpl implements
+ GenericArrayType
+ {
+
+ private final Type componentType;
+
+ /**
+ * Constructor
+ *
+ * @param componentType of this array type
+ */
+ private GenericArrayTypeImpl(final Type componentType) {
+ this.componentType = componentType;
+ }
+
+ @Override
+ public Type getGenericComponentType() {
+ return componentType;
+ }
+
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof GenericArrayType && TypeUtils
+ .equals(this, (GenericArrayType) obj);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 67 << 4;
+ result |= componentType.hashCode();
+ return result;
+ }
+ }
+
+ /**
+ * ParameterizedType implementation class.
+ *
+ * @since 3.2
+ */
+ private static final class ParameterizedTypeImpl implements
+ ParameterizedType
+ {
+
+ private final Class> raw;
+ private final Type useOwner;
+ private final Type[] typeArguments;
+
+ /**
+ * Constructor
+ *
+ * @param raw type
+ * @param useOwner owner type to use, if any
+ * @param typeArguments formal type arguments
+ */
+ private ParameterizedTypeImpl(final Class> raw, final Type useOwner,
+ final Type[] typeArguments)
+ {
+ this.raw = raw;
+ this.useOwner = useOwner;
+ this.typeArguments = typeArguments;
+ }
+
+ @Override
+ public Type getRawType() {
+ return raw;
+ }
+
+ @Override
+ public Type getOwnerType() {
+ return useOwner;
+ }
+
+ @Override
+ public Type[] getActualTypeArguments() {
+ return typeArguments.clone();
+ }
+
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof ParameterizedType && TypeUtils
+ .equals(this, ((ParameterizedType) obj));
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 71 << 4;
+ result |= raw.hashCode();
+ result <<= 4;
+ result |= Objects.hashCode(useOwner);
+ result <<= 8;
+ result |= Arrays.hashCode(typeArguments);
+ return result;
+ }
+ }
+
+ /**
+ * WildcardType implementation class.
+ *
+ * @since 3.2
+ */
+ private static final class WildcardTypeImpl implements WildcardType {
+
+ private static final Type[] EMPTY_BOUNDS = new Type[0];
+
+ private final Type[] upperBounds;
+ private final Type[] lowerBounds;
+
+ /**
+ * Constructor
+ *
+ * @param upperBound of this type
+ * @param lowerBound of this type
+ */
+ private WildcardTypeImpl(final Type upperBound, final Type lowerBound) {
+ this(upperBound == null ? null : new Type[] { upperBound },
+ lowerBound == null ? null : new Type[] { lowerBound });
+ }
+
+ /**
+ * Constructor
+ *
+ * @param upperBounds of this type
+ * @param lowerBounds of this type
+ */
+ private WildcardTypeImpl(final Type[] upperBounds,
+ final Type[] lowerBounds)
+ {
+ this.upperBounds = upperBounds == null ? EMPTY_BOUNDS : upperBounds;
+ this.lowerBounds = lowerBounds == null ? EMPTY_BOUNDS : lowerBounds;
+ }
+
+ @Override
+ public Type[] getUpperBounds() {
+ return upperBounds.clone();
+ }
+
+ @Override
+ public Type[] getLowerBounds() {
+ return lowerBounds.clone();
+ }
+
+ @Override
+ public String toString() {
+ return TypeUtils.toString(this);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ return obj == this || obj instanceof WildcardType && TypeUtils.equals(
+ this, (WildcardType) obj);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 73 << 8;
+ result |= Arrays.hashCode(upperBounds);
+ result <<= 8;
+ result |= Arrays.hashCode(lowerBounds);
+ return result;
+ }
+ }
+
+ /**
+ * A wildcard instance matching {@code ?}.
+ *
+ * @since 3.2
+ */
+ public static final WildcardType WILDCARD_ALL = //
+ wildcardType().withUpperBounds(Object.class).build();
+
+ /**
+ *
+ * Checks if the subject type may be implicitly cast to the target type
+ * following the Java generics rules. If both types are {@link Class}
+ * objects, the method returns the result of
+ * {@link Class#isAssignableFrom(Class)}.
+ *
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toType the target type
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
+ */
+ public static boolean isAssignable(final Type type, final Type toType) {
+ return isAssignable(type, toType, null);
+ }
+
+ /**
+ *
+ * Checks if the subject type may be implicitly cast to the target type
+ * following the Java generics rules.
+ *
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toType the target type
+ * @param typeVarAssigns optional map of type variable assignments
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
+ */
+ private static boolean isAssignable(final Type type, final Type toType,
+ final Map, Type> typeVarAssigns)
+ {
+ if (toType == null || toType instanceof Class>) {
+ return isAssignable(type, (Class>) toType);
+ }
+
+ if (toType instanceof ParameterizedType) {
+ return isAssignable(type, (ParameterizedType) toType, typeVarAssigns);
+ }
+
+ if (toType instanceof GenericArrayType) {
+ return isAssignable(type, (GenericArrayType) toType, typeVarAssigns);
+ }
+
+ if (toType instanceof WildcardType) {
+ return isAssignable(type, (WildcardType) toType, typeVarAssigns);
+ }
+
+ if (toType instanceof TypeVariable>) {
+ return isAssignable(type, (TypeVariable>) toType, typeVarAssigns);
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + toType);
+ }
+
+ /**
+ *
+ * Checks if the subject type may be implicitly cast to the target class
+ * following the Java generics rules.
+ *
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toClass the target class
+ * @return {@code true} if {@code type} is assignable to {@code toClass}.
+ */
+ private static boolean isAssignable(final Type type,
+ final Class> toClass)
+ {
+ if (type == null) {
+ // consistency with ClassUtils.isAssignable() behavior
+ return toClass == null || !toClass.isPrimitive();
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toClass == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toClass.equals(type)) {
+ return true;
+ }
+
+ if (type instanceof Class>) {
+ // just comparing two classes
+ return toClass.isAssignableFrom((Class>) type);
+ }
+
+ if (type instanceof ParameterizedType) {
+ // only have to compare the raw type to the class
+ return isAssignable(getRawType((ParameterizedType) type), toClass);
+ }
+
+ // *
+ if (type instanceof TypeVariable>) {
+ // if any of the bounds are assignable to the class, then the
+ // type is assignable to the class.
+ for (final Type bound : ((TypeVariable>) type).getBounds()) {
+ if (isAssignable(bound, toClass)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // the only classes to which a generic array type can be assigned
+ // are class Object and array classes
+ if (type instanceof GenericArrayType) {
+ return toClass.equals(Object.class) || toClass.isArray() &&
+ isAssignable(((GenericArrayType) type).getGenericComponentType(),
+ toClass.getComponentType());
+ }
+
+ // wildcard types are not assignable to a class (though one would think
+ // "? super Object" would be assignable to Object)
+ if (type instanceof WildcardType) {
+ return false;
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ *
+ * Checks if the subject type may be implicitly cast to the target
+ * parameterized type following the Java generics rules.
+ *
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toParameterizedType the target parameterized type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to {@code toType}.
+ */
+ private static boolean isAssignable(final Type type,
+ final ParameterizedType toParameterizedType,
+ final Map, Type> typeVarAssigns)
+ {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toParameterizedType == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toParameterizedType.equals(type)) {
+ return true;
+ }
+
+ // get the target type's raw type
+ final Class> toClass = getRawType(toParameterizedType);
+ // get the subject type's type arguments including owner type arguments
+ // and supertype arguments up to and including the target class.
+ final Map, Type> fromTypeVarAssigns = getTypeArguments(
+ type, toClass, null);
+
+ // null means the two types are not compatible
+ if (fromTypeVarAssigns == null) {
+ return false;
+ }
+
+ // compatible types, but there's no type arguments. this is equivalent
+ // to comparing Map< ?, ? > to Map, and raw types are always assignable
+ // to parameterized types.
+ if (fromTypeVarAssigns.isEmpty()) {
+ return true;
+ }
+
+ // get the target type's type arguments including owner type arguments
+ final Map, Type> toTypeVarAssigns = getTypeArguments(
+ toParameterizedType, toClass, typeVarAssigns);
+
+ // now to check each type argument
+ for (final TypeVariable> var : toTypeVarAssigns.keySet()) {
+ final Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns);
+ final Type fromTypeArg = unrollVariableAssignments(var,
+ fromTypeVarAssigns);
+
+ // parameters must either be absent from the subject type, within
+ // the bounds of the wildcard type, or be an exact match to the
+ // parameters of the target type.
+ if (fromTypeArg != null && !toTypeArg.equals(fromTypeArg) &&
+ !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg,
+ toTypeArg, typeVarAssigns)))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Look up {@code var} in {@code typeVarAssigns} transitively, i.e.
+ * keep looking until the value found is not a type variable.
+ *
+ * @param var the type variable to look up
+ * @param typeVarAssigns the map used for the look up
+ * @return Type or {@code null} if some variable was not in the map
+ * @since 3.2
+ */
+ private static Type unrollVariableAssignments(TypeVariable> var,
+ final Map, Type> typeVarAssigns)
+ {
+ Type result;
+ do {
+ result = typeVarAssigns.get(var);
+ if (result instanceof TypeVariable> && !result.equals(var)) {
+ var = (TypeVariable>) result;
+ continue;
+ }
+ break;
+ }
+ while (true);
+ return result;
+ }
+
+ /**
+ *
+ * Checks if the subject type may be implicitly cast to the target generic
+ * array type following the Java generics rules.
+ *
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toGenericArrayType the target generic array type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toGenericArrayType}.
+ */
+ private static boolean isAssignable(final Type type,
+ final GenericArrayType toGenericArrayType,
+ final Map, Type> typeVarAssigns)
+ {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toGenericArrayType == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toGenericArrayType.equals(type)) {
+ return true;
+ }
+
+ final Type toComponentType = toGenericArrayType.getGenericComponentType();
+
+ if (type instanceof Class>) {
+ final Class> cls = (Class>) type;
+
+ // compare the component types
+ return cls.isArray() && isAssignable(cls.getComponentType(),
+ toComponentType, typeVarAssigns);
+ }
+
+ if (type instanceof GenericArrayType) {
+ // compare the component types
+ return isAssignable(((GenericArrayType) type).getGenericComponentType(),
+ toComponentType, typeVarAssigns);
+ }
+
+ if (type instanceof WildcardType) {
+ // so long as one of the upper bounds is assignable, it's good
+ for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
+ if (isAssignable(bound, toGenericArrayType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (type instanceof TypeVariable>) {
+ // probably should remove the following logic and just return false.
+ // type variables cannot specify arrays as bounds.
+ for (final Type bound : getImplicitBounds((TypeVariable>) type)) {
+ if (isAssignable(bound, toGenericArrayType)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ if (type instanceof ParameterizedType) {
+ // the raw type of a parameterized type is never an array or
+ // generic array, otherwise the declaration would look like this:
+ // Collection[]< ? extends String > collection;
+ return false;
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ *
+ * Checks if the subject type may be implicitly cast to the target wildcard
+ * type following the Java generics rules.
+ *
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toWildcardType the target wildcard type
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toWildcardType}.
+ */
+ private static boolean isAssignable(final Type type,
+ final WildcardType toWildcardType,
+ final Map, Type> typeVarAssigns)
+ {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toWildcardType == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toWildcardType.equals(type)) {
+ return true;
+ }
+
+ final Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType);
+ final Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType);
+
+ if (type instanceof WildcardType) {
+ final WildcardType wildcardType = (WildcardType) type;
+ final Type[] upperBounds = getImplicitUpperBounds(wildcardType);
+ final Type[] lowerBounds = getImplicitLowerBounds(wildcardType);
+
+ for (Type toBound : toUpperBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ toBound = substituteTypeVariables(toBound, typeVarAssigns);
+
+ // each upper bound of the subject type has to be assignable to
+ // each
+ // upper bound of the target type
+ for (final Type bound : upperBounds) {
+ if (!isAssignable(bound, toBound, typeVarAssigns)) {
+ return false;
+ }
+ }
+ }
+
+ for (Type toBound : toLowerBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ toBound = substituteTypeVariables(toBound, typeVarAssigns);
+
+ // each lower bound of the target type has to be assignable to
+ // each
+ // lower bound of the subject type
+ for (final Type bound : lowerBounds) {
+ if (!isAssignable(toBound, bound, typeVarAssigns)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ for (final Type toBound : toUpperBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ if (!isAssignable(type, substituteTypeVariables(toBound,
+ typeVarAssigns), typeVarAssigns))
+ {
+ return false;
+ }
+ }
+
+ for (final Type toBound : toLowerBounds) {
+ // if there are assignments for unresolved type variables,
+ // now's the time to substitute them.
+ if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns),
+ type, typeVarAssigns))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
+ * Checks if the subject type may be implicitly cast to the target type
+ * variable following the Java generics rules.
+ *
+ *
+ * @param type the subject type to be assigned to the target type
+ * @param toTypeVariable the target type variable
+ * @param typeVarAssigns a map with type variables
+ * @return {@code true} if {@code type} is assignable to
+ * {@code toTypeVariable}.
+ */
+ private static boolean isAssignable(final Type type,
+ final TypeVariable> toTypeVariable,
+ final Map, Type> typeVarAssigns)
+ {
+ if (type == null) {
+ return true;
+ }
+
+ // only a null type can be assigned to null type which
+ // would have cause the previous to return true
+ if (toTypeVariable == null) {
+ return false;
+ }
+
+ // all types are assignable to themselves
+ if (toTypeVariable.equals(type)) {
+ return true;
+ }
+
+ if (type instanceof TypeVariable>) {
+ // a type variable is assignable to another type variable, if
+ // and only if the former is the latter, extends the latter, or
+ // is otherwise a descendant of the latter.
+ final Type[] bounds = getImplicitBounds((TypeVariable>) type);
+
+ for (final Type bound : bounds) {
+ if (isAssignable(bound, toTypeVariable, typeVarAssigns)) {
+ return true;
+ }
+ }
+ }
+
+ if (type instanceof Class> || type instanceof ParameterizedType ||
+ type instanceof GenericArrayType || type instanceof WildcardType)
+ {
+ return false;
+ }
+
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ *
+ * Find the mapping for {@code type} in {@code typeVarAssigns}.
+ *
+ *
+ * @param type the type to be replaced
+ * @param typeVarAssigns the map with type variables
+ * @return the replaced type
+ * @throws IllegalArgumentException if the type cannot be substituted
+ */
+ private static Type substituteTypeVariables(final Type type,
+ final Map, Type> typeVarAssigns)
+ {
+ if (type instanceof TypeVariable> && typeVarAssigns != null) {
+ final Type replacementType = typeVarAssigns.get(type);
+
+ if (replacementType == null) {
+ throw new IllegalArgumentException(
+ "missing assignment type for type variable " + type);
+ }
+ return replacementType;
+ }
+ return type;
+ }
+
+ /**
+ *
+ * Retrieves all the type arguments for this parameterized type including
+ * owner hierarchy arguments such as {@code Outer.Inner.DeepInner
+ * } . The arguments are returned in a {@link Map} specifying the
+ * argument type for each {@link TypeVariable}.
+ *
+ *
+ * @param type specifies the subject parameterized type from which to
+ * harvest the parameters.
+ * @return a {@code Map} of the type arguments to their respective type
+ * variables.
+ */
+ public static Map, Type> getTypeArguments(
+ final ParameterizedType type)
+ {
+ return getTypeArguments(type, getRawType(type), null);
+ }
+
+ /**
+ *
+ * Gets the type arguments of a class/interface based on a subtype. For
+ * instance, this method will determine that both of the parameters for the
+ * interface {@link Map} are {@link Object} for the subtype
+ * {@link java.util.Properties Properties} even though the subtype does not
+ * directly implement the {@code Map} interface.
+ *
+ *
+ * This method returns {@code null} if {@code type} is not assignable to
+ * {@code toClass}. It returns an empty map if none of the classes or
+ * interfaces in its inheritance hierarchy specify any type arguments.
+ *
+ *
+ * A side effect of this method is that it also retrieves the type arguments
+ * for the classes and interfaces that are part of the hierarchy between
+ * {@code type} and {@code toClass}. So with the above example, this method
+ * will also determine that the type arguments for
+ * {@link java.util.Hashtable Hashtable} are also both {@code Object}. In
+ * cases where the interface specified by {@code toClass} is (indirectly)
+ * implemented more than once (e.g. where {@code toClass} specifies the
+ * interface {@link java.lang.Iterable Iterable} and {@code type} specifies
+ * a parameterized type that implements both {@link java.util.Set Set} and
+ * {@link java.util.Collection Collection}), this method will look at the
+ * inheritance hierarchy of only one of the implementations/subclasses; the
+ * first interface encountered that isn't a subinterface to one of the
+ * others in the {@code type} to {@code toClass} hierarchy.
+ *
+ *
+ * @param type the type from which to determine the type parameters of
+ * {@code toClass}
+ * @param toClass the class whose type parameters are to be determined based
+ * on the subtype {@code type}
+ * @return a {@code Map} of the type assignments for the type variables in
+ * each type in the inheritance hierarchy from {@code type} to
+ * {@code toClass} inclusive.
+ */
+ public static Map, Type> getTypeArguments(final Type type,
+ final Class> toClass)
+ {
+ return getTypeArguments(type, toClass, null);
+ }
+
+ /**
+ *
+ * Return a map of the type arguments of @{code type} in the context of
+ * {@code toClass}.
+ *
+ *
+ * @param type the type in question
+ * @param toClass the class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
+ */
+ private static Map, Type> getTypeArguments(final Type type,
+ final Class> toClass,
+ final Map, Type> subtypeVarAssigns)
+ {
+ if (type instanceof Class>) {
+ return getTypeArguments((Class>) type, toClass, subtypeVarAssigns);
+ }
+
+ if (type instanceof ParameterizedType) {
+ return getTypeArguments((ParameterizedType) type, toClass,
+ subtypeVarAssigns);
+ }
+
+ if (type instanceof GenericArrayType) {
+ return getTypeArguments(((GenericArrayType) type)
+ .getGenericComponentType(), toClass.isArray() ? toClass
+ .getComponentType() : toClass, subtypeVarAssigns);
+ }
+
+ // since wildcard types are not assignable to classes, should this just
+ // return null?
+ if (type instanceof WildcardType) {
+ for (final Type bound : getImplicitUpperBounds((WildcardType) type)) {
+ // find the first bound that is assignable to the target class
+ if (isAssignable(bound, toClass)) {
+ return getTypeArguments(bound, toClass, subtypeVarAssigns);
+ }
+ }
+
+ return null;
+ }
+
+ if (type instanceof TypeVariable>) {
+ for (final Type bound : getImplicitBounds((TypeVariable>) type)) {
+ // find the first bound that is assignable to the target class
+ if (isAssignable(bound, toClass)) {
+ return getTypeArguments(bound, toClass, subtypeVarAssigns);
+ }
+ }
+
+ return null;
+ }
+ throw new IllegalStateException("found an unhandled type: " + type);
+ }
+
+ /**
+ *
+ * Return a map of the type arguments of a parameterized type in the context
+ * of {@code toClass}.
+ *
+ *
+ * @param parameterizedType the parameterized type
+ * @param toClass the class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
+ */
+ private static Map, Type> getTypeArguments(
+ final ParameterizedType parameterizedType, final Class> toClass,
+ final Map, Type> subtypeVarAssigns)
+ {
+ final Class> cls = getRawType(parameterizedType);
+
+ // make sure they're assignable
+ if (!isAssignable(cls, toClass)) {
+ return null;
+ }
+
+ final Type ownerType = parameterizedType.getOwnerType();
+ Map, Type> typeVarAssigns;
+
+ if (ownerType instanceof ParameterizedType) {
+ // get the owner type arguments first
+ final ParameterizedType parameterizedOwnerType =
+ (ParameterizedType) ownerType;
+ typeVarAssigns = getTypeArguments(parameterizedOwnerType, getRawType(
+ parameterizedOwnerType), subtypeVarAssigns);
+ }
+ else {
+ // no owner, prep the type variable assignments map
+ typeVarAssigns = subtypeVarAssigns == null ? new HashMap<>()
+ : new HashMap<>(subtypeVarAssigns);
+ }
+
+ // get the subject parameterized type's arguments
+ final Type[] typeArgs = parameterizedType.getActualTypeArguments();
+ // and get the corresponding type variables from the raw class
+ final TypeVariable>[] typeParams = cls.getTypeParameters();
+
+ // map the arguments to their respective type variables
+ for (int i = 0; i < typeParams.length; i++) {
+ final Type typeArg = typeArgs[i];
+ typeVarAssigns.put(typeParams[i], typeVarAssigns.containsKey(typeArg)
+ ? typeVarAssigns.get(typeArg) : typeArg);
+ }
+
+ if (toClass.equals(cls)) {
+ // target class has been reached. Done.
+ return typeVarAssigns;
+ }
+
+ // walk the inheritance hierarchy until the target class is reached
+ return getTypeArguments(getClosestParentType(cls, toClass), toClass,
+ typeVarAssigns);
+ }
+
+ /**
+ *
+ * Return a map of the type arguments of a class in the context of @{code
+ * toClass}.
+ *
+ *
+ * @param cls the class in question
+ * @param toClass the context class
+ * @param subtypeVarAssigns a map with type variables
+ * @return the {@code Map} with type arguments
+ */
+ private static Map, Type> getTypeArguments(Class> cls,
+ final Class> toClass,
+ final Map, Type> subtypeVarAssigns)
+ {
+ // make sure they're assignable
+ if (!isAssignable(cls, toClass)) {
+ return null;
+ }
+
+ // can't work with primitives
+ if (cls.isPrimitive()) {
+ // both classes are primitives?
+ if (toClass.isPrimitive()) {
+ // dealing with widening here. No type arguments to be
+ // harvested with these two types.
+ return new HashMap<>();
+ }
+
+ // work with wrapper the wrapper class instead of the primitive
+ cls = ConversionUtils.getNonprimitiveType(cls);
+ }
+
+ // create a copy of the incoming map, or an empty one if it's null
+ final HashMap, Type> typeVarAssigns =
+ subtypeVarAssigns == null ? new HashMap<>() : new HashMap<>(
+ subtypeVarAssigns);
+
+ // has target class been reached?
+ if (toClass.equals(cls)) {
+ return typeVarAssigns;
+ }
+
+ // walk the inheritance hierarchy until the target class is reached
+ return getTypeArguments(getClosestParentType(cls, toClass), toClass,
+ typeVarAssigns);
+ }
+
+ /**
+ *
+ * Tries to determine the type arguments of a class/interface based on a
+ * super parameterized type's type arguments. This method is the inverse of
+ * {@link #getTypeArguments(Type, Class)} which gets a class/interface's
+ * type arguments based on a subtype. It is far more limited in determining
+ * the type arguments for the subject class's type variables in that it can
+ * only determine those parameters that map from the subject {@link Class}
+ * object to the supertype.
+ *
+ *
+ * Example: {@link java.util.TreeSet TreeSet} sets its parameter as the
+ * parameter for {@link java.util.NavigableSet NavigableSet}, which in turn
+ * sets the parameter of {@link java.util.SortedSet}, which in turn sets the
+ * parameter of {@link Set}, which in turn sets the parameter of
+ * {@link java.util.Collection}, which in turn sets the parameter of
+ * {@link java.lang.Iterable}. Since {@code TreeSet}'s parameter maps
+ * (indirectly) to {@code Iterable}'s parameter, it will be able to
+ * determine that based on the super type {@code Iterable extends
+ * Map>>}, the parameter of {@code TreeSet}
+ * is {@code ? extends Map>}.
+ *
+ *
+ * @param cls the class whose type parameters are to be determined, not
+ * {@code null}
+ * @param superType the super type from which {@code cls}'s type arguments
+ * are to be determined, not {@code null}
+ * @return a {@code Map} of the type assignments that could be determined
+ * for the type variables in each type in the inheritance hierarchy
+ * from {@code type} to {@code toClass} inclusive.
+ */
+ public static Map, Type> determineTypeArguments(
+ final Class> cls, final ParameterizedType superType)
+ {
+ validateNotNull(cls, "cls is null");
+ validateNotNull(superType, "superType is null");
+
+ final Class> superClass = getRawType(superType);
+
+ // compatibility check
+ if (!isAssignable(cls, superClass)) {
+ return null;
+ }
+
+ if (cls.equals(superClass)) {
+ return getTypeArguments(superType, superClass, null);
+ }
+
+ // get the next class in the inheritance hierarchy
+ final Type midType = getClosestParentType(cls, superClass);
+
+ // can only be a class or a parameterized type
+ if (midType instanceof Class>) {
+ return determineTypeArguments((Class>) midType, superType);
+ }
+
+ final ParameterizedType midParameterizedType =
+ (ParameterizedType) midType;
+ final Class> midClass = getRawType(midParameterizedType);
+ // get the type variables of the mid class that map to the type
+ // arguments of the super class
+ final Map, Type> typeVarAssigns = determineTypeArguments(
+ midClass, superType);
+ // map the arguments of the mid type to the class type variables
+ mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns);
+
+ return typeVarAssigns;
+ }
+
+ /**
+ *
+ * Performs a mapping of type variables.
+ *
+ *
+ * @param the generic type of the class in question
+ * @param cls the class in question
+ * @param parameterizedType the parameterized type
+ * @param typeVarAssigns the map to be filled
+ */
+ private static void mapTypeVariablesToArguments(final Class cls,
+ final ParameterizedType parameterizedType,
+ final Map, Type> typeVarAssigns)
+ {
+ // capture the type variables from the owner type that have assignments
+ final Type ownerType = parameterizedType.getOwnerType();
+
+ if (ownerType instanceof ParameterizedType) {
+ // recursion to make sure the owner's owner type gets processed
+ mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType,
+ typeVarAssigns);
+ }
+
+ // parameterizedType is a generic interface/class (or it's in the owner
+ // hierarchy of said interface/class) implemented/extended by the class
+ // cls. Find out which type variables of cls are type arguments of
+ // parameterizedType:
+ final Type[] typeArgs = parameterizedType.getActualTypeArguments();
+
+ // of the cls's type variables that are arguments of parameterizedType,
+ // find out which ones can be determined from the super type's arguments
+ final TypeVariable>[] typeVars = getRawType(parameterizedType)
+ .getTypeParameters();
+
+ // use List view of type parameters of cls so the contains() method can be
+ // used:
+ final List>> typeVarList = Arrays.asList(cls
+ .getTypeParameters());
+
+ for (int i = 0; i < typeArgs.length; i++) {
+ final TypeVariable> typeVar = typeVars[i];
+ final Type typeArg = typeArgs[i];
+
+ // argument of parameterizedType is a type variable of cls
+ if (typeVarList.contains(typeArg)
+ // type variable of parameterizedType has an assignment in
+ // the super type.
+ && typeVarAssigns.containsKey(typeVar)) {
+ // map the assignment to the cls's type variable
+ typeVarAssigns.put((TypeVariable>) typeArg, typeVarAssigns.get(
+ typeVar));
+ }
+ }
+ }
+
+ /**
+ *
+ * Get the closest parent type to the super class specified by
+ * {@code superClass}.
+ *
+ *
+ * @param cls the class in question
+ * @param superClass the super class
+ * @return the closes parent type
+ */
+ private static Type getClosestParentType(final Class> cls,
+ final Class> superClass)
+ {
+ // only look at the interfaces if the super class is also an interface
+ if (superClass.isInterface()) {
+ // get the generic interfaces of the subject class
+ final Type[] interfaceTypes = cls.getGenericInterfaces();
+ // will hold the best generic interface match found
+ Type genericInterface = null;
+
+ // find the interface closest to the super class
+ for (final Type midType : interfaceTypes) {
+ Class> midClass = null;
+
+ if (midType instanceof ParameterizedType) {
+ midClass = getRawType((ParameterizedType) midType);
+ }
+ else if (midType instanceof Class>) {
+ midClass = (Class>) midType;
+ }
+ else {
+ throw new IllegalStateException("Unexpected generic" +
+ " interface type found: " + midType);
+ }
+
+ // check if this interface is further up the inheritance chain
+ // than the previously found match
+ if (isAssignable(midClass, superClass) && isAssignable(
+ genericInterface, (Type) midClass))
+ {
+ genericInterface = midType;
+ }
+ }
+
+ // found a match?
+ if (genericInterface != null) {
+ return genericInterface;
+ }
+ }
+
+ // none of the interfaces were descendants of the target class, so the
+ // super class has to be one, instead
+ return cls.getGenericSuperclass();
+ }
+
+ /**
+ *
+ * Checks if the given value can be assigned to the target type following
+ * the Java generics rules.
+ *
+ *
+ * @param value the value to be checked
+ * @param type the target type
+ * @return {@code true} if {@code value} is an instance of {@code type}.
+ */
+ public static boolean isInstance(final Object value, final Type type) {
+ if (type == null) {
+ return false;
+ }
+
+ return value == null ? !(type instanceof Class>) || !((Class>) type)
+ .isPrimitive() : isAssignable(value.getClass(), type, null);
+ }
+
+ /**
+ *
+ * This method strips out the redundant upper bound types in type variable
+ * types and wildcard types (or it would with wildcard types if multiple
+ * upper bounds were allowed).
+ *
+ *
+ * Example, with the variable type declaration:
+ *
+ *
+ * since {@code List} is a subinterface of {@code Collection}, this method
+ * will return the bounds as if the declaration had been:
+ *
+ *
+ *
+ * <K extends java.util.List<String>>
+ *
+ *
+ * @param bounds an array of types representing the upper bounds of either
+ * {@link WildcardType} or {@link TypeVariable}, not {@code null}.
+ * @return an array containing the values from {@code bounds} minus the
+ * redundant types.
+ */
+ public static Type[] normalizeUpperBounds(final Type[] bounds) {
+ validateNotNull(bounds, "null value specified for bounds array");
+ // don't bother if there's only one (or none) type
+ if (bounds.length < 2) {
+ return bounds;
+ }
+
+ final Set types = new HashSet<>(bounds.length);
+
+ for (final Type type1 : bounds) {
+ boolean subtypeFound = false;
+
+ for (final Type type2 : bounds) {
+ if (type1 != type2 && isAssignable(type2, type1, null)) {
+ subtypeFound = true;
+ break;
+ }
+ }
+
+ if (!subtypeFound) {
+ types.add(type1);
+ }
+ }
+
+ return types.toArray(new Type[types.size()]);
+ }
+
+ /**
+ *
+ * Returns an array containing the sole type of {@link Object} if
+ * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it
+ * returns the result of {@link TypeVariable#getBounds()} passed into
+ * {@link #normalizeUpperBounds}.
+ *
+ *
+ * @param typeVariable the subject type variable, not {@code null}
+ * @return a non-empty array containing the bounds of the type variable.
+ */
+ public static Type[] getImplicitBounds(final TypeVariable> typeVariable) {
+ validateNotNull(typeVariable, "typeVariable is null");
+ final Type[] bounds = typeVariable.getBounds();
+
+ return bounds.length == 0 ? new Type[] { Object.class }
+ : normalizeUpperBounds(bounds);
+ }
+
+ /**
+ *
+ * Returns an array containing the sole value of {@link Object} if
+ * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise,
+ * it returns the result of {@link WildcardType#getUpperBounds()} passed
+ * into {@link #normalizeUpperBounds}.
+ *
+ *
+ * @param wildcardType the subject wildcard type, not {@code null}
+ * @return a non-empty array containing the upper bounds of the wildcard
+ * type.
+ */
+ public static Type[] getImplicitUpperBounds(
+ final WildcardType wildcardType)
+ {
+ validateNotNull(wildcardType, "wildcardType is null");
+ final Type[] bounds = wildcardType.getUpperBounds();
+
+ return bounds.length == 0 ? new Type[] { Object.class }
+ : normalizeUpperBounds(bounds);
+ }
+
+ /**
+ *
+ * Returns an array containing a single value of {@code null} if
+ * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise,
+ * it returns the result of {@link WildcardType#getLowerBounds()}.
+ *
+ *
+ * @param wildcardType the subject wildcard type, not {@code null}
+ * @return a non-empty array containing the lower bounds of the wildcard
+ * type.
+ */
+ public static Type[] getImplicitLowerBounds(
+ final WildcardType wildcardType)
+ {
+ validateNotNull(wildcardType, "wildcardType is null");
+ final Type[] bounds = wildcardType.getLowerBounds();
+
+ return bounds.length == 0 ? new Type[] { null } : bounds;
+ }
+
+ /**
+ *
+ * Determines whether or not specified types satisfy the bounds of their
+ * mapped type variables. When a type parameter extends another (such as
+ * {@code }), uses another as a type parameter (such as
+ * {@code >}), or otherwise depends on another type
+ * variable to be specified, the dependencies must be included in
+ * {@code typeVarAssigns}.
+ *
+ *
+ * @param typeVarAssigns specifies the potential types to be assigned to the
+ * type variables, not {@code null}.
+ * @return whether or not the types can be assigned to their respective type
+ * variables.
+ */
+ public static boolean typesSatisfyVariables(
+ final Map, Type> typeVarAssigns)
+ {
+ validateNotNull(typeVarAssigns, "typeVarAssigns is null");
+ // all types must be assignable to all the bounds of the their mapped
+ // type variable.
+ for (final Map.Entry, Type> entry : typeVarAssigns
+ .entrySet())
+ {
+ final TypeVariable> typeVar = entry.getKey();
+ final Type type = entry.getValue();
+
+ for (final Type bound : getImplicitBounds(typeVar)) {
+ if (!isAssignable(type, substituteTypeVariables(bound,
+ typeVarAssigns), typeVarAssigns))
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ *
+ * Transforms the passed in type to a {@link Class} object. Type-checking
+ * method of convenience.
+ *
+ *
+ * @param parameterizedType the type to be converted
+ * @return the corresponding {@code Class} object
+ * @throws IllegalStateException if the conversion fails
+ */
+ private static Class> getRawType(
+ final ParameterizedType parameterizedType)
+ {
+ final Type rawType = parameterizedType.getRawType();
+
+ // check if raw type is a Class object
+ // not currently necessary, but since the return type is Type instead of
+ // Class, there's enough reason to believe that future versions of Java
+ // may return other Type implementations. And type-safety checking is
+ // rarely a bad idea.
+ if (!(rawType instanceof Class>)) {
+ throw new IllegalStateException("Wait... What!? Type of rawType: " +
+ rawType);
+ }
+
+ return (Class>) rawType;
+ }
+
+ /**
+ *
+ * Get the raw type of a Java type, given its context. Primarily for use
+ * with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do
+ * not know the runtime type of {@code type}: if you know you have a
+ * {@link Class} instance, it is already raw; if you know you have a
+ * {@link ParameterizedType}, its raw type is only a method call away.
+ *
+ *
+ * @param type to resolve
+ * @param assigningType type to be resolved against
+ * @return the resolved {@link Class} object or {@code null} if the type
+ * could not be resolved
+ */
+ public static Class> getRawType(final Type type,
+ final Type assigningType)
+ {
+ if (type instanceof Class>) {
+ // it is raw, no problem
+ return (Class>) type;
+ }
+
+ if (type instanceof ParameterizedType) {
+ // simple enough to get the raw type of a ParameterizedType
+ return getRawType((ParameterizedType) type);
+ }
+
+ if (type instanceof TypeVariable>) {
+ if (assigningType == null) {
+ return null;
+ }
+
+ // get the entity declaring this type variable
+ final Object genericDeclaration = ((TypeVariable>) type)
+ .getGenericDeclaration();
+
+ // can't get the raw type of a method- or constructor-declared type
+ // variable
+ if (!(genericDeclaration instanceof Class>)) {
+ return null;
+ }
+
+ // get the type arguments for the declaring class/interface based
+ // on the enclosing type
+ final Map, Type> typeVarAssigns = getTypeArguments(
+ assigningType, (Class>) genericDeclaration);
+
+ // enclosingType has to be a subclass (or subinterface) of the
+ // declaring type
+ if (typeVarAssigns == null) {
+ return null;
+ }
+
+ // get the argument assigned to this type variable
+ final Type typeArgument = typeVarAssigns.get(type);
+
+ if (typeArgument == null) {
+ return null;
+ }
+
+ // get the argument for this type variable
+ return getRawType(typeArgument, assigningType);
+ }
+
+ if (type instanceof GenericArrayType) {
+ // get raw component type
+ final Class> rawComponentType = getRawType(((GenericArrayType) type)
+ .getGenericComponentType(), assigningType);
+
+ // create array type from raw component type and return its class
+ return Array.newInstance(rawComponentType, 0).getClass();
+ }
+
+ // (hand-waving) this is not the method you're looking for
+ if (type instanceof WildcardType) {
+ return null;
+ }
+
+ throw new IllegalArgumentException("unknown type: " + type);
+ }
+
+ /**
+ * Learn whether the specified type denotes an array type.
+ *
+ * @param type the type to be checked
+ * @return {@code true} if {@code type} is an array class or a
+ * {@link GenericArrayType}.
+ */
+ public static boolean isArrayType(final Type type) {
+ return type instanceof GenericArrayType || type instanceof Class> &&
+ ((Class>) type).isArray();
+ }
+
+ /**
+ * Get the array component type of {@code type}.
+ *
+ * @param type the type to be checked
+ * @return component type or null if type is not an array type
+ */
+ public static Type getArrayComponentType(final Type type) {
+ if (type instanceof Class>) {
+ final Class> clazz = (Class>) type;
+ return clazz.isArray() ? clazz.getComponentType() : null;
+ }
+ if (type instanceof GenericArrayType) {
+ return ((GenericArrayType) type).getGenericComponentType();
+ }
+ return null;
+ }
+
+ /**
+ * Get a type representing {@code type} with variable assignments
+ * "unrolled."
+ *
+ * @param typeArguments as from
+ * {@link TypeUtils#getTypeArguments(Type, Class)}
+ * @param type the type to unroll variable assignments for
+ * @return Type
+ * @since 3.2
+ */
+ public static Type unrollVariables(Map, Type> typeArguments,
+ final Type type)
+ {
+ if (typeArguments == null) {
+ typeArguments = Collections., Type> emptyMap();
+ }
+ if (containsTypeVariables(type)) {
+ if (type instanceof TypeVariable>) {
+ return unrollVariables(typeArguments, typeArguments.get(type));
+ }
+ if (type instanceof ParameterizedType) {
+ final ParameterizedType p = (ParameterizedType) type;
+ final Map, Type> parameterizedTypeArguments;
+ if (p.getOwnerType() == null) {
+ parameterizedTypeArguments = typeArguments;
+ }
+ else {
+ parameterizedTypeArguments = new HashMap<>(typeArguments);
+ parameterizedTypeArguments.putAll(TypeUtils.getTypeArguments(p));
+ }
+ final Type[] args = p.getActualTypeArguments();
+ for (int i = 0; i < args.length; i++) {
+ final Type unrolled = unrollVariables(parameterizedTypeArguments,
+ args[i]);
+ if (unrolled != null) {
+ args[i] = unrolled;
+ }
+ }
+ return parameterizeWithOwner(p.getOwnerType(), (Class>) p
+ .getRawType(), args);
+ }
+ if (type instanceof WildcardType) {
+ final WildcardType wild = (WildcardType) type;
+ return wildcardType().withUpperBounds(unrollBounds(typeArguments, wild
+ .getUpperBounds())).withLowerBounds(unrollBounds(typeArguments, wild
+ .getLowerBounds())).build();
+ }
+ }
+ return type;
+ }
+
+ /**
+ * Local helper method to unroll variables in a type bounds array.
+ *
+ * @param typeArguments assignments {@link Map}
+ * @param bounds in which to expand variables
+ * @return {@code bounds} with any variables reassigned
+ * @since 3.2
+ */
+ private static Type[] unrollBounds(
+ final Map, Type> typeArguments, final Type[] bounds)
+ {
+ final ArrayList result = new ArrayList<>();
+ for (final Type bound : bounds) {
+ final Type unrolled = unrollVariables(typeArguments, bound);
+ if (unrolled != null) result.add(unrolled);
+ }
+ return result.toArray(new Type[result.size()]);
+ }
+
+ /**
+ * Learn, recursively, whether any of the type parameters associated with
+ * {@code type} are bound to variables.
+ *
+ * @param type the type to check for type variables
+ * @return boolean
+ * @since 3.2
+ */
+ public static boolean containsTypeVariables(final Type type) {
+ if (type instanceof TypeVariable>) {
+ return true;
+ }
+ if (type instanceof Class>) {
+ return ((Class>) type).getTypeParameters().length > 0;
+ }
+ if (type instanceof ParameterizedType) {
+ for (final Type arg : ((ParameterizedType) type)
+ .getActualTypeArguments())
+ {
+ if (containsTypeVariables(arg)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ if (type instanceof WildcardType) {
+ final WildcardType wild = (WildcardType) type;
+ return containsTypeVariables(TypeUtils.getImplicitLowerBounds(
+ wild)[0]) || containsTypeVariables(TypeUtils.getImplicitUpperBounds(
+ wild)[0]);
+ }
+ return false;
+ }
+
+ /**
+ * Create a parameterized type instance.
+ *
+ * @param raw the raw class to create a parameterized type instance for
+ * @param typeArguments the types used for parameterization
+ * @return {@link ParameterizedType}
+ * @since 3.2
+ */
+ public static final ParameterizedType parameterize(final Class> raw,
+ final Type... typeArguments)
+ {
+ return parameterizeWithOwner(null, raw, typeArguments);
+ }
+
+ /**
+ * Create a parameterized type instance.
+ *
+ * @param raw the raw class to create a parameterized type instance for
+ * @param typeArgMappings the mapping used for parameterization
+ * @return {@link ParameterizedType}
+ * @since 3.2
+ */
+ public static final ParameterizedType parameterize(final Class> raw,
+ final Map, Type> typeArgMappings)
+ {
+ validateNotNull(raw, "raw class is null");
+ validateNotNull(typeArgMappings, "typeArgMappings is null");
+ return parameterizeWithOwner(null, raw, extractTypeArgumentsFrom(
+ typeArgMappings, raw.getTypeParameters()));
+ }
+
+ /**
+ * Create a parameterized type instance.
+ *
+ * @param owner the owning type
+ * @param raw the raw class to create a parameterized type instance for
+ * @param typeArguments the types used for parameterization
+ * @return {@link ParameterizedType}
+ * @since 3.2
+ */
+ public static final ParameterizedType parameterizeWithOwner(
+ final Type owner, final Class> raw, final Type... typeArguments)
+ {
+ validateNotNull(raw, "raw class is null");
+ final Type useOwner;
+ if (raw.getEnclosingClass() == null) {
+ validateIsTrue(owner == null, "no owner allowed for top-level %s", raw);
+ useOwner = null;
+ }
+ else if (owner == null) {
+ useOwner = raw.getEnclosingClass();
+ }
+ else {
+ validateIsTrue(TypeUtils.isAssignable(owner, raw.getEnclosingClass()),
+ "%s is invalid owner type for parameterized %s", owner, raw);
+ useOwner = owner;
+ }
+ validateNoNullElements(typeArguments, "null type argument at index %s");
+ validateIsTrue(raw.getTypeParameters().length == typeArguments.length,
+ "invalid number of type parameters specified: expected %s, got %s", raw
+ .getTypeParameters().length, typeArguments.length);
+
+ return new ParameterizedTypeImpl(raw, useOwner, typeArguments);
+ }
+
+ /**
+ * Create a parameterized type instance.
+ *
+ * @param owner the owning type
+ * @param raw the raw class to create a parameterized type instance for
+ * @param typeArgMappings the mapping used for parameterization
+ * @return {@link ParameterizedType}
+ * @since 3.2
+ */
+ public static final ParameterizedType parameterizeWithOwner(
+ final Type owner, final Class> raw,
+ final Map, Type> typeArgMappings)
+ {
+ validateNotNull(raw, "raw class is null");
+ validateNotNull(typeArgMappings, "typeArgMappings is null");
+ return parameterizeWithOwner(owner, raw, extractTypeArgumentsFrom(
+ typeArgMappings, raw.getTypeParameters()));
+ }
+
+ /**
+ * Helper method to establish the formal parameters for a parameterized
+ * type.
+ *
+ * @param mappings map containing the assignements
+ * @param variables expected map keys
+ * @return array of map values corresponding to specified keys
+ */
+ private static Type[] extractTypeArgumentsFrom(
+ final Map, Type> mappings,
+ final TypeVariable>[] variables)
+ {
+ final Type[] result = new Type[variables.length];
+ int index = 0;
+ for (final TypeVariable> var : variables) {
+ validateIsTrue(mappings.containsKey(var),
+ "missing argument mapping for %s", toString(var));
+ result[index++] = mappings.get(var);
+ }
+ return result;
+ }
+
+ /**
+ * Get a {@link WildcardTypeBuilder}.
+ *
+ * @return {@link WildcardTypeBuilder}
+ * @since 3.2
+ */
+ public static WildcardTypeBuilder wildcardType() {
+ return new WildcardTypeBuilder();
+ }
+
+ /**
+ * Create a generic array type instance.
+ *
+ * @param componentType the type of the elements of the array. For example
+ * the component type of {@code boolean[]} is {@code boolean}
+ * @return {@link GenericArrayType}
+ * @since 3.2
+ */
+ public static GenericArrayType genericArrayType(final Type componentType) {
+ return new GenericArrayTypeImpl(validateNotNull(componentType,
+ "componentType is null"));
+ }
+
+ /**
+ * Check equality of types.
+ *
+ * @param t1 the first type
+ * @param t2 the second type
+ * @return boolean
+ * @since 3.2
+ */
+ public static boolean equals(final Type t1, final Type t2) {
+ if (Objects.equals(t1, t2)) {
+ return true;
+ }
+ if (t1 instanceof ParameterizedType) {
+ return equals((ParameterizedType) t1, t2);
+ }
+ if (t1 instanceof GenericArrayType) {
+ return equals((GenericArrayType) t1, t2);
+ }
+ if (t1 instanceof WildcardType) {
+ return equals((WildcardType) t1, t2);
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether {@code t} equals {@code p}.
+ *
+ * @param p LHS
+ * @param t RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final ParameterizedType p, final Type t) {
+ if (t instanceof ParameterizedType) {
+ final ParameterizedType other = (ParameterizedType) t;
+ if (equals(p.getRawType(), other.getRawType()) && equals(p
+ .getOwnerType(), other.getOwnerType()))
+ {
+ return equals(p.getActualTypeArguments(), other
+ .getActualTypeArguments());
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Learn whether {@code t} equals {@code a}.
+ *
+ * @param a LHS
+ * @param t RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final GenericArrayType a, final Type t) {
+ return t instanceof GenericArrayType && equals(a
+ .getGenericComponentType(), ((GenericArrayType) t)
+ .getGenericComponentType());
+ }
+
+ /**
+ * Learn whether {@code t} equals {@code w}.
+ *
+ * @param w LHS
+ * @param t RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final WildcardType w, final Type t) {
+ if (t instanceof WildcardType) {
+ final WildcardType other = (WildcardType) t;
+ return equals(getImplicitLowerBounds(w), getImplicitLowerBounds(
+ other)) && equals(getImplicitUpperBounds(w), getImplicitUpperBounds(
+ other));
+ }
+ return true;
+ }
+
+ /**
+ * Learn whether {@code t1} equals {@code t2}.
+ *
+ * @param t1 LHS
+ * @param t2 RHS
+ * @return boolean
+ * @since 3.2
+ */
+ private static boolean equals(final Type[] t1, final Type[] t2) {
+ if (t1.length == t2.length) {
+ for (int i = 0; i < t1.length; i++) {
+ if (!equals(t1[i], t2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Present a given type as a Java-esque String.
+ *
+ * @param type the type to create a String representation for, not
+ * {@code null}
+ * @return String
+ * @since 3.2
+ */
+ public static String toString(final Type type) {
+ validateNotNull(type);
+ if (type instanceof Class>) {
+ return classToString((Class>) type);
+ }
+ if (type instanceof ParameterizedType) {
+ return parameterizedTypeToString((ParameterizedType) type);
+ }
+ if (type instanceof WildcardType) {
+ return wildcardTypeToString((WildcardType) type);
+ }
+ if (type instanceof TypeVariable>) {
+ return typeVariableToString((TypeVariable>) type);
+ }
+ if (type instanceof GenericArrayType) {
+ return genericArrayTypeToString((GenericArrayType) type);
+ }
+ throw new IllegalArgumentException("Unknown generic type: " + //
+ type.getClass().getName());
+ }
+
+ /**
+ * Format a {@link TypeVariable} including its {@link GenericDeclaration}.
+ *
+ * @param var the type variable to create a String representation for, not
+ * {@code null}
+ * @return String
+ * @since 3.2
+ */
+ public static String toLongString(final TypeVariable> var) {
+ validateNotNull(var, "var is null");
+ final StringBuilder buf = new StringBuilder();
+ final GenericDeclaration d = ((TypeVariable>) var)
+ .getGenericDeclaration();
+ if (d instanceof Class>) {
+ Class> c = (Class>) d;
+ while (true) {
+ if (c.getEnclosingClass() == null) {
+ buf.insert(0, c.getName());
+ break;
+ }
+ buf.insert(0, c.getSimpleName()).insert(0, '.');
+ c = c.getEnclosingClass();
+ }
+ }
+ else if (d instanceof Type) {// not possible as of now
+ buf.append(toString((Type) d));
+ }
+ else {
+ buf.append(d);
+ }
+ return buf.append(':').append(typeVariableToString(var)).toString();
+ }
+
+// /**
+// * Wrap the specified {@link Type} in a {@link Typed} wrapper.
+// *
+// * @param inferred generic type
+// * @param type to wrap
+// * @return Typed<T>
+// * @since 3.2
+// */
+// public static Typed wrap(final Type type) {
+// return new Typed() {
+//
+// @Override
+// public Type getType() {
+// return type;
+// }
+// };
+// }
+//
+// /**
+// * Wrap the specified {@link Class} in a {@link Typed} wrapper.
+// *
+// * @param generic type
+// * @param type to wrap
+// * @return Typed<T>
+// * @since 3.2
+// */
+// public static Typed wrap(final Class type) {
+// return TypeUtils. wrap((Type) type);
+// }
+
+ /**
+ * Format a {@link Class} as a {@link String}.
+ *
+ * @param c {@code Class} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String classToString(final Class> c) {
+ final StringBuilder buf = new StringBuilder();
+
+ if (c.getEnclosingClass() != null) {
+ buf.append(classToString(c.getEnclosingClass())).append('.').append(c
+ .getSimpleName());
+ }
+ else {
+ buf.append(c.getName());
+ }
+ if (c.getTypeParameters().length > 0) {
+ buf.append('<');
+ appendAllTo(buf, ", ", c.getTypeParameters());
+ buf.append('>');
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Format a {@link TypeVariable} as a {@link String}.
+ *
+ * @param v {@code TypeVariable} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String typeVariableToString(final TypeVariable> v) {
+ final StringBuilder buf = new StringBuilder(v.getName());
+ final Type[] bounds = v.getBounds();
+ if (bounds.length > 0 && !(bounds.length == 1 && Object.class.equals(
+ bounds[0])))
+ {
+ buf.append(" extends ");
+ appendAllTo(buf, " & ", v.getBounds());
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Format a {@link ParameterizedType} as a {@link String}.
+ *
+ * @param p {@code ParameterizedType} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String parameterizedTypeToString(final ParameterizedType p) {
+ final StringBuilder buf = new StringBuilder();
+
+ final Type useOwner = p.getOwnerType();
+ final Class> raw = (Class>) p.getRawType();
+ final Type[] typeArguments = p.getActualTypeArguments();
+ if (useOwner == null) {
+ buf.append(raw.getName());
+ }
+ else {
+ if (useOwner instanceof Class>) {
+ buf.append(((Class>) useOwner).getName());
+ }
+ else {
+ buf.append(useOwner.toString());
+ }
+ buf.append('.').append(raw.getSimpleName());
+ }
+
+ appendAllTo(buf.append('<'), ", ", typeArguments).append('>');
+ return buf.toString();
+ }
+
+ /**
+ * Format a {@link WildcardType} as a {@link String}.
+ *
+ * @param w {@code WildcardType} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String wildcardTypeToString(final WildcardType w) {
+ final StringBuilder buf = new StringBuilder().append('?');
+ final Type[] lowerBounds = w.getLowerBounds();
+ final Type[] upperBounds = w.getUpperBounds();
+ if (lowerBounds.length > 1 || lowerBounds.length == 1 &&
+ lowerBounds[0] != null)
+ {
+ appendAllTo(buf.append(" super "), " & ", lowerBounds);
+ }
+ else if (upperBounds.length > 1 || upperBounds.length == 1 &&
+ !Object.class.equals(upperBounds[0]))
+ {
+ appendAllTo(buf.append(" extends "), " & ", upperBounds);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Format a {@link GenericArrayType} as a {@link String}.
+ *
+ * @param g {@code GenericArrayType} to format
+ * @return String
+ * @since 3.2
+ */
+ private static String genericArrayTypeToString(final GenericArrayType g) {
+ return String.format("%s[]", toString(g.getGenericComponentType()));
+ }
+
+ /**
+ * Append {@code types} to {@code buf} with separator {@code sep}.
+ *
+ * @param buf destination
+ * @param sep separator
+ * @param types to append
+ * @return {@code buf}
+ * @since 3.2
+ */
+ private static StringBuilder appendAllTo(final StringBuilder buf,
+ final String sep, final Type... types)
+ {
+ validateNotEmpty(validateNoNullElements(types));
+ if (types.length > 0) {
+ buf.append(toString(types[0]));
+ for (int i = 1; i < types.length; i++) {
+ buf.append(sep).append(toString(types[i]));
+ }
+ }
+ return buf;
+ }
+
+ private static final String DEFAULT_IS_NULL_EX_MESSAGE =
+ "The validated object is null";
+
+ /** Forked from {@code org.apache.commons.lang3.Validate#notNull}. */
+ private static T validateNotNull(final T object) {
+ return validateNotNull(object, DEFAULT_IS_NULL_EX_MESSAGE);
+ }
+
+ /** Forked from {@code org.apache.commons.lang3.Validate#notNull}. */
+ private static T validateNotNull(final T object, final String message,
+ final Object... values)
+ {
+ if (object == null) {
+ throw new NullPointerException(String.format(message, values));
+ }
+ return object;
+ }
+
+ /** Forked from {@code org.apache.commons.lang3.Validate#isTrue}. */
+ private static void validateIsTrue(final boolean expression,
+ final String message, final Object... values)
+ {
+ if (expression == false) {
+ throw new IllegalArgumentException(String.format(message, values));
+ }
+ }
+
+ private static final String DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE =
+ "The validated array contains null element at index: %d";
+
+ /** Forked from {@code org.apache.commons.lang3.Validate#noNullElements}. */
+ private static T[] validateNoNullElements(final T[] array) {
+ return validateNoNullElements(array,
+ DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE);
+ }
+
+ /** Forked from {@code org.apache.commons.lang3.Validate#noNullElements}. */
+ private static T[] validateNoNullElements(final T[] array,
+ final String message, final Object... values)
+ {
+ validateNotNull(array);
+ for (int i = 0; i < array.length; i++) {
+ if (array[i] == null) {
+ final Object[] values2 = new Object[values.length + 1];
+ System.arraycopy(values, 0, values2, 0, values.length);
+ values2[values.length] = Integer.valueOf(i);
+ throw new IllegalArgumentException(String.format(message, values2));
+ }
+ }
+ return array;
+ }
+
+ private static final String DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE =
+ "The validated array is empty";
+
+ /** Forked from {@code org.apache.commons.lang3.Validate#notEmpty}. */
+ private static T[] validateNotEmpty(final T[] array) {
+ return validateNotEmpty(array, DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE);
+ }
+
+ /** Forked from {@code org.apache.commons.lang3.Validate#notEmpty}. */
+ private static T[] validateNotEmpty(final T[] array,
+ final String message, final Object... values)
+ {
+ if (array == null) {
+ throw new NullPointerException(String.format(message, values));
+ }
+ if (array.length == 0) {
+ throw new IllegalArgumentException(String.format(message, values));
+ }
+ return array;
+ }
+ }
+
+ // -- END FORK OF APACHE COMMONS LANG 3.4 CODE --
+
}
From 359f22a644d989b2cfc477fb20cb4e8010b72a9a Mon Sep 17 00:00:00 2001
From: Curtis Rueden
Date: Wed, 27 Jul 2016 15:45:41 -0500
Subject: [PATCH 007/500] Types: fix bug in TypeUtils.toString(Type) method
When string-izing a generic type which contains a type variable (or,
theoretically, a wildcard) whose bounds include itself, the method
would previously recurse infinitely, resulting in StackOverflowError.
The solution here is to only enumerate a type's bounds the first time it
is encountered; after that, we simply return the name of the type alone.
---
src/main/java/org/scijava/util/Types.java | 60 ++++++++++++++++-------
1 file changed, 41 insertions(+), 19 deletions(-)
diff --git a/src/main/java/org/scijava/util/Types.java b/src/main/java/org/scijava/util/Types.java
index bd4018ff5..17c8662ba 100644
--- a/src/main/java/org/scijava/util/Types.java
+++ b/src/main/java/org/scijava/util/Types.java
@@ -1917,18 +1917,22 @@ private static boolean equals(final Type[] t1, final Type[] t2) {
* @since 3.2
*/
public static String toString(final Type type) {
+ return toString(type, new HashSet<>());
+ }
+
+ private static String toString(final Type type, final Set done) {
validateNotNull(type);
if (type instanceof Class>) {
- return classToString((Class>) type);
+ return classToString((Class>) type, done);
}
if (type instanceof ParameterizedType) {
- return parameterizedTypeToString((ParameterizedType) type);
+ return parameterizedTypeToString((ParameterizedType) type, done);
}
if (type instanceof WildcardType) {
- return wildcardTypeToString((WildcardType) type);
+ return wildcardTypeToString((WildcardType) type, done);
}
if (type instanceof TypeVariable>) {
- return typeVariableToString((TypeVariable>) type);
+ return typeVariableToString((TypeVariable>) type, done);
}
if (type instanceof GenericArrayType) {
return genericArrayTypeToString((GenericArrayType) type);
@@ -1967,7 +1971,8 @@ else if (d instanceof Type) {// not possible as of now
else {
buf.append(d);
}
- return buf.append(':').append(typeVariableToString(var)).toString();
+ return buf.append(':').append(typeVariableToString(var, new HashSet<>()))
+ .toString();
}
// /**
@@ -2004,22 +2009,25 @@ else if (d instanceof Type) {// not possible as of now
* Format a {@link Class} as a {@link String}.
*
* @param c {@code Class} to format
+ * @param done list of already-encountered types
* @return String
* @since 3.2
*/
- private static String classToString(final Class> c) {
+ private static String classToString(final Class> c,
+ final Set done)
+ {
final StringBuilder buf = new StringBuilder();
if (c.getEnclosingClass() != null) {
- buf.append(classToString(c.getEnclosingClass())).append('.').append(c
- .getSimpleName());
+ buf.append(classToString(c.getEnclosingClass(), done)).append('.')
+ .append(c.getSimpleName());
}
else {
buf.append(c.getName());
}
if (c.getTypeParameters().length > 0) {
buf.append('<');
- appendAllTo(buf, ", ", c.getTypeParameters());
+ appendAllTo(buf, ", ", done, c.getTypeParameters());
buf.append('>');
}
return buf.toString();
@@ -2029,17 +2037,22 @@ private static String classToString(final Class> c) {
* Format a {@link TypeVariable} as a {@link String}.
*
* @param v {@code TypeVariable} to format
+ * @param done list of already-encountered types
* @return String
* @since 3.2
*/
- private static String typeVariableToString(final TypeVariable> v) {
+ private static String typeVariableToString(final TypeVariable> v,
+ final Set done)
+ {
final StringBuilder buf = new StringBuilder(v.getName());
+ if (done.contains(v)) return buf.toString();
+ done.add(v);
final Type[] bounds = v.getBounds();
if (bounds.length > 0 && !(bounds.length == 1 && Object.class.equals(
bounds[0])))
{
buf.append(" extends ");
- appendAllTo(buf, " & ", v.getBounds());
+ appendAllTo(buf, " & ", done, v.getBounds());
}
return buf.toString();
}
@@ -2048,10 +2061,13 @@ private static String typeVariableToString(final TypeVariable> v) {
* Format a {@link ParameterizedType} as a {@link String}.
*
* @param p {@code ParameterizedType} to format
+ * @param done list of already-encountered types
* @return String
* @since 3.2
*/
- private static String parameterizedTypeToString(final ParameterizedType p) {
+ private static String parameterizedTypeToString(final ParameterizedType p,
+ final Set done)
+ {
final StringBuilder buf = new StringBuilder();
final Type useOwner = p.getOwnerType();
@@ -2070,7 +2086,7 @@ private static String parameterizedTypeToString(final ParameterizedType p) {
buf.append('.').append(raw.getSimpleName());
}
- appendAllTo(buf.append('<'), ", ", typeArguments).append('>');
+ appendAllTo(buf.append('<'), ", ", done, typeArguments).append('>');
return buf.toString();
}
@@ -2078,22 +2094,27 @@ private static String parameterizedTypeToString(final ParameterizedType p) {
* Format a {@link WildcardType} as a {@link String}.
*
* @param w {@code WildcardType} to format
+ * @param done list of already-encountered types
* @return String
* @since 3.2
*/
- private static String wildcardTypeToString(final WildcardType w) {
+ private static String wildcardTypeToString(final WildcardType w,
+ final Set done)
+ {
final StringBuilder buf = new StringBuilder().append('?');
+ if (done.contains(w)) return buf.toString();
+ done.add(w);
final Type[] lowerBounds = w.getLowerBounds();
final Type[] upperBounds = w.getUpperBounds();
if (lowerBounds.length > 1 || lowerBounds.length == 1 &&
lowerBounds[0] != null)
{
- appendAllTo(buf.append(" super "), " & ", lowerBounds);
+ appendAllTo(buf.append(" super "), " & ", done, lowerBounds);
}
else if (upperBounds.length > 1 || upperBounds.length == 1 &&
!Object.class.equals(upperBounds[0]))
{
- appendAllTo(buf.append(" extends "), " & ", upperBounds);
+ appendAllTo(buf.append(" extends "), " & ", done, upperBounds);
}
return buf.toString();
}
@@ -2114,18 +2135,19 @@ private static String genericArrayTypeToString(final GenericArrayType g) {
*
* @param buf destination
* @param sep separator
+ * @param done list of already-encountered types
* @param types to append
* @return {@code buf}
* @since 3.2
*/
private static StringBuilder appendAllTo(final StringBuilder buf,
- final String sep, final Type... types)
+ final String sep, final Set done, final Type... types)
{
validateNotEmpty(validateNoNullElements(types));
if (types.length > 0) {
- buf.append(toString(types[0]));
+ buf.append(toString(types[0], done));
for (int i = 1; i < types.length; i++) {
- buf.append(sep).append(toString(types[i]));
+ buf.append(sep).append(toString(types[i], done));
}
}
return buf;
From 4b90eca4ee2c362e7ed0bde96fcad0d27fe103f4 Mon Sep 17 00:00:00 2001
From: Curtis Rueden
Date: Wed, 27 Jul 2016 20:53:09 -0500
Subject: [PATCH 008/500] Types: remove ineffectual wildcards in TypeUtils
---
src/main/java/org/scijava/util/Types.java | 56 +++++++++++------------
1 file changed, 28 insertions(+), 28 deletions(-)
diff --git a/src/main/java/org/scijava/util/Types.java b/src/main/java/org/scijava/util/Types.java
index 17c8662ba..05be06ead 100644
--- a/src/main/java/org/scijava/util/Types.java
+++ b/src/main/java/org/scijava/util/Types.java
@@ -422,7 +422,7 @@ public static boolean isAssignable(final Type type, final Type toType) {
private static boolean isAssignable(final Type type, final Type toType,
final Map, Type> typeVarAssigns)
{
- if (toType == null || toType instanceof Class>) {
+ if (toType == null || toType instanceof Class) {
return isAssignable(type, (Class>) toType);
}
@@ -438,7 +438,7 @@ private static boolean isAssignable(final Type type, final Type toType,
return isAssignable(type, (WildcardType) toType, typeVarAssigns);
}
- if (toType instanceof TypeVariable>) {
+ if (toType instanceof TypeVariable) {
return isAssignable(type, (TypeVariable>) toType, typeVarAssigns);
}
@@ -474,7 +474,7 @@ private static boolean isAssignable(final Type type,
return true;
}
- if (type instanceof Class>) {
+ if (type instanceof Class) {
// just comparing two classes
return toClass.isAssignableFrom((Class>) type);
}
@@ -485,7 +485,7 @@ private static boolean isAssignable(final Type type,
}
// *
- if (type instanceof TypeVariable>) {
+ if (type instanceof TypeVariable) {
// if any of the bounds are assignable to the class, then the
// type is assignable to the class.
for (final Type bound : ((TypeVariable>) type).getBounds()) {
@@ -601,7 +601,7 @@ private static Type unrollVariableAssignments(TypeVariable> var,
Type result;
do {
result = typeVarAssigns.get(var);
- if (result instanceof TypeVariable> && !result.equals(var)) {
+ if (result instanceof TypeVariable && !result.equals(var)) {
var = (TypeVariable>) result;
continue;
}
@@ -644,7 +644,7 @@ private static boolean isAssignable(final Type type,
final Type toComponentType = toGenericArrayType.getGenericComponentType();
- if (type instanceof Class>) {
+ if (type instanceof Class) {
final Class> cls = (Class>) type;
// compare the component types
@@ -669,7 +669,7 @@ private static boolean isAssignable(final Type type,
return false;
}
- if (type instanceof TypeVariable>) {
+ if (type instanceof TypeVariable) {
// probably should remove the following logic and just return false.
// type variables cannot specify arrays as bounds.
for (final Type bound : getImplicitBounds((TypeVariable>) type)) {
@@ -815,7 +815,7 @@ private static boolean isAssignable(final Type type,
return true;
}
- if (type instanceof TypeVariable>) {
+ if (type instanceof TypeVariable) {
// a type variable is assignable to another type variable, if
// and only if the former is the latter, extends the latter, or
// is otherwise a descendant of the latter.
@@ -828,7 +828,7 @@ private static boolean isAssignable(final Type type,
}
}
- if (type instanceof Class> || type instanceof ParameterizedType ||
+ if (type instanceof Class || type instanceof ParameterizedType ||
type instanceof GenericArrayType || type instanceof WildcardType)
{
return false;
@@ -850,7 +850,7 @@ private static boolean isAssignable(final Type type,
private static Type substituteTypeVariables(final Type type,
final Map, Type> typeVarAssigns)
{
- if (type instanceof TypeVariable> && typeVarAssigns != null) {
+ if (type instanceof TypeVariable && typeVarAssigns != null) {
final Type replacementType = typeVarAssigns.get(type);
if (replacementType == null) {
@@ -939,7 +939,7 @@ private static Map, Type> getTypeArguments(final Type type,
final Class> toClass,
final Map, Type> subtypeVarAssigns)
{
- if (type instanceof Class>) {
+ if (type instanceof Class) {
return getTypeArguments((Class>) type, toClass, subtypeVarAssigns);
}
@@ -967,7 +967,7 @@ private static Map, Type> getTypeArguments(final Type type,
return null;
}
- if (type instanceof TypeVariable>) {
+ if (type instanceof TypeVariable) {
for (final Type bound : getImplicitBounds((TypeVariable>) type)) {
// find the first bound that is assignable to the target class
if (isAssignable(bound, toClass)) {
@@ -1141,7 +1141,7 @@ public static Map, Type> determineTypeArguments(
final Type midType = getClosestParentType(cls, superClass);
// can only be a class or a parameterized type
- if (midType instanceof Class>) {
+ if (midType instanceof Class) {
return determineTypeArguments((Class>) midType, superType);
}
@@ -1240,7 +1240,7 @@ private static Type getClosestParentType(final Class> cls,
if (midType instanceof ParameterizedType) {
midClass = getRawType((ParameterizedType) midType);
}
- else if (midType instanceof Class>) {
+ else if (midType instanceof Class) {
midClass = (Class>) midType;
}
else {
@@ -1283,7 +1283,7 @@ public static boolean isInstance(final Object value, final Type type) {
return false;
}
- return value == null ? !(type instanceof Class>) || !((Class>) type)
+ return value == null ? !(type instanceof Class) || !((Class>) type)
.isPrimitive() : isAssignable(value.getClass(), type, null);
}
@@ -1460,7 +1460,7 @@ private static Class> getRawType(
// Class, there's enough reason to believe that future versions of Java
// may return other Type implementations. And type-safety checking is
// rarely a bad idea.
- if (!(rawType instanceof Class>)) {
+ if (!(rawType instanceof Class)) {
throw new IllegalStateException("Wait... What!? Type of rawType: " +
rawType);
}
@@ -1485,7 +1485,7 @@ private static Class> getRawType(
public static Class> getRawType(final Type type,
final Type assigningType)
{
- if (type instanceof Class>) {
+ if (type instanceof Class) {
// it is raw, no problem
return (Class>) type;
}
@@ -1495,7 +1495,7 @@ public static Class> getRawType(final Type type,
return getRawType((ParameterizedType) type);
}
- if (type instanceof TypeVariable>) {
+ if (type instanceof TypeVariable) {
if (assigningType == null) {
return null;
}
@@ -1506,7 +1506,7 @@ public static Class> getRawType(final Type type,
// can't get the raw type of a method- or constructor-declared type
// variable
- if (!(genericDeclaration instanceof Class>)) {
+ if (!(genericDeclaration instanceof Class)) {
return null;
}
@@ -1557,7 +1557,7 @@ public static Class> getRawType(final Type type,
* {@link GenericArrayType}.
*/
public static boolean isArrayType(final Type type) {
- return type instanceof GenericArrayType || type instanceof Class> &&
+ return type instanceof GenericArrayType || type instanceof Class &&
((Class>) type).isArray();
}
@@ -1568,7 +1568,7 @@ public static boolean isArrayType(final Type type) {
* @return component type or null if type is not an array type
*/
public static Type getArrayComponentType(final Type type) {
- if (type instanceof Class>) {
+ if (type instanceof Class) {
final Class> clazz = (Class>) type;
return clazz.isArray() ? clazz.getComponentType() : null;
}
@@ -1595,7 +1595,7 @@ public static Type unrollVariables(Map, Type> typeArguments,
typeArguments = Collections., Type> emptyMap();
}
if (containsTypeVariables(type)) {
- if (type instanceof TypeVariable>) {
+ if (type instanceof TypeVariable) {
return unrollVariables(typeArguments, typeArguments.get(type));
}
if (type instanceof ParameterizedType) {
@@ -1657,10 +1657,10 @@ private static Type[] unrollBounds(
* @since 3.2
*/
public static boolean containsTypeVariables(final Type type) {
- if (type instanceof TypeVariable>) {
+ if (type instanceof TypeVariable) {
return true;
}
- if (type instanceof Class>) {
+ if (type instanceof Class) {
return ((Class>) type).getTypeParameters().length > 0;
}
if (type instanceof ParameterizedType) {
@@ -1922,7 +1922,7 @@ public static String toString(final Type type) {
private static String toString(final Type type, final Set done) {
validateNotNull(type);
- if (type instanceof Class>) {
+ if (type instanceof Class) {
return classToString((Class>) type, done);
}
if (type instanceof ParameterizedType) {
@@ -1931,7 +1931,7 @@ private static String toString(final Type type, final Set done) {
if (type instanceof WildcardType) {
return wildcardTypeToString((WildcardType) type, done);
}
- if (type instanceof TypeVariable>) {
+ if (type instanceof TypeVariable) {
return typeVariableToString((TypeVariable>) type, done);
}
if (type instanceof GenericArrayType) {
@@ -1954,7 +1954,7 @@ public static String toLongString(final TypeVariable> var) {
final StringBuilder buf = new StringBuilder();
final GenericDeclaration d = ((TypeVariable>) var)
.getGenericDeclaration();
- if (d instanceof Class>) {
+ if (d instanceof Class) {
Class> c = (Class>) d;
while (true) {
if (c.getEnclosingClass() == null) {
@@ -2077,7 +2077,7 @@ private static String parameterizedTypeToString(final ParameterizedType p,
buf.append(raw.getName());
}
else {
- if (useOwner instanceof Class>) {
+ if (useOwner instanceof Class) {
buf.append(((Class>) useOwner).getName());
}
else {
From 17f2f60da0743ef8479a8c725d61774025c0e458 Mon Sep 17 00:00:00 2001
From: Curtis Rueden
Date: Fri, 29 Jul 2016 16:53:06 -0500
Subject: [PATCH 009/500] Types: expose some needed TypeUtils methods
The ultimate goal will probably be to expose them all -- perhaps even
factor them all directly into the Types class and remove the private
inner TypeUtils class -- but for now, we expose only a few we need.
---
src/main/java/org/scijava/util/Types.java | 162 ++++++++++++++++++++++
1 file changed, 162 insertions(+)
diff --git a/src/main/java/org/scijava/util/Types.java b/src/main/java/org/scijava/util/Types.java
index 05be06ead..f18977592 100644
--- a/src/main/java/org/scijava/util/Types.java
+++ b/src/main/java/org/scijava/util/Types.java
@@ -134,6 +134,168 @@ public static Field field(final Class> c, final String name) {
return field(c.getSuperclass(), name);
}
+ /**
+ * Discerns whether it would be legal to assign a reference of type
+ * {@code source} to a reference of type {@code target}.
+ *
+ * @see Class#isAssignableFrom(Class)
+ */
+ public static boolean isAssignable(final Type source, final Type target) {
+ return TypeUtils.isAssignable(source, target);
+ }
+
+ /**
+ * Creates a new {@link ParameterizedType} of the given class together with
+ * the specified type arguments.
+ *
+ * @param rawType The class of the {@link ParameterizedType}.
+ * @param typeArgs The type arguments to use in parameterizing it.
+ * @return The newly created {@link ParameterizedType}.
+ */
+ public static ParameterizedType newParameterizedType(final Class> rawType,
+ final Type... typeArgs)
+ {
+ return newParameterizedType(rawType, rawType.getDeclaringClass(), typeArgs);
+ }
+
+ /**
+ * Creates a new {@link ParameterizedType} of the given class together with
+ * the specified type arguments.
+ *
+ * @param rawType The class of the {@link ParameterizedType}.
+ * @param ownerType The owner type of the parameterized class.
+ * @param typeArgs The type arguments to use in parameterizing it.
+ * @return The newly created {@link ParameterizedType}.
+ */
+ public static ParameterizedType newParameterizedType(final Class> rawType,
+ final Type ownerType, final Type... typeArgs)
+ {
+ return new TypeUtils.ParameterizedTypeImpl(rawType, ownerType, typeArgs);
+ }
+
+ /**
+ * Creates a new {@link WildcardType} with no upper or lower bounds (i.e.,
+ * {@code ?}).
+ *
+ * @return The newly created {@link WildcardType}.
+ */
+ public static WildcardType newWildcardType() {
+ return newWildcardType(null, null);
+ }
+
+ /**
+ * Creates a new {@link WildcardType} with the given upper and/or lower bound.
+ *
+ * @param upperBound Upper bound of the wildcard, or null for none.
+ * @param lowerBound Lower bound of the wildcard, or null for none.
+ * @return The newly created {@link WildcardType}.
+ */
+ public static WildcardType newWildcardType(final Type upperBound,
+ final Type lowerBound)
+ {
+ return new TypeUtils.WildcardTypeImpl(upperBound, lowerBound);
+ }
+
+ /**
+ * Learn, recursively, whether any of the type parameters associated with
+ * {@code type} are bound to variables.
+ *
+ * @param type the type to check for type variables
+ * @return boolean
+ */
+ public static boolean containsTypeVars(final Type type) {
+ return TypeUtils.containsTypeVariables(type);
+ }
+
+ /**
+ * Gets the type arguments of a class/interface based on a subtype. For
+ * instance, this method will determine that both of the parameters for the
+ * interface {@link Map} are {@link Object} for the subtype
+ * {@link java.util.Properties Properties} even though the subtype does not
+ * directly implement the {@code Map} interface.
+ *
+ * This method returns {@code null} if {@code type} is not assignable to
+ * {@code toClass}. It returns an empty map if none of the classes or
+ * interfaces in its inheritance hierarchy specify any type arguments.
+ *
+ *
+ * A side effect of this method is that it also retrieves the type arguments
+ * for the classes and interfaces that are part of the hierarchy between
+ * {@code type} and {@code toClass}. So with the above example, this method
+ * will also determine that the type arguments for {@link java.util.Hashtable
+ * Hashtable} are also both {@code Object}. In cases where the interface
+ * specified by {@code toClass} is (indirectly) implemented more than once
+ * (e.g. where {@code toClass} specifies the interface
+ * {@link java.lang.Iterable Iterable} and {@code type} specifies a
+ * parameterized type that implements both {@link java.util.Set Set} and
+ * {@link java.util.Collection Collection}), this method will look at the
+ * inheritance hierarchy of only one of the implementations/subclasses; the
+ * first interface encountered that isn't a subinterface to one of the others
+ * in the {@code type} to {@code toClass} hierarchy.
+ *
+ *
+ * @param type the type from which to determine the type parameters of
+ * {@code toClass}
+ * @param toClass the class whose type parameters are to be determined based
+ * on the subtype {@code type}
+ * @return a {@code Map} of the type assignments for the type variables in
+ * each type in the inheritance hierarchy from {@code type} to
+ * {@code toClass} inclusive.
+ */
+ public static Map, Type> args(final Type type,
+ final Class> toClass)
+ {
+ return TypeUtils.getTypeArguments(type, toClass);
+ }
+
+ /**
+ * Tries to determine the type arguments of a class/interface based on a super
+ * parameterized type's type arguments. This method is the inverse of
+ * {@link #args(Type, Class)} which gets a class/interface's type arguments
+ * based on a subtype. It is far more limited in determining the type
+ * arguments for the subject class's type variables in that it can only
+ * determine those parameters that map from the subject {@link Class} object
+ * to the supertype.
+ *
+ * Example: {@link java.util.TreeSet TreeSet} sets its parameter as the
+ * parameter for {@link java.util.NavigableSet NavigableSet}, which in turn
+ * sets the parameter of {@link java.util.SortedSet}, which in turn sets the
+ * parameter of {@link Set}, which in turn sets the parameter of
+ * {@link java.util.Collection}, which in turn sets the parameter of
+ * {@link java.lang.Iterable}. Since {@code TreeSet}'s parameter maps
+ * (indirectly) to {@code Iterable}'s parameter, it will be able to determine
+ * that based on the super type {@code Iterable extends
+ * Map>>}, the parameter of {@code TreeSet}
+ * is {@code ? extends Map>}.
+ *
+ *
+ * @param c the class whose type parameters are to be determined, not
+ * {@code null}
+ * @param superType the super type from which {@code c}'s type arguments are
+ * to be determined, not {@code null}
+ * @return a {@code Map} of the type assignments that could be determined for
+ * the type variables in each type in the inheritance hierarchy from
+ * {@code type} to {@code c} inclusive.
+ */
+ public static Map, Type> args(final Class> c,
+ final ParameterizedType superType)
+ {
+ return TypeUtils.determineTypeArguments(c, superType);
+ }
+
+ /**
+ * Create a parameterized type instance.
+ *
+ * @param raw the raw class to create a parameterized type instance for
+ * @param typeArgMappings the mapping used for parameterization
+ * @return {@link ParameterizedType}
+ */
+ public static final ParameterizedType parameterize(final Class> raw,
+ final Map, Type> typeArgMappings)
+ {
+ return TypeUtils.parameterize(raw, typeArgMappings);
+ }
+
// -- BEGIN FORK OF APACHE COMMONS LANG 3.4 CODE --
/*
From d87cc91aa7115022ef701e8fd065c1aeb4852990 Mon Sep 17 00:00:00 2001
From: Curtis Rueden
Date: Fri, 29 Jul 2016 20:54:52 -0500
Subject: [PATCH 010/500] Types: add missing javadoc
---
src/main/java/org/scijava/util/Types.java | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/src/main/java/org/scijava/util/Types.java b/src/main/java/org/scijava/util/Types.java
index f18977592..161c66a2b 100644
--- a/src/main/java/org/scijava/util/Types.java
+++ b/src/main/java/org/scijava/util/Types.java
@@ -82,6 +82,12 @@ private Types() {
// TODO: Migrate all GenericUtils methods here.
+ /**
+ * Gets a string representation of the given type.
+ *
+ * @param t Type whose name is desired.
+ * @return The name of the given type.
+ */
public static String name(final Type t) {
// NB: It is annoying that Class.toString() prepends "class " or
// "interface "; this method exists to work around that behavior.
@@ -105,6 +111,8 @@ public static String name(final Type t) {
*
* If you want all raw classes of the given type, use {@link #raws}.
*
+ * @param type The type from which to discern the (first) raw class.
+ * @return The type's first raw class.
*/
public static Class> raw(final Type type) {
// TODO: Consolidate with GenericUtils.
@@ -118,6 +126,8 @@ public static Class> raw(final Type type) {
* return both {@link Number} and {@link Iterable} as its raw classes.
*
*
+ * @param type The type from which to discern the raw classes.
+ * @return List of the type's raw classes.
* @see #raw
*/
public static List> raws(final Type type) {
@@ -138,6 +148,10 @@ public static Field field(final Class> c, final String name) {
* Discerns whether it would be legal to assign a reference of type
* {@code source} to a reference of type {@code target}.
*
+ * @param source The type from which assignment is desired.
+ * @param target The type to which assignment is desired.
+ * @return True if the source is assignable to the target.
+ * @throws NullPointerException if {@code target} is null.
* @see Class#isAssignableFrom(Class)
*/
public static boolean isAssignable(final Type source, final Type target) {
From 74092f67d417de505d68f854a32d52d81e94fa4f Mon Sep 17 00:00:00 2001
From: Curtis Rueden
Date: Fri, 29 Jul 2016 20:27:49 -0500
Subject: [PATCH 011/500] Migrate GenericUtils methods to Types class
This also updates Types.raw to return the first raw type, rather
than null, in the case where multiple raw type bounds exist. This
returns null in fewer cases, and matches what the javadoc says.
For backwards compatibility, the deprecated method preserves the
legacy behavior of returning null in that case.
---
.../java/org/scijava/util/GenericUtils.java | 179 ++++--------------
src/main/java/org/scijava/util/Types.java | 84 +++++++-
.../{GenericUtilsTest.java => TypesTest.java} | 100 +++++-----
3 files changed, 165 insertions(+), 198 deletions(-)
rename src/test/java/org/scijava/util/{GenericUtilsTest.java => TypesTest.java} (60%)
diff --git a/src/main/java/org/scijava/util/GenericUtils.java b/src/main/java/org/scijava/util/GenericUtils.java
index df6165ccb..b653a67aa 100644
--- a/src/main/java/org/scijava/util/GenericUtils.java
+++ b/src/main/java/org/scijava/util/GenericUtils.java
@@ -32,198 +32,89 @@
package org.scijava.util;
-import com.googlecode.gentyref.GenericTypeReflector;
-
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.List;
-/**
- * Useful methods for working with {@link Type} objects, particularly generic
- * types.
- *
- * This class leans heavily on the excellent gentyref library, and exists
- * mainly to keep the gentyref dependency encapsulated within SciJava Common.
- *
- *
- * @author Curtis Rueden
- * @see ClassUtils For utility methods specific to {@link Class} objects.
- * @see ConversionUtils For utility methods that convert between {@link Type}s.
- */
+import org.scijava.util.Types;
+
+/** @deprecated Use {@link Types} instead. */
+@Deprecated
public final class GenericUtils {
private GenericUtils() {
// prevent instantiation of utility class
}
- /**
- * Gets the sole raw class corresponding to the given type, or null if none.
- */
+ /** @deprecated Use {@link Types#raw} instead. */
+ @Deprecated
public static Class> getClass(final Type type) {
- if (type == null) return null;
- if (type instanceof Class) return (Class>) type;
- final List> c = getClasses(type);
- if (c == null || c.size() != 1) return null;
- return c.get(0);
+ final List> bounds = Types.raws(type);
+ return bounds != null && bounds.size() == 1 ? bounds.get(0) : null;
}
- /**
- * Gets all raw classes corresponding to the given type.
- *
- * For example, a type parameter {@code A extends Number & Iterable} will
- * return both {@link Number} and {@link Iterable} as its raw classes.
- *
- */
+ /** @deprecated Use {@link Types#raws} instead. */
+ @Deprecated
public static List> getClasses(final Type type) {
- if (type == null) return null;
- return GenericTypeReflector.getUpperBoundClassAndInterfaces(type);
+ return Types.raws(type);
}
- /**
- * Gets the component type of the given array type, or null if not an array.
- */
+ /** @deprecated Use {@link Types#component} instead. */
+ @Deprecated
public static Type getComponentType(final Type type) {
- return GenericTypeReflector.getArrayComponentType(type);
+ return Types.component(type);
}
/**
- * Gets the sole component class of the given array type, or null if none.
+ * @deprecated Use {@link Types#component} and {@link Types#raw} instead.
*/
+ @Deprecated
public static Class> getComponentClass(final Type type) {
- return getClass(getComponentType(type));
+ return Types.raw(Types.component(type));
}
- /**
- * Returns the "safe" generic type of the given field, as viewed from the
- * given type. This may be narrower than what {@link Field#getGenericType()}
- * returns, if the field is declared in a superclass, or {@code type} has a
- * type parameter that is used in the type of the field.
- *
- * For example, suppose we have the following three classes:
- *
- *
- *
- * public class Thing<T> {
- * public T thing;
- * }
- *
- * public class NumberThing<N extends Number> extends Thing<N> { }
- *
- * public class IntegerThing extends NumberThing<Integer> { }
- *
- *
- * Then this method operates as follows:
- *
- *
- * field = ClassUtils.getField(Thing.class, "thing");
- *
- * field.getType(); // Object
- * field.getGenericType(); // T
- *
- * GenericUtils.getFieldType(field, Thing.class); // T
- * GenericUtils.getFieldType(field, NumberThing.class); // N extends Number
- * GenericUtils.getFieldType(field, IntegerThing.class); // Integer
- *
- */
+ /** @deprecated Use {@link Types#type(Field, Class)} instead. */
+ @Deprecated
public static Type getFieldType(final Field field, final Class> type) {
- final Type wildType = GenericTypeReflector.addWildcardParameters(type);
- return GenericTypeReflector.getExactFieldType(field, wildType);
+ return Types.type(field, type);
}
/**
- * Returns the "safe" class(es) of the given field, as viewed from the
- * specified type. This may be narrower than what {@link Field#getType()}
- * returns, if the field is declared in a superclass, or {@code type} has a
- * type parameter that is used in the type of the field.
- *
- * For example, suppose we have the following three classes:
- *
- *
- *
- *
- * public class Thing<T> {
- *
- * public T thing;
- * }
- *
- * public class NumberThing<N extends Number> extends Thing<N> {}
- *
- * public class IntegerThing extends NumberThing<Integer> {}
- *
- *
- * Then this method operates as follows:
- *
- *
- * In cases of complex generics which take the intersection of multiple types
- * using the {@code &} operator, there may be multiple types returned by this
- * method. For example:
- *
- *
- * @see #getFieldType(Field, Class)
- * @see #getClasses(Type)
+ * @deprecated Use {@link Types#type(Field, Class)} and {@link Types#raws}
+ * instead.
*/
+ @Deprecated
public static List> getFieldClasses(final Field field,
final Class> type)
{
- final Type genericType = getFieldType(field, type);
- return getClasses(genericType);
+ return Types.raws(Types.type(field, type));
}
- /**
- * As {@link #getFieldType(Field, Class)}, but with respect to the return
- * type of the given {@link Method} rather than a {@link Field}.
- */
+ /** @deprecated Use {@link Types#returnType} instead. */
+ @Deprecated
public static Type getMethodReturnType(final Method method,
final Class> type)
{
- final Type wildType = GenericTypeReflector.addWildcardParameters(type);
- return GenericTypeReflector.getExactReturnType(method, wildType);
+ return Types.returnType(method, type);
}
/**
- * As {@link #getFieldClasses(Field, Class)}, but with respect to the return
- * type of the given {@link Method} rather than a {@link Field}.
- *
- * @see #getMethodReturnType(Method, Class)
- * @see #getClasses(Type)
+ * @deprecated Use {@link Types#returnType} and {@link Types#raws} instead.
*/
- public static List>
- getMethodReturnClasses(final Method method, final Class> type)
+ @Deprecated
+ public static List> getMethodReturnClasses(final Method method,
+ final Class> type)
{
- final Type genericType = getMethodReturnType(method, type);
- return getClasses(genericType);
+ return Types.raws(Types.returnType(method, type));
}
- /**
- * Gets the given type's {@code n}th type parameter of the specified class.
- *
- * For example, with class {@code StringList implements List},
- * {@code getTypeParameter(StringList.class, Collection.class, 0)} returns
- * {@code String}.
- *
- */
+ /** @deprecated Use {@link Types#param} instead. */
+ @Deprecated
public static Type getTypeParameter(final Type type, final Class> c,
final int paramNo)
{
- return GenericTypeReflector.getTypeParameter(type,
- c.getTypeParameters()[paramNo]);
+ return Types.param(type, c, paramNo);
}
}
diff --git a/src/main/java/org/scijava/util/Types.java b/src/main/java/org/scijava/util/Types.java
index 161c66a2b..45b9263b3 100644
--- a/src/main/java/org/scijava/util/Types.java
+++ b/src/main/java/org/scijava/util/Types.java
@@ -38,10 +38,13 @@
// under the Apache 2 license.
// See lines below starting with "BEGIN FORK".
+import com.googlecode.gentyref.GenericTypeReflector;
+
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
@@ -57,7 +60,6 @@
import java.util.Set;
import org.scijava.util.ConversionUtils;
-import org.scijava.util.GenericUtils;
/**
* Utility class for working with generic types, fields and methods.
@@ -80,8 +82,6 @@ private Types() {
// NB: Prevent instantiation of utility class.
}
- // TODO: Migrate all GenericUtils methods here.
-
/**
* Gets a string representation of the given type.
*
@@ -115,8 +115,11 @@ public static String name(final Type t) {
* @return The type's first raw class.
*/
public static Class> raw(final Type type) {
- // TODO: Consolidate with GenericUtils.
- return GenericUtils.getClass(type);
+ if (type == null) return null;
+ if (type instanceof Class) return (Class>) type;
+ final List> c = raws(type);
+ if (c == null || c.size() == 0) return null;
+ return c.get(0);
}
/**
@@ -131,8 +134,8 @@ public static Class> raw(final Type type) {
* @see #raw
*/
public static List> raws(final Type type) {
- // TODO: Consolidate with GenericUtils.
- return GenericUtils.getClasses(type);
+ if (type == null) return null;
+ return GenericTypeReflector.getUpperBoundClassAndInterfaces(type);
}
public static Field field(final Class> c, final String name) {
@@ -144,6 +147,73 @@ public static Field field(final Class> c, final String name) {
return field(c.getSuperclass(), name);
}
+ /**
+ * Gets the component type of the given array type, or null if not an array.
+ */
+ public static Type component(final Type type) {
+ return GenericTypeReflector.getArrayComponentType(type);
+ }
+
+ /**
+ * Returns the "safe" generic type of the given field, as viewed from the
+ * given type. This may be narrower than what {@link Field#getGenericType()}
+ * returns, if the field is declared in a superclass, or {@code type} has a
+ * type parameter that is used in the type of the field.
+ *
+ * For example, suppose we have the following three classes:
+ *
+ *
+ *
+ * public class Thing<T> {
+ *
+ * public T thing;
+ * }
+ *
+ * public class NumberThing<N extends Number> extends Thing<N> {}
+ *
+ * public class IntegerThing extends NumberThing<Integer> {}
+ *
+ *
+ * Then this method operates as follows:
+ *
+ *
+ * field = Types.field(Thing.class, "thing");
+ *
+ * field.getType(); // Object
+ * field.getGenericType(); // T
+ *
+ * Types.type(field, Thing.class); // T
+ * Types.type(field, NumberThing.class); // N extends Number
+ * Types.type(field, IntegerThing.class); // Integer
+ *
+ */
+ public static Type type(final Field field, final Class> type) {
+ final Type wildType = GenericTypeReflector.addWildcardParameters(type);
+ return GenericTypeReflector.getExactFieldType(field, wildType);
+ }
+
+ /**
+ * As {@link #type(Field, Class)}, but with respect to the return type of the
+ * given {@link Method} rather than a {@link Field}.
+ */
+ public static Type returnType(final Method method, final Class> type) {
+ final Type wildType = GenericTypeReflector.addWildcardParameters(type);
+ return GenericTypeReflector.getExactReturnType(method, wildType);
+ }
+
+ /**
+ * Gets the given type's {@code n}th type parameter of the specified class.
+ *
+ * For example, with class {@code StringList implements List},
+ * {@code Types.param(StringList.class, Collection.class, 0)} returns
+ * {@code String}.
+ *
+ */
+ public static Type param(final Type type, final Class> c, final int no) {
+ return GenericTypeReflector.getTypeParameter(type, c
+ .getTypeParameters()[no]);
+ }
+
/**
* Discerns whether it would be legal to assign a reference of type
* {@code source} to a reference of type {@code target}.
diff --git a/src/test/java/org/scijava/util/GenericUtilsTest.java b/src/test/java/org/scijava/util/TypesTest.java
similarity index 60%
rename from src/test/java/org/scijava/util/GenericUtilsTest.java
rename to src/test/java/org/scijava/util/TypesTest.java
index 389370007..ec8c1f2cc 100644
--- a/src/test/java/org/scijava/util/GenericUtilsTest.java
+++ b/src/test/java/org/scijava/util/TypesTest.java
@@ -45,16 +45,16 @@
import org.junit.Test;
/**
- * Tests {@link GenericUtils}.
+ * Tests {@link Types}.
*
- * @author Mark Hiner
* @author Curtis Rueden
+ * @author Mark Hiner
*/
-public class GenericUtilsTest {
+public class TypesTest {
- /** Tests {@link GenericUtils#getClass(Type)}. */
+ /** Tests {@link Types#raw(Type)}. */
@Test
- public void testGetClass() {
+ public void testRaw() {
@SuppressWarnings("unused")
class Struct {
@@ -65,17 +65,17 @@ class Struct {
private List list;
private HashMap map;
}
- assertSame(int[].class, getClass(Struct.class, "intArray"));
- assertSame(double.class, getClass(Struct.class, "d"));
- assertSame(String[][].class, getClass(Struct.class, "strings"));
- assertSame(Void.class, getClass(Struct.class, "v"));
- assertSame(List.class, getClass(Struct.class, "list"));
- assertSame(HashMap.class, getClass(Struct.class, "map"));
+ assertSame(int[].class, raw(Struct.class, "intArray"));
+ assertSame(double.class, raw(Struct.class, "d"));
+ assertSame(String[][].class, raw(Struct.class, "strings"));
+ assertSame(Void.class, raw(Struct.class, "v"));
+ assertSame(List.class, raw(Struct.class, "list"));
+ assertSame(HashMap.class, raw(Struct.class, "map"));
}
- /** Tests {@link GenericUtils#getComponentClass(Type)}. */
+ /** Tests {@link Types#component(Type)}. */
@Test
- public void testGetComponentClass() {
+ public void testComponent() {
@SuppressWarnings("unused")
class Struct {
@@ -86,56 +86,67 @@ class Struct {
private List[] list;
private HashMap map;
}
- assertSame(int.class, getComponentClass(Struct.class, "intArray"));
- assertNull(getComponentClass(Struct.class, "d"));
- assertSame(String[].class, getComponentClass(Struct.class, "strings"));
- assertSame(null, getComponentClass(Struct.class, "v"));
- assertSame(List.class, getComponentClass(Struct.class, "list"));
- assertSame(null, getComponentClass(Struct.class, "map"));
+ assertSame(int.class, componentType(Struct.class, "intArray"));
+ assertNull(componentType(Struct.class, "d"));
+ assertSame(String[].class, componentType(Struct.class, "strings"));
+ assertSame(null, componentType(Struct.class, "v"));
+ assertSame(List.class, componentType(Struct.class, "list"));
+ assertSame(null, componentType(Struct.class, "map"));
}
- /**
- * Tests {@link GenericUtils#getFieldClasses(java.lang.reflect.Field, Class)}.
- */
+ /** Tests {@link Types#type(Field, Class)}. */
@Test
- public void testGetFieldClasses() {
- final Field field = ClassUtils.getField(Thing.class, "thing");
+ public void testTypeField() {
+ final Field field = Types.field(Thing.class, "thing");
// T
- final Type tType = GenericUtils.getFieldType(field, Thing.class);
+ final Type tType = Types.type(field, Thing.class);
assertEquals("capture of ?", tType.toString());
// N extends Number
- final Type nType = GenericUtils.getFieldType(field, NumberThing.class);
+ final Type nType = Types.type(field, NumberThing.class);
assertEquals("capture of ?", nType.toString());
// Integer
- final Type iType = GenericUtils.getFieldType(field, IntegerThing.class);
+ final Type iType = Types.type(field, IntegerThing.class);
assertSame(Integer.class, iType);
}
- /** Tests {@link GenericUtils#getFieldClasses(Field, Class)}. */
+ /** Tests {@link Types#raws}. */
@Test
- public void testGetGenericType() {
- final Field field = ClassUtils.getField(Thing.class, "thing");
+ public void testRaws() {
+ final Field field = Types.field(Thing.class, "thing");
// Object
- assertAllTheSame(GenericUtils.getFieldClasses(field, Thing.class),
- Object.class);
+ assertAllTheSame(Types.raws(Types.type(field, Thing.class)), Object.class);
// N extends Number
- assertAllTheSame(GenericUtils.getFieldClasses(field, NumberThing.class),
+ assertAllTheSame(Types.raws(Types.type(field, NumberThing.class)),
Number.class);
// Integer
- assertAllTheSame(GenericUtils.getFieldClasses(field, IntegerThing.class),
+ assertAllTheSame(Types.raws(Types.type(field, IntegerThing.class)),
Integer.class);
// Serializable & Cloneable
- assertAllTheSame(GenericUtils.getFieldClasses(field, ComplexThing.class),
+ assertAllTheSame(Types.raws(Types.type(field, ComplexThing.class)),
Serializable.class, Cloneable.class);
}
+ /** Tests {@link Types#param}. */
+ @Test
+ public void testParam() {
+ class Struct {
+
+ @SuppressWarnings("unused")
+ private List list;
+ }
+ final Type listType = type(Struct.class, "list");
+ final Type paramType = Types.param(listType, List.class, 0);
+ final Class> paramClass = Types.raw(paramType);
+ assertSame(int[].class, paramClass);
+ }
+
// -- Helper classes --
private static class Thing {
@@ -161,22 +172,17 @@ private static class ComplexThing extends
/** Convenience method to get the {@link Type} of a field. */
private Type type(final Class> c, final String fieldName) {
- return ClassUtils.getField(c, fieldName).getGenericType();
+ return Types.field(c, fieldName).getGenericType();
}
- /**
- * Convenience method to call {@link GenericUtils#getClass(Type)} on a field.
- */
- private Class> getClass(final Class> c, final String fieldName) {
- return GenericUtils.getClass(type(c, fieldName));
+ /** Convenience method to call {@link Types#raw} on a field. */
+ private Class> raw(final Class> c, final String fieldName) {
+ return Types.raw(type(c, fieldName));
}
- /**
- * Convenience method to call {@link GenericUtils#getComponentClass(Type)} on
- * a field.
- */
- private Class> getComponentClass(final Class> c, final String fieldName) {
- return GenericUtils.getComponentClass(type(c, fieldName));
+ /** Convenience method to call {@link Types#component} on a field. */
+ private Class> componentType(final Class> c, final String fieldName) {
+ return Types.raw(Types.component(type(c, fieldName)));
}
private void assertAllTheSame(final List list, final T... values) {
From 9066d1dbe53c073d38880b48945f475c722149bf Mon Sep 17 00:00:00 2001
From: Curtis Rueden
Date: Wed, 3 Aug 2016 16:11:06 -0500
Subject: [PATCH 012/500] Eliminate uses of deprecated GenericUtils methods
We now use the consolidated Types utility class.
---
.../scijava/command/CommandModuleItem.java | 7 +++---
.../scijava/convert/AbstractConverter.java | 6 ++---
.../org/scijava/convert/CastingConverter.java | 4 ++--
.../scijava/convert/ConversionRequest.java | 6 ++---
.../org/scijava/convert/DefaultConverter.java | 22 +++++++++----------
.../java/org/scijava/util/ClassUtils.java | 16 +++++++++-----
.../org/scijava/util/ConversionUtils.java | 9 ++++----
.../org/scijava/convert/ConverterTest.java | 4 ++--
8 files changed, 39 insertions(+), 35 deletions(-)
diff --git a/src/main/java/org/scijava/command/CommandModuleItem.java b/src/main/java/org/scijava/command/CommandModuleItem.java
index 01bc4d1af..f186743a2 100644
--- a/src/main/java/org/scijava/command/CommandModuleItem.java
+++ b/src/main/java/org/scijava/command/CommandModuleItem.java
@@ -46,7 +46,7 @@
import org.scijava.plugin.Attr;
import org.scijava.plugin.Parameter;
import org.scijava.util.ConversionUtils;
-import org.scijava.util.GenericUtils;
+import org.scijava.util.Types;
/**
* {@link ModuleItem} implementation describing an input or output of a command.
@@ -76,8 +76,7 @@ public Parameter getParameter() {
@Override
public Class getType() {
- final Class> type =
- GenericUtils.getFieldClasses(field, getDelegateClass()).get(0);
+ final Class> type = Types.raw(Types.type(field, getDelegateClass()));
@SuppressWarnings("unchecked")
final Class typedType = (Class) type;
return typedType;
@@ -85,7 +84,7 @@ public Class getType() {
@Override
public Type getGenericType() {
- return GenericUtils.getFieldType(field, getDelegateClass());
+ return Types.type(field, getDelegateClass());
}
@Override
diff --git a/src/main/java/org/scijava/convert/AbstractConverter.java b/src/main/java/org/scijava/convert/AbstractConverter.java
index 3b1a299c5..f7a072ab9 100644
--- a/src/main/java/org/scijava/convert/AbstractConverter.java
+++ b/src/main/java/org/scijava/convert/AbstractConverter.java
@@ -39,7 +39,7 @@
import org.scijava.plugin.AbstractHandlerPlugin;
import org.scijava.plugin.Parameter;
import org.scijava.util.ConversionUtils;
-import org.scijava.util.GenericUtils;
+import org.scijava.util.Types;
/**
* Abstract superclass for {@link Converter} plugins. Performs appropriate
@@ -117,7 +117,7 @@ public boolean canConvert(final Class> src, final Class> dest) {
@Override
public Object convert(final Object src, final Type dest) {
- final Class> destClass = GenericUtils.getClass(dest);
+ final Class> destClass = Types.raw(dest);
return convert(src, destClass);
}
@@ -155,7 +155,7 @@ public Class getType() {
@Override
@Deprecated
public boolean canConvert(final Class> src, final Type dest) {
- final Class> destClass = GenericUtils.getClass(dest);
+ final Class> destClass = Types.raw(dest);
return canConvert(src, destClass);
}
}
diff --git a/src/main/java/org/scijava/convert/CastingConverter.java b/src/main/java/org/scijava/convert/CastingConverter.java
index 4ba3ed38a..fa877ec6f 100644
--- a/src/main/java/org/scijava/convert/CastingConverter.java
+++ b/src/main/java/org/scijava/convert/CastingConverter.java
@@ -35,7 +35,7 @@
import org.scijava.plugin.Plugin;
import org.scijava.util.ClassUtils;
import org.scijava.util.ConversionUtils;
-import org.scijava.util.GenericUtils;
+import org.scijava.util.Types;
/**
* Minimal {@link Converter} implementation to do direct casting.
@@ -72,7 +72,7 @@ public T convert(final Object src, final Class dest) {
// rather than only Classes. However, the logic could become complex
// very quickly in various subclassing cases, generic parameters
// resolved vs. propagated, etc.
- final Class> c = GenericUtils.getClass(dest);
+ final Class> c = Types.raw(dest);
return (T) ConversionUtils.cast(src, c);
}
diff --git a/src/main/java/org/scijava/convert/ConversionRequest.java b/src/main/java/org/scijava/convert/ConversionRequest.java
index 961351b0b..409af735f 100644
--- a/src/main/java/org/scijava/convert/ConversionRequest.java
+++ b/src/main/java/org/scijava/convert/ConversionRequest.java
@@ -35,7 +35,7 @@
import java.lang.reflect.Type;
import org.scijava.plugin.HandlerService;
-import org.scijava.util.GenericUtils;
+import org.scijava.util.Types;
/**
* Currency for use in {@link Converter} and {@link ConvertService} methods.
@@ -100,7 +100,7 @@ public Type sourceType() {
* @return Source class for conversion or lookup.
*/
public Class> sourceClass() {
- return GenericUtils.getClass(srcType);
+ return Types.raw(srcType);
}
/**
@@ -121,7 +121,7 @@ public Type destType() {
* @return Destination class for conversion.
*/
public Class> destClass() {
- return GenericUtils.getClass(destType);
+ return Types.raw(destType);
}
// -- Setters --
diff --git a/src/main/java/org/scijava/convert/DefaultConverter.java b/src/main/java/org/scijava/convert/DefaultConverter.java
index 46c3e7159..211e85b92 100644
--- a/src/main/java/org/scijava/convert/DefaultConverter.java
+++ b/src/main/java/org/scijava/convert/DefaultConverter.java
@@ -48,7 +48,7 @@
import org.scijava.util.ArrayUtils;
import org.scijava.util.ClassUtils;
import org.scijava.util.ConversionUtils;
-import org.scijava.util.GenericUtils;
+import org.scijava.util.Types;
/**
* Default {@link Converter} implementation. Provides useful conversion
@@ -79,8 +79,10 @@ public class DefaultConverter extends AbstractConverter