Skip to content

Commit e4741de

Browse files
committed
Add utility class with generic type methods
This class recapitulates some of the functions from ConversionUtils and ClassUtiles, as well as adding new functionality. The previous methods will be deprecated in favor of those in this class.
1 parent daab41c commit e4741de

File tree

1 file changed

+228
-0
lines changed

1 file changed

+228
-0
lines changed
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
/*
2+
* #%L
3+
* SciJava Common shared library for SciJava software.
4+
* %%
5+
* Copyright (C) 2009 - 2014 Board of Regents of the University of
6+
* Wisconsin-Madison, Broad Institute of MIT and Harvard, and Max Planck
7+
* Institute of Molecular Cell Biology and Genetics.
8+
* %%
9+
* Redistribution and use in source and binary forms, with or without
10+
* modification, are permitted provided that the following conditions are met:
11+
*
12+
* 1. Redistributions of source code must retain the above copyright notice,
13+
* this list of conditions and the following disclaimer.
14+
* 2. Redistributions in binary form must reproduce the above copyright notice,
15+
* this list of conditions and the following disclaimer in the documentation
16+
* and/or other materials provided with the distribution.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
22+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28+
* POSSIBILITY OF SUCH DAMAGE.
29+
* #L%
30+
*/
31+
32+
package org.scijava.util;
33+
34+
import com.googlecode.gentyref.GenericTypeReflector;
35+
36+
import java.lang.reflect.Field;
37+
import java.lang.reflect.Method;
38+
import java.lang.reflect.Type;
39+
import java.util.List;
40+
41+
/**
42+
* Useful methods for working with {@link Type} objects, particularly generic
43+
* types.
44+
* <p>
45+
* This class leans heavily on the excellent <a
46+
* href="https://code.google.com/p/gentyref/">gentyref</a> library, and exists
47+
* mainly to keep the gentyref dependency encapsulated within SciJava Common.
48+
* </p>
49+
*
50+
* @author Curtis Rueden
51+
* @see ClassUtils For utility methods specific to {@link Class} objects.
52+
* @see ConversionUtils For utility methods that convert between {@link Type}s.
53+
*/
54+
public final class GenericUtils {
55+
56+
private GenericUtils() {
57+
// prevent instantiation of utility class
58+
}
59+
60+
/**
61+
* Gets the sole raw class corresponding to the given type, or null if none.
62+
*/
63+
public static Class<?> getClass(final Type type) {
64+
if (type == null) return null;
65+
if (type instanceof Class) return (Class<?>) type;
66+
final List<Class<?>> c = getClasses(type);
67+
if (c == null || c.size() != 1) return null;
68+
return c.get(0);
69+
}
70+
71+
/**
72+
* Gets all raw classes corresponding to the given type.
73+
* <p>
74+
* For example, a type parameter {@code A extends Number & Iterable} will
75+
* return both {@link Number} and {@link Iterable} as its raw classes.
76+
* </p>
77+
*/
78+
public static List<Class<?>> getClasses(final Type type) {
79+
if (type == null) return null;
80+
return GenericTypeReflector.getUpperBoundClassAndInterfaces(type);
81+
}
82+
83+
/**
84+
* Gets the component type of the given array type, or null if not an array.
85+
*/
86+
public static Type getComponentType(final Type type) {
87+
return GenericTypeReflector.getArrayComponentType(type);
88+
}
89+
90+
/**
91+
* Gets the sole component class of the given array type, or null if none.
92+
*/
93+
public static Class<?> getComponentClass(final Type type) {
94+
return getClass(getComponentType(type));
95+
}
96+
97+
/**
98+
* Returns the "safe" generic type of the given field, as viewed from the
99+
* given type. This may be narrower than what {@link Field#getGenericType()}
100+
* returns, if the field is declared in a superclass, or {@code type} has a
101+
* type parameter that is used in the type of the field.
102+
* <p>
103+
* For example, suppose we have the following three classes:
104+
* </p>
105+
*
106+
* <pre>
107+
* public class Thing&lt;T&gt; {
108+
* public T thing;
109+
* }
110+
*
111+
* public class NumberThing&lt;N extends Number&gt; extends Thing&lt;N&gt; { }
112+
*
113+
* public class IntegerThing extends NumberThing&lt;Integer&gt; { }
114+
* </pre>
115+
*
116+
* Then this method operates as follows:
117+
*
118+
* <pre>
119+
* field = ClassUtils.getField(Thing.class, "thing");
120+
*
121+
* field.getType(); // Object
122+
* field.getGenericType(); // T
123+
*
124+
* ClassUtils.getGenericType(field, Thing.class); // T
125+
* ClassUtils.getGenericType(field, NumberThing.class); // N extends Number
126+
* ClassUtils.getGenericType(field, IntegerThing.class); // Integer
127+
* </pre>
128+
*/
129+
public static Type getFieldType(final Field field, final Class<?> type) {
130+
final Type wildType = GenericTypeReflector.addWildcardParameters(type);
131+
return GenericTypeReflector.getExactFieldType(field, wildType);
132+
}
133+
134+
/**
135+
* Returns the "safe" class(es) of the given field, as viewed from the
136+
* specified type. This may be narrower than what {@link Field#getType()}
137+
* returns, if the field is declared in a superclass, or {@code type} has a
138+
* type parameter that is used in the type of the field.
139+
* <p>
140+
* For example, suppose we have the following three classes:
141+
* </p>
142+
*
143+
* <pre>
144+
*
145+
* public class Thing&lt;T&gt; {
146+
*
147+
* public T thing;
148+
* }
149+
*
150+
* public class NumberThing&lt;N extends Number&gt; extends Thing&lt;N&gt; {}
151+
*
152+
* public class IntegerThing extends NumberThing&lt;Integer&gt; {}
153+
* </pre>
154+
*
155+
* Then this method operates as follows:
156+
*
157+
* <pre>
158+
* field = ClassUtils.getField(Thing.class, &quot;thing&quot;);
159+
*
160+
* field.getType(); // Object
161+
*
162+
* ClassUtils.getTypes(field, Thing.class).get(0); // Object
163+
* ClassUtils.getTypes(field, NumberThing.class).get(0); // Number
164+
* ClassUtils.getTypes(field, IntegerThing.class).get(0); // Integer
165+
* </pre>
166+
* <p>
167+
* In cases of complex generics which take the intersection of multiple types
168+
* using the {@code &} operator, there may be multiple types returned by this
169+
* method. For example:
170+
* </p>
171+
*
172+
* <pre>
173+
* public class ComplexThing&lt;T extends Serializable &amp; Cloneable&gt; extends Thing&lt;T&gt; {}
174+
*
175+
* ClassUtils.getTypes(field, ComplexThing.class); // Serializable, Cloneable
176+
* </pre>
177+
*
178+
* @see #getFieldType(Field, Class)
179+
* @see #getClasses(Type)
180+
*/
181+
public static List<Class<?>> getFieldClasses(final Field field,
182+
final Class<?> type)
183+
{
184+
final Type genericType = getFieldType(field, type);
185+
return getClasses(genericType);
186+
}
187+
188+
/**
189+
* As {@link #getFieldType(Field, Class)}, but with respect to the return
190+
* type of the given {@link Method} rather than a {@link Field}.
191+
*/
192+
public static Type getMethodReturnType(final Method method,
193+
final Class<?> type)
194+
{
195+
final Type wildType = GenericTypeReflector.addWildcardParameters(type);
196+
return GenericTypeReflector.getExactReturnType(method, wildType);
197+
}
198+
199+
/**
200+
* As {@link #getFieldClasses(Field, Class)}, but with respect to the return
201+
* type of the given {@link Method} rather than a {@link Field}.
202+
*
203+
* @see #getMethodReturnType(Method, Class)
204+
* @see #getClasses(Type)
205+
*/
206+
public static List<Class<?>>
207+
getMethodReturnClasses(final Method method, final Class<?> type)
208+
{
209+
final Type genericType = getMethodReturnType(method, type);
210+
return getClasses(genericType);
211+
}
212+
213+
/**
214+
* Gets the given type's {@code n}th type parameter of the specified class.
215+
* <p>
216+
* For example, with class {@code StringList implements List<String>},
217+
* {@code getTypeParameter(StringList.class, Collection.class, 0)} returns
218+
* {@code String}.
219+
* </p>
220+
*/
221+
public static Type getTypeParameter(final Type type, final Class<?> c,
222+
final int paramNo)
223+
{
224+
return GenericTypeReflector.getTypeParameter(type,
225+
c.getTypeParameters()[paramNo]);
226+
}
227+
228+
}

0 commit comments

Comments
 (0)