[LANG-553] Add TypeUtils
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@835975 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
56e830a235
commit
5646355247
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang.reflect;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.GenericArrayType;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
|
||||
import org.apache.commons.lang.Validate;
|
||||
|
||||
/**
|
||||
* <p>Utility methods focusing on type inspection, particularly with regard to
|
||||
* generics.</p>
|
||||
* @author James Carman
|
||||
* @author Matt Benson
|
||||
* @since 3.0
|
||||
* @version $Id$
|
||||
*/
|
||||
public class TypeUtils {
|
||||
|
||||
/**
|
||||
* 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</code>: 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 enclosingType context
|
||||
* @param type to read
|
||||
* @return Class<?>
|
||||
*/
|
||||
// original code stolen from commons [proxy]'s 2.0 branch, then kneaded until firm
|
||||
public static Class<?> getRawType(Type enclosingType, Type type) {
|
||||
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 (Class<?>) ((ParameterizedType) type).getRawType();
|
||||
}
|
||||
if (type instanceof TypeVariable<?>) {
|
||||
Validate.notNull(enclosingType,
|
||||
"Cannot get raw type of TypeVariable without enclosing type");
|
||||
// resolve the variable against the enclosing type, hope for the best (casting)
|
||||
return (Class<?>) resolveVariable(enclosingType, (TypeVariable<?>) type);
|
||||
}
|
||||
if (type instanceof GenericArrayType) {
|
||||
Validate.notNull(enclosingType,
|
||||
"Cannot get raw type of GenericArrayType without enclosing type");
|
||||
// not included in original code, but not too difficult: just have to get raw component type...
|
||||
Class<?> rawComponentType = getRawType(enclosingType, ((GenericArrayType) type)
|
||||
.getGenericComponentType());
|
||||
// ...and know how to reflectively create array types, uncommon but not unheard of:
|
||||
return Array.newInstance(rawComponentType, 0).getClass();
|
||||
}
|
||||
throw new IllegalArgumentException(String.valueOf(type));
|
||||
}
|
||||
|
||||
/**
|
||||
* We plan to return Class<?> from the top-level call, as evidenced by the
|
||||
* cast in the above method, but to handle recursion and falling back up the
|
||||
* graph, as it were, return Type
|
||||
* @param enclosingType
|
||||
* @param typeVar
|
||||
* @return Type resolved
|
||||
*/
|
||||
// original code stolen from commons [proxy]'s 2.0 branch, then kneaded until firm
|
||||
private static Type resolveVariable(Type enclosingType, TypeVariable<?> typeVar) {
|
||||
if (enclosingType instanceof ParameterizedType) {
|
||||
ParameterizedType parameterizedEnclosingType = (ParameterizedType) enclosingType;
|
||||
TypeVariable<?>[] typeVariables = getRawType(null,
|
||||
parameterizedEnclosingType.getRawType()).getTypeParameters();
|
||||
//look for the matching variable:
|
||||
for (int i = 0; i < typeVariables.length; i++) {
|
||||
if (typeVariables[i].equals(typeVar)) {
|
||||
return parameterizedEnclosingType.getActualTypeArguments()[i];
|
||||
}
|
||||
}
|
||||
//otherwise recurse to try against raw class
|
||||
Type result = resolveVariable(parameterizedEnclosingType.getRawType(), typeVar);
|
||||
//unroll variable if returned
|
||||
if (result instanceof TypeVariable<?>) {
|
||||
return resolveVariable(enclosingType, (TypeVariable<?>) result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (enclosingType instanceof Class<?>) {
|
||||
Class<?> enclosingClass = (Class<?>) enclosingType;
|
||||
Type result = null;
|
||||
Type genericSuperclass = enclosingClass.getGenericSuperclass();
|
||||
if (genericSuperclass != null && !Object.class.equals(genericSuperclass)) {
|
||||
result = resolveVariable(genericSuperclass, typeVar);
|
||||
}
|
||||
if (result == null) {
|
||||
for (Type genericInterface : enclosingClass.getGenericInterfaces()) {
|
||||
result = resolveVariable(genericInterface, typeVar);
|
||||
if (result != null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException(String.valueOf(typeVar));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang.reflect;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.lang.reflect.testbed.*;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test TypeUtils
|
||||
* @author mbenson
|
||||
* @version $Id$
|
||||
*/
|
||||
public class TypeUtilsTest {
|
||||
private Field stringParentField;
|
||||
private Field integerParentField;
|
||||
private Field foosField;
|
||||
private Field barParentsField;
|
||||
private TypeVariable<?> genericParentT;
|
||||
private TypeVariable<?> listType;
|
||||
private TypeVariable<?> iterableType;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
stringParentField = GenericTypeHolder.class.getDeclaredField("stringParent");
|
||||
integerParentField = GenericTypeHolder.class.getDeclaredField("integerParent");
|
||||
foosField = GenericTypeHolder.class.getDeclaredField("foos");
|
||||
barParentsField = GenericTypeHolder.class.getDeclaredField("barParents");
|
||||
genericParentT = GenericParent.class.getTypeParameters()[0];
|
||||
listType = List.class.getTypeParameters()[0];
|
||||
iterableType = Iterable.class.getTypeParameters()[0];
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRawTypeClass() throws Exception {
|
||||
assertEquals(GenericParent.class, TypeUtils.getRawType(null, GenericParent.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRawTypeParameterizedType() throws Exception {
|
||||
assertEquals(GenericParent.class, TypeUtils.getRawType(GenericTypeHolder.class,
|
||||
stringParentField.getGenericType()));
|
||||
assertEquals(GenericParent.class, TypeUtils.getRawType(GenericTypeHolder.class,
|
||||
integerParentField.getGenericType()));
|
||||
assertEquals(List.class, TypeUtils.getRawType(GenericTypeHolder.class, foosField
|
||||
.getGenericType()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRawTypeTypeVariable() throws Exception {
|
||||
assertEquals(String.class, TypeUtils.getRawType(StringParameterizedChild.class,
|
||||
genericParentT));
|
||||
assertEquals(String.class, TypeUtils.getRawType(stringParentField.getGenericType(),
|
||||
genericParentT));
|
||||
assertEquals(Foo.class, TypeUtils.getRawType(foosField.getGenericType(), iterableType));
|
||||
assertEquals(Foo.class, TypeUtils.getRawType(foosField.getGenericType(), listType));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testGetRawTypeUnresolvableTypeVariable() {
|
||||
TypeUtils.getRawType(GenericParent.class, genericParentT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRawTypeGenericArray() throws Exception {
|
||||
assertEquals(GenericParent[].class, TypeUtils.getRawType(GenericTypeHolder.class,
|
||||
barParentsField.getGenericType()));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang.reflect.testbed;
|
||||
|
||||
/**
|
||||
* Class declaring a parameter variable.
|
||||
* @author mbenson
|
||||
* @version $Id$
|
||||
*/
|
||||
public class GenericParent<T> {
|
||||
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang.reflect.testbed;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Holds generic testbed types.
|
||||
* @author mbenson
|
||||
* @version $Id$
|
||||
*/
|
||||
public class GenericTypeHolder {
|
||||
public GenericParent<String> stringParent;
|
||||
public GenericParent<Integer> integerParent;
|
||||
public List<Foo> foos;
|
||||
public GenericParent<Bar>[] barParents;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.commons.lang.reflect.testbed;
|
||||
|
||||
/**
|
||||
* {@link GenericParent} subclass that explicitly specifies <T> as {@link String}.
|
||||
* @author mbenson
|
||||
* @version $Id$
|
||||
*/
|
||||
public class StringParameterizedChild extends GenericParent<String> {
|
||||
|
||||
}
|
Loading…
Reference in New Issue