[SCM] Helper library for the Java 5 Type branch, upstream, updated. ae87e50125415b818406b61af9aea707eecf4abf

tony mancill tmancill at debian.org
Wed May 8 04:14:57 UTC 2013


The following commit has been merged in the upstream branch:
commit ae87e50125415b818406b61af9aea707eecf4abf
Author: tony mancill <tmancill at debian.org>
Date:   Tue May 7 21:13:21 2013 -0700

    Imported Upstream version 0.1.3

diff --git a/pom.xml b/pom.xml
index 063803f..290a7e0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
 	<groupId>com.googlecode.jtype</groupId>
 	<artifactId>jtype</artifactId>
 	<packaging>jar</packaging>
-	<version>0.1.1</version>
+	<version>0.1.3</version>
 	
 	<name>JType</name>
 	<description>Library for working with the Java 5 type system</description>
@@ -26,9 +26,9 @@
 	</licenses>
 	
 	<scm>
-		<connection>scm:svn:http://jtype.googlecode.com/svn/tags/0.1.1</connection>
-		<developerConnection>scm:svn:https://jtype.googlecode.com/svn/tags/0.1.1</developerConnection>
-		<url>http://code.google.com/p/jtype/source/browse/tags/0.1.1</url>
+		<connection>scm:svn:http://jtype.googlecode.com/svn/tags/0.1.3</connection>
+		<developerConnection>scm:svn:https://jtype.googlecode.com/svn/tags/0.1.3</developerConnection>
+		<url>http://code.google.com/p/jtype/source/browse/tags/0.1.3</url>
 	</scm>
 	
 	<issueManagement>
@@ -100,6 +100,11 @@
 				</plugin>
 				<plugin>
 					<groupId>org.apache.maven.plugins</groupId>
+					<artifactId>maven-release-plugin</artifactId>
+					<version>2.0-beta-9</version>
+				</plugin>
+				<plugin>
+					<groupId>org.apache.maven.plugins</groupId>
 					<artifactId>maven-resources-plugin</artifactId>
 					<version>2.4.1</version>
 				</plugin>
@@ -201,10 +206,15 @@
 
 	<distributionManagement>
 		<repository>
-			<id>jtype</id>
-			<name>JType Maven Repository</name>
-			<url>svn:https://jtype.googlecode.com/svn/repository/</url>
+			<id>sonatype-nexus-staging</id>
+			<name>Sonatype Nexus Staging Repository</name>
+			<url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
 		</repository>
+		<snapshotRepository>
+			<id>sonatype-nexus-snapshots</id>
+			<name>Sonatype Nexus Snapshot Repository</name>
+			<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
+		</snapshotRepository>
 		<site>
 			<id>jtype-site</id>
 			<name>JType Maven Site</name>
