[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:
Matthew Jason Benson 2009-11-13 20:17:47 +00:00
parent 56e830a235
commit 5646355247
5 changed files with 297 additions and 0 deletions

View File

@ -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));
}
}

View File

@ -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()));
}
}

View File

@ -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> {
}

View File

@ -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;
}

View File

@ -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> {
}