diff --git a/src/main/java/com/googlecode/jtype/AbstractTypeVisitor.java b/src/main/java/com/googlecode/jtype/AbstractTypeVisitor.java
new file mode 100644
index 0000000..e35dc99
--- /dev/null
+++ b/src/main/java/com/googlecode/jtype/AbstractTypeVisitor.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2009 IIZUKA Software Technologies Ltd
+ * 
+ * Licensed 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 com.googlecode.jtype;
+
+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;
+
+/**
+ * 
+ * 
+ * @author Mark Hobson
+ * @version $Id: AbstractTypeVisitor.java 73 2010-03-24 21:47:29Z markhobson $
+ */
+abstract class AbstractTypeVisitor implements TypeVisitor
+{
+	// TypeVisitor methods ----------------------------------------------------
+
+	/**
+	 * {@inheritDoc}
+	 */
+	public void visit(Class<?> type)
+	{
+		// no-op
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public <D extends GenericDeclaration> boolean beginVisit(TypeVariable<D> type)
+	{
+		return true;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void visitTypeVariableBound(Type bound, int index)
+	{
+		visit(bound);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public <D extends GenericDeclaration> void endVisit(TypeVariable<D> type)
+	{
+		// no-op
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void visit(GenericArrayType type)
+	{
+		visit(type.getGenericComponentType());
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean beginVisit(ParameterizedType type)
+	{
+		visit(type.getRawType());
+		
+		return true;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void visitActualTypeArgument(Type type, int index)
+	{
+		visit(type);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void endVisit(ParameterizedType type)
+	{
+		// no-op
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public boolean beginVisit(WildcardType type)
+	{
+		return true;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void visitUpperBound(Type bound, int index)
+	{
+		visit(bound);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void visitLowerBound(Type bound, int index)
+	{
+		visit(bound);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	public void endVisit(WildcardType type)
+	{
+		// no-op
+	}
+	
+	// protected methods ------------------------------------------------------
+	
+	protected void visit(Type type)
+	{
+		TypeUtils.accept(type, this);
+	}
+}
diff --git a/src/main/java/com/googlecode/jtype/ClassUtils.java b/src/main/java/com/googlecode/jtype/ClassUtils.java
index 0827341..db411ad 100644
--- a/src/main/java/com/googlecode/jtype/ClassUtils.java
+++ b/src/main/java/com/googlecode/jtype/ClassUtils.java
@@ -16,16 +16,24 @@
 package com.googlecode.jtype;
 
 import java.lang.reflect.Array;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Provides utility methods for working with classes.
  * 
  * @author Mark Hobson
- * @version $Id: ClassUtils.java 38 2009-08-26 11:21:03Z markhobson $
+ * @version $Id: ClassUtils.java 82 2010-11-01 11:22:10Z markhobson $
  * @see Class
  */
 final class ClassUtils
 {
+	// constants --------------------------------------------------------------
+	
+	private static final Map<String, String> PRIMITIVE_DESCRIPTORS_BY_CLASS_NAME =
+		createPrimitiveDescriptorsByClassName();
+	
 	// constructors -----------------------------------------------------------
 	
 	private ClassUtils()
@@ -68,4 +76,74 @@ final class ClassUtils
 	{
 		return Array.newInstance(componentType, 0).getClass();
 	}
+	
+	public static Class<?> valueOf(String className)
+	{
+		if (isPrimitiveClassName(className))
+		{
+			return valueOfPrimitive(className);
+		}
+		
+		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+
+		try
+		{
+			return Class.forName(className, true, classLoader);
+		}
+		catch (ClassNotFoundException exception)
+		{
+			return null;
+		}
+	}
+	
+	// private methods --------------------------------------------------------
+	
+	private static Map<String, String> createPrimitiveDescriptorsByClassName()
+	{
+		Map<String, String> primitiveDescriptorsByClassName = new HashMap<String, String>();
+		
+		// from JVM specification 4.3.2
+		// see http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#84645
+		primitiveDescriptorsByClassName.put("byte", "B");
+		primitiveDescriptorsByClassName.put("char", "C");
+		primitiveDescriptorsByClassName.put("double", "D");
+		primitiveDescriptorsByClassName.put("float", "F");
+		primitiveDescriptorsByClassName.put("int", "I");
+		primitiveDescriptorsByClassName.put("long", "J");
+		primitiveDescriptorsByClassName.put("short", "S");
+		primitiveDescriptorsByClassName.put("boolean", "Z");
+		
+		return Collections.unmodifiableMap(primitiveDescriptorsByClassName);
+	}
+	
+	private static Class<?> valueOfPrimitive(String className)
+	{
+		// cannot load primitives directly so load primitive array type and use component type instead
+		
+		String descriptor = getPrimitiveDescriptor(className);
+		String arrayDescriptor = getArrayDescriptor(descriptor);
+		Class<?> arrayType = valueOf(arrayDescriptor);
+		
+		return arrayType.getComponentType();
+	}
+	
+	private static boolean isPrimitiveClassName(String className)
+	{
+		return PRIMITIVE_DESCRIPTORS_BY_CLASS_NAME.containsKey(className);
+	}
+	
+	private static String getPrimitiveDescriptor(String className)
+	{
+		if (!isPrimitiveClassName(className))
+		{
+			throw new IllegalArgumentException("className is not a primitive class name: " + className);
+		}
+		
+		return PRIMITIVE_DESCRIPTORS_BY_CLASS_NAME.get(className);
+	}
+	
+	private static String getArrayDescriptor(String componentDescriptor)
+	{
+		return "[" + componentDescriptor;
+	}
 }
diff --git a/src/main/java/com/googlecode/jtype/DefaultGenericArrayType.java b/src/main/java/com/googlecode/jtype/DefaultGenericArrayType.java
index 0169553..7469884 100644
--- a/src/main/java/com/googlecode/jtype/DefaultGenericArrayType.java
+++ b/src/main/java/com/googlecode/jtype/DefaultGenericArrayType.java
@@ -15,6 +15,9 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.Utils.checkNotNull;
+
+import java.io.Serializable;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.Type;
 
@@ -22,22 +25,29 @@ import java.lang.reflect.Type;
  * Default implementation of a generic array type.
  * 
  * @author Mark Hobson
- * @version $Id: DefaultGenericArrayType.java 2 2009-02-02 22:28:39Z markhobson $
+ * @version $Id: DefaultGenericArrayType.java 110 2011-11-23 17:19:43Z markhobson $
  * @see GenericArrayType
  */
-class DefaultGenericArrayType implements GenericArrayType
+class DefaultGenericArrayType implements GenericArrayType, Serializable
 {
-	// TODO: make serializable?
+	// constants --------------------------------------------------------------
+	
+	private static final long serialVersionUID = 1L;
 	
 	// fields -----------------------------------------------------------------
 	
+	/**
+	 * The component type of this array.
+	 * 
+	 * @serial
+	 */
 	private final Type componentType;
 	
 	// constructors -----------------------------------------------------------
 	
 	public DefaultGenericArrayType(Type componentType)
 	{
-		this.componentType = Utils.checkNotNull(componentType, "componentType");
+		this.componentType = checkNotNull(componentType, "componentType");
 	}
 	
 	// GenericArrayType methods -----------------------------------------------
@@ -83,18 +93,6 @@ class DefaultGenericArrayType implements GenericArrayType
 	@Override
 	public String toString()
 	{
-		return toString(this);
-	}
-	
-	// public methods ---------------------------------------------------------
-	
-	public static String toString(GenericArrayType type)
-	{
-		return toString(type, ClassSerializers.QUALIFIED);
-	}
-	
-	public static String toString(GenericArrayType type, ClassSerializer serializer)
-	{
-		return TypeUtils.toString(type.getGenericComponentType(), serializer) + "[]";
+		return TypeUtils.toString(this);
 	}
 }
diff --git a/src/main/java/com/googlecode/jtype/DefaultParameterizedType.java b/src/main/java/com/googlecode/jtype/DefaultParameterizedType.java
index 783aeae..c181d11 100644
--- a/src/main/java/com/googlecode/jtype/DefaultParameterizedType.java
+++ b/src/main/java/com/googlecode/jtype/DefaultParameterizedType.java
@@ -15,6 +15,11 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.Utils.checkNotNull;
+import static com.googlecode.jtype.Utils.nullEquals;
+import static com.googlecode.jtype.Utils.nullHashCode;
+
+import java.io.Serializable;
 import java.lang.reflect.MalformedParameterizedTypeException;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -25,26 +30,43 @@ import java.util.Arrays;
  * Default implementation of a parameterized type.
  * 
  * @author Mark Hobson
- * @version $Id: DefaultParameterizedType.java 35 2009-07-03 10:14:37Z markhobson $
+ * @version $Id: DefaultParameterizedType.java 110 2011-11-23 17:19:43Z markhobson $
  * @see ParameterizedType
  */
-class DefaultParameterizedType implements ParameterizedType
+class DefaultParameterizedType implements ParameterizedType, Serializable
 {
-	// TODO: make serializable?
+	// constants --------------------------------------------------------------
+	
+	private static final long serialVersionUID = 1L;
 	
 	// fields -----------------------------------------------------------------
 	
+	/**
+	 * The type that this type is a member of.
+	 * 
+	 * @serial
+	 */
 	private final Type ownerType;
 	
+	/**
+	 * The class or interface that declared this type.
+	 * 
+	 * @serial
+	 */
 	private final Type rawType;
 	
+	/**
+	 * The types representing the actual type arguments to this type.
+	 * 
+	 * @serial
+	 */
 	private final Type[] actualTypeArguments;
 	
 	// constructors -----------------------------------------------------------
 	
 	public DefaultParameterizedType(Type ownerType, Class<?> rawType, Type[] actualTypeArguments)
 	{
-		this.rawType = Utils.checkNotNull(rawType, "rawType");
+		this.rawType = checkNotNull(rawType, "rawType");
 		
 		if (actualTypeArguments == null)
 		{
@@ -53,6 +75,12 @@ class DefaultParameterizedType implements ParameterizedType
 		
 		TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
 		
+		// disallow unparameterized raw types
+		if (typeParameters.length == 0)
+		{
+			throw new MalformedParameterizedTypeException();
+		}
+		
 		if (typeParameters.length != actualTypeArguments.length)
 		{
 			throw new MalformedParameterizedTypeException();
@@ -101,7 +129,7 @@ class DefaultParameterizedType implements ParameterizedType
 	@Override
 	public int hashCode()
 	{
-		int hashCode = Utils.nullHashCode(ownerType);
+		int hashCode = nullHashCode(ownerType);
 		hashCode = (37 * hashCode) + rawType.hashCode();
 		hashCode = (37 * hashCode) + Arrays.hashCode(actualTypeArguments);
 		
@@ -126,7 +154,7 @@ class DefaultParameterizedType implements ParameterizedType
 			return true;
 		}
 		
-		return Utils.nullEquals(ownerType, type.getOwnerType())
+		return nullEquals(ownerType, type.getOwnerType())
 			&& rawType.equals(type.getRawType())
 			&& Arrays.equals(actualTypeArguments, type.getActualTypeArguments());
 	}
@@ -137,61 +165,6 @@ class DefaultParameterizedType implements ParameterizedType
 	@Override
 	public String toString()
 	{
-		return toString(this);
-	}
-	
-	// public methods ---------------------------------------------------------
-	
-	public static String toString(ParameterizedType type)
-	{
-		return toString(type, ClassSerializers.QUALIFIED);
-	}
-	
-	public static String toString(ParameterizedType type, ClassSerializer serializer)
-	{
-		StringBuilder builder = new StringBuilder();
-		
-		Type ownerType = type.getOwnerType();
-		String rawTypeString = TypeUtils.toString(type.getRawType(), serializer);
-		
-		if (ownerType != null)
-		{
-			String ownerTypeString = TypeUtils.toString(ownerType, serializer);
-			
-			builder.append(ownerTypeString);
-			
-			if (rawTypeString.startsWith(ownerTypeString))
-			{
-				// use simple name for member types
-				rawTypeString = rawTypeString.substring(ownerTypeString.length());
-			}
-			else
-			{
-				builder.append(".");
-			}
-		}
-		
-		builder.append(rawTypeString);
-		
-		Type[] actualTypeArguments = type.getActualTypeArguments();
-		
-		if (actualTypeArguments != null && actualTypeArguments.length > 0)
-		{
-			builder.append("<");
-			
-			for (int i = 0; i < actualTypeArguments.length; i++)
-			{
-				if (i > 0)
-				{
-					builder.append(",");
-				}
-				
-				builder.append(TypeUtils.toString(actualTypeArguments[i], serializer));
-			}
-			
-			builder.append(">");
-		}
-		
-		return builder.toString();
+		return TypeUtils.toString(this);
 	}
 }
diff --git a/src/main/java/com/googlecode/jtype/DefaultTypeVariable.java b/src/main/java/com/googlecode/jtype/DefaultTypeVariable.java
index 68775ef..1f31bf2 100644
--- a/src/main/java/com/googlecode/jtype/DefaultTypeVariable.java
+++ b/src/main/java/com/googlecode/jtype/DefaultTypeVariable.java
@@ -15,6 +15,10 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.Utils.checkNotNull;
+import static com.googlecode.jtype.Utils.checkTrue;
+
+import java.io.Serializable;
 import java.lang.reflect.GenericDeclaration;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -25,25 +29,40 @@ import java.util.Arrays;
  * Default implementation of a type variable.
  * 
  * @author Mark Hobson
- * @version $Id: DefaultTypeVariable.java 42 2009-10-02 14:54:54Z markhobson $
+ * @version $Id: DefaultTypeVariable.java 110 2011-11-23 17:19:43Z markhobson $
  * @param <D>
  *            the type of generic declaration that declared the type variable
  * @see TypeVariable
  */
-class DefaultTypeVariable<D extends GenericDeclaration> implements TypeVariable<D>
+class DefaultTypeVariable<D extends GenericDeclaration> implements TypeVariable<D>, Serializable
 {
-	// TODO: make serializable?
-	
 	// constants --------------------------------------------------------------
 	
 	private static final Type[] DEFAULT_BOUNDS = new Type[] {Object.class};
 	
+	private static final long serialVersionUID = 1L;
+	
 	// fields -----------------------------------------------------------------
 	
+	/**
+	 * The generic declaration that declared this type variable.
+	 * 
+	 * @serial
+	 */
 	private final D declaration;
 	
+	/**
+	 * The name of this type variable, as it occurs in the source code.
+	 * 
+	 * @serial
+	 */
 	private final String name;
 	
+	/**
+	 * The upper bound(s) of this type variable.
+	 * 
+	 * @serial
+	 */
 	private final Type[] bounds;
 	
 	// constructors -----------------------------------------------------------
@@ -57,15 +76,14 @@ class DefaultTypeVariable<D extends GenericDeclaration> implements TypeVariable<
 		
 		// initial bound must be either a class type, an interface type or a type variable
 		
-		Utils.checkTrue(isValidFirstBound(bounds[0]), "First bound must be either a class type, an interface type or a "
-			+ "type variable", bounds[0]);
+		checkTrue(isValidFirstBound(bounds[0]), "First bound must be either a class type, an interface type or a type "
+			+ "variable", bounds[0]);
 		
 		// subsequent bounds must be an interface type
 		
 		for (int i = 1; i < bounds.length; i++)
 		{
-			Utils.checkTrue(isValidSecondaryBound(bounds[i]), "Secondary bounds must be an interface type: ",
-				bounds[i]);
+			checkTrue(isValidSecondaryBound(bounds[i]), "Secondary bounds must be an interface type: ", bounds[i]);
 		}
 		
 		// TODO: the erasures of all constituent types of a bound must be pairwise different
@@ -73,8 +91,8 @@ class DefaultTypeVariable<D extends GenericDeclaration> implements TypeVariable<
 		// TODO: type variable may not be a subtype of two interface types which are different parameterizations of the
 		// same generic interface
 		
-		this.declaration = Utils.checkNotNull(declaration, "declaration");
-		this.name = Utils.checkNotNull(name, "name");
+		this.declaration = checkNotNull(declaration, "declaration");
+		this.name = checkNotNull(name, "name");
 		this.bounds = bounds.clone();
 	}
 	
@@ -143,30 +161,7 @@ class DefaultTypeVariable<D extends GenericDeclaration> implements TypeVariable<
 	@Override
 	public String toString()
 	{
-		return toString(this);
-	}
-	
-	// public methods ---------------------------------------------------------
-	
-	public static String toString(TypeVariable<?> type)
-	{
-		return toString(type, ClassSerializers.QUALIFIED);
-	}
-	
-	public static String toString(TypeVariable<?> type, ClassSerializer serializer)
-	{
-		StringBuilder builder = new StringBuilder();
-		
-		builder.append(type.getName());
-		
-		if (!Arrays.equals(DEFAULT_BOUNDS, type.getBounds()))
-		{
-			builder.append(" extends ");
-			
-			TypeUtils.appendBounds(builder, type.getBounds(), serializer);
-		}
-		
-		return builder.toString();
+		return TypeUtils.toString(this);
 	}
 	
 	// private methods --------------------------------------------------------
diff --git a/src/main/java/com/googlecode/jtype/DefaultWildcardType.java b/src/main/java/com/googlecode/jtype/DefaultWildcardType.java
index 409f27d..9f910ab 100644
--- a/src/main/java/com/googlecode/jtype/DefaultWildcardType.java
+++ b/src/main/java/com/googlecode/jtype/DefaultWildcardType.java
@@ -15,6 +15,9 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.Utils.checkFalse;
+
+import java.io.Serializable;
 import java.lang.reflect.Type;
 import java.lang.reflect.WildcardType;
 import java.util.Arrays;
@@ -23,23 +26,33 @@ import java.util.Arrays;
  * Default implementation of a wildcard type.
  * 
  * @author Mark Hobson
- * @version $Id: DefaultWildcardType.java 2 2009-02-02 22:28:39Z markhobson $
+ * @version $Id: DefaultWildcardType.java 110 2011-11-23 17:19:43Z markhobson $
  * @see WildcardType
  */
-class DefaultWildcardType implements WildcardType
+class DefaultWildcardType implements WildcardType, Serializable
 {
-	// TODO: make serializable?
-	
 	// constants --------------------------------------------------------------
 	
 	private static final Type[] DEFAULT_UPPER_BOUNDS = new Type[] {Object.class};
 	
 	private static final Type[] DEFAULT_LOWER_BOUNDS = new Type[0];
 	
+	private static final long serialVersionUID = 1L;
+	
 	// fields -----------------------------------------------------------------
 	
+	/**
+	 * The upper bound(s) of this type variable.
+	 * 
+	 * @serial
+	 */
 	private final Type[] upperBounds;
 	
+	/**
+	 * The lower bound(s) of this type variable.
+	 * 
+	 * @serial
+	 */
 	private final Type[] lowerBounds;
 	
 	// constructors -----------------------------------------------------------
@@ -61,7 +74,7 @@ class DefaultWildcardType implements WildcardType
 		
 		boolean hasUpperBounds = !Arrays.equals(this.upperBounds, DEFAULT_UPPER_BOUNDS);
 		boolean hasLowerBounds = !Arrays.equals(this.lowerBounds, DEFAULT_LOWER_BOUNDS);
-		Utils.checkFalse(hasUpperBounds && hasLowerBounds, "Wildcard type cannot have both upper and lower bounds");
+		checkFalse(hasUpperBounds && hasLowerBounds, "Wildcard type cannot have both upper and lower bounds");
 	}
 	
 	// WildcardType methods ---------------------------------------------------
@@ -116,38 +129,6 @@ class DefaultWildcardType implements WildcardType
 	@Override
 	public String toString()
 	{
-		return toString(this);
-	}
-	
-	// public methods ---------------------------------------------------------
-	
-	public static String toString(WildcardType type)
-	{
-		return toString(type, ClassSerializers.QUALIFIED);
-	}
-	
-	public static String toString(WildcardType type, ClassSerializer serializer)
-	{
-		StringBuilder builder = new StringBuilder();
-		
-		builder.append("?");
-		
-		Type[] lowerBounds = type.getLowerBounds();
-		Type[] upperBounds = type.getUpperBounds();
-		
-		if (!Arrays.equals(DEFAULT_LOWER_BOUNDS, lowerBounds))
-		{
-			builder.append(" super ");
-			
-			TypeUtils.appendBounds(builder, lowerBounds, serializer);
-		}
-		else if (!Arrays.equals(DEFAULT_UPPER_BOUNDS, upperBounds))
-		{
-			builder.append(" extends ");
-			
-			TypeUtils.appendBounds(builder, upperBounds, serializer);
-		}
-		
-		return builder.toString();
+		return TypeUtils.toString(this);
 	}
 }
diff --git a/src/main/java/com/googlecode/jtype/Generic.java b/src/main/java/com/googlecode/jtype/Generic.java
index bc689fc..3816d50 100644
--- a/src/main/java/com/googlecode/jtype/Generic.java
+++ b/src/main/java/com/googlecode/jtype/Generic.java
@@ -15,11 +15,19 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.Utils.checkFalse;
+import static com.googlecode.jtype.Utils.checkNotNull;
+import static com.googlecode.jtype.Utils.checkTrue;
+
+import java.io.Serializable;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
 import java.lang.reflect.WildcardType;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
 
 /**
  * Provides a generic type literal.
@@ -44,15 +52,13 @@ import java.lang.reflect.WildcardType;
  * {@code add(new ArrayList<String>(), new Generic<List<String>>() }<code>{});</code>
  * 
  * @author Mark Hobson
- * @version $Id: Generic.java 47 2009-10-02 15:59:23Z markhobson $
+ * @version $Id: Generic.java 110 2011-11-23 17:19:43Z markhobson $
  * @param <T> the type that this generic type literal represents
  * @see Generics
  * @see <a href="http://gafter.blogspot.com/2006/12/super-type-tokens.html">Neal Gafter's blog: Super Type Tokens</a>
  */
-public abstract class Generic<T>
+public abstract class Generic<T> implements Serializable
 {
-	// TODO: make serializable?
-	
 	// classes ----------------------------------------------------------------
 	
 	private static final class DefaultGeneric<T> extends Generic<T>
@@ -63,8 +69,30 @@ public abstract class Generic<T>
 		}
 	}
 	
+	/**
+	 * Simple read-only cache for common generics. Implemented as an inner class for lazy instantiation.
+	 */
+	private static class GenericCache
+	{
+		private static final Map<Type, Generic<?>> GENERICS_BY_TYPE = createCache();
+		
+		public static Generic<?> get(Type type)
+		{
+			return GENERICS_BY_TYPE.get(type);
+		}
+	}
+	
+	// constants --------------------------------------------------------------
+	
+	private static final long serialVersionUID = 1L;
+	
 	// fields -----------------------------------------------------------------
 	
+	/**
+	 * The type that this generic type literal represents.
+	 * 
+	 * @serial
+	 */
 	private final Type type;
 	
 	// constructors -----------------------------------------------------------
@@ -105,17 +133,33 @@ public abstract class Generic<T>
 	
 	public static <T> Generic<T> get(Class<T> klass)
 	{
-		return new DefaultGeneric<T>(klass);
+		// guaranteed by definition
+		@SuppressWarnings("unchecked")
+		Generic<T> generic = (Generic<T>) get((Type) klass);
+		
+		return generic;
 	}
 	
 	public static Generic<?> get(Type type)
 	{
-		return new DefaultGeneric<Object>(type);
+		Generic<?> generic = GenericCache.get(type);
+		
+		if (generic == null)
+		{
+			generic = create(type);
+		}
+		
+		return generic;
 	}
 	
 	@SuppressWarnings("unchecked")
 	public static <T> Generic<? extends T> get(Class<T> rawType, Type... actualTypeArguments)
 	{
+		if (actualTypeArguments == null || actualTypeArguments.length == 0)
+		{
+			return get(rawType);
+		}
+		
 		ParameterizedType paramType = Types.parameterizedType(rawType, actualTypeArguments);
 		
 		return (Generic<? extends T>) get(paramType);
@@ -164,13 +208,42 @@ public abstract class Generic<T>
 	
 	// private methods --------------------------------------------------------
 	
+	private static Map<Type, Generic<?>> createCache()
+	{
+		Map<Type, Generic<?>> genericsByType = new HashMap<Type, Generic<?>>();
+		
+		putCacheEntry(genericsByType, Object.class);
+		
+		putCacheEntry(genericsByType, Boolean.class);
+		putCacheEntry(genericsByType, Byte.class);
+		putCacheEntry(genericsByType, Character.class);
+		putCacheEntry(genericsByType, Double.class);
+		putCacheEntry(genericsByType, Float.class);
+		putCacheEntry(genericsByType, Integer.class);
+		putCacheEntry(genericsByType, Long.class);
+		putCacheEntry(genericsByType, Short.class);
+		putCacheEntry(genericsByType, String.class);
+		
+		return Collections.unmodifiableMap(genericsByType);
+	}
+	
+	private static void putCacheEntry(Map<Type, Generic<?>> genericsByType, Type type)
+	{
+		genericsByType.put(type, create(type));
+	}
+	
+	private static Generic<Object> create(Type type)
+	{
+		return new DefaultGeneric<Object>(type);
+	}
+	
 	private static void validateType(Type type)
 	{
-		Utils.checkNotNull(type, "type");
-		Utils.checkFalse(type instanceof TypeVariable<?>, "Type variables are not supported: ", type);
-		Utils.checkFalse(type instanceof WildcardType, "Wildcard types are not supported: ", type);
-		Utils.checkTrue(type instanceof Class<?> || type instanceof ParameterizedType
-			|| type instanceof GenericArrayType, "Unsupported type: ", type);
+		checkNotNull(type, "type");
+		checkFalse(type instanceof TypeVariable<?>, "Type variables are not supported: ", type);
+		checkFalse(type instanceof WildcardType, "Wildcard types are not supported: ", type);
+		checkTrue(type instanceof Class<?> || type instanceof ParameterizedType || type instanceof GenericArrayType,
+			"Unsupported type: ", type);
 	}
 	
 	private Type getActualTypeArgument()
diff --git a/src/main/java/com/googlecode/jtype/Generics.java b/src/main/java/com/googlecode/jtype/Generics.java
index d7bf2d1..7f6bd56 100644
--- a/src/main/java/com/googlecode/jtype/Generics.java
+++ b/src/main/java/com/googlecode/jtype/Generics.java
@@ -31,12 +31,52 @@ import java.util.SortedSet;
  * Factory for creating common generics.
  * 
  * @author Mark Hobson
- * @version $Id: Generics.java 2 2009-02-02 22:28:39Z markhobson $
+ * @version $Id: Generics.java 86 2010-11-05 13:23:09Z markhobson $
  * @see Generic
  */
 @SuppressWarnings("unchecked")
 public final class Generics
 {
+	// constants --------------------------------------------------------------
+	
+	private static final Generic<Comparable<?>> COMPARABLE = (Generic<Comparable<?>>) Generic.get(Comparable.class,
+		Types.unboundedWildcardType());
+
+	private static final Generic<Comparator<?>> COMPARATOR = (Generic<Comparator<?>>) Generic.get(Comparator.class,
+		Types.unboundedWildcardType());
+	
+	private static final Generic<Enumeration<?>> ENUMERATION = (Generic<Enumeration<?>>) Generic.get(Enumeration.class,
+		Types.unboundedWildcardType());
+	
+	private static final Generic<Iterable<?>> ITERABLE = (Generic<Iterable<?>>) Generic.get(Iterable.class,
+		Types.unboundedWildcardType());
+	
+	private static final Generic<Iterator<?>> ITERATOR = (Generic<Iterator<?>>) Generic.get(Iterator.class,
+		Types.unboundedWildcardType());
+	
+	private static final Generic<ListIterator<?>> LIST_ITERATOR = (Generic<ListIterator<?>>) Generic.get(
+		ListIterator.class, Types.unboundedWildcardType());
+	
+	private static final Generic<Collection<?>> COLLECTION = (Generic<Collection<?>>) Generic.get(Collection.class,
+		Types.unboundedWildcardType());
+	
+	private static final Generic<Set<?>> SET = (Generic<Set<?>>) Generic.get(Set.class, Types.unboundedWildcardType());
+	
+	private static final Generic<SortedSet<?>> SORTED_SET = (Generic<SortedSet<?>>) Generic.get(SortedSet.class,
+		Types.unboundedWildcardType());
+	
+	private static final Generic<List<?>> LIST = (Generic<List<?>>) Generic.get(List.class,
+		Types.unboundedWildcardType());
+	
+	private static final Generic<Map<?, ?>> MAP = (Generic<Map<?, ?>>) Generic.get(Map.class,
+		Types.unboundedWildcardType(), Types.unboundedWildcardType());
+	
+	private static final Generic<SortedMap<?, ?>> SORTED_MAP = (Generic<SortedMap<?, ?>>) Generic.get(SortedMap.class,
+		Types.unboundedWildcardType(), Types.unboundedWildcardType());
+	
+	private static final Generic<Queue<?>> QUEUE = (Generic<Queue<?>>) Generic.get(Queue.class,
+		Types.unboundedWildcardType());
+	
 	// constructors -----------------------------------------------------------
 	
 	private Generics()
@@ -46,56 +86,131 @@ public final class Generics
 	
 	// public methods ---------------------------------------------------------
 	
+	public static Generic<Comparable<?>> comparable()
+	{
+		return COMPARABLE;
+	}
+	
+	public static <T> Generic<Comparable<T>> comparable(Class<T> type)
+	{
+		return (Generic<Comparable<T>>) Generic.get(Comparable.class, type);
+	}
+	
+	public static Generic<Comparator<?>> comparator()
+	{
+		return COMPARATOR;
+	}
+	
 	public static <T> Generic<Comparator<T>> comparator(Class<T> type)
 	{
 		return (Generic<Comparator<T>>) Generic.get(Comparator.class, type);
 	}
 	
+	public static Generic<Enumeration<?>> enumeration()
+	{
+		return ENUMERATION;
+	}
+	
 	public static <E> Generic<Enumeration<E>> enumeration(Class<E> elementClass)
 	{
 		return (Generic<Enumeration<E>>) Generic.get(Enumeration.class, elementClass);
 	}
 	
+	public static Generic<Iterable<?>> iterable()
+	{
+		return ITERABLE;
+	}
+	
+	public static <T> Generic<Iterable<T>> iterable(Class<T> elementClass)
+	{
+		return (Generic<Iterable<T>>) Generic.get(Iterable.class, elementClass);
+	}
+	
+	public static Generic<Iterator<?>> iterator()
+	{
+		return ITERATOR;
+	}
+	
 	public static <E> Generic<Iterator<E>> iterator(Class<E> elementClass)
 	{
 		return (Generic<Iterator<E>>) Generic.get(Iterator.class, elementClass);
 	}
 	
+	public static Generic<ListIterator<?>> listIterator()
+	{
+		return LIST_ITERATOR;
+	}
+	
 	public static <E> Generic<ListIterator<E>> listIterator(Class<E> elementClass)
 	{
 		return (Generic<ListIterator<E>>) Generic.get(ListIterator.class, elementClass);
 	}
 	
+	public static Generic<Collection<?>> collection()
+	{
+		return COLLECTION;
+	}
+	
 	public static <E> Generic<Collection<E>> collection(Class<E> elementClass)
 	{
 		return (Generic<Collection<E>>) Generic.get(Collection.class, elementClass);
 	}
 	
+	public static Generic<Set<?>> set()
+	{
+		return SET;
+	}
+	
 	public static <E> Generic<Set<E>> set(Class<E> elementClass)
 	{
 		return (Generic<Set<E>>) Generic.get(Set.class, elementClass);
 	}
 	
+	public static Generic<SortedSet<?>> sortedSet()
+	{
+		return SORTED_SET;
+	}
+	
 	public static <E> Generic<SortedSet<E>> sortedSet(Class<E> elementClass)
 	{
 		return (Generic<SortedSet<E>>) Generic.get(SortedSet.class, elementClass);
 	}
 	
+	public static Generic<List<?>> list()
+	{
+		return LIST;
+	}
+	
 	public static <E> Generic<List<E>> list(Class<E> elementClass)
 	{
 		return (Generic<List<E>>) Generic.get(List.class, elementClass);
 	}
 	
+	public static Generic<Map<?, ?>> map()
+	{
+		return MAP;
+	}
+	
 	public static <K, V> Generic<Map<K, V>> map(Class<K> keyClass, Class<V> valueClass)
 	{
 		return (Generic<Map<K, V>>) Generic.get(Map.class, keyClass, valueClass);
 	}
 	
+	public static Generic<SortedMap<?, ?>> sortedMap()
+	{
+		return SORTED_MAP;
+	}
+	
 	public static <K, V> Generic<SortedMap<K, V>> sortedMap(Class<K> keyClass, Class<V> valueClass)
 	{
 		return (Generic<SortedMap<K, V>>) Generic.get(SortedMap.class, keyClass, valueClass);
 	}
 	
+	public static Generic<Queue<?>> queue()
+	{
+		return QUEUE;
+	}
+	
 	public static <E> Generic<Queue<E>> queue(Class<E> elementClass)
 	{
 		return (Generic<Queue<E>>) Generic.get(Queue.class, elementClass);
diff --git a/src/main/java/com/googlecode/jtype/SerializingTypeVisitor.java b/src/main/java/com/googlecode/jtype/SerializingTypeVisitor.java
new file mode 100644
index 0000000..3e0d453
--- /dev/null
+++ b/src/main/java/com/googlecode/jtype/SerializingTypeVisitor.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2009 IIZUKA Software Technologies Ltd
+ * 
+ * Licensed 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 com.googlecode.jtype;
+
+import static com.googlecode.jtype.Utils.checkNotNull;
+
+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;
+
+/**
+ * 
+ * 
+ * @author Mark Hobson
+ * @version $Id: SerializingTypeVisitor.java 110 2011-11-23 17:19:43Z markhobson $
+ */
+class SerializingTypeVisitor extends AbstractTypeVisitor
+{
+	// fields -----------------------------------------------------------------
+	
+	private final ClassSerializer serializer;
+	
+	private final StringBuilder builder;
+	
+	// constructors -----------------------------------------------------------
+	
+	public SerializingTypeVisitor(ClassSerializer serializer)
+	{
+		checkNotNull(serializer, "serializer");
+		
+		this.serializer = serializer;
+		
+		builder = new StringBuilder();
+	}
+
+	// TypeVisitor methods ----------------------------------------------------
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void visit(Class<?> type)
+	{
+		if (type.isArray())
+		{
+			visit(type.getComponentType());
+			
+			builder.append("[]");
+		}
+		else
+		{
+			builder.append(serializer.toString(type));
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public <D extends GenericDeclaration> boolean beginVisit(TypeVariable<D> type)
+	{
+		builder.append(type.getName());
+		
+		return true;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void visitTypeVariableBound(Type bound, int index)
+	{
+		if (!(bound == Object.class && index == 0))
+		{
+			builder.append((index == 0) ? " extends " : " & ");
+		
+			visit(bound);
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void visit(GenericArrayType type)
+	{
+		visit(type.getGenericComponentType());
+		
+		builder.append("[]");
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean beginVisit(ParameterizedType type)
+	{
+		Type ownerType = type.getOwnerType();
+		
+		if (ownerType != null)
+		{
+			visit(ownerType);
+			
+			builder.append(".");
+		}
+		
+		visit(type.getRawType());
+		
+		if (type.getActualTypeArguments().length > 0)
+		{
+			builder.append("<");
+		}
+		
+		return true;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void visitActualTypeArgument(Type type, int index)
+	{
+		if (index > 0)
+		{
+			builder.append(", ");
+		}
+		
+		visit(type);
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void endVisit(ParameterizedType type)
+	{
+		if (type.getActualTypeArguments().length > 0)
+		{
+			builder.append(">");
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public boolean beginVisit(WildcardType type)
+	{
+		builder.append("?");
+		
+		return true;
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void visitUpperBound(Type bound, int index)
+	{
+		if (!(bound == Object.class && index == 0))
+		{
+			builder.append((index == 0) ? " extends " : " & ");
+		
+			visit(bound);
+		}
+	}
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public void visitLowerBound(Type bound, int index)
+	{
+		builder.append((index == 0) ? " super " : " & ");
+		
+		visit(bound);
+	}
+	
+	// Object methods ---------------------------------------------------------
+
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	public String toString()
+	{
+		return builder.toString();
+	}
+}
diff --git a/src/main/java/com/googlecode/jtype/TypeUtils.java b/src/main/java/com/googlecode/jtype/TypeUtils.java
index 85a6632..addd65d 100644
--- a/src/main/java/com/googlecode/jtype/TypeUtils.java
+++ b/src/main/java/com/googlecode/jtype/TypeUtils.java
@@ -15,6 +15,9 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.Utils.checkNotNull;
+import static com.googlecode.jtype.Utils.checkTrue;
+
 import java.io.Serializable;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.MalformedParameterizedTypeException;
@@ -27,14 +30,14 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.Map;
-import java.util.Set;
 import java.util.Map.Entry;
+import java.util.Set;
 
 /**
  * Provides utility methods for working with types.
  * 
  * @author Mark Hobson
- * @version $Id: TypeUtils.java 49 2009-10-07 17:10:57Z markhobson $
+ * @version $Id: TypeUtils.java 113 2011-11-25 18:14:23Z markhobson at gmail.com $
  */
 public final class TypeUtils
 {
@@ -68,10 +71,92 @@ public final class TypeUtils
 	
 	// public methods ---------------------------------------------------------
 	
+	public static void accept(Type type, TypeVisitor visitor)
+	{
+		checkNotNull(type, "type");
+		checkNotNull(visitor, "visitor");
+		
+		if (type instanceof Class<?>)
+		{
+			visitor.visit((Class<?>) type);
+		}
+		else if (type instanceof TypeVariable<?>)
+		{
+			TypeVariable<?> typeVariable = (TypeVariable<?>) type;
+			
+			if (visitor.beginVisit(typeVariable))
+			{
+				// visit bounds
+			
+				Type[] bounds = typeVariable.getBounds();
+				
+				for (int i = 0; i < bounds.length; i++)
+				{
+					visitor.visitTypeVariableBound(bounds[i], i);
+				}
+			}
+			
+			visitor.endVisit(typeVariable);
+		}
+		else if (type instanceof GenericArrayType)
+		{
+			visitor.visit((GenericArrayType) type);
+		}
+		else if (type instanceof ParameterizedType)
+		{
+			ParameterizedType parameterizedType = (ParameterizedType) type;
+			
+			if (visitor.beginVisit(parameterizedType))
+			{
+				// visit actual type arguments
+				
+				Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+				
+				for (int i = 0; i < actualTypeArguments.length; i++)
+				{
+					visitor.visitActualTypeArgument(actualTypeArguments[i], i);
+				}
+			}
+			
+			visitor.endVisit(parameterizedType);
+		}
+		else if (type instanceof WildcardType)
+		{
+			WildcardType wildcardType = (WildcardType) type;
+
+			if (visitor.beginVisit(wildcardType))
+			{
+				// visit upper bounds
+				
+				Type[] upperBounds = wildcardType.getUpperBounds();
+				
+				for (int i = 0; i < upperBounds.length; i++)
+				{
+					visitor.visitUpperBound(upperBounds[i], i);
+				}
+				
+				// visit lower bounds
+				
+				Type[] lowerBounds = wildcardType.getLowerBounds();
+				
+				for (int i = 0; i < lowerBounds.length; i++)
+				{
+					visitor.visitLowerBound(lowerBounds[i], i);
+				}
+			}
+				
+			visitor.endVisit(wildcardType);
+		}
+		else
+		{
+			throw new IllegalArgumentException("Unknown type: " + type);
+		}
+	}
+	
 	public static boolean isAssignable(Type supertype, Type type)
 	{
-		Utils.checkNotNull(supertype, "supertype");
-		Utils.checkNotNull(type, "type");
+		checkNotNull(supertype, "supertype");
+		checkNotNull(type, "type");
 		
 		if (supertype.equals(type))
 		{
@@ -105,6 +190,11 @@ public final class TypeUtils
 				return isArraySupertype((Class<?>) supertype);
 			}
 			
+			if (type instanceof WildcardType)
+			{
+				return isClassAssignableToWildcardType((Class<?>) supertype, (WildcardType) type);
+			}
+			
 			return false;
 		}
 		
@@ -194,7 +284,7 @@ public final class TypeUtils
 	
 	public static Class<?> getErasedReferenceType(Type type)
 	{
-		Utils.checkTrue(isReferenceType(type), "type is not a reference type: ", type);
+		checkTrue(isReferenceType(type), "type is not a reference type: ", type);
 		
 		return (Class<?>) getErasedType(type);
 	}
@@ -214,6 +304,11 @@ public final class TypeUtils
 			|| (type instanceof GenericArrayType);
 	}
 	
+	public static boolean isPrimitive(Type type)
+	{
+		return (type instanceof Class<?>) && ((Class<?>) type).isPrimitive();
+	}
+	
 	public static Type getComponentType(Type type)
 	{
 		if (type instanceof Class<?>)
@@ -233,7 +328,7 @@ public final class TypeUtils
 	
 	public static Type getArrayType(Type componentType)
 	{
-		Utils.checkNotNull(componentType, "componentType");
+		checkNotNull(componentType, "componentType");
 		
 		if (componentType instanceof Class<?>)
 		{
@@ -243,53 +338,68 @@ public final class TypeUtils
 		return Types.genericArrayType(componentType);
 	}
 	
-	public static boolean isSimpleParameterizedType(Type type, Class<?> rawType)
+	public static boolean isParameterizedType(Type type, Class<?> rawType)
 	{
-		Utils.checkNotNull(type, "type");
-		Utils.checkNotNull(rawType, "rawType");
+		checkNotNull(type, "type");
+		checkNotNull(rawType, "rawType");
 		
-		if (!(type instanceof ParameterizedType))
+		ParameterizedType parameterizedType;
+		
+		try
 		{
-			return false;
+			parameterizedType = Types.unboundedParameterizedType(rawType);
 		}
-		
-		ParameterizedType paramType = (ParameterizedType) type;
-		
-		Type paramRawType = paramType.getRawType();
-		
-		if (!(paramRawType instanceof Class<?>))
+		catch (MalformedParameterizedTypeException exception)
 		{
 			return false;
 		}
 		
-		Class<?> paramRawClass = (Class<?>) paramRawType;
-		
-		if (!rawType.isAssignableFrom(paramRawClass))
+		return isAssignable(parameterizedType, type);
+	}
+
+	/**
+	 * @deprecated Use {@link #isParameterizedType(Type, Class)} instead.
+	 */
+	@Deprecated
+	public static boolean isSimpleParameterizedType(Type type, Class<?> rawType)
+	{
+		if (!isParameterizedType(type, rawType))
 		{
 			return false;
 		}
 		
-		Type[] typeArgs = paramType.getActualTypeArguments();
+		Type[] typeArgs = ((ParameterizedType) type).getActualTypeArguments();
 		
 		return (typeArgs.length == 1);
 	}
 	
+	/**
+	 * @deprecated Use {@link #getActualTypeArgument(Type, int)} instead.
+	 */
+	@Deprecated
 	public static Type getActualTypeArgument(Type type)
 	{
-		Utils.checkNotNull(type, "type");
+		return getActualTypeArgument(type, 0);
+	}
+	
+	public static Type getActualTypeArgument(Type type, int argIndex)
+	{
+		checkNotNull(type, "type");
+		checkTrue(type instanceof ParameterizedType, "type must be a ParameterizedType: ", type);
 		
 		ParameterizedType paramType = (ParameterizedType) type;
 		
 		Type[] typeArgs = paramType.getActualTypeArguments();
 		
-		Utils.checkTrue(typeArgs.length == 1, "type must be a ParameterizedType with one actual type argument: ", type);
+		checkTrue(argIndex >= 0, "argIndex must be non-negative: ", argIndex);
+		checkTrue(argIndex < typeArgs.length, "argIndex must be less than the number of type parameters: ", argIndex);
 		
-		return typeArgs[0];
+		return typeArgs[argIndex];
 	}
 	
 	public static Type getResolvedSuperclass(Type type)
 	{
-		Utils.checkNotNull(type, "type");
+		checkNotNull(type, "type");
 		
 		Class<?> rawType = getErasedReferenceType(type);
 		Type supertype = rawType.getGenericSuperclass();
@@ -304,7 +414,7 @@ public final class TypeUtils
 	
 	public static Type[] getResolvedInterfaces(Type type)
 	{
-		Utils.checkNotNull(type, "type");
+		checkNotNull(type, "type");
 		
 		Class<?> rawType = getErasedReferenceType(type);
 		Type[] interfaces = rawType.getGenericInterfaces();
@@ -318,6 +428,18 @@ public final class TypeUtils
 		return resolvedInterfaces;
 	}
 	
+	public static <T> Type getResolvedSupertype(Class<? extends T> type, Class<T> rawSupertype)
+	{
+		Type resolvedSupertype = getResolvedSupertype((Type) type, rawSupertype);
+		
+		if (resolvedSupertype == null)
+		{
+			throw new IllegalStateException("type must extend or implement supertype: " + type.getName());
+		}
+
+		return resolvedSupertype;
+	}
+	
 	public static String toString(Type type)
 	{
 		return toString(type, ClassSerializers.QUALIFIED);
@@ -325,39 +447,16 @@ public final class TypeUtils
 	
 	public static String toString(Type type, ClassSerializer serializer)
 	{
-		if (type instanceof Class<?>)
+		if (type == null)
 		{
-			Class<?> klass = (Class<?>) type;
-			
-			if (klass.isArray())
-			{
-				return toString(klass.getComponentType(), serializer) + "[]";
-			}
-			
-			return serializer.toString(klass);
+			return String.valueOf(type);
 		}
 		
-		if (type instanceof TypeVariable<?>)
-		{
-			return DefaultTypeVariable.toString((TypeVariable<?>) type, serializer);
-		}
-		
-		if (type instanceof GenericArrayType)
-		{
-			return DefaultGenericArrayType.toString((GenericArrayType) type, serializer);
-		}
-		
-		if (type instanceof ParameterizedType)
-		{
-			return DefaultParameterizedType.toString((ParameterizedType) type, serializer);
-		}
+		SerializingTypeVisitor visitor = new SerializingTypeVisitor(serializer);
 		
-		if (type instanceof WildcardType)
-		{
-			return DefaultWildcardType.toString((WildcardType) type, serializer);
-		}
+		accept(type, visitor);
 		
-		return String.valueOf(type);
+		return visitor.toString();
 	}
 
 	public static String toUnqualifiedString(Type type)
@@ -370,23 +469,6 @@ public final class TypeUtils
 		return toString(type, ClassSerializers.SIMPLE);
 	}
 	
-	// package methods --------------------------------------------------------
-	
-	static StringBuilder appendBounds(StringBuilder builder, Type[] bounds, ClassSerializer serializer)
-	{
-		for (int i = 0; i < bounds.length; i++)
-		{
-			if (i > 0)
-			{
-				builder.append(" & ");
-			}
-			
-			builder.append(toString(bounds[i], serializer));
-		}
-		
-		return builder;
-	}
-	
 	// private methods --------------------------------------------------------
 	
 	private static void putPrimitiveSubtypes(Map<Class<?>, Set<Class<?>>> subtypesByPrimitive, Class<?> primitiveType,
@@ -414,6 +496,19 @@ public final class TypeUtils
 		return supertype.isAssignableFrom(type);
 	}
 	
+	private static boolean isClassAssignableToWildcardType(Class<?> supertype, WildcardType type)
+	{
+		for (Type upperBound : type.getUpperBounds())
+		{
+			if (!isAssignable(supertype, upperBound))
+			{
+				return false;
+			}
+		}
+		
+		return true;
+	}
+	
 	private static boolean isParameterizedTypeAssignable(ParameterizedType supertype, ParameterizedType type)
 	{
 		Type rawSupertype = supertype.getRawType();
@@ -632,7 +727,7 @@ public final class TypeUtils
 	
 	private static <K, V> Map<K, V> normalize(Map<K, V> map)
 	{
-		// TODO: will this cause an infinite look with recursive bounds?
+		// TODO: will this cause an infinite loop with recursive bounds?
 		
 		for (Entry<K, V> entry : map.entrySet())
 		{
@@ -649,4 +744,37 @@ public final class TypeUtils
 		
 		return map;
 	}
+	
+	private static Type getResolvedSupertype(Type type, Class<?> rawSupertype)
+	{
+		Class<?> rawCurrentType = TypeUtils.getErasedReferenceType(type);
+		
+		if (rawSupertype.equals(rawCurrentType))
+		{
+			return type;
+		}
+		
+		// try interfaces
+		
+		for (Type interfaceType : TypeUtils.getResolvedInterfaces(type))
+		{
+			Type resolvedType = getResolvedSupertype(interfaceType, rawSupertype);
+			
+			if (resolvedType != null)
+			{
+				return resolvedType;
+			}
+		}
+		
+		// try superclass
+		
+		Type supertype = TypeUtils.getResolvedSuperclass(type);
+		
+		if (supertype == null)
+		{
+			return null;
+		}
+		
+		return getResolvedSupertype(supertype, rawSupertype);
+	}
 }
diff --git a/src/main/java/com/googlecode/jtype/TypeVisitor.java b/src/main/java/com/googlecode/jtype/TypeVisitor.java
new file mode 100644
index 0000000..09e465d
--- /dev/null
+++ b/src/main/java/com/googlecode/jtype/TypeVisitor.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2009 IIZUKA Software Technologies Ltd
+ * 
+ * Licensed 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 com.googlecode.jtype;
+
+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;
+
+/**
+ * 
+ * 
+ * @author Mark Hobson
+ * @version $Id: TypeVisitor.java 111 2011-11-23 17:25:02Z markhobson $
+ */
+public interface TypeVisitor
+{
+	void visit(Class<?> type);
+
+	// TODO: rename to visit
+	<D extends GenericDeclaration> boolean beginVisit(TypeVariable<D> type);
+	
+	void visitTypeVariableBound(Type bound, int index);
+	
+	<D extends GenericDeclaration> void endVisit(TypeVariable<D> type);
+	
+	void visit(GenericArrayType type);
+	
+	// TODO: rename to visit
+	boolean beginVisit(ParameterizedType type);
+	
+	void visitActualTypeArgument(Type type, int index);
+	
+	void endVisit(ParameterizedType type);
+	
+	// TODO: rename to visit
+	boolean beginVisit(WildcardType type);
+	
+	void visitUpperBound(Type bound, int index);
+	
+	void visitLowerBound(Type bound, int index);
+
+	void endVisit(WildcardType type);
+}
diff --git a/src/main/java/com/googlecode/jtype/Types.java b/src/main/java/com/googlecode/jtype/Types.java
index ea64d23..2c718cc 100644
--- a/src/main/java/com/googlecode/jtype/Types.java
+++ b/src/main/java/com/googlecode/jtype/Types.java
@@ -15,6 +15,8 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.Utils.checkNotNull;
+
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.GenericDeclaration;
 import java.lang.reflect.MalformedParameterizedTypeException;
@@ -22,6 +24,7 @@ import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
 import java.lang.reflect.TypeVariable;
 import java.lang.reflect.WildcardType;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
@@ -33,13 +36,15 @@ import java.util.regex.Pattern;
  * Factory for creating types.
  * 
  * @author Mark Hobson
- * @version $Id: Types.java 37 2009-07-24 11:27:39Z markhobson $
+ * @version $Id: Types.java 110 2011-11-23 17:19:43Z markhobson $
  * @see Type
  */
 public final class Types
 {
 	// constants --------------------------------------------------------------
 	
+	private static final WildcardType UNBOUNDED_WILDCARD_TYPE = wildcardType(null, null);
+	
 	private static final Pattern ARRAY_PATTERN = Pattern.compile("\\[\\s*\\]$");
 
 	private static final Pattern UPPER_BOUND_PATTERN = Pattern.compile("^\\?\\s+extends\\s+");
@@ -95,7 +100,8 @@ public final class Types
 	 *            the actual type arguments
 	 * @return the parameterized type
 	 * @throws MalformedParameterizedTypeException
-	 *             if the number of actual type arguments differs from those defined on the raw type
+	 *             if the raw type is not a parameterized type or the number of actual type arguments differs from those
+	 *             defined on the raw type
 	 */
 	public static ParameterizedType parameterizedType(Class<?> rawType, Type... actualTypeArguments)
 	{
@@ -103,13 +109,34 @@ public final class Types
 	}
 	
 	/**
+	 * Creates a parameterized type for the specified raw type with unbounded wildcard actual type arguments.
+	 * 
+	 * @param rawType
+	 *            the raw type
+	 * @return the parameterized type
+	 * @throws MalformedParameterizedTypeException
+	 *             if the raw type is not a parameterized type
+	 */
+	public static ParameterizedType unboundedParameterizedType(Class<?> rawType)
+	{
+		checkNotNull(rawType, "rawType");
+		
+		int typeParameterCount = rawType.getTypeParameters().length;
+		
+		Type[] actualTypeArguments = new Type[typeParameterCount];
+		Arrays.fill(actualTypeArguments, unboundedWildcardType());
+		
+		return parameterizedType(rawType, actualTypeArguments);
+	}
+
+	/**
 	 * Creates an unbounded wildcard type.
 	 * 
 	 * @return the wildcard type
 	 */
 	public static WildcardType unboundedWildcardType()
 	{
-		return wildcardType(null, null);
+		return UNBOUNDED_WILDCARD_TYPE;
 	}
 	
 	/**
@@ -121,7 +148,7 @@ public final class Types
 	 */
 	public static WildcardType upperBoundedWildcardType(Type upperBound)
 	{
-		Utils.checkNotNull(upperBound, "upperBound");
+		checkNotNull(upperBound, "upperBound");
 		
 		return wildcardType(new Type[] {upperBound}, null);
 	}
@@ -135,7 +162,7 @@ public final class Types
 	 */
 	public static WildcardType lowerBoundedWildcardType(Type lowerBound)
 	{
-		Utils.checkNotNull(lowerBound, "lowerBound");
+		checkNotNull(lowerBound, "lowerBound");
 		
 		return wildcardType(null, new Type[] {lowerBound});
 	}
@@ -149,7 +176,7 @@ public final class Types
 	 */
 	public static Type valueOf(String typeName)
 	{
-		return valueOf(typeName, Collections.<String>emptySet());
+		return valueOf(typeName, (Set<String>) null);
 	}
 	
 	/**
@@ -165,6 +192,27 @@ public final class Types
 	 */
 	public static Type valueOf(String typeName, Set<String> imports)
 	{
+		checkNotNull(typeName, "typeName");
+		
+		Map<String, String> importMap = createImportMap(imports);
+		
+		return valueOf(typeName, importMap);
+	}
+	
+	// private methods --------------------------------------------------------
+	
+	private static WildcardType wildcardType(Type[] upperBounds, Type[] lowerBounds)
+	{
+		return new DefaultWildcardType(upperBounds, lowerBounds);
+	}
+	
+	private static Map<String, String> createImportMap(Set<String> imports)
+	{
+		if (imports == null)
+		{
+			return Collections.emptyMap();
+		}
+		
 		Map<String, String> importMap = new HashMap<String, String>();
 		
 		for (String className : imports)
@@ -180,14 +228,7 @@ public final class Types
 			importMap.put(simpleClassName, className);
 		}
 		
-		return valueOf(typeName, importMap);
-	}
-	
-	// private methods --------------------------------------------------------
-	
-	private static WildcardType wildcardType(Type[] upperBounds, Type[] lowerBounds)
-	{
-		return new DefaultWildcardType(upperBounds, lowerBounds);
+		return importMap;
 	}
 	
 	private static Type valueOf(String typeName, Map<String, String> imports)
@@ -248,7 +289,7 @@ public final class Types
 	
 	private static Class<?> parseClass(String className, Map<String, String> imports)
 	{
-		Class<?> klass = parseClass(className);
+		Class<?> klass = ClassUtils.valueOf(className);
 		
 		if (klass != null)
 		{
@@ -259,7 +300,7 @@ public final class Types
 		{
 			String qualifiedClassName = imports.get(className);
 			
-			klass = parseClass(qualifiedClassName);
+			klass = ClassUtils.valueOf(qualifiedClassName);
 			
 			if (klass != null)
 			{
@@ -270,20 +311,6 @@ public final class Types
 		throw new IllegalArgumentException("Class not found: " + className);
 	}
 	
-	private static Class<?> parseClass(String className)
-	{
-		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-		
-		try
-		{
-			return Class.forName(className, true, classLoader);
-		}
-		catch (ClassNotFoundException exception)
-		{
-			return null;
-		}
-	}
-	
 	private static WildcardType parseWildcardType(String typeName, Map<String, String> imports)
 	{
 		Type[] upperBounds;
diff --git a/src/test/java/com/googlecode/jtype/ClassUtilsTest.java b/src/test/java/com/googlecode/jtype/ClassUtilsTest.java
index 4332495..e3e8d99 100644
--- a/src/test/java/com/googlecode/jtype/ClassUtilsTest.java
+++ b/src/test/java/com/googlecode/jtype/ClassUtilsTest.java
@@ -23,7 +23,7 @@ import org.junit.Test;
  * Tests {@code ClassUtils}.
  * 
  * @author Mark Hobson
- * @version $Id: ClassUtilsTest.java 36 2009-07-03 11:18:03Z markhobson $
+ * @version $Id: ClassUtilsTest.java 82 2010-11-01 11:22:10Z markhobson $
  * @see ClassUtils
  */
 public class ClassUtilsTest
@@ -83,4 +83,58 @@ public class ClassUtilsTest
 	{
 		assertEquals("E", ClassUtils.getSimpleClassName("a.b.C$D$E"));
 	}
+	
+	@Test
+	public void valueOfWithBooleanPrimitive()
+	{
+		assertEquals(Boolean.TYPE, ClassUtils.valueOf("boolean"));
+	}
+	
+	@Test
+	public void valueOfWithBytePrimitive()
+	{
+		assertEquals(Byte.TYPE, ClassUtils.valueOf("byte"));
+	}
+	
+	@Test
+	public void valueOfWithCharPrimitive()
+	{
+		assertEquals(Character.TYPE, ClassUtils.valueOf("char"));
+	}
+	
+	@Test
+	public void valueOfWithDoublePrimitive()
+	{
+		assertEquals(Double.TYPE, ClassUtils.valueOf("double"));
+	}
+	
+	@Test
+	public void valueOfWithFloatPrimitive()
+	{
+		assertEquals(Float.TYPE, ClassUtils.valueOf("float"));
+	}
+	
+	@Test
+	public void valueOfWithIntPrimitive()
+	{
+		assertEquals(Integer.TYPE, ClassUtils.valueOf("int"));
+	}
+	
+	@Test
+	public void valueOfWithLongPrimitive()
+	{
+		assertEquals(Long.TYPE, ClassUtils.valueOf("long"));
+	}
+	
+	@Test
+	public void valueOfWithShortPrimitive()
+	{
+		assertEquals(Short.TYPE, ClassUtils.valueOf("short"));
+	}
+	
+	@Test
+	public void valueOfWithClass()
+	{
+		assertEquals(Integer.class, ClassUtils.valueOf("java.lang.Integer"));
+	}
 }
diff --git a/src/test/java/com/googlecode/jtype/DefaultGenericArrayTypeTest.java b/src/test/java/com/googlecode/jtype/DefaultGenericArrayTypeTest.java
index 23f5ff5..49cc752 100644
--- a/src/test/java/com/googlecode/jtype/DefaultGenericArrayTypeTest.java
+++ b/src/test/java/com/googlecode/jtype/DefaultGenericArrayTypeTest.java
@@ -15,9 +15,11 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.test.SerializableAssert.assertSerializable;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
+import java.io.IOException;
 import java.lang.reflect.GenericArrayType;
 
 import org.junit.Test;
@@ -26,7 +28,7 @@ import org.junit.Test;
  * Tests {@code DefaultGenericArrayType}.
  * 
  * @author Mark Hobson
- * @version $Id: DefaultGenericArrayTypeTest.java 2 2009-02-02 22:28:39Z markhobson $
+ * @version $Id: DefaultGenericArrayTypeTest.java 115 2011-11-25 18:17:40Z markhobson at gmail.com $
  * @see DefaultGenericArrayType
  */
 public class DefaultGenericArrayTypeTest
@@ -98,4 +100,12 @@ public class DefaultGenericArrayTypeTest
 		
 		assertEquals("java.lang.Integer[]", genericArrayType.toString());
 	}
+	
+	@Test
+	public void serializable() throws IOException, ClassNotFoundException
+	{
+		GenericArrayType type = new DefaultGenericArrayType(Integer.class);
+		
+		assertSerializable(type);
+	}
 }
diff --git a/src/test/java/com/googlecode/jtype/DefaultParameterizedTypeTest.java b/src/test/java/com/googlecode/jtype/DefaultParameterizedTypeTest.java
index 1942331..c60d77f 100644
--- a/src/test/java/com/googlecode/jtype/DefaultParameterizedTypeTest.java
+++ b/src/test/java/com/googlecode/jtype/DefaultParameterizedTypeTest.java
@@ -15,11 +15,13 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.test.SerializableAssert.assertSerializable;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 
+import java.io.IOException;
 import java.lang.reflect.MalformedParameterizedTypeException;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
@@ -33,7 +35,7 @@ import org.junit.Test;
  * Tests {@code DefaultParameterizedType}.
  * 
  * @author Mark Hobson
- * @version $Id: DefaultParameterizedTypeTest.java 35 2009-07-03 10:14:37Z markhobson $
+ * @version $Id: DefaultParameterizedTypeTest.java 115 2011-11-25 18:17:40Z markhobson at gmail.com $
  * @see DefaultParameterizedType
  */
 public class DefaultParameterizedTypeTest
@@ -76,14 +78,10 @@ public class DefaultParameterizedTypeTest
 		}
 	}
 	
-	@Test
-	public void constructorWithNullActualTypeArguments()
+	@Test(expected = MalformedParameterizedTypeException.class)
+	public void constructorWithUnparameterizedRawType()
 	{
-		ParameterizedType type = new DefaultParameterizedType(null, String.class, null);
-		
-		assertNull(type.getOwnerType());
-		assertEquals(String.class, type.getRawType());
-		assertArrayEquals(new Type[0], type.getActualTypeArguments());
+		new DefaultParameterizedType(null, String.class, null);
 	}
 	
 	@Test(expected = MalformedParameterizedTypeException.class)
@@ -114,15 +112,6 @@ public class DefaultParameterizedTypeTest
 	}
 	
 	@Test
-	public void hashCodeWithNullActualTypeArguments()
-	{
-		ParameterizedType type1 = new DefaultParameterizedType(null, String.class, null);
-		ParameterizedType type2 = new DefaultParameterizedType(null, String.class, null);
-		
-		assertEquals(type1.hashCode(), type2.hashCode());
-	}
-	
-	@Test
 	public void equalsWhenEqual()
 	{
 		ParameterizedType type1 = new DefaultParameterizedType(Map.class, Map.Entry.class,
@@ -152,15 +141,6 @@ public class DefaultParameterizedTypeTest
 	}
 	
 	@Test
-	public void equalsWithNullActualTypeArguments()
-	{
-		ParameterizedType type1 = new DefaultParameterizedType(null, String.class, null);
-		ParameterizedType type2 = new DefaultParameterizedType(null, String.class, null);
-		
-		assertEquals(type1, type2);
-	}
-	
-	@Test
 	public void equalsWithUnequalOwnerType()
 	{
 		ParameterizedType type1 = new DefaultParameterizedType(Map.class, Map.Entry.class,
@@ -196,15 +176,7 @@ public class DefaultParameterizedTypeTest
 		ParameterizedType type = new DefaultParameterizedType(Map.class, Map.Entry.class,
 			new Type[] {String.class, Integer.class});
 		
-		assertEquals("java.util.Map$Entry<java.lang.String,java.lang.Integer>", type.toString());
-	}
-	
-	@Test
-	public void toStringWithRawType()
-	{
-		ParameterizedType type = new DefaultParameterizedType(null, String.class, null);
-		
-		assertEquals("java.lang.String", type.toString());
+		assertEquals("java.util.Map.java.util.Map$Entry<java.lang.String, java.lang.Integer>", type.toString());
 	}
 	
 	@Test
@@ -221,6 +193,15 @@ public class DefaultParameterizedTypeTest
 		ParameterizedType type = new DefaultParameterizedType(null, Map.class,
 			new Type[] {String.class, Integer.class});
 		
-		assertEquals("java.util.Map<java.lang.String,java.lang.Integer>", type.toString());
+		assertEquals("java.util.Map<java.lang.String, java.lang.Integer>", type.toString());
+	}
+	
+	@Test
+	public void serializable() throws IOException, ClassNotFoundException
+	{
+		ParameterizedType type = new DefaultParameterizedType(Map.class, Map.Entry.class,
+			new Type[] {String.class, Integer.class});
+		
+		assertSerializable(type);
 	}
 }
diff --git a/src/test/java/com/googlecode/jtype/DefaultTypeVariableTest.java b/src/test/java/com/googlecode/jtype/DefaultTypeVariableTest.java
index 34dfbe3..253ba2f 100644
--- a/src/test/java/com/googlecode/jtype/DefaultTypeVariableTest.java
+++ b/src/test/java/com/googlecode/jtype/DefaultTypeVariableTest.java
@@ -15,10 +15,12 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.test.SerializableAssert.assertSerializable;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
+import java.io.IOException;
 import java.io.Serializable;
 import java.lang.reflect.Constructor;
 import java.lang.reflect.GenericDeclaration;
@@ -35,7 +37,7 @@ import org.junit.Test;
  * Tests {@code DefaultTypeVariable}.
  * 
  * @author Mark Hobson
- * @version $Id: DefaultTypeVariableTest.java 42 2009-10-02 14:54:54Z markhobson $
+ * @version $Id: DefaultTypeVariableTest.java 115 2011-11-25 18:17:40Z markhobson at gmail.com $
  * @see DefaultTypeVariable
  */
 public class DefaultTypeVariableTest
@@ -284,6 +286,15 @@ public class DefaultTypeVariableTest
 		assertEquals("T extends java.lang.Number & java.lang.Comparable", typeVariable.toString());
 	}
 	
+	@Test
+	public void serializable() throws IOException, ClassNotFoundException
+	{
+		TypeVariable<Class<?>> type = new DefaultTypeVariable<Class<?>>(getClass(), "T",
+			new Type[] {Number.class, Comparable.class});
+		
+		assertSerializable(type);
+	}
+
 	// private methods --------------------------------------------------------
 	
 	private static <D extends GenericDeclaration> void assertConstructor(D declaration, String name, Type... bounds)
diff --git a/src/test/java/com/googlecode/jtype/DefaultWildcardTypeTest.java b/src/test/java/com/googlecode/jtype/DefaultWildcardTypeTest.java
index ae79af0..8089da5 100644
--- a/src/test/java/com/googlecode/jtype/DefaultWildcardTypeTest.java
+++ b/src/test/java/com/googlecode/jtype/DefaultWildcardTypeTest.java
@@ -15,10 +15,12 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.test.SerializableAssert.assertSerializable;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 
+import java.io.IOException;
 import java.lang.reflect.Type;
 import java.lang.reflect.WildcardType;
 
@@ -28,7 +30,7 @@ import org.junit.Test;
  * Tests {@code DefaultWildcardType}.
  * 
  * @author Mark Hobson
- * @version $Id: DefaultWildcardTypeTest.java 2 2009-02-02 22:28:39Z markhobson $
+ * @version $Id: DefaultWildcardTypeTest.java 115 2011-11-25 18:17:40Z markhobson at gmail.com $
  * @see DefaultWildcardType
  */
 public class DefaultWildcardTypeTest
@@ -169,4 +171,12 @@ public class DefaultWildcardTypeTest
 		
 		assertEquals("? super java.lang.Integer & java.lang.Number", wildcardType.toString());
 	}
+
+	@Test
+	public void serializable() throws IOException, ClassNotFoundException
+	{
+		WildcardType type = new DefaultWildcardType(null, new Type[] {Integer.class});
+		
+		assertSerializable(type);
+	}
 }
diff --git a/src/test/java/com/googlecode/jtype/GenericTest.java b/src/test/java/com/googlecode/jtype/GenericTest.java
index dc579c7..958a05d 100644
--- a/src/test/java/com/googlecode/jtype/GenericTest.java
+++ b/src/test/java/com/googlecode/jtype/GenericTest.java
@@ -15,13 +15,18 @@
  */
 package com.googlecode.jtype;
 
+import static com.googlecode.jtype.test.SerializableAssert.assertSerializable;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
+import java.io.IOException;
+import java.io.Serializable;
 import java.lang.reflect.GenericArrayType;
 import java.lang.reflect.GenericDeclaration;
 import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
 import java.util.List;
 import java.util.Map;
 
@@ -32,11 +37,13 @@ import org.junit.Test;
  * Tests {@code Generic}.
  * 
  * @author Mark Hobson
- * @version $Id: GenericTest.java 31 2009-04-30 15:02:29Z markhobson $
+ * @version $Id: GenericTest.java 115 2011-11-25 18:17:40Z markhobson at gmail.com $
  * @see Generic
  */
-public class GenericTest
+public class GenericTest implements Serializable
 {
+	// test is serializable with transient fields for serializableWhenSubclassed
+	
 	// classes ----------------------------------------------------------------
 	
 	private static class GenericSubclass<T> extends Generic<T>
@@ -46,7 +53,7 @@ public class GenericTest
 	
 	// fields -----------------------------------------------------------------
 	
-	private GenericDeclaration declaration;
+	private transient GenericDeclaration declaration;
 	
 	// public methods ---------------------------------------------------------
 	
@@ -108,6 +115,32 @@ public class GenericTest
 	}
 	
 	@Test
+	public void getWithClassIsSerializable() throws IOException, ClassNotFoundException
+	{
+		assertSerializable(Generic.get(String.class));
+	}
+	
+	@Test
+	public void getWithRawTypeAndActualTypeArguments()
+	{
+		ParameterizedType parameterizedType = Types.parameterizedType(List.class, String.class);
+		
+		assertEquals(parameterizedType, Generic.get(List.class, String.class).getType());
+	}
+	
+	@Test
+	public void getWithRawTypeAndNullActualTypeArguments()
+	{
+		assertEquals(String.class, Generic.get(String.class, (Type[]) null).getType());
+	}
+	
+	@Test
+	public void getWithRawTypeAndEmptyActualTypeArguments()
+	{
+		assertEquals(String.class, Generic.get(String.class, new Type[0]).getType());
+	}
+	
+	@Test
 	public void getWithParameterizedType()
 	{
 		ParameterizedType parameterizedType = Types.parameterizedType(List.class, String.class);
@@ -154,6 +187,66 @@ public class GenericTest
 	}
 	
 	@Test
+	public void getWithObjectClassIsCached()
+	{
+		assertSame(Generic.get(Object.class), Generic.get(Object.class));
+	}
+	
+	@Test
+	public void getWithBooleanClassIsCached()
+	{
+		assertSame(Generic.get(Boolean.class), Generic.get(Boolean.class));
+	}
+	
+	@Test
+	public void getWithByteClassIsCached()
+	{
+		assertSame(Generic.get(Byte.class), Generic.get(Byte.class));
+	}
+	
+	@Test
+	public void getWithCharacterClassIsCached()
+	{
+		assertSame(Generic.get(Character.class), Generic.get(Character.class));
+	}
+	
+	@Test
+	public void getWithDoubleClassIsCached()
+	{
+		assertSame(Generic.get(Double.class), Generic.get(Double.class));
+	}
+	
+	@Test
+	public void getWithFloatClassIsCached()
+	{
+		assertSame(Generic.get(Float.class), Generic.get(Float.class));
+	}
+	
+	@Test
+	public void getWithIntegerClassIsCached()
+	{
+		assertSame(Generic.get(Integer.class), Generic.get(Integer.class));
+	}
+	
+	@Test
+	public void getWithLongClassIsCached()
+	{
+		assertSame(Generic.get(Long.class), Generic.get(Long.class));
+	}
+	
+	@Test
+	public void getWithShortClassIsCached()
+	{
+		assertSame(Generic.get(Short.class), Generic.get(Short.class));
+	}
+	
+	@Test
+	public void getWithStringClassIsCached()
+	{
+		assertSame(Generic.get(String.class), Generic.get(String.class));
+	}
+	
+	@Test
 	public void valueOfWithClass()
 	{
 		assertEquals(String.class, Generic.valueOf("java.lang.String").getType());
@@ -265,4 +358,10 @@ public class GenericTest
 	{
 		assertEquals("List<String>", new Generic<List<String>>() {/**/}.toUnqualifiedString());
 	}
+
+	@Test
+	public void serializableWhenSubclassed() throws IOException, ClassNotFoundException
+	{
+		assertSerializable(new Generic<String>() {/**/});
+	}
 }
diff --git a/src/test/java/com/googlecode/jtype/GenericsTest.java b/src/test/java/com/googlecode/jtype/GenericsTest.java
index edc1ac9..01e98b3 100644
--- a/src/test/java/com/googlecode/jtype/GenericsTest.java
+++ b/src/test/java/com/googlecode/jtype/GenericsTest.java
@@ -35,7 +35,7 @@ import org.junit.Test;
  * Tests {@code Generics}.
  * 
  * @author Mark Hobson
- * @version $Id: GenericsTest.java 2 2009-02-02 22:28:39Z markhobson $
+ * @version $Id: GenericsTest.java 86 2010-11-05 13:23:09Z markhobson $
  * @see Generics
  */
 public class GenericsTest
@@ -43,68 +43,158 @@ public class GenericsTest
 	// tests ------------------------------------------------------------------
 	
 	@Test
+	public void comparable()
+	{
+		assertEquals(new Generic<Comparable<?>>() {/**/}, Generics.comparable());
+	}
+	
+	@Test
+	public void comparableWithClass()
+	{
+		assertEquals(new Generic<Comparable<String>>() {/**/}, Generics.comparable(String.class));
+	}
+	
+	@Test
 	public void comparator()
 	{
+		assertEquals(new Generic<Comparator<?>>() {/**/}, Generics.comparator());
+	}
+	
+	@Test
+	public void comparatorWithClass()
+	{
 		assertEquals(new Generic<Comparator<String>>() {/**/}, Generics.comparator(String.class));
 	}
 	
 	@Test
 	public void enumeration()
 	{
+		assertEquals(new Generic<Enumeration<?>>() {/**/}, Generics.enumeration());
+	}
+	
+	@Test
+	public void enumerationWithClass()
+	{
 		assertEquals(new Generic<Enumeration<String>>() {/**/}, Generics.enumeration(String.class));
 	}
 	
 	@Test
+	public void iterable()
+	{
+		assertEquals(new Generic<Iterable<?>>() {/**/}, Generics.iterable());
+	}
+	
+	@Test
+	public void iterableWithClass()
+	{
+		assertEquals(new Generic<Iterable<String>>() {/**/}, Generics.iterable(String.class));
+	}
+	
+	@Test
 	public void iterator()
 	{
+		assertEquals(new Generic<Iterator<?>>() {/**/}, Generics.iterator());
+	}
+	
+	@Test
+	public void iteratorWithClass()
+	{
 		assertEquals(new Generic<Iterator<String>>() {/**/}, Generics.iterator(String.class));
 	}
 	
 	@Test
 	public void listIterator()
 	{
+		assertEquals(new Generic<ListIterator<?>>() {/**/}, Generics.listIterator());
+	}
+	
+	@Test
+	public void listIteratorWithClass()
+	{
 		assertEquals(new Generic<ListIterator<String>>() {/**/}, Generics.listIterator(String.class));
 	}
 	
 	@Test
 	public void collection()
 	{
+		assertEquals(new Generic<Collection<?>>() {/**/}, Generics.collection());
+	}
+	
+	@Test
+	public void collectionWithClass()
+	{
 		assertEquals(new Generic<Collection<String>>() {/**/}, Generics.collection(String.class));
 	}
 	
 	@Test
 	public void set()
 	{
+		assertEquals(new Generic<Set<?>>() {/**/}, Generics.set());
+	}
+	
+	@Test
+	public void setWithClass()
+	{
 		assertEquals(new Generic<Set<String>>() {/**/}, Generics.set(String.class));
 	}
 	
 	@Test
 	public void sortedSet()
 	{
+		assertEquals(new Generic<SortedSet<?>>() {/**/}, Generics.sortedSet());
+	}
+	
+	@Test
+	public void sortedSetWithClass()
+	{
 		assertEquals(new Generic<SortedSet<String>>() {/**/}, Generics.sortedSet(String.class));
 	}
 	
 	@Test
 	public void list()
 	{
+		assertEquals(new Generic<List<?>>() {/**/}, Generics.list());
+	}
+	
+	@Test
+	public void listWithClass()
+	{
 		assertEquals(new Generic<List<String>>() {/**/}, Generics.list(String.class));
 	}
 	
 	@Test
 	public void map()
 	{
+		assertEquals(new Generic<Map<?, ?>>() {/**/}, Generics.map());
+	}
+	
+	@Test
+	public void mapWithClasses()
+	{
 		assertEquals(new Generic<Map<String, Integer>>() {/**/}, Generics.map(String.class, Integer.class));
 	}
 	
 	@Test
 	public void sortedMap()
 	{
+		assertEquals(new Generic<SortedMap<?, ?>>() {/**/}, Generics.sortedMap());
+	}
+	
+	@Test
+	public void sortedMapWithClasses()
+	{
 		assertEquals(new Generic<SortedMap<String, Integer>>() {/**/}, Generics.sortedMap(String.class, Integer.class));
 	}
 	
 	@Test
 	public void queue()
 	{
+		assertEquals(new Generic<Queue<?>>() {/**/}, Generics.queue());
+	}
+	
+	@Test
+	public void queueWithClass()
+	{
 		assertEquals(new Generic<Queue<String>>() {/**/}, Generics.queue(String.class));
 	}
 }
diff --git a/src/test/java/com/googlecode/jtype/TypeUtilsGetResolvedSupertypeTest.java b/src/test/java/com/googlecode/jtype/TypeUtilsGetResolvedSupertypeTest.java
new file mode 100644
index 0000000..0005212
--- /dev/null
+++ b/src/test/java/com/googlecode/jtype/TypeUtilsGetResolvedSupertypeTest.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2011 IIZUKA Software Technologies Ltd
+ * 
+ * Licensed 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 com.googlecode.jtype;
+
+import static com.googlecode.jtype.Types.parameterizedType;
+import static com.googlecode.jtype.Types.typeVariable;
+import static org.junit.Assert.assertEquals;
+
+import java.lang.reflect.Type;
+import java.util.Set;
+
+import com.googlecode.jtype.test.AbstractTypeTest;
+
+import org.junit.Test;
+
+/**
+ * Tests {@code TypeUtils.getResolvedSupertype}.
+ * 
+ * @author Mark Hobson
+ * @version $Id: TypeUtilsGetResolvedSupertypeTest.java 115 2011-11-25 18:17:40Z markhobson at gmail.com $
+ * @see TypeUtils#getResolvedSupertype(Class, Class)
+ */
+public class TypeUtilsGetResolvedSupertypeTest extends AbstractTypeTest
+{
+	// types ------------------------------------------------------------------
+	
+	private static class DummyClass
+	{
+		// simple subtype
+	}
+	
+	private static interface IFake<T>
+	{
+		// simple subtype
+	}
+	
+	private static class Fake implements IFake<DummyClass>
+	{
+		// simple subtype
+	}
+	
+	private static class ParameterizedTypeFake implements IFake<IFake<?>>
+	{
+		// simple subtype
+	}
+	
+	private static class TypeVariableFake<T> implements IFake<T>
+	{
+		// simple subtype
+	}
+	
+	private static class SubFake extends Fake
+	{
+		// simple subtype
+	}
+	
+	private static interface IFake2<T> extends IFake<T>
+	{
+		// simple subtype
+	}
+	
+	private static class Fake2 implements IFake2<DummyClass>
+	{
+		// simple subtype
+	}
+	
+	// AbstractTypeTest methods -----------------------------------------------
+	
+	/**
+	 * {@inheritDoc}
+	 */
+	@Override
+	protected void addImports(Set<Class<?>> imports)
+	{
+		imports.add(DummyClass.class);
+		imports.add(IFake.class);
+	}
+	
+	// tests ------------------------------------------------------------------
+	
+	@Test
+	public void getResolvedSupertypeWithClassImplementation()
+	{
+		Type actual = TypeUtils.getResolvedSupertype(Fake.class, IFake.class);
+		
+		assertEquals(type("IFake<DummyClass>"), actual);
+	}
+	
+	@Test
+	public void getResolvedSupertypeWithParameterizedTypeImplementation()
+	{
+		Type actual = TypeUtils.getResolvedSupertype(ParameterizedTypeFake.class, IFake.class);
+		
+		assertEquals(type("IFake<IFake<?>>"), actual);
+	}
+	
+	@Test
+	public void getResolvedSupertypeWithTypeVariableImplementation()
+	{
+		Type actual = TypeUtils.getResolvedSupertype(TypeVariableFake.class, IFake.class);
+		
+		Type expected = parameterizedType(IFake.class, typeVariable(TypeVariableFake.class, "T"));
+		assertEquals(expected, actual);
+	}
+
+	@Test
+	public void getResolvedSupertypeWithSuperclassClassImplementation()
+	{
+		Type actual = TypeUtils.getResolvedSupertype(SubFake.class, IFake.class);
+		
+		assertEquals(type("IFake<DummyClass>"), actual);
+	}
+	
+	@Test
+	public void getResolvedSupertypeWithSuperinterfaceClassImplementation()
+	{
+		Type actual = TypeUtils.getResolvedSupertype(Fake2.class, IFake.class);
+		
+		assertEquals(type("IFake<DummyClass>"), actual);
+	}
+}
diff --git a/src/test/java/com/googlecode/jtype/TypeUtilsTest.java b/src/test/java/com/googlecode/jtype/TypeUtilsTest.java
index 802321a..828f5e4 100644
--- a/src/test/java/com/googlecode/jtype/TypeUtilsTest.java
+++ b/src/test/java/com/googlecode/jtype/TypeUtilsTest.java
@@ -48,11 +48,14 @@ import org.junit.Test;
  * Tests {@code TypeUtils}.
  * 
  * @author Mark Hobson
- * @version $Id: TypeUtilsTest.java 48 2009-10-02 16:57:38Z markhobson $
+ * @version $Id: TypeUtilsTest.java 114 2011-11-25 18:15:48Z markhobson at gmail.com $
  * @see TypeUtils
  */
 public class TypeUtilsTest
 {
+	// TODO: split into smaller test cases
+	// TODO: use local types rather than JDK classes in tests
+	
 	// classes ----------------------------------------------------------------
 	
 	private static class IntegerArrayList extends ArrayList<Integer>
@@ -467,6 +470,30 @@ public class TypeUtilsTest
 	}
 	
 	/**
+	 * Tests that parameterized type upper bounded wildcard type arguments are assignable to wildcard types.
+	 * 
+	 * {@literal List<?> <: List<? extends Number>}
+	 */
+	@Test
+	public void isAssignableWithWildcardParameterizedTypeFromUpperBoundedWildcardParameterizedType()
+	{
+		assertAsymmetricallyAssignable(valueOf("List<?>"), valueOf("List<? extends Number>"));
+	}
+	
+	/**
+	 * Tests that parameterized type lower bounded wildcard type arguments are assignable to wildcard types.
+	 * 
+	 * {@literal List<?> <: List<? super Number>}
+	 */
+	// TODO: fix
+	@Ignore
+	@Test
+	public void isAssignableWithWildcardParameterizedTypeFromLowerBoundedWildcardParameterizedType()
+	{
+		assertAsymmetricallyAssignable(valueOf("List<?>"), valueOf("List<? super Number>"));
+	}
+	
+	/**
 	 * Tests that parameterized type arguments are assignable to wildcard types on their upper bound.
 	 * 
 	 * {@literal List<? extends Number> <: List<Number>}
@@ -503,8 +530,6 @@ public class TypeUtilsTest
 	/**
 	 * {@literal List<? extends Number> <: List<? extends Integer>}
 	 */
-	// TODO: fix
-	@Ignore
 	@Test
 	public void isAssignableWithUpperBoundedWildcardParameterizedTypeFromInBoundsUpperBoundedWildcardParameterizedType()
 	{
@@ -665,6 +690,94 @@ public class TypeUtilsTest
 		assertAssignable(Collection.class, type);
 	}
 	
+	/**
+	 * Tests that unbounded wildcards are assignable to Object.
+	 * 
+	 * {@literal Object <: ?}
+	 */
+	@Test
+	public void isAssignableWithObjectFromUnboundedWildcardType()
+	{
+		assertAssignable(Object.class, Types.unboundedWildcardType());
+	}
+	
+	/**
+	 * Tests that upper bounded wildcards are assignable to their upper bound.
+	 * 
+	 * {@literal Number <: ? extends Number}
+	 */
+	@Test
+	public void isAssignableWithBoundFromUpperBoundedWildcardType()
+	{
+		assertAssignable(Number.class, Types.upperBoundedWildcardType(Number.class));
+	}
+	
+	/**
+	 * Tests that upper bounded wildcards are assignable to supertypes of their upper bound.
+	 * 
+	 * {@literal Number <: ? extends Integer}
+	 */
+	@Test
+	public void isAssignableWithBoundSupertypeFromUpperBoundedWildcardType()
+	{
+		assertAssignable(Number.class, Types.upperBoundedWildcardType(Integer.class));
+	}
+	
+	/**
+	 * Tests that upper bounded wildcards are not assignable to subtypes of their upper bound.
+	 * 
+	 * {@literal Integer !<: ? extends Number}
+	 */
+	@Test
+	public void isAssignableWithBoundSubtypeFromUpperBoundedWildcardType()
+	{
+		assertUnassignable(Integer.class, Types.upperBoundedWildcardType(Number.class));
+	}
+	
+	/**
+	 * Tests that lower bounded wildcards are assignable to Object.
+	 * 
+	 * {@literal Object <: ? super Number}
+	 */
+	@Test
+	public void isAssignableWithObjectFromLowerBoundedWildcardType()
+	{
+		assertAssignable(Object.class, Types.lowerBoundedWildcardType(Number.class));
+	}
+	
+	/**
+	 * Tests that lower bounded wildcards are not assignable to their lower bound.
+	 * 
+	 * {@literal Number !<: ? super Number}
+	 */
+	@Test
+	public void isAssignableWithBoundFromLowerBoundedWildcardType()
+	{
+		assertUnassignable(Number.class, Types.lowerBoundedWildcardType(Number.class));
+	}
+	
+	/**
+	 * Tests that lower bounded wildcards are not assignable to supertypes of their lower bound.
+	 * 
+	 * {@literal Number !<: ? super Integer}
+	 */
+	@Test
+	public void isAssignableWithBoundSupertypeFromLowerBoundedWildcardType()
+	{
+		assertUnassignable(Number.class, Types.lowerBoundedWildcardType(Integer.class));
+	}
+	
+	/**
+	 * Tests that upper bounded wildcards are not assignable to subtypes of their upper bound.
+	 * 
+	 * {@literal Integer <: ? super Number}
+	 */
+	@Test
+	public void isAssignableWithBoundSubtypeFromLowerBoundedWildcardType()
+	{
+		assertUnassignable(Integer.class, Types.lowerBoundedWildcardType(Number.class));
+	}
+	
 	// isInstance tests -------------------------------------------------------
 	
 	@Test(expected = NullPointerException.class)
@@ -948,6 +1061,58 @@ public class TypeUtilsTest
 		assertFalse(TypeUtils.isArray(Types.unboundedWildcardType()));
 	}
 	
+	// isPrimitive tests ------------------------------------------------------
+	
+	@Test
+	public void isPrimitiveWithNull()
+	{
+		assertFalse(TypeUtils.isPrimitive(null));
+	}
+	
+	@Test
+	public void isPrimitiveWithPrimitives()
+	{
+		assertTrue(TypeUtils.isPrimitive(Boolean.TYPE));
+		assertTrue(TypeUtils.isPrimitive(Character.TYPE));
+		assertTrue(TypeUtils.isPrimitive(Byte.TYPE));
+		assertTrue(TypeUtils.isPrimitive(Short.TYPE));
+		assertTrue(TypeUtils.isPrimitive(Integer.TYPE));
+		assertTrue(TypeUtils.isPrimitive(Long.TYPE));
+		assertTrue(TypeUtils.isPrimitive(Float.TYPE));
+		assertTrue(TypeUtils.isPrimitive(Double.TYPE));
+		assertTrue(TypeUtils.isPrimitive(Void.TYPE));
+	}
+	
+	@Test
+	public void isPrimitiveWithClass()
+	{
+		assertFalse(TypeUtils.isPrimitive(Integer.class));
+	}
+	
+	@Test
+	public void isPrimitiveWithTypeVariable()
+	{
+		assertFalse(TypeUtils.isPrimitive(Types.typeVariable(declaration, "T")));
+	}
+	
+	@Test
+	public void isPrimitiveWithGenericArrayType()
+	{
+		assertFalse(TypeUtils.isPrimitive(Types.genericArrayType(Integer.class)));
+	}
+	
+	@Test
+	public void isPrimitiveWithParameterizedType()
+	{
+		assertFalse(TypeUtils.isPrimitive(Types.parameterizedType(List.class, Integer.class)));
+	}
+	
+	@Test
+	public void isPrimitiveWithWildcardType()
+	{
+		assertFalse(TypeUtils.isPrimitive(Types.unboundedWildcardType()));
+	}
+	
 	// getComponentType tests -------------------------------------------------
 	
 	@Test
@@ -1034,6 +1199,50 @@ public class TypeUtilsTest
 			TypeUtils.getArrayType(Types.unboundedWildcardType()));
 	}
 	
+	// isParameterizedType tests ----------------------------------------------
+	
+	@Test(expected = NullPointerException.class)
+	public void isParameterizedTypeWithNullType()
+	{
+		TypeUtils.isParameterizedType(null, List.class);
+	}
+	
+	@Test(expected = NullPointerException.class)
+	public void isParameterizedTypeWithNullRawType()
+	{
+		TypeUtils.isParameterizedType(List.class, null);
+	}
+	
+	@Test
+	public void isParameterizedTypeWithSameRawType()
+	{
+		assertTrue(TypeUtils.isParameterizedType(valueOf("List<Integer>"), List.class));
+	}
+	
+	@Test
+	public void isParameterizedTypeWithSuperRawType()
+	{
+		assertTrue(TypeUtils.isParameterizedType(valueOf("List<Integer>"), Collection.class));
+	}
+	
+	@Test
+	public void isParameterizedTypeWithUnassignableRawType()
+	{
+		assertFalse(TypeUtils.isParameterizedType(valueOf("List<Integer>"), Number.class));
+	}
+	
+	@Test
+	public void isParameterizedTypeWithClass()
+	{
+		assertFalse(TypeUtils.isParameterizedType(List.class, List.class));
+	}
+	
+	@Test
+	public void isParameterizedTypeWithParameterizedTypeArgument()
+	{
+		assertTrue(TypeUtils.isParameterizedType(valueOf("List<List<Integer>>"), List.class));
+	}
+	
 	// isSimpleParameterizedType tests ----------------------------------------
 	
 	@Test(expected = NullPointerException.class)
@@ -1092,6 +1301,48 @@ public class TypeUtilsTest
 		assertEquals(Integer.class, TypeUtils.getActualTypeArgument(valueOf("List<Integer>")));
 	}
 	
+	@Test(expected = IllegalArgumentException.class)
+	public void getActualTypeArgumentWithUnparameterizedType()
+	{
+		TypeUtils.getActualTypeArgument(Integer.class);
+	}
+	
+	@Test(expected = NullPointerException.class)
+	public void getActualTypeArgumentIndexWithNull()
+	{
+		TypeUtils.getActualTypeArgument(null, 0);
+	}
+	
+	@Test(expected = IllegalArgumentException.class)
+	public void getActualTypeArgumentIndexWithUnparameterizedType()
+	{
+		TypeUtils.getActualTypeArgument(Integer.class, 0);
+	}
+	
+	@Test
+	public void getActualTypeArgumentIndexWithFirstIndex()
+	{
+		assertEquals(Integer.class, TypeUtils.getActualTypeArgument(valueOf("List<Integer>"), 0));
+	}
+	
+	@Test
+	public void getActualTypeArgumentIndexWithSecondIndex()
+	{
+		assertEquals(Number.class, TypeUtils.getActualTypeArgument(valueOf("Map<Integer, Number>"), 1));
+	}
+	
+	@Test(expected = IllegalArgumentException.class)
+	public void getActualTypeArgumentIndexWithNegativeIndex()
+	{
+		TypeUtils.getActualTypeArgument(valueOf("List<Integer>"), -1);
+	}
+	
+	@Test(expected = IllegalArgumentException.class)
+	public void getActualTypeArgumentIndexWithOutOfBoundsIndex()
+	{
+		TypeUtils.getActualTypeArgument(valueOf("List<Integer>"), 1);
+	}
+	
 	// getResolvedSuperclass tests ---------------------------------------------
 	
 	@Test
@@ -1251,6 +1502,12 @@ public class TypeUtilsTest
 	}
 	
 	@Test
+	public void toStringWithMultidimensionalArrayClass()
+	{
+		assertEquals("java.lang.Integer[][]", TypeUtils.toString(Integer[][].class));
+	}
+	
+	@Test
 	public void toStringWithUnboundedTypeVariable()
 	{
 		assertEquals("T", TypeUtils.toString(Types.typeVariable(declaration, "T")));
diff --git a/src/test/java/com/googlecode/jtype/TypesTest.java b/src/test/java/com/googlecode/jtype/TypesTest.java
index 9155f60..168fa92 100644
--- a/src/test/java/com/googlecode/jtype/TypesTest.java
+++ b/src/test/java/com/googlecode/jtype/TypesTest.java
@@ -15,10 +15,12 @@
  */
 package com.googlecode.jtype;
 
-import static org.junit.Assert.assertArrayEquals;
+import static com.googlecode.jtype.test.TypeAssert.assertGenericArrayType;
+import static com.googlecode.jtype.test.TypeAssert.assertParameterizedType;
+import static com.googlecode.jtype.test.TypeAssert.assertTypeVariable;
+import static com.googlecode.jtype.test.TypeAssert.assertWildcardType;
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.GenericArrayType;
@@ -48,7 +50,7 @@ import org.junit.runner.RunWith;
  * Tests {@code Types}.
  * 
  * @author Mark Hobson
- * @version $Id: TypesTest.java 37 2009-07-24 11:27:39Z markhobson $
+ * @version $Id: TypesTest.java 115 2011-11-25 18:17:40Z markhobson at gmail.com $
  * @see Types
  */
 @RunWith(JMock.class)
@@ -100,11 +102,8 @@ public class TypesTest
 		Constructor<TypesTest> constructor = TypesTest.class.getConstructor();
 		
 		TypeVariable<Constructor<TypesTest>> typeVariable = Types.typeVariable(constructor, "T");
-		
-		assertNotNull(typeVariable);
-		assertEquals(constructor, typeVariable.getGenericDeclaration());
-		assertEquals("T", typeVariable.getName());
-		assertArrayEquals(new Type[] {Object.class}, typeVariable.getBounds());
+
+		assertTypeVariable(constructor, "T", new Type[] {Object.class}, typeVariable);
 	}
 	
 	@Test
@@ -115,10 +114,7 @@ public class TypesTest
 		TypeVariable<Constructor<TypesTest>> typeVariable = Types.typeVariable(constructor, "T", Number.class,
 			Comparable.class);
 		
-		assertNotNull(typeVariable);
-		assertEquals(constructor, typeVariable.getGenericDeclaration());
-		assertEquals("T", typeVariable.getName());
-		assertArrayEquals(new Type[] {Number.class, Comparable.class}, typeVariable.getBounds());
+		assertTypeVariable(constructor, "T", new Type[] {Number.class, Comparable.class}, typeVariable);
 	}
 	
 	// genericArrayType tests -------------------------------------------------
@@ -128,8 +124,7 @@ public class TypesTest
 	{
 		GenericArrayType type = Types.genericArrayType(Integer.class);
 		
-		assertNotNull(type);
-		assertEquals(Integer.class, type.getGenericComponentType());
+		assertGenericArrayType(Integer.class, type);
 	}
 	
 	@Test(expected = NullPointerException.class)
@@ -154,10 +149,7 @@ public class TypesTest
 	{
 		ParameterizedType type = Types.parameterizedType(Map.class, new Type[] {String.class, Integer.class});
 		
-		assertNotNull(type);
-		assertEquals(Map.class, type.getRawType());
-		assertNull(type.getOwnerType());
-		assertArrayEquals(new Type[] {String.class, Integer.class}, type.getActualTypeArguments());
+		assertParameterizedType(Map.class, new Type[] {String.class, Integer.class}, type);
 		
 		assertEquals(type, stringIntegerMapType);
 		assertEquals(stringIntegerMapType, type);
@@ -178,15 +170,10 @@ public class TypesTest
 		}
 	}
 	
-	@Test
-	public void parameterizedTypeWithNoActualTypeArguments()
+	@Test(expected = MalformedParameterizedTypeException.class)
+	public void parameterizedTypeWithUnparameterizedRawType()
 	{
-		ParameterizedType type = Types.parameterizedType(Integer.class);
-		
-		assertNotNull(type);
-		assertEquals(Integer.class, type.getRawType());
-		assertNull(type.getOwnerType());
-		assertArrayEquals(new Type[0], type.getActualTypeArguments());
+		Types.parameterizedType(Integer.class);
 	}
 	
 	@Test(expected = MalformedParameterizedTypeException.class)
@@ -195,21 +182,55 @@ public class TypesTest
 		Types.parameterizedType(Map.class, String.class);
 	}
 	
+	@Test
+	public void unboundedParameterizedType()
+	{
+		ParameterizedType type = Types.unboundedParameterizedType(Map.class);
+		
+		Type[] expectedActualTypeArguments = new Type[] {Types.unboundedWildcardType(), Types.unboundedWildcardType()};
+		assertParameterizedType(Map.class, expectedActualTypeArguments, type);
+	}
+	
+	@Test(expected = NullPointerException.class)
+	public void unboundedParameterizedTypeWithNullRawType()
+	{
+		try
+		{
+			Types.unboundedParameterizedType(null);
+		}
+		catch (NullPointerException exception)
+		{
+			assertEquals("rawType cannot be null", exception.getMessage());
+			
+			throw exception;
+		}
+	}
+	
+	@Test(expected = MalformedParameterizedTypeException.class)
+	public void unboundedParameterizedTypeWithUnparameterizedRawType()
+	{
+		Types.unboundedParameterizedType(Integer.class);
+	}
+	
 	// unboundedWildcardType tests --------------------------------------------
 	
 	@Test
 	public void unboundedWildcardType()
 	{
 		WildcardType type = Types.unboundedWildcardType();
-		
-		assertNotNull(type);
-		assertArrayEquals(new Type[] {Object.class}, type.getUpperBounds());
-		assertArrayEquals(new Type[0], type.getLowerBounds());
+
+		assertWildcardType(new Type[] {Object.class}, new Type[0], type);
 		
 		assertEquals(type, unboundedWildcardType);
 		assertEquals(unboundedWildcardType, type);
 	}
 	
+	@Test
+	public void unboundedWildcardTypeIsCached()
+	{
+		assertSame(Types.unboundedWildcardType(), Types.unboundedWildcardType());
+	}
+	
 	// upperBoundedWildcardType tests -----------------------------------------
 	
 	@Test
@@ -217,9 +238,7 @@ public class TypesTest
 	{
 		WildcardType type = Types.upperBoundedWildcardType(Number.class);
 		
-		assertNotNull(type);
-		assertArrayEquals(new Type[] {Number.class}, type.getUpperBounds());
-		assertArrayEquals(new Type[0], type.getLowerBounds());
+		assertWildcardType(new Type[] {Number.class}, new Type[0], type);
 		
 		assertEquals(type, numberUpperBoundedWildcardType);
 		assertEquals(numberUpperBoundedWildcardType, type);
@@ -247,9 +266,7 @@ public class TypesTest
 	{
 		WildcardType type = Types.lowerBoundedWildcardType(Integer.class);
 		
-		assertNotNull(type);
-		assertArrayEquals(new Type[] {Object.class}, type.getUpperBounds());
-		assertArrayEquals(new Type[] {Integer.class}, type.getLowerBounds());
+		assertWildcardType(new Type[] {Object.class}, new Type[] {Integer.class}, type);
 		
 		assertEquals(type, integerLowerBoundedWildcardType);
 		assertEquals(integerLowerBoundedWildcardType, type);
@@ -273,6 +290,54 @@ public class TypesTest
 	// valueOf tests ----------------------------------------------------------
 	
 	@Test
+	public void valueOfWithBooleanPrimitive()
+	{
+		assertEquals(Boolean.TYPE, Types.valueOf("boolean"));
+	}
+	
+	@Test
+	public void valueOfWithBytePrimitive()
+	{
+		assertEquals(Byte.TYPE, Types.valueOf("byte"));
+	}
+	
+	@Test
+	public void valueOfWithCharPrimitive()
+	{
+		assertEquals(Character.TYPE, Types.valueOf("char"));
+	}
+	
+	@Test
+	public void valueOfWithDoublePrimitive()
+	{
+		assertEquals(Double.TYPE, Types.valueOf("double"));
+	}
+	
+	@Test
+	public void valueOfWithFloatPrimitive()
+	{
+		assertEquals(Float.TYPE, Types.valueOf("float"));
+	}
+	
+	@Test
+	public void valueOfWithIntPrimitive()
+	{
+		assertEquals(Integer.TYPE, Types.valueOf("int"));
+	}
+	
+	@Test
+	public void valueOfWithLongPrimitive()
+	{
+		assertEquals(Long.TYPE, Types.valueOf("long"));
+	}
+	
+	@Test
+	public void valueOfWithShortPrimitive()
+	{
+		assertEquals(Short.TYPE, Types.valueOf("short"));
+	}
+	
+	@Test
 	public void valueOfWithClass()
 	{
 		assertEquals(Integer.class, Types.valueOf("java.lang.Integer"));
@@ -351,6 +416,13 @@ public class TypesTest
 	public void valueOfWithMultipleArgumentParameterizedType()
 	{
 		assertEquals(Types.parameterizedType(Map.class, String.class, Integer.class),
+			Types.valueOf("java.util.Map<java.lang.String, java.lang.Integer>"));
+	}
+	
+	@Test
+	public void valueOfWithMultipleArgumentParameterizedTypeAndNoWhitespace()
+	{
+		assertEquals(Types.parameterizedType(Map.class, String.class, Integer.class),
 			Types.valueOf("java.util.Map<java.lang.String,java.lang.Integer>"));
 	}
 	
@@ -365,7 +437,7 @@ public class TypesTest
 	public void valueOfWithMultipleUnboundedWildcardParameterizedType()
 	{
 		assertEquals(Types.parameterizedType(Map.class, Types.unboundedWildcardType(), Types.unboundedWildcardType()),
-			Types.valueOf("java.util.Map<?,?>"));
+			Types.valueOf("java.util.Map<?, ?>"));
 	}
 	
 	@Test(expected = MalformedParameterizedTypeException.class)
@@ -388,7 +460,7 @@ public class TypesTest
 			Types.parameterizedType(List.class, Integer.class));
 		
 		assertEquals(expected,
-			Types.valueOf("java.util.Map<java.util.List<java.lang.String>,java.util.List<java.lang.Integer>>"));
+			Types.valueOf("java.util.Map<java.util.List<java.lang.String>, java.util.List<java.lang.Integer>>"));
 	}
 	
 	@Test
@@ -427,6 +499,12 @@ public class TypesTest
 		assertEquals(Types.lowerBoundedWildcardType(Integer.class), Types.valueOf(" ?  super  java.lang.Integer "));
 	}
 	
+	@Test(expected = NullPointerException.class)
+	public void valueOfWithNullTypeName()
+	{
+		Types.valueOf(null);
+	}
+	
 	@Test
 	public void valueOfWithClassAndImportContext()
 	{
@@ -505,6 +583,12 @@ public class TypesTest
 		assertEquals(Types.upperBoundedWildcardType(Number.class), Types.valueOf("? extends Number", importContext));
 	}
 	
+	@Test
+	public void valueOfWithClassAndNullImportContext()
+	{
+		assertEquals(Integer.class, Types.valueOf("java.lang.Integer", null));
+	}
+	
 	// private methods --------------------------------------------------------
 	
 	private Type getFieldType(String name) throws NoSuchFieldException
diff --git a/src/test/java/com/googlecode/jtype/test/AbstractTypeTest.java b/src/test/java/com/googlecode/jtype/test/AbstractTypeTest.java
new file mode 100644
index 0000000..a15c375
--- /dev/null
+++ b/src/test/java/com/googlecode/jtype/test/AbstractTypeTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2011 IIZUKA Software Technologies Ltd
+ * 
+ * Licensed 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 com.googlecode.jtype.test;
+
+import java.lang.reflect.Type;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.googlecode.jtype.Types;
+
+import org.junit.Before;
+
+/**
+ * Provides support for testing with types.
+ * 
+ * @author Mark Hobson
+ * @version $Id: AbstractTypeTest.java 115 2011-11-25 18:17:40Z markhobson at gmail.com $
+ */
+public abstract class AbstractTypeTest
+{
+	// fields -----------------------------------------------------------------
+	
+	private Set<String> imports;
+	
+	// public methods ---------------------------------------------------------
+	
+	@Before
+	public final void setUpAbstractTypeTest()
+	{
+		imports = Collections.unmodifiableSet(new HashSet<String>(createImports()));
+	}
+	
+	// protected methods ------------------------------------------------------
+	
+	protected void addImports(Set<Class<?>> imports)
+	{
+		// no-op
+	}
+	
+	protected final Type type(String typeName)
+	{
+		return Types.valueOf(typeName, imports);
+	}
+	
+	// private methods --------------------------------------------------------
+	
+	private Set<String> createImports()
+	{
+		Set<Class<?>> classImports = new HashSet<Class<?>>();
+		addImports(classImports);
+		return toClassNames(classImports);
+	}
+	
+	private static Set<String> toClassNames(Collection<Class<?>> classes)
+	{
+		Set<String> names = new HashSet<String>();
+		
+		for (Class<?> klass : classes)
+		{
+			names.add(klass.getName());
+		}
+		
+		return names;
+	}
+}
diff --git a/src/test/java/com/googlecode/jtype/test/SerializableAssert.java b/src/test/java/com/googlecode/jtype/test/SerializableAssert.java
new file mode 100644
index 0000000..9ac64ef
--- /dev/null
+++ b/src/test/java/com/googlecode/jtype/test/SerializableAssert.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2010 IIZUKA Software Technologies Ltd
+ * 
+ * Licensed 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 com.googlecode.jtype.test;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+
+/**
+ * Provides custom assertions for testing serializable objects.
+ * 
+ * @author Mark Hobson
+ * @version $Id: SerializableAssert.java 115 2011-11-25 18:17:40Z markhobson at gmail.com $
+ */
+public final class SerializableAssert
+{
+	// constructors -----------------------------------------------------------
+	
+	private SerializableAssert()
+	{
+		throw new AssertionError();
+	}
+
+	// public methods ---------------------------------------------------------
+	
+	public static void assertSerializable(Object object) throws IOException, ClassNotFoundException
+	{
+		byte[] bytes = serialize(object);
+		Object actual = deserialize(bytes);
+		
+		assertEquals("Serialized object", object, actual);
+	}
+	
+	// private methods --------------------------------------------------------
+	
+	private static byte[] serialize(Object object) throws IOException
+	{
+		ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
+		ObjectOutputStream objectOut = new ObjectOutputStream(byteOut);
+		
+		try
+		{
+			objectOut.writeObject(object);
+		}
+		finally
+		{
+			objectOut.close();
+		}
+		
+		return byteOut.toByteArray();
+	}
+	
+	private static Object deserialize(byte[] bytes) throws IOException, ClassNotFoundException
+	{
+		ByteArrayInputStream byteIn = new ByteArrayInputStream(bytes);
+		ObjectInputStream objectIn = new ObjectInputStream(byteIn);
+		
+		try
+		{
+			return objectIn.readObject();
+		}
+		finally
+		{
+			objectIn.close();
+		}
+	}
+}
diff --git a/src/test/java/com/googlecode/jtype/test/TypeAssert.java b/src/test/java/com/googlecode/jtype/test/TypeAssert.java
new file mode 100644
index 0000000..b3a2867
--- /dev/null
+++ b/src/test/java/com/googlecode/jtype/test/TypeAssert.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 IIZUKA Software Technologies Ltd
+ * 
+ * Licensed 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 com.googlecode.jtype.test;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+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;
+
+/**
+ * Provides custom assertions for testing types.
+ * 
+ * @author Mark Hobson
+ * @version $Id: TypeAssert.java 115 2011-11-25 18:17:40Z markhobson at gmail.com $
+ */
+public final class TypeAssert
+{
+	// constructors -----------------------------------------------------------
+	
+	private TypeAssert()
+	{
+		throw new AssertionError();
+	}
+	
+	// public methods ---------------------------------------------------------
+	
+	public static <D extends GenericDeclaration> void assertTypeVariable(D expectedGenericDeclaration,
+		String expectedName, Type[] expectedBounds, TypeVariable<D> actual)
+	{
+		assertNotNull(actual);
+		assertEquals("Generic declaration", expectedGenericDeclaration, actual.getGenericDeclaration());
+		assertEquals("Name", expectedName, actual.getName());
+		assertArrayEquals("Bounds", expectedBounds, actual.getBounds());
+	}
+	
+	public static void assertGenericArrayType(Type expectedComponentType, GenericArrayType actual)
+	{
+		assertNotNull(actual);
+		assertEquals("Component type", expectedComponentType, actual.getGenericComponentType());
+	}
+	
+	public static void assertParameterizedType(Class<?> expectedRawType, Type[] expectedActualTypeArguments,
+		ParameterizedType actual)
+	{
+		assertParameterizedType(null, expectedRawType, expectedActualTypeArguments, actual);
+	}
+	
+	public static void assertParameterizedType(Type expectedOwnerType, Class<?> expectedRawType,
+		Type[] expectedActualTypeArguments, ParameterizedType actual)
+	{
+		assertNotNull(actual);
+		assertEquals("Owner type", expectedOwnerType, actual.getOwnerType());
+		assertEquals("Raw type", expectedRawType, actual.getRawType());
+		assertArrayEquals("Actual type arguments", expectedActualTypeArguments, actual.getActualTypeArguments());
+	}
+	
+	public static void assertWildcardType(Type[] expectedUpperBounds, Type[] expectedLowerBounds, WildcardType actual)
+	{
+		assertNotNull(actual);
+		assertArrayEquals("Upper bounds", expectedUpperBounds, actual.getUpperBounds());
+		assertArrayEquals("Lower bounds", expectedLowerBounds, actual.getLowerBounds());
+	}
+}

-- 
Helper library for the Java 5 Type



More information about the pkg-java-commits mailing list