Rename modules {org.springframework.*=>spring-*}

This renaming more intuitively expresses the relationship between
subprojects and the JAR artifacts they produce.

Tracking history across these renames is possible, but it requires
use of the --follow flag to `git log`, for example

    $ git log spring-aop/src/main/java/org/springframework/aop/Advisor.java

will show history up until the renaming event, where

    $ git log --follow spring-aop/src/main/java/org/springframework/aop/Advisor.java

will show history for all changes to the file, before and after the
renaming.

See http://chrisbeams.com/git-diff-across-renamed-directories
This commit is contained in:
Chris Beams
2012-01-20 22:51:02 +01:00
parent b6cb514d38
commit 02a4473c62
5671 changed files with 20 additions and 32 deletions

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* 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 org.springframework.core;
/**
* Common interface for managing aliases. Serves as super-interface for
* {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
public interface AliasRegistry {
/**
* Given a name, register an alias for it.
* @param name the canonical name
* @param alias the alias to be registered
* @throws IllegalStateException if the alias is already in use
* and may not be overridden
*/
void registerAlias(String name, String alias);
/**
* Remove the specified alias from this registry.
* @param alias the alias to remove
* @throws IllegalStateException if no such alias was found
*/
void removeAlias(String alias);
/**
* Determine whether this given name is defines as an alias
* (as opposed to the name of an actually registered component).
* @param beanName the bean name to check
* @return whether the given name is an alias
*/
boolean isAlias(String beanName);
/**
* Return the aliases for the given name, if defined.
* @param name the name to check for aliases
* @return the aliases, or an empty array if none
*/
String[] getAliases(String name);
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* 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 org.springframework.core;
/**
* Interface defining a generic contract for attaching and accessing metadata
* to/from arbitrary objects.
*
* @author Rob Harrop
* @since 2.0
*/
public interface AttributeAccessor {
/**
* Set the attribute defined by <code>name</code> to the supplied <code>value</code>.
* If <code>value</code> is <code>null</code>, the attribute is {@link #removeAttribute removed}.
* <p>In general, users should take care to prevent overlaps with other
* metadata attributes by using fully-qualified names, perhaps using
* class or package names as prefix.
* @param name the unique attribute key
* @param value the attribute value to be attached
*/
void setAttribute(String name, Object value);
/**
* Get the value of the attribute identified by <code>name</code>.
* Return <code>null</code> if the attribute doesn't exist.
* @param name the unique attribute key
* @return the current value of the attribute, if any
*/
Object getAttribute(String name);
/**
* Remove the attribute identified by <code>name</code> and return its value.
* Return <code>null</code> if no attribute under <code>name</code> is found.
* @param name the unique attribute key
* @return the last value of the attribute, if any
*/
Object removeAttribute(String name);
/**
* Return <code>true</code> if the attribute identified by <code>name</code> exists.
* Otherwise return <code>false</code>.
* @param name the unique attribute key
*/
boolean hasAttribute(String name);
/**
* Return the names of all attributes.
*/
String[] attributeNames();
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* Support class for {@link AttributeAccessor AttributeAccessors}, providing
* a base implementation of all methods. To be extended by subclasses.
*
* <p>{@link Serializable} if subclasses and all attribute values are {@link Serializable}.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
*/
public abstract class AttributeAccessorSupport implements AttributeAccessor, Serializable {
/** Map with String keys and Object values */
private final Map<String, Object> attributes = new LinkedHashMap<String, Object>(0);
public void setAttribute(String name, Object value) {
Assert.notNull(name, "Name must not be null");
if (value != null) {
this.attributes.put(name, value);
}
else {
removeAttribute(name);
}
}
public Object getAttribute(String name) {
Assert.notNull(name, "Name must not be null");
return this.attributes.get(name);
}
public Object removeAttribute(String name) {
Assert.notNull(name, "Name must not be null");
return this.attributes.remove(name);
}
public boolean hasAttribute(String name) {
Assert.notNull(name, "Name must not be null");
return this.attributes.containsKey(name);
}
public String[] attributeNames() {
return this.attributes.keySet().toArray(new String[this.attributes.size()]);
}
/**
* Copy the attributes from the supplied AttributeAccessor to this accessor.
* @param source the AttributeAccessor to copy from
*/
protected void copyAttributesFrom(AttributeAccessor source) {
Assert.notNull(source, "Source must not be null");
String[] attributeNames = source.attributeNames();
for (String attributeName : attributeNames) {
setAttribute(attributeName, source.getAttribute(attributeName));
}
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof AttributeAccessorSupport)) {
return false;
}
AttributeAccessorSupport that = (AttributeAccessorSupport) other;
return this.attributes.equals(that.attributes);
}
@Override
public int hashCode() {
return this.attributes.hashCode();
}
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* Helper for resolving synthetic {@link Method#isBridge bridge Methods} to the
* {@link Method} being bridged.
*
* <p>Given a synthetic {@link Method#isBridge bridge Method} returns the {@link Method}
* being bridged. A bridge method may be created by the compiler when extending a
* parameterized type whose methods have parameterized arguments. During runtime
* invocation the bridge {@link Method} may be invoked and/or used via reflection.
* When attempting to locate annotations on {@link Method Methods}, it is wise to check
* for bridge {@link Method Methods} as appropriate and find the bridged {@link Method}.
*
* <p>See <a href="http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.12.4.5">
* The Java Language Specification</a> for more details on the use of bridge methods.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
*/
public abstract class BridgeMethodResolver {
/**
* Find the original method for the supplied {@link Method bridge Method}.
* <p>It is safe to call this method passing in a non-bridge {@link Method} instance.
* In such a case, the supplied {@link Method} instance is returned directly to the caller.
* Callers are <strong>not</strong> required to check for bridging before calling this method.
* @param bridgeMethod the method to introspect
* @return the original method (either the bridged method or the passed-in method
* if no more specific one could be found)
*/
public static Method findBridgedMethod(Method bridgeMethod) {
if (bridgeMethod == null || !bridgeMethod.isBridge()) {
return bridgeMethod;
}
// Gather all methods with matching name and parameter size.
List<Method> candidateMethods = new ArrayList<Method>();
Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
for (Method candidateMethod : methods) {
if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
candidateMethods.add(candidateMethod);
}
}
// Now perform simple quick check.
if (candidateMethods.size() == 1) {
return candidateMethods.get(0);
}
// Search for candidate match.
Method bridgedMethod = searchCandidates(candidateMethods, bridgeMethod);
if (bridgedMethod != null) {
// Bridged method found...
return bridgedMethod;
}
else {
// A bridge method was passed in but we couldn't find the bridged method.
// Let's proceed with the passed-in method and hope for the best...
return bridgeMethod;
}
}
/**
* Searches for the bridged method in the given candidates.
* @param candidateMethods the List of candidate Methods
* @param bridgeMethod the bridge method
* @return the bridged method, or <code>null</code> if none found
*/
private static Method searchCandidates(List<Method> candidateMethods, Method bridgeMethod) {
if (candidateMethods.isEmpty()) {
return null;
}
Map<TypeVariable, Type> typeParameterMap = GenericTypeResolver.getTypeVariableMap(bridgeMethod.getDeclaringClass());
Method previousMethod = null;
boolean sameSig = true;
for (Method candidateMethod : candidateMethods) {
if (isBridgeMethodFor(bridgeMethod, candidateMethod, typeParameterMap)) {
return candidateMethod;
}
else if (previousMethod != null) {
sameSig = sameSig &&
Arrays.equals(candidateMethod.getGenericParameterTypes(), previousMethod.getGenericParameterTypes());
}
previousMethod = candidateMethod;
}
return (sameSig ? candidateMethods.get(0) : null);
}
/**
* Returns <code>true</code> if the supplied '<code>candidateMethod</code>' can be
* consider a validate candidate for the {@link Method} that is {@link Method#isBridge() bridged}
* by the supplied {@link Method bridge Method}. This method performs inexpensive
* checks and can be used quickly filter for a set of possible matches.
*/
private static boolean isBridgedCandidateFor(Method candidateMethod, Method bridgeMethod) {
return (!candidateMethod.isBridge() && !candidateMethod.equals(bridgeMethod) &&
candidateMethod.getName().equals(bridgeMethod.getName()) &&
candidateMethod.getParameterTypes().length == bridgeMethod.getParameterTypes().length);
}
/**
* Determines whether or not the bridge {@link Method} is the bridge for the
* supplied candidate {@link Method}.
*/
static boolean isBridgeMethodFor(Method bridgeMethod, Method candidateMethod, Map<TypeVariable, Type> typeVariableMap) {
if (isResolvedTypeMatch(candidateMethod, bridgeMethod, typeVariableMap)) {
return true;
}
Method method = findGenericDeclaration(bridgeMethod);
return (method != null && isResolvedTypeMatch(method, candidateMethod, typeVariableMap));
}
/**
* Searches for the generic {@link Method} declaration whose erased signature
* matches that of the supplied bridge method.
* @throws IllegalStateException if the generic declaration cannot be found
*/
private static Method findGenericDeclaration(Method bridgeMethod) {
// Search parent types for method that has same signature as bridge.
Class superclass = bridgeMethod.getDeclaringClass().getSuperclass();
while (!Object.class.equals(superclass)) {
Method method = searchForMatch(superclass, bridgeMethod);
if (method != null && !method.isBridge()) {
return method;
}
superclass = superclass.getSuperclass();
}
// Search interfaces.
Class[] interfaces = ClassUtils.getAllInterfacesForClass(bridgeMethod.getDeclaringClass());
for (Class ifc : interfaces) {
Method method = searchForMatch(ifc, bridgeMethod);
if (method != null && !method.isBridge()) {
return method;
}
}
return null;
}
/**
* Returns <code>true</code> if the {@link Type} signature of both the supplied
* {@link Method#getGenericParameterTypes() generic Method} and concrete {@link Method}
* are equal after resolving all {@link TypeVariable TypeVariables} using the supplied
* TypeVariable Map, otherwise returns <code>false</code>.
*/
private static boolean isResolvedTypeMatch(
Method genericMethod, Method candidateMethod, Map<TypeVariable, Type> typeVariableMap) {
Type[] genericParameters = genericMethod.getGenericParameterTypes();
Class[] candidateParameters = candidateMethod.getParameterTypes();
if (genericParameters.length != candidateParameters.length) {
return false;
}
for (int i = 0; i < genericParameters.length; i++) {
Type genericParameter = genericParameters[i];
Class candidateParameter = candidateParameters[i];
if (candidateParameter.isArray()) {
// An array type: compare the component type.
Type rawType = GenericTypeResolver.getRawType(genericParameter, typeVariableMap);
if (rawType instanceof GenericArrayType) {
if (!candidateParameter.getComponentType().equals(
GenericTypeResolver.resolveType(((GenericArrayType) rawType).getGenericComponentType(), typeVariableMap))) {
return false;
}
break;
}
}
// A non-array type: compare the type itself.
Class resolvedParameter = GenericTypeResolver.resolveType(genericParameter, typeVariableMap);
if (!candidateParameter.equals(resolvedParameter)) {
return false;
}
}
return true;
}
/**
* If the supplied {@link Class} has a declared {@link Method} whose signature matches
* that of the supplied {@link Method}, then this matching {@link Method} is returned,
* otherwise <code>null</code> is returned.
*/
private static Method searchForMatch(Class type, Method bridgeMethod) {
return ReflectionUtils.findMethod(type, bridgeMethod.getName(), bridgeMethod.getParameterTypes());
}
/**
* Compare the signatures of the bridge method and the method which it bridges. If
* the parameter and return types are the same, it is a 'visibility' bridge method
* introduced in Java 6 to fix http://bugs.sun.com/view_bug.do?bug_id=6342411.
* See also http://stas-blogspot.blogspot.com/2010/03/java-bridge-methods-explained.html
* @return whether signatures match as described
*/
public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) {
Assert.isTrue(bridgeMethod != null);
Assert.isTrue(bridgedMethod != null);
if (bridgeMethod == bridgedMethod) {
return true;
}
return Arrays.equals(bridgeMethod.getParameterTypes(), bridgedMethod.getParameterTypes()) &&
bridgeMethod.getReturnType().equals(bridgedMethod.getReturnType());
}
}

View File

@@ -0,0 +1,337 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* 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 org.springframework.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import org.springframework.util.LinkedCaseInsensitiveMap;
/**
* Factory for collections, being aware of Java 5 and Java 6 collections.
* Mainly for internal use within the framework.
*
* <p>The goal of this class is to avoid runtime dependencies on a specific
* Java version, while nevertheless using the best collection implementation
* that is available at runtime.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
* @since 1.1.1
*/
public abstract class CollectionFactory {
private static Class navigableSetClass = null;
private static Class navigableMapClass = null;
private static final Set<Class> approximableCollectionTypes = new HashSet<Class>(10);
private static final Set<Class> approximableMapTypes = new HashSet<Class>(6);
static {
// Standard collection interfaces
approximableCollectionTypes.add(Collection.class);
approximableCollectionTypes.add(List.class);
approximableCollectionTypes.add(Set.class);
approximableCollectionTypes.add(SortedSet.class);
approximableMapTypes.add(Map.class);
approximableMapTypes.add(SortedMap.class);
// New Java 6 collection interfaces
ClassLoader cl = CollectionFactory.class.getClassLoader();
try {
navigableSetClass = cl.loadClass("java.util.NavigableSet");
navigableMapClass = cl.loadClass("java.util.NavigableMap");
approximableCollectionTypes.add(navigableSetClass);
approximableMapTypes.add(navigableMapClass);
}
catch (ClassNotFoundException ex) {
// not running on Java 6 or above...
}
// Common concrete collection classes
approximableCollectionTypes.add(ArrayList.class);
approximableCollectionTypes.add(LinkedList.class);
approximableCollectionTypes.add(HashSet.class);
approximableCollectionTypes.add(LinkedHashSet.class);
approximableCollectionTypes.add(TreeSet.class);
approximableMapTypes.add(HashMap.class);
approximableMapTypes.add(LinkedHashMap.class);
approximableMapTypes.add(TreeMap.class);
}
/**
* Create a linked Set if possible: This implementation always
* creates a {@link java.util.LinkedHashSet}, since Spring 2.5
* requires JDK 1.4 anyway.
* @param initialCapacity the initial capacity of the Set
* @return the new Set instance
* @deprecated as of Spring 2.5, for usage on JDK 1.4 or higher
*/
@Deprecated
public static <T> Set<T> createLinkedSetIfPossible(int initialCapacity) {
return new LinkedHashSet<T>(initialCapacity);
}
/**
* Create a copy-on-write Set (allowing for synchronization-less iteration) if possible:
* This implementation always creates a {@link java.util.concurrent.CopyOnWriteArraySet},
* since Spring 3 requires JDK 1.5 anyway.
* @return the new Set instance
* @deprecated as of Spring 3.0, for usage on JDK 1.5 or higher
*/
@Deprecated
public static <T> Set<T> createCopyOnWriteSet() {
return new CopyOnWriteArraySet<T>();
}
/**
* Create a linked Map if possible: This implementation always
* creates a {@link java.util.LinkedHashMap}, since Spring 2.5
* requires JDK 1.4 anyway.
* @param initialCapacity the initial capacity of the Map
* @return the new Map instance
* @deprecated as of Spring 2.5, for usage on JDK 1.4 or higher
*/
@Deprecated
public static <K,V> Map<K,V> createLinkedMapIfPossible(int initialCapacity) {
return new LinkedHashMap<K,V>(initialCapacity);
}
/**
* Create a linked case-insensitive Map if possible: This implementation
* always returns a {@link org.springframework.util.LinkedCaseInsensitiveMap}.
* @param initialCapacity the initial capacity of the Map
* @return the new Map instance
* @deprecated as of Spring 3.0, for usage on JDK 1.5 or higher
*/
@Deprecated
public static Map createLinkedCaseInsensitiveMapIfPossible(int initialCapacity) {
return new LinkedCaseInsensitiveMap(initialCapacity);
}
/**
* Create an identity Map if possible: This implementation always
* creates a {@link java.util.IdentityHashMap}, since Spring 2.5
* requires JDK 1.4 anyway.
* @param initialCapacity the initial capacity of the Map
* @return the new Map instance
* @deprecated as of Spring 2.5, for usage on JDK 1.4 or higher
*/
@Deprecated
public static Map createIdentityMapIfPossible(int initialCapacity) {
return new IdentityHashMap(initialCapacity);
}
/**
* Create a concurrent Map if possible: This implementation always
* creates a {@link java.util.concurrent.ConcurrentHashMap}, since Spring 3.0
* requires JDK 1.5 anyway.
* @param initialCapacity the initial capacity of the Map
* @return the new Map instance
* @deprecated as of Spring 3.0, for usage on JDK 1.5 or higher
*/
@Deprecated
public static Map createConcurrentMapIfPossible(int initialCapacity) {
return new ConcurrentHashMap(initialCapacity);
}
/**
* Create a concurrent Map with a dedicated {@link ConcurrentMap} interface:
* This implementation always creates a {@link java.util.concurrent.ConcurrentHashMap},
* since Spring 3.0 requires JDK 1.5 anyway.
* @param initialCapacity the initial capacity of the Map
* @return the new ConcurrentMap instance
* @deprecated as of Spring 3.0, for usage on JDK 1.5 or higher
*/
@Deprecated
public static ConcurrentMap createConcurrentMap(int initialCapacity) {
return new JdkConcurrentHashMap(initialCapacity);
}
/**
* Determine whether the given collection type is an approximable type,
* i.e. a type that {@link #createApproximateCollection} can approximate.
* @param collectionType the collection type to check
* @return <code>true</code> if the type is approximable,
* <code>false</code> if it is not
*/
public static boolean isApproximableCollectionType(Class<?> collectionType) {
return (collectionType != null && approximableCollectionTypes.contains(collectionType));
}
/**
* Create the most approximate collection for the given collection.
* <p>Creates an ArrayList, TreeSet or linked Set for a List, SortedSet
* or Set, respectively.
* @param collection the original Collection object
* @param initialCapacity the initial capacity
* @return the new Collection instance
* @see java.util.ArrayList
* @see java.util.TreeSet
* @see java.util.LinkedHashSet
*/
@SuppressWarnings("unchecked")
public static Collection createApproximateCollection(Object collection, int initialCapacity) {
if (collection instanceof LinkedList) {
return new LinkedList();
}
else if (collection instanceof List) {
return new ArrayList(initialCapacity);
}
else if (collection instanceof SortedSet) {
return new TreeSet(((SortedSet) collection).comparator());
}
else {
return new LinkedHashSet(initialCapacity);
}
}
/**
* Create the most appropriate collection for the given collection type.
* <p>Creates an ArrayList, TreeSet or linked Set for a List, SortedSet
* or Set, respectively.
* @param collectionType the desired type of the target Collection
* @param initialCapacity the initial capacity
* @return the new Collection instance
* @see java.util.ArrayList
* @see java.util.TreeSet
* @see java.util.LinkedHashSet
*/
public static Collection createCollection(Class<?> collectionType, int initialCapacity) {
if (collectionType.isInterface()) {
if (List.class.equals(collectionType)) {
return new ArrayList(initialCapacity);
}
else if (SortedSet.class.equals(collectionType) || collectionType.equals(navigableSetClass)) {
return new TreeSet();
}
else if (Set.class.equals(collectionType) || Collection.class.equals(collectionType)) {
return new LinkedHashSet(initialCapacity);
}
else {
throw new IllegalArgumentException("Unsupported Collection interface: " + collectionType.getName());
}
}
else {
if (!Collection.class.isAssignableFrom(collectionType)) {
throw new IllegalArgumentException("Unsupported Collection type: " + collectionType.getName());
}
try {
return (Collection) collectionType.newInstance();
}
catch (Exception ex) {
throw new IllegalArgumentException("Could not instantiate Collection type: " + collectionType.getName());
}
}
}
/**
* Determine whether the given map type is an approximable type,
* i.e. a type that {@link #createApproximateMap} can approximate.
* @param mapType the map type to check
* @return <code>true</code> if the type is approximable,
* <code>false</code> if it is not
*/
public static boolean isApproximableMapType(Class<?> mapType) {
return (mapType != null && approximableMapTypes.contains(mapType));
}
/**
* Create the most approximate map for the given map.
* <p>Creates a TreeMap or linked Map for a SortedMap or Map, respectively.
* @param map the original Map object
* @param initialCapacity the initial capacity
* @return the new Map instance
* @see java.util.TreeMap
* @see java.util.LinkedHashMap
*/
@SuppressWarnings("unchecked")
public static Map createApproximateMap(Object map, int initialCapacity) {
if (map instanceof SortedMap) {
return new TreeMap(((SortedMap) map).comparator());
}
else {
return new LinkedHashMap(initialCapacity);
}
}
/**
* Create the most approximate map for the given map.
* <p>Creates a TreeMap or linked Map for a SortedMap or Map, respectively.
* @param collectionType the desired type of the target Map
* @param initialCapacity the initial capacity
* @return the new Map instance
* @see java.util.TreeMap
* @see java.util.LinkedHashMap
*/
public static Map createMap(Class<?> mapType, int initialCapacity) {
if (mapType.isInterface()) {
if (Map.class.equals(mapType)) {
return new LinkedHashMap(initialCapacity);
}
else if (SortedMap.class.equals(mapType) || mapType.equals(navigableMapClass)) {
return new TreeMap();
}
else {
throw new IllegalArgumentException("Unsupported Map interface: " + mapType.getName());
}
}
else {
if (!Map.class.isAssignableFrom(mapType)) {
throw new IllegalArgumentException("Unsupported Map type: " + mapType.getName());
}
try {
return (Map) mapType.newInstance();
}
catch (Exception ex) {
throw new IllegalArgumentException("Could not instantiate Map type: " + mapType.getName());
}
}
}
/**
* ConcurrentMap adapter for the JDK ConcurrentHashMap class.
*/
@Deprecated
private static class JdkConcurrentHashMap extends ConcurrentHashMap implements ConcurrentMap {
private JdkConcurrentHashMap(int initialCapacity) {
super(initialCapacity);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core;
import java.util.Map;
/**
* Common interface for a concurrent Map, as exposed by
* {@link CollectionFactory#createConcurrentMap}. Mirrors
* {@link java.util.concurrent.ConcurrentMap}, allowing to be backed by a
* JDK ConcurrentHashMap as well as a backport-concurrent ConcurrentHashMap.
*
* <p>Check out the {@link java.util.concurrent.ConcurrentMap ConcurrentMap javadoc}
* for details on the interface's methods.
*
* @author Juergen Hoeller
* @since 2.5
* @deprecated as of Spring 3.0, since standard {@link java.util.concurrent.ConcurrentMap}
* is available on Java 5+ anyway
*/
@Deprecated
public interface ConcurrentMap extends Map {
Object putIfAbsent(Object key, Object value);
boolean remove(Object key, Object value);
boolean replace(Object key, Object oldValue, Object newValue);
Object replace(Object key, Object value);
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.Proxy;
import org.springframework.util.ClassUtils;
/**
* Special ObjectInputStream subclass that resolves class names
* against a specific ClassLoader. Serves as base class for
* {@link org.springframework.remoting.rmi.CodebaseAwareObjectInputStream}.
*
* @author Juergen Hoeller
* @since 2.5.5
*/
public class ConfigurableObjectInputStream extends ObjectInputStream {
private final ClassLoader classLoader;
private final boolean acceptProxyClasses;
/**
* Create a new ConfigurableObjectInputStream for the given InputStream and ClassLoader.
* @param in the InputStream to read from
* @param classLoader the ClassLoader to use for loading local classes
* @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream)
*/
public ConfigurableObjectInputStream(InputStream in, ClassLoader classLoader) throws IOException {
this(in, classLoader, true);
}
/**
* Create a new ConfigurableObjectInputStream for the given InputStream and ClassLoader.
* @param in the InputStream to read from
* @param classLoader the ClassLoader to use for loading local classes
* @param acceptProxyClasses whether to accept deserialization of proxy classes
* (may be deactivated as a security measure)
* @see java.io.ObjectInputStream#ObjectInputStream(java.io.InputStream)
*/
public ConfigurableObjectInputStream(
InputStream in, ClassLoader classLoader, boolean acceptProxyClasses) throws IOException {
super(in);
this.classLoader = classLoader;
this.acceptProxyClasses = acceptProxyClasses;
}
@Override
protected Class resolveClass(ObjectStreamClass classDesc) throws IOException, ClassNotFoundException {
try {
if (this.classLoader != null) {
// Use the specified ClassLoader to resolve local classes.
return ClassUtils.forName(classDesc.getName(), this.classLoader);
}
else {
// Use the default ClassLoader...
return super.resolveClass(classDesc);
}
}
catch (ClassNotFoundException ex) {
return resolveFallbackIfPossible(classDesc.getName(), ex);
}
}
@Override
protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
if (!this.acceptProxyClasses) {
throw new NotSerializableException("Not allowed to accept serialized proxy classes");
}
if (this.classLoader != null) {
// Use the specified ClassLoader to resolve local proxy classes.
Class[] resolvedInterfaces = new Class[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
try {
resolvedInterfaces[i] = ClassUtils.forName(interfaces[i], this.classLoader);
}
catch (ClassNotFoundException ex) {
resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex);
}
}
try {
return Proxy.getProxyClass(this.classLoader, resolvedInterfaces);
}
catch (IllegalArgumentException ex) {
throw new ClassNotFoundException(null, ex);
}
}
else {
// Use ObjectInputStream's default ClassLoader...
try {
return super.resolveProxyClass(interfaces);
}
catch (ClassNotFoundException ex) {
Class[] resolvedInterfaces = new Class[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex);
}
return Proxy.getProxyClass(getFallbackClassLoader(), resolvedInterfaces);
}
}
}
/**
* Resolve the given class name against a fallback class loader.
* <p>The default implementation simply rethrows the original exception,
* since there is no fallback available.
* @param className the class name to resolve
* @param ex the original exception thrown when attempting to load the class
* @return the newly resolved class (never <code>null</code>)
*/
protected Class resolveFallbackIfPossible(String className, ClassNotFoundException ex)
throws IOException, ClassNotFoundException{
throw ex;
}
/**
* Return the fallback ClassLoader to use when no ClassLoader was specified
* and ObjectInputStream's own default ClassLoader failed.
* <p>The default implementation simply returns <code>null</code>.
*/
protected ClassLoader getFallbackClassLoader() throws IOException {
return null;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* 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 org.springframework.core;
/**
* Exception thrown when the {@link Constants} class is asked for
* an invalid constant name.
*
* @author Rod Johnson
* @since 28.04.2003
* @see org.springframework.core.Constants
*/
public class ConstantException extends IllegalArgumentException {
/**
* Thrown when an invalid constant name is requested.
* @param className name of the class containing the constant definitions
* @param field invalid constant name
* @param message description of the problem
*/
public ConstantException(String className, String field, String message) {
super("Field '" + field + "' " + message + " in class [" + className + "]");
}
/**
* Thrown when an invalid constant value is looked up.
* @param className name of the class containing the constant definitions
* @param namePrefix prefix of the searched constant names
* @param value the looked up constant value
*/
public ConstantException(String className, String namePrefix, Object value) {
super("No '" + namePrefix + "' field with value '" + value + "' found in class [" + className + "]");
}
}

View File

@@ -0,0 +1,336 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* 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 org.springframework.core;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* This class can be used to parse other classes containing constant definitions
* in public static final members. The <code>asXXXX</code> methods of this class
* allow these constant values to be accessed via their string names.
*
* <p>Consider class Foo containing <code>public final static int CONSTANT1 = 66;</code>
* An instance of this class wrapping <code>Foo.class</code> will return the constant value
* of 66 from its <code>asNumber</code> method given the argument <code>"CONSTANT1"</code>.
*
* <p>This class is ideal for use in PropertyEditors, enabling them to
* recognize the same names as the constants themselves, and freeing them
* from maintaining their own mapping.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 16.03.2003
*/
public class Constants {
/** The name of the introspected class */
private final String className;
/** Map from String field name to object value */
private final Map<String, Object> fieldCache = new HashMap<String, Object>();
/**
* Create a new Constants converter class wrapping the given class.
* <p>All <b>public</b> static final variables will be exposed, whatever their type.
* @param clazz the class to analyze
* @throws IllegalArgumentException if the supplied <code>clazz</code> is <code>null</code>
*/
public Constants(Class clazz) {
Assert.notNull(clazz);
this.className = clazz.getName();
Field[] fields = clazz.getFields();
for (Field field : fields) {
if (ReflectionUtils.isPublicStaticFinal(field)) {
String name = field.getName();
try {
Object value = field.get(null);
this.fieldCache.put(name, value);
}
catch (IllegalAccessException ex) {
// just leave this field and continue
}
}
}
}
/**
* Return the name of the analyzed class.
*/
public final String getClassName() {
return this.className;
}
/**
* Return the number of constants exposed.
*/
public final int getSize() {
return this.fieldCache.size();
}
/**
* Exposes the field cache to subclasses:
* a Map from String field name to object value.
*/
protected final Map<String, Object> getFieldCache() {
return this.fieldCache;
}
/**
* Return a constant value cast to a Number.
* @param code the name of the field (never <code>null</code>)
* @return the Number value
* @see #asObject
* @throws ConstantException if the field name wasn't found
* or if the type wasn't compatible with Number
*/
public Number asNumber(String code) throws ConstantException {
Object obj = asObject(code);
if (!(obj instanceof Number)) {
throw new ConstantException(this.className, code, "not a Number");
}
return (Number) obj;
}
/**
* Return a constant value as a String.
* @param code the name of the field (never <code>null</code>)
* @return the String value
* Works even if it's not a string (invokes <code>toString()</code>).
* @see #asObject
* @throws ConstantException if the field name wasn't found
*/
public String asString(String code) throws ConstantException {
return asObject(code).toString();
}
/**
* Parse the given String (upper or lower case accepted) and return
* the appropriate value if it's the name of a constant field in the
* class that we're analysing.
* @param code the name of the field (never <code>null</code>)
* @return the Object value
* @throws ConstantException if there's no such field
*/
public Object asObject(String code) throws ConstantException {
Assert.notNull(code, "Code must not be null");
String codeToUse = code.toUpperCase(Locale.ENGLISH);
Object val = this.fieldCache.get(codeToUse);
if (val == null) {
throw new ConstantException(this.className, codeToUse, "not found");
}
return val;
}
/**
* Return all names of the given group of constants.
* <p>Note that this method assumes that constants are named
* in accordance with the standard Java convention for constant
* values (i.e. all uppercase). The supplied <code>namePrefix</code>
* will be uppercased (in a locale-insensitive fashion) prior to
* the main logic of this method kicking in.
* @param namePrefix prefix of the constant names to search (may be <code>null</code>)
* @return the set of constant names
*/
public Set<String> getNames(String namePrefix) {
String prefixToUse = (namePrefix != null ? namePrefix.trim().toUpperCase(Locale.ENGLISH) : "");
Set<String> names = new HashSet<String>();
for (String code : this.fieldCache.keySet()) {
if (code.startsWith(prefixToUse)) {
names.add(code);
}
}
return names;
}
/**
* Return all names of the group of constants for the
* given bean property name.
* @param propertyName the name of the bean property
* @return the set of values
* @see #propertyToConstantNamePrefix
*/
public Set<String> getNamesForProperty(String propertyName) {
return getNames(propertyToConstantNamePrefix(propertyName));
}
/**
* Return all names of the given group of constants.
* <p>Note that this method assumes that constants are named
* in accordance with the standard Java convention for constant
* values (i.e. all uppercase). The supplied <code>nameSuffix</code>
* will be uppercased (in a locale-insensitive fashion) prior to
* the main logic of this method kicking in.
* @param nameSuffix suffix of the constant names to search (may be <code>null</code>)
* @return the set of constant names
*/
public Set getNamesForSuffix(String nameSuffix) {
String suffixToUse = (nameSuffix != null ? nameSuffix.trim().toUpperCase(Locale.ENGLISH) : "");
Set<String> names = new HashSet<String>();
for (String code : this.fieldCache.keySet()) {
if (code.endsWith(suffixToUse)) {
names.add(code);
}
}
return names;
}
/**
* Return all values of the given group of constants.
* <p>Note that this method assumes that constants are named
* in accordance with the standard Java convention for constant
* values (i.e. all uppercase). The supplied <code>namePrefix</code>
* will be uppercased (in a locale-insensitive fashion) prior to
* the main logic of this method kicking in.
* @param namePrefix prefix of the constant names to search (may be <code>null</code>)
* @return the set of values
*/
public Set<Object> getValues(String namePrefix) {
String prefixToUse = (namePrefix != null ? namePrefix.trim().toUpperCase(Locale.ENGLISH) : "");
Set<Object> values = new HashSet<Object>();
for (String code : this.fieldCache.keySet()) {
if (code.startsWith(prefixToUse)) {
values.add(this.fieldCache.get(code));
}
}
return values;
}
/**
* Return all values of the group of constants for the
* given bean property name.
* @param propertyName the name of the bean property
* @return the set of values
* @see #propertyToConstantNamePrefix
*/
public Set<Object> getValuesForProperty(String propertyName) {
return getValues(propertyToConstantNamePrefix(propertyName));
}
/**
* Return all values of the given group of constants.
* <p>Note that this method assumes that constants are named
* in accordance with the standard Java convention for constant
* values (i.e. all uppercase). The supplied <code>nameSuffix</code>
* will be uppercased (in a locale-insensitive fashion) prior to
* the main logic of this method kicking in.
* @param nameSuffix suffix of the constant names to search (may be <code>null</code>)
* @return the set of values
*/
public Set<Object> getValuesForSuffix(String nameSuffix) {
String suffixToUse = (nameSuffix != null ? nameSuffix.trim().toUpperCase(Locale.ENGLISH) : "");
Set<Object> values = new HashSet<Object>();
for (String code : this.fieldCache.keySet()) {
if (code.endsWith(suffixToUse)) {
values.add(this.fieldCache.get(code));
}
}
return values;
}
/**
* Look up the given value within the given group of constants.
* <p>Will return the first match.
* @param value constant value to look up
* @param namePrefix prefix of the constant names to search (may be <code>null</code>)
* @return the name of the constant field
* @throws ConstantException if the value wasn't found
*/
public String toCode(Object value, String namePrefix) throws ConstantException {
String prefixToUse = (namePrefix != null ? namePrefix.trim().toUpperCase(Locale.ENGLISH) : null);
for (Map.Entry<String, Object> entry : this.fieldCache.entrySet()) {
if (entry.getKey().startsWith(prefixToUse) && entry.getValue().equals(value)) {
return entry.getKey();
}
}
throw new ConstantException(this.className, prefixToUse, value);
}
/**
* Look up the given value within the group of constants for
* the given bean property name. Will return the first match.
* @param value constant value to look up
* @param propertyName the name of the bean property
* @return the name of the constant field
* @throws ConstantException if the value wasn't found
* @see #propertyToConstantNamePrefix
*/
public String toCodeForProperty(Object value, String propertyName) throws ConstantException {
return toCode(value, propertyToConstantNamePrefix(propertyName));
}
/**
* Look up the given value within the given group of constants.
* <p>Will return the first match.
* @param value constant value to look up
* @param nameSuffix suffix of the constant names to search (may be <code>null</code>)
* @return the name of the constant field
* @throws ConstantException if the value wasn't found
*/
public String toCodeForSuffix(Object value, String nameSuffix) throws ConstantException {
String suffixToUse = (nameSuffix != null ? nameSuffix.trim().toUpperCase(Locale.ENGLISH) : null);
for (Map.Entry<String, Object> entry : this.fieldCache.entrySet()) {
if (entry.getKey().endsWith(suffixToUse) && entry.getValue().equals(value)) {
return entry.getKey();
}
}
throw new ConstantException(this.className, suffixToUse, value);
}
/**
* Convert the given bean property name to a constant name prefix.
* <p>Uses a common naming idiom: turning all lower case characters to
* upper case, and prepending upper case characters with an underscore.
* <p>Example: "imageSize" -> "IMAGE_SIZE"<br>
* Example: "imagesize" -> "IMAGESIZE".<br>
* Example: "ImageSize" -> "_IMAGE_SIZE".<br>
* Example: "IMAGESIZE" -> "_I_M_A_G_E_S_I_Z_E"
* @param propertyName the name of the bean property
* @return the corresponding constant name prefix
* @see #getValuesForProperty
* @see #toCodeForProperty
*/
public String propertyToConstantNamePrefix(String propertyName) {
StringBuilder parsedPrefix = new StringBuilder();
for(int i = 0; i < propertyName.length(); i++) {
char c = propertyName.charAt(i);
if (Character.isUpperCase(c)) {
parsedPrefix.append("_");
parsedPrefix.append(c);
}
else {
parsedPrefix.append(Character.toUpperCase(c));
}
}
return parsedPrefix.toString();
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* 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 org.springframework.core;
/**
* Interface to be implemented by objects that can return information about
* the current call stack. Useful in AOP (as in AspectJ cflow concept)
* but not AOP-specific.
*
* @author Rod Johnson
* @since 02.02.2004
*/
public interface ControlFlow {
/**
* Detect whether we're under the given class,
* according to the current stack trace.
* @param clazz the clazz to look for
*/
boolean under(Class clazz);
/**
* Detect whether we're under the given class and method,
* according to the current stack trace.
* @param clazz the clazz to look for
* @param methodName the name of the method to look for
*/
boolean under(Class clazz, String methodName);
/**
* Detect whether the current stack trace contains the given token.
* @param token the token to look for
*/
boolean underToken(String token);
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* 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 org.springframework.core;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.springframework.util.Assert;
/**
* Static factory to conceal the automatic choice of the ControlFlow
* implementation class.
*
* <p>This implementation always uses the efficient Java 1.4 StackTraceElement
* mechanism for analyzing control flows.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 02.02.2004
*/
public abstract class ControlFlowFactory {
/**
* Return an appropriate {@link ControlFlow} instance.
*/
public static ControlFlow createControlFlow() {
return new Jdk14ControlFlow();
}
/**
* Utilities for cflow-style pointcuts. Note that such pointcuts are
* 5-10 times more expensive to evaluate than other pointcuts, as they require
* analysis of the stack trace (through constructing a new throwable).
* However, they are useful in some cases.
* <p>This implementation uses the StackTraceElement class introduced in Java 1.4.
* @see java.lang.StackTraceElement
*/
static class Jdk14ControlFlow implements ControlFlow {
private StackTraceElement[] stack;
public Jdk14ControlFlow() {
this.stack = new Throwable().getStackTrace();
}
/**
* Searches for class name match in a StackTraceElement.
*/
public boolean under(Class clazz) {
Assert.notNull(clazz, "Class must not be null");
String className = clazz.getName();
for (int i = 0; i < stack.length; i++) {
if (this.stack[i].getClassName().equals(className)) {
return true;
}
}
return false;
}
/**
* Searches for class name match plus method name match
* in a StackTraceElement.
*/
public boolean under(Class clazz, String methodName) {
Assert.notNull(clazz, "Class must not be null");
Assert.notNull(methodName, "Method name must not be null");
String className = clazz.getName();
for (int i = 0; i < this.stack.length; i++) {
if (this.stack[i].getClassName().equals(className) &&
this.stack[i].getMethodName().equals(methodName)) {
return true;
}
}
return false;
}
/**
* Leave it up to the caller to decide what matches.
* Caller must understand stack trace format, so there's less abstraction.
*/
public boolean underToken(String token) {
if (token == null) {
return false;
}
StringWriter sw = new StringWriter();
new Throwable().printStackTrace(new PrintWriter(sw));
String stackTrace = sw.toString();
return stackTrace.indexOf(token) != -1;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Jdk14ControlFlow: ");
for (int i = 0; i < this.stack.length; i++) {
if (i > 0) {
sb.append("\n\t@");
}
sb.append(this.stack[i]);
}
return sb.toString();
}
}
}

View File

@@ -0,0 +1,302 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core;
import java.io.Externalizable;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Provides methods to support various naming and other conventions used
* throughout the framework. Mainly for internal use within the framework.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
*/
public abstract class Conventions {
/**
* Suffix added to names when using arrays.
*/
private static final String PLURAL_SUFFIX = "List";
/**
* Set of interfaces that are supposed to be ignored
* when searching for the 'primary' interface of a proxy.
*/
private static final Set<Class> ignoredInterfaces = new HashSet<Class>();
static {
ignoredInterfaces.add(Serializable.class);
ignoredInterfaces.add(Externalizable.class);
ignoredInterfaces.add(Cloneable.class);
ignoredInterfaces.add(Comparable.class);
}
/**
* Determine the conventional variable name for the supplied
* <code>Object</code> based on its concrete type. The convention
* used is to return the uncapitalized short name of the <code>Class</code>,
* according to JavaBeans property naming rules: So,
* <code>com.myapp.Product</code> becomes <code>product</code>;
* <code>com.myapp.MyProduct</code> becomes <code>myProduct</code>;
* <code>com.myapp.UKProduct</code> becomes <code>UKProduct</code>.
* <p>For arrays, we use the pluralized version of the array component type.
* For <code>Collection</code>s we attempt to 'peek ahead' in the
* <code>Collection</code> to determine the component type and
* return the pluralized version of that component type.
* @param value the value to generate a variable name for
* @return the generated variable name
*/
public static String getVariableName(Object value) {
Assert.notNull(value, "Value must not be null");
Class valueClass;
boolean pluralize = false;
if (value.getClass().isArray()) {
valueClass = value.getClass().getComponentType();
pluralize = true;
}
else if (value instanceof Collection) {
Collection collection = (Collection) value;
if (collection.isEmpty()) {
throw new IllegalArgumentException("Cannot generate variable name for an empty Collection");
}
Object valueToCheck = peekAhead(collection);
valueClass = getClassForValue(valueToCheck);
pluralize = true;
}
else {
valueClass = getClassForValue(value);
}
String name = ClassUtils.getShortNameAsProperty(valueClass);
return (pluralize ? pluralize(name) : name);
}
/**
* Determine the conventional variable name for the supplied parameter,
* taking the generic collection type (if any) into account.
* @param parameter the method or constructor parameter to generate a variable name for
* @return the generated variable name
*/
public static String getVariableNameForParameter(MethodParameter parameter) {
Assert.notNull(parameter, "MethodParameter must not be null");
Class valueClass;
boolean pluralize = false;
if (parameter.getParameterType().isArray()) {
valueClass = parameter.getParameterType().getComponentType();
pluralize = true;
}
else if (Collection.class.isAssignableFrom(parameter.getParameterType())) {
valueClass = GenericCollectionTypeResolver.getCollectionParameterType(parameter);
if (valueClass == null) {
throw new IllegalArgumentException(
"Cannot generate variable name for non-typed Collection parameter type");
}
pluralize = true;
}
else {
valueClass = parameter.getParameterType();
}
String name = ClassUtils.getShortNameAsProperty(valueClass);
return (pluralize ? pluralize(name) : name);
}
/**
* Determine the conventional variable name for the return type of the supplied method,
* taking the generic collection type (if any) into account.
* @param method the method to generate a variable name for
* @return the generated variable name
*/
public static String getVariableNameForReturnType(Method method) {
return getVariableNameForReturnType(method, method.getReturnType(), null);
}
/**
* Determine the conventional variable name for the return type of the supplied method,
* taking the generic collection type (if any) into account, falling back to the
* given return value if the method declaration is not specific enough (i.e. in case of
* the return type being declared as <code>Object</code> or as untyped collection).
* @param method the method to generate a variable name for
* @param value the return value (may be <code>null</code> if not available)
* @return the generated variable name
*/
public static String getVariableNameForReturnType(Method method, Object value) {
return getVariableNameForReturnType(method, method.getReturnType(), value);
}
/**
* Determine the conventional variable name for the return type of the supplied method,
* taking the generic collection type (if any) into account, falling back to the
* given return value if the method declaration is not specific enough (i.e. in case of
* the return type being declared as <code>Object</code> or as untyped collection).
* @param method the method to generate a variable name for
* @param resolvedType the resolved return type of the method
* @param value the return value (may be <code>null</code> if not available)
* @return the generated variable name
*/
public static String getVariableNameForReturnType(Method method, Class resolvedType, Object value) {
Assert.notNull(method, "Method must not be null");
if (Object.class.equals(resolvedType)) {
if (value == null) {
throw new IllegalArgumentException("Cannot generate variable name for an Object return type with null value");
}
return getVariableName(value);
}
Class valueClass;
boolean pluralize = false;
if (resolvedType.isArray()) {
valueClass = resolvedType.getComponentType();
pluralize = true;
}
else if (Collection.class.isAssignableFrom(resolvedType)) {
valueClass = GenericCollectionTypeResolver.getCollectionReturnType(method);
if (valueClass == null) {
if (!(value instanceof Collection)) {
throw new IllegalArgumentException(
"Cannot generate variable name for non-typed Collection return type and a non-Collection value");
}
Collection collection = (Collection) value;
if (collection.isEmpty()) {
throw new IllegalArgumentException(
"Cannot generate variable name for non-typed Collection return type and an empty Collection value");
}
Object valueToCheck = peekAhead(collection);
valueClass = getClassForValue(valueToCheck);
}
pluralize = true;
}
else {
valueClass = resolvedType;
}
String name = ClassUtils.getShortNameAsProperty(valueClass);
return (pluralize ? pluralize(name) : name);
}
/**
* Convert <code>String</code>s in attribute name format (lowercase, hyphens separating words)
* into property name format (camel-cased). For example, <code>transaction-manager</code> is
* converted into <code>transactionManager</code>.
*/
public static String attributeNameToPropertyName(String attributeName) {
Assert.notNull(attributeName, "'attributeName' must not be null");
if (!attributeName.contains("-")) {
return attributeName;
}
char[] chars = attributeName.toCharArray();
char[] result = new char[chars.length -1]; // not completely accurate but good guess
int currPos = 0;
boolean upperCaseNext = false;
for (char c : chars) {
if (c == '-') {
upperCaseNext = true;
}
else if (upperCaseNext) {
result[currPos++] = Character.toUpperCase(c);
upperCaseNext = false;
}
else {
result[currPos++] = c;
}
}
return new String(result, 0, currPos);
}
/**
* Return an attribute name qualified by the supplied enclosing {@link Class}. For example,
* the attribute name '<code>foo</code>' qualified by {@link Class} '<code>com.myapp.SomeClass</code>'
* would be '<code>com.myapp.SomeClass.foo</code>'
*/
public static String getQualifiedAttributeName(Class enclosingClass, String attributeName) {
Assert.notNull(enclosingClass, "'enclosingClass' must not be null");
Assert.notNull(attributeName, "'attributeName' must not be null");
return enclosingClass.getName() + "." + attributeName;
}
/**
* Determines the class to use for naming a variable that contains
* the given value.
* <p>Will return the class of the given value, except when
* encountering a JDK proxy, in which case it will determine
* the 'primary' interface implemented by that proxy.
* @param value the value to check
* @return the class to use for naming a variable
*/
private static Class getClassForValue(Object value) {
Class valueClass = value.getClass();
if (Proxy.isProxyClass(valueClass)) {
Class[] ifcs = valueClass.getInterfaces();
for (Class ifc : ifcs) {
if (!ignoredInterfaces.contains(ifc)) {
return ifc;
}
}
}
else if (valueClass.getName().lastIndexOf('$') != -1 && valueClass.getDeclaringClass() == null) {
// '$' in the class name but no inner class -
// assuming it's a special subclass (e.g. by OpenJPA)
valueClass = valueClass.getSuperclass();
}
return valueClass;
}
/**
* Pluralize the given name.
*/
private static String pluralize(String name) {
return name + PLURAL_SUFFIX;
}
/**
* Retrieves the <code>Class</code> of an element in the <code>Collection</code>.
* The exact element for which the <code>Class</code> is retreived will depend
* on the concrete <code>Collection</code> implementation.
*/
private static Object peekAhead(Collection collection) {
Iterator it = collection.iterator();
if (!it.hasNext()) {
throw new IllegalStateException(
"Unable to peek ahead in non-empty collection - no element found");
}
Object value = it.next();
if (value == null) {
throw new IllegalStateException(
"Unable to peek ahead in non-empty collection - only null element found");
}
return value;
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* 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 org.springframework.core;
import java.util.HashSet;
import java.util.Set;
import org.springframework.util.Assert;
/**
* Base class for decorating ClassLoaders such as {@link OverridingClassLoader}
* and {@link org.springframework.instrument.classloading.ShadowingClassLoader},
* providing common handling of excluded packages and classes.
*
* @author Juergen Hoeller
* @author Rod Johnson
* @since 2.5.2
*/
public abstract class DecoratingClassLoader extends ClassLoader {
private final Set<String> excludedPackages = new HashSet<String>();
private final Set<String> excludedClasses = new HashSet<String>();
private final Object exclusionMonitor = new Object();
/**
* Create a new DecoratingClassLoader with no parent ClassLoader.
*/
public DecoratingClassLoader() {
}
/**
* Create a new DecoratingClassLoader using the given parent ClassLoader
* for delegation.
*/
public DecoratingClassLoader(ClassLoader parent) {
super(parent);
}
/**
* Add a package name to exclude from decoration (e.g. overriding).
* <p>Any class whose fully-qualified name starts with the name registered
* here will be handled by the parent ClassLoader in the usual fashion.
* @param packageName the package name to exclude
*/
public void excludePackage(String packageName) {
Assert.notNull(packageName, "Package name must not be null");
synchronized (this.exclusionMonitor) {
this.excludedPackages.add(packageName);
}
}
/**
* Add a class name to exclude from decoration (e.g. overriding).
* <p>Any class name registered here will be handled by the parent
* ClassLoader in the usual fashion.
* @param className the class name to exclude
*/
public void excludeClass(String className) {
Assert.notNull(className, "Class name must not be null");
synchronized (this.exclusionMonitor) {
this.excludedClasses.add(className);
}
}
/**
* Determine whether the specified class is excluded from decoration
* by this class loader.
* <p>The default implementation checks against excluded packages and classes.
* @param className the class name to check
* @return whether the specified class is eligible
* @see #excludePackage
* @see #excludeClass
*/
protected boolean isExcluded(String className) {
synchronized (this.exclusionMonitor) {
if (this.excludedClasses.contains(className)) {
return true;
}
for (String packageName : this.excludedPackages) {
if (className.startsWith(packageName)) {
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* 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 org.springframework.core;
/**
* Interface that can be implemented by exceptions etc that are error coded.
* The error code is a String, rather than a number, so it can be given
* user-readable values, such as "object.failureDescription".
*
* <p>An error code can be resolved by a MessageSource, for example.
*
* @author Rod Johnson
* @see org.springframework.context.MessageSource
*/
public interface ErrorCoded {
/**
* Return the error code associated with this failure.
* The GUI can render this any way it pleases, allowing for localization etc.
* @return a String error code associated with this failure,
* or <code>null</code> if not error-coded
*/
String getErrorCode();
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.springframework.util.Assert;
/**
* Comparator capable of sorting exceptions based on their depth from the thrown exception type.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
* @since 3.0.3
*/
public class ExceptionDepthComparator implements Comparator<Class<? extends Throwable>> {
private final Class<? extends Throwable> targetException;
/**
* Create a new ExceptionDepthComparator for the given exception.
* @param exception the target exception to compare to when sorting by depth
*/
public ExceptionDepthComparator(Throwable exception) {
Assert.notNull(exception, "Target exception must not be null");
this.targetException = exception.getClass();
}
/**
* Create a new ExceptionDepthComparator for the given exception type.
* @param exceptionType the target exception type to compare to when sorting by depth
*/
public ExceptionDepthComparator(Class<? extends Throwable> exceptionType) {
Assert.notNull(exceptionType, "Target exception type must not be null");
this.targetException = exceptionType;
}
public int compare(Class<? extends Throwable> o1, Class<? extends Throwable> o2) {
int depth1 = getDepth(o1, this.targetException, 0);
int depth2 = getDepth(o2, this.targetException, 0);
return (depth1 - depth2);
}
private int getDepth(Class declaredException, Class exceptionToMatch, int depth) {
if (declaredException.equals(exceptionToMatch)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (Throwable.class.equals(exceptionToMatch)) {
return Integer.MAX_VALUE;
}
return getDepth(declaredException, exceptionToMatch.getSuperclass(), depth + 1);
}
/**
* Obtain the closest match from the given exception types for the given target exception.
* @param exceptionTypes the collection of exception types
* @param targetException the target exception to find a match for
* @return the closest matching exception type from the given collection
*/
public static Class<? extends Throwable> findClosestMatch(
Collection<Class<? extends Throwable>> exceptionTypes, Throwable targetException) {
Assert.notEmpty(exceptionTypes, "Exception types must not be empty");
if (exceptionTypes.size() == 1) {
return exceptionTypes.iterator().next();
}
List<Class<? extends Throwable>> handledExceptions =
new ArrayList<Class<? extends Throwable>>(exceptionTypes);
Collections.sort(handledExceptions, new ExceptionDepthComparator(targetException));
return handledExceptions.get(0);
}
}

View File

@@ -0,0 +1,477 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Collection;
import java.util.Map;
/**
* Helper class for determining element types of collections and maps.
*
* <p>Mainly intended for usage within the framework, determining the
* target type of values to be added to a collection or map
* (to be able to attempt type conversion if appropriate).
*
* @author Juergen Hoeller
* @since 2.0
*/
public abstract class GenericCollectionTypeResolver {
/**
* Determine the generic element type of the given Collection class
* (if it declares one through a generic superclass or generic interface).
* @param collectionClass the collection class to introspect
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getCollectionType(Class<? extends Collection> collectionClass) {
return extractTypeFromClass(collectionClass, Collection.class, 0);
}
/**
* Determine the generic key type of the given Map class
* (if it declares one through a generic superclass or generic interface).
* @param mapClass the map class to introspect
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapKeyType(Class<? extends Map> mapClass) {
return extractTypeFromClass(mapClass, Map.class, 0);
}
/**
* Determine the generic value type of the given Map class
* (if it declares one through a generic superclass or generic interface).
* @param mapClass the map class to introspect
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapValueType(Class<? extends Map> mapClass) {
return extractTypeFromClass(mapClass, Map.class, 1);
}
/**
* Determine the generic element type of the given Collection field.
* @param collectionField the collection field to introspect
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getCollectionFieldType(Field collectionField) {
return getGenericFieldType(collectionField, Collection.class, 0, null, 1);
}
/**
* Determine the generic element type of the given Collection field.
* @param collectionField the collection field to introspect
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getCollectionFieldType(Field collectionField, int nestingLevel) {
return getGenericFieldType(collectionField, Collection.class, 0, null, nestingLevel);
}
/**
* Determine the generic element type of the given Collection field.
* @param collectionField the collection field to introspect
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* @param typeIndexesPerLevel Map keyed by nesting level, with each value
* expressing the type index for traversal at that level
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getCollectionFieldType(Field collectionField, int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
return getGenericFieldType(collectionField, Collection.class, 0, typeIndexesPerLevel, nestingLevel);
}
/**
* Determine the generic key type of the given Map field.
* @param mapField the map field to introspect
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapKeyFieldType(Field mapField) {
return getGenericFieldType(mapField, Map.class, 0, null, 1);
}
/**
* Determine the generic key type of the given Map field.
* @param mapField the map field to introspect
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapKeyFieldType(Field mapField, int nestingLevel) {
return getGenericFieldType(mapField, Map.class, 0, null, nestingLevel);
}
/**
* Determine the generic key type of the given Map field.
* @param mapField the map field to introspect
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* @param typeIndexesPerLevel Map keyed by nesting level, with each value
* expressing the type index for traversal at that level
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapKeyFieldType(Field mapField, int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
return getGenericFieldType(mapField, Map.class, 0, typeIndexesPerLevel, nestingLevel);
}
/**
* Determine the generic value type of the given Map field.
* @param mapField the map field to introspect
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapValueFieldType(Field mapField) {
return getGenericFieldType(mapField, Map.class, 1, null, 1);
}
/**
* Determine the generic value type of the given Map field.
* @param mapField the map field to introspect
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapValueFieldType(Field mapField, int nestingLevel) {
return getGenericFieldType(mapField, Map.class, 1, null, nestingLevel);
}
/**
* Determine the generic value type of the given Map field.
* @param mapField the map field to introspect
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* @param typeIndexesPerLevel Map keyed by nesting level, with each value
* expressing the type index for traversal at that level
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapValueFieldType(Field mapField, int nestingLevel, Map<Integer, Integer> typeIndexesPerLevel) {
return getGenericFieldType(mapField, Map.class, 1, typeIndexesPerLevel, nestingLevel);
}
/**
* Determine the generic element type of the given Collection parameter.
* @param methodParam the method parameter specification
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getCollectionParameterType(MethodParameter methodParam) {
return getGenericParameterType(methodParam, Collection.class, 0);
}
/**
* Determine the generic key type of the given Map parameter.
* @param methodParam the method parameter specification
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapKeyParameterType(MethodParameter methodParam) {
return getGenericParameterType(methodParam, Map.class, 0);
}
/**
* Determine the generic value type of the given Map parameter.
* @param methodParam the method parameter specification
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapValueParameterType(MethodParameter methodParam) {
return getGenericParameterType(methodParam, Map.class, 1);
}
/**
* Determine the generic element type of the given Collection return type.
* @param method the method to check the return type for
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getCollectionReturnType(Method method) {
return getGenericReturnType(method, Collection.class, 0, 1);
}
/**
* Determine the generic element type of the given Collection return type.
* <p>If the specified nesting level is higher than 1, the element type of
* a nested Collection/Map will be analyzed.
* @param method the method to check the return type for
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getCollectionReturnType(Method method, int nestingLevel) {
return getGenericReturnType(method, Collection.class, 0, nestingLevel);
}
/**
* Determine the generic key type of the given Map return type.
* @param method the method to check the return type for
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapKeyReturnType(Method method) {
return getGenericReturnType(method, Map.class, 0, 1);
}
/**
* Determine the generic key type of the given Map return type.
* @param method the method to check the return type for
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapKeyReturnType(Method method, int nestingLevel) {
return getGenericReturnType(method, Map.class, 0, nestingLevel);
}
/**
* Determine the generic value type of the given Map return type.
* @param method the method to check the return type for
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapValueReturnType(Method method) {
return getGenericReturnType(method, Map.class, 1, 1);
}
/**
* Determine the generic value type of the given Map return type.
* @param method the method to check the return type for
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
* @return the generic type, or <code>null</code> if none
*/
public static Class<?> getMapValueReturnType(Method method, int nestingLevel) {
return getGenericReturnType(method, Map.class, 1, nestingLevel);
}
/**
* Extract the generic parameter type from the given method or constructor.
* @param methodParam the method parameter specification
* @param source the source class/interface defining the generic parameter types
* @param typeIndex the index of the type (e.g. 0 for Collections,
* 0 for Map keys, 1 for Map values)
* @return the generic type, or <code>null</code> if none
*/
private static Class<?> getGenericParameterType(MethodParameter methodParam, Class<?> source, int typeIndex) {
return extractType(GenericTypeResolver.getTargetType(methodParam), source, typeIndex,
methodParam.typeVariableMap, methodParam.typeIndexesPerLevel, methodParam.getNestingLevel(), 1);
}
/**
* Extract the generic type from the given field.
* @param field the field to check the type for
* @param source the source class/interface defining the generic parameter types
* @param typeIndex the index of the type (e.g. 0 for Collections,
* 0 for Map keys, 1 for Map values)
* @param nestingLevel the nesting level of the target type
* @return the generic type, or <code>null</code> if none
*/
private static Class<?> getGenericFieldType(Field field, Class<?> source, int typeIndex,
Map<Integer, Integer> typeIndexesPerLevel, int nestingLevel) {
return extractType(field.getGenericType(), source, typeIndex, null, typeIndexesPerLevel, nestingLevel, 1);
}
/**
* Extract the generic return type from the given method.
* @param method the method to check the return type for
* @param source the source class/interface defining the generic parameter types
* @param typeIndex the index of the type (e.g. 0 for Collections,
* 0 for Map keys, 1 for Map values)
* @param nestingLevel the nesting level of the target type
* @return the generic type, or <code>null</code> if none
*/
private static Class<?> getGenericReturnType(Method method, Class<?> source, int typeIndex, int nestingLevel) {
return extractType(method.getGenericReturnType(), source, typeIndex, null, null, nestingLevel, 1);
}
/**
* Extract the generic type from the given Type object.
* @param type the Type to check
* @param source the source collection/map Class that we check
* @param typeIndex the index of the actual type argument
* @param nestingLevel the nesting level of the target type
* @param currentLevel the current nested level
* @return the generic type as Class, or <code>null</code> if none
*/
private static Class<?> extractType(Type type, Class<?> source, int typeIndex,
Map<TypeVariable, Type> typeVariableMap, Map<Integer, Integer> typeIndexesPerLevel,
int nestingLevel, int currentLevel) {
Type resolvedType = type;
if (type instanceof TypeVariable && typeVariableMap != null) {
Type mappedType = typeVariableMap.get((TypeVariable) type);
if (mappedType != null) {
resolvedType = mappedType;
}
}
if (resolvedType instanceof ParameterizedType) {
return extractTypeFromParameterizedType((ParameterizedType) resolvedType, source, typeIndex, typeVariableMap, typeIndexesPerLevel,
nestingLevel, currentLevel);
}
else if (resolvedType instanceof Class) {
return extractTypeFromClass((Class) resolvedType, source, typeIndex, typeVariableMap, typeIndexesPerLevel,
nestingLevel, currentLevel);
}
else if (resolvedType instanceof GenericArrayType) {
Type compType = ((GenericArrayType) resolvedType).getGenericComponentType();
return extractType(compType, source, typeIndex, typeVariableMap, typeIndexesPerLevel, nestingLevel, currentLevel + 1);
}
else {
return null;
}
}
/**
* Extract the generic type from the given ParameterizedType object.
* @param ptype the ParameterizedType to check
* @param source the expected raw source type (can be <code>null</code>)
* @param typeIndex the index of the actual type argument
* @param nestingLevel the nesting level of the target type
* @param currentLevel the current nested level
* @return the generic type as Class, or <code>null</code> if none
*/
private static Class<?> extractTypeFromParameterizedType(ParameterizedType ptype, Class<?> source, int typeIndex,
Map<TypeVariable, Type> typeVariableMap, Map<Integer, Integer> typeIndexesPerLevel,
int nestingLevel, int currentLevel) {
if (!(ptype.getRawType() instanceof Class)) {
return null;
}
Class rawType = (Class) ptype.getRawType();
Type[] paramTypes = ptype.getActualTypeArguments();
if (nestingLevel - currentLevel > 0) {
int nextLevel = currentLevel + 1;
Integer currentTypeIndex = (typeIndexesPerLevel != null ? typeIndexesPerLevel.get(nextLevel) : null);
// Default is last parameter type: Collection element or Map value.
int indexToUse = (currentTypeIndex != null ? currentTypeIndex : paramTypes.length - 1);
Type paramType = paramTypes[indexToUse];
return extractType(paramType, source, typeIndex, typeVariableMap, typeIndexesPerLevel, nestingLevel, nextLevel);
}
if (source != null && !source.isAssignableFrom(rawType)) {
return null;
}
Class fromSuperclassOrInterface = extractTypeFromClass(rawType, source, typeIndex, typeVariableMap, typeIndexesPerLevel,
nestingLevel, currentLevel);
if (fromSuperclassOrInterface != null) {
return fromSuperclassOrInterface;
}
if (paramTypes == null || typeIndex >= paramTypes.length) {
return null;
}
Type paramType = paramTypes[typeIndex];
if (paramType instanceof TypeVariable && typeVariableMap != null) {
Type mappedType = typeVariableMap.get((TypeVariable) paramType);
if (mappedType != null) {
paramType = mappedType;
}
}
if (paramType instanceof WildcardType) {
WildcardType wildcardType = (WildcardType) paramType;
Type[] upperBounds = wildcardType.getUpperBounds();
if (upperBounds != null && upperBounds.length > 0 && !Object.class.equals(upperBounds[0])) {
paramType = upperBounds[0];
}
else {
Type[] lowerBounds = wildcardType.getLowerBounds();
if (lowerBounds != null && lowerBounds.length > 0 && !Object.class.equals(lowerBounds[0])) {
paramType = lowerBounds[0];
}
}
}
if (paramType instanceof ParameterizedType) {
paramType = ((ParameterizedType) paramType).getRawType();
}
if (paramType instanceof GenericArrayType) {
// A generic array type... Let's turn it into a straight array type if possible.
Type compType = ((GenericArrayType) paramType).getGenericComponentType();
if (compType instanceof Class) {
return Array.newInstance((Class) compType, 0).getClass();
}
}
else if (paramType instanceof Class) {
// We finally got a straight Class...
return (Class) paramType;
}
return null;
}
/**
* Extract the generic type from the given Class object.
* @param clazz the Class to check
* @param source the expected raw source type (can be <code>null</code>)
* @param typeIndex the index of the actual type argument
* @return the generic type as Class, or <code>null</code> if none
*/
private static Class<?> extractTypeFromClass(Class<?> clazz, Class<?> source, int typeIndex) {
return extractTypeFromClass(clazz, source, typeIndex, null, null, 1, 1);
}
/**
* Extract the generic type from the given Class object.
* @param clazz the Class to check
* @param source the expected raw source type (can be <code>null</code>)
* @param typeIndex the index of the actual type argument
* @param nestingLevel the nesting level of the target type
* @param currentLevel the current nested level
* @return the generic type as Class, or <code>null</code> if none
*/
private static Class<?> extractTypeFromClass(Class<?> clazz, Class<?> source, int typeIndex,
Map<TypeVariable, Type> typeVariableMap, Map<Integer, Integer> typeIndexesPerLevel,
int nestingLevel, int currentLevel) {
if (clazz.getName().startsWith("java.util.")) {
return null;
}
if (clazz.getSuperclass() != null && isIntrospectionCandidate(clazz.getSuperclass())) {
return extractType(clazz.getGenericSuperclass(), source, typeIndex, typeVariableMap, typeIndexesPerLevel,
nestingLevel, currentLevel);
}
Type[] ifcs = clazz.getGenericInterfaces();
if (ifcs != null) {
for (Type ifc : ifcs) {
Type rawType = ifc;
if (ifc instanceof ParameterizedType) {
rawType = ((ParameterizedType) ifc).getRawType();
}
if (rawType instanceof Class && isIntrospectionCandidate((Class) rawType)) {
return extractType(ifc, source, typeIndex, typeVariableMap, typeIndexesPerLevel, nestingLevel, currentLevel);
}
}
}
return null;
}
/**
* Determine whether the given class is a potential candidate
* that defines generic collection or map types.
* @param clazz the class to check
* @return whether the given class is assignable to Collection or Map
*/
private static boolean isIntrospectionCandidate(Class clazz) {
return (Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz));
}
}

View File

@@ -0,0 +1,397 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.Method;
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;
import java.util.WeakHashMap;
import org.springframework.util.Assert;
/**
* Helper class for resolving generic types against type variables.
*
* <p>Mainly intended for usage within the framework, resolving method
* parameter types even when they are declared generically.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 2.5.2
* @see GenericCollectionTypeResolver
*/
public abstract class GenericTypeResolver {
/** Cache from Class to TypeVariable Map */
private static final Map<Class, Reference<Map<TypeVariable, Type>>> typeVariableCache =
Collections.synchronizedMap(new WeakHashMap<Class, Reference<Map<TypeVariable, Type>>>());
/**
* Determine the target type for the given parameter specification.
* @param methodParam the method parameter specification
* @return the corresponding generic parameter type
*/
public static Type getTargetType(MethodParameter methodParam) {
Assert.notNull(methodParam, "MethodParameter must not be null");
if (methodParam.getConstructor() != null) {
return methodParam.getConstructor().getGenericParameterTypes()[methodParam.getParameterIndex()];
}
else {
if (methodParam.getParameterIndex() >= 0) {
return methodParam.getMethod().getGenericParameterTypes()[methodParam.getParameterIndex()];
}
else {
return methodParam.getMethod().getGenericReturnType();
}
}
}
/**
* Determine the target type for the given generic parameter type.
* @param methodParam the method parameter specification
* @param clazz the class to resolve type variables against
* @return the corresponding generic parameter or return type
*/
public static Class<?> resolveParameterType(MethodParameter methodParam, Class clazz) {
Type genericType = getTargetType(methodParam);
Assert.notNull(clazz, "Class must not be null");
Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
Type rawType = getRawType(genericType, typeVariableMap);
Class result = (rawType instanceof Class ? (Class) rawType : methodParam.getParameterType());
methodParam.setParameterType(result);
methodParam.typeVariableMap = typeVariableMap;
return result;
}
/**
* Determine the target type for the generic return type of the given method.
* @param method the method to introspect
* @param clazz the class to resolve type variables against
* @return the corresponding generic parameter or return type
*/
public static Class<?> resolveReturnType(Method method, Class clazz) {
Assert.notNull(method, "Method must not be null");
Type genericType = method.getGenericReturnType();
Assert.notNull(clazz, "Class must not be null");
Map<TypeVariable, Type> typeVariableMap = getTypeVariableMap(clazz);
Type rawType = getRawType(genericType, typeVariableMap);
return (rawType instanceof Class ? (Class) rawType : method.getReturnType());
}
/**
* Resolve the single type argument of the given generic interface against the given
* target method which is assumed to return the given interface or an implementation
* of it.
* @param method the target method to check the return type of
* @param genericIfc the generic interface or superclass to resolve the type argument from
* @return the resolved parameter type of the method return type, or <code>null</code>
* if not resolvable or if the single argument is of type {@link WildcardType}.
*/
public static Class<?> resolveReturnTypeArgument(Method method, Class<?> genericIfc) {
Assert.notNull(method, "method must not be null");
Type returnType = method.getReturnType();
Type genericReturnType = method.getGenericReturnType();
if (returnType.equals(genericIfc)) {
if (genericReturnType instanceof ParameterizedType) {
ParameterizedType targetType = (ParameterizedType) genericReturnType;
Type[] actualTypeArguments = targetType.getActualTypeArguments();
Type typeArg = actualTypeArguments[0];
if (!(typeArg instanceof WildcardType)) {
return (Class<?>) typeArg;
}
}
else {
return null;
}
}
return GenericTypeResolver.resolveTypeArgument((Class<?>) returnType, genericIfc);
}
/**
* Resolve the single type argument of the given generic interface against
* the given target class which is assumed to implement the generic interface
* and possibly declare a concrete type for its type variable.
* @param clazz the target class to check against
* @param genericIfc the generic interface or superclass to resolve the type argument from
* @return the resolved type of the argument, or <code>null</code> if not resolvable
*/
public static Class<?> resolveTypeArgument(Class clazz, Class genericIfc) {
Class[] typeArgs = resolveTypeArguments(clazz, genericIfc);
if (typeArgs == null) {
return null;
}
if (typeArgs.length != 1) {
throw new IllegalArgumentException("Expected 1 type argument on generic interface [" +
genericIfc.getName() + "] but found " + typeArgs.length);
}
return typeArgs[0];
}
/**
* Resolve the type arguments of the given generic interface against the given
* target class which is assumed to implement the generic interface and possibly
* declare concrete types for its type variables.
* @param clazz the target class to check against
* @param genericIfc the generic interface or superclass to resolve the type argument from
* @return the resolved type of each argument, with the array size matching the
* number of actual type arguments, or <code>null</code> if not resolvable
*/
public static Class[] resolveTypeArguments(Class clazz, Class genericIfc) {
return doResolveTypeArguments(clazz, clazz, genericIfc);
}
private static Class[] doResolveTypeArguments(Class ownerClass, Class classToIntrospect, Class genericIfc) {
while (classToIntrospect != null) {
if (genericIfc.isInterface()) {
Type[] ifcs = classToIntrospect.getGenericInterfaces();
for (Type ifc : ifcs) {
Class[] result = doResolveTypeArguments(ownerClass, ifc, genericIfc);
if (result != null) {
return result;
}
}
}
else {
Class[] result = doResolveTypeArguments(
ownerClass, classToIntrospect.getGenericSuperclass(), genericIfc);
if (result != null) {
return result;
}
}
classToIntrospect = classToIntrospect.getSuperclass();
}
return null;
}
private static Class[] doResolveTypeArguments(Class ownerClass, Type ifc, Class genericIfc) {
if (ifc instanceof ParameterizedType) {
ParameterizedType paramIfc = (ParameterizedType) ifc;
Type rawType = paramIfc.getRawType();
if (genericIfc.equals(rawType)) {
Type[] typeArgs = paramIfc.getActualTypeArguments();
Class[] result = new Class[typeArgs.length];
for (int i = 0; i < typeArgs.length; i++) {
Type arg = typeArgs[i];
result[i] = extractClass(ownerClass, arg);
}
return result;
}
else if (genericIfc.isAssignableFrom((Class) rawType)) {
return doResolveTypeArguments(ownerClass, (Class) rawType, genericIfc);
}
}
else if (ifc != null && genericIfc.isAssignableFrom((Class) ifc)) {
return doResolveTypeArguments(ownerClass, (Class) ifc, genericIfc);
}
return null;
}
/**
* Extract a class instance from given Type.
*/
private static Class extractClass(Class ownerClass, Type arg) {
if (arg instanceof ParameterizedType) {
return extractClass(ownerClass, ((ParameterizedType) arg).getRawType());
}
else if (arg instanceof GenericArrayType) {
GenericArrayType gat = (GenericArrayType) arg;
Type gt = gat.getGenericComponentType();
Class<?> componentClass = extractClass(ownerClass, gt);
return Array.newInstance(componentClass, 0).getClass();
}
else if (arg instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) arg;
arg = getTypeVariableMap(ownerClass).get(tv);
if (arg == null) {
arg = extractBoundForTypeVariable(tv);
}
else {
arg = extractClass(ownerClass, arg);
}
}
return (arg instanceof Class ? (Class) arg : Object.class);
}
/**
* Resolve the specified generic type against the given TypeVariable map.
* @param genericType the generic type to resolve
* @param typeVariableMap the TypeVariable Map to resolved against
* @return the type if it resolves to a Class, or <code>Object.class</code> otherwise
*/
public static Class<?> resolveType(Type genericType, Map<TypeVariable, Type> typeVariableMap) {
Type rawType = getRawType(genericType, typeVariableMap);
return (rawType instanceof Class ? (Class) rawType : Object.class);
}
/**
* Determine the raw type for the given generic parameter type.
* @param genericType the generic type to resolve
* @param typeVariableMap the TypeVariable Map to resolved against
* @return the resolved raw type
*/
static Type getRawType(Type genericType, Map<TypeVariable, Type> typeVariableMap) {
Type resolvedType = genericType;
if (genericType instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) genericType;
resolvedType = typeVariableMap.get(tv);
if (resolvedType == null) {
resolvedType = extractBoundForTypeVariable(tv);
}
}
if (resolvedType instanceof ParameterizedType) {
return ((ParameterizedType) resolvedType).getRawType();
}
else {
return resolvedType;
}
}
/**
* Build a mapping of {@link TypeVariable#getName TypeVariable names} to concrete
* {@link Class} for the specified {@link Class}. Searches all super types,
* enclosing types and interfaces.
*/
public static Map<TypeVariable, Type> getTypeVariableMap(Class clazz) {
Reference<Map<TypeVariable, Type>> ref = typeVariableCache.get(clazz);
Map<TypeVariable, Type> typeVariableMap = (ref != null ? ref.get() : null);
if (typeVariableMap == null) {
typeVariableMap = new HashMap<TypeVariable, Type>();
// interfaces
extractTypeVariablesFromGenericInterfaces(clazz.getGenericInterfaces(), typeVariableMap);
// super class
Type genericType = clazz.getGenericSuperclass();
Class type = clazz.getSuperclass();
while (type != null && !Object.class.equals(type)) {
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
populateTypeMapFromParameterizedType(pt, typeVariableMap);
}
extractTypeVariablesFromGenericInterfaces(type.getGenericInterfaces(), typeVariableMap);
genericType = type.getGenericSuperclass();
type = type.getSuperclass();
}
// enclosing class
type = clazz;
while (type.isMemberClass()) {
genericType = type.getGenericSuperclass();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
populateTypeMapFromParameterizedType(pt, typeVariableMap);
}
type = type.getEnclosingClass();
}
typeVariableCache.put(clazz, new WeakReference<Map<TypeVariable, Type>>(typeVariableMap));
}
return typeVariableMap;
}
/**
* Extracts the bound <code>Type</code> for a given {@link TypeVariable}.
*/
static Type extractBoundForTypeVariable(TypeVariable typeVariable) {
Type[] bounds = typeVariable.getBounds();
if (bounds.length == 0) {
return Object.class;
}
Type bound = bounds[0];
if (bound instanceof TypeVariable) {
bound = extractBoundForTypeVariable((TypeVariable) bound);
}
return bound;
}
private static void extractTypeVariablesFromGenericInterfaces(Type[] genericInterfaces, Map<TypeVariable, Type> typeVariableMap) {
for (Type genericInterface : genericInterfaces) {
if (genericInterface instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericInterface;
populateTypeMapFromParameterizedType(pt, typeVariableMap);
if (pt.getRawType() instanceof Class) {
extractTypeVariablesFromGenericInterfaces(
((Class) pt.getRawType()).getGenericInterfaces(), typeVariableMap);
}
}
else if (genericInterface instanceof Class) {
extractTypeVariablesFromGenericInterfaces(
((Class) genericInterface).getGenericInterfaces(), typeVariableMap);
}
}
}
/**
* Read the {@link TypeVariable TypeVariables} from the supplied {@link ParameterizedType}
* and add mappings corresponding to the {@link TypeVariable#getName TypeVariable name} ->
* concrete type to the supplied {@link Map}.
* <p>Consider this case:
* <pre class="code>
* public interface Foo<S, T> {
* ..
* }
*
* public class FooImpl implements Foo<String, Integer> {
* ..
* }</pre>
* For '<code>FooImpl</code>' the following mappings would be added to the {@link Map}:
* {S=java.lang.String, T=java.lang.Integer}.
*/
private static void populateTypeMapFromParameterizedType(ParameterizedType type, Map<TypeVariable, Type> typeVariableMap) {
if (type.getRawType() instanceof Class) {
Type[] actualTypeArguments = type.getActualTypeArguments();
TypeVariable[] typeVariables = ((Class) type.getRawType()).getTypeParameters();
for (int i = 0; i < actualTypeArguments.length; i++) {
Type actualTypeArgument = actualTypeArguments[i];
TypeVariable variable = typeVariables[i];
if (actualTypeArgument instanceof Class) {
typeVariableMap.put(variable, actualTypeArgument);
}
else if (actualTypeArgument instanceof GenericArrayType) {
typeVariableMap.put(variable, actualTypeArgument);
}
else if (actualTypeArgument instanceof ParameterizedType) {
typeVariableMap.put(variable, actualTypeArgument);
}
else if (actualTypeArgument instanceof TypeVariable) {
// We have a type that is parameterized at instantiation time
// the nearest match on the bridge method will be the bounded type.
TypeVariable typeVariableArgument = (TypeVariable) actualTypeArgument;
Type resolvedType = typeVariableMap.get(typeVariableArgument);
if (resolvedType == null) {
resolvedType = extractBoundForTypeVariable(typeVariableArgument);
}
typeVariableMap.put(variable, resolvedType);
}
}
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* 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 org.springframework.core;
/**
* Interface to be implemented by transparent resource proxies that need to be
* considered as equal to the underlying resource, for example for consistent
* lookup key comparisons. Note that this interface does imply such special
* semantics and does not constitute a general-purpose mixin!
*
* <p>Such wrappers will automatically be unwrapped for key comparisons in
* {@link org.springframework.transaction.support.TransactionSynchronizationManager}.
*
* <p>Only fully transparent proxies, e.g. for redirection or service lookups,
* are supposed to implement this interface. Proxies that decorate the target
* object with new behavior, such as AOP proxies, do <i>not</i> qualify here!
*
* @author Juergen Hoeller
* @since 2.5.4
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public interface InfrastructureProxy {
/**
* Return the underlying resource (never <code>null</code>).
*/
Object getWrappedObject();
}

View File

@@ -0,0 +1,148 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core;
/**
* Internal helper class used to find the Java/JVM version
* that Spring is operating on, to allow for automatically
* adapting to the present platform's capabilities.
*
* <p>Note that Spring requires JVM 1.5 or higher, as of Spring 3.0.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rick Evans
*/
public abstract class JdkVersion {
/**
* Constant identifying the 1.3.x JVM (JDK 1.3).
*/
public static final int JAVA_13 = 0;
/**
* Constant identifying the 1.4.x JVM (J2SE 1.4).
*/
public static final int JAVA_14 = 1;
/**
* Constant identifying the 1.5 JVM (Java 5).
*/
public static final int JAVA_15 = 2;
/**
* Constant identifying the 1.6 JVM (Java 6).
*/
public static final int JAVA_16 = 3;
/**
* Constant identifying the 1.7 JVM (Java 7).
*/
public static final int JAVA_17 = 4;
private static final String javaVersion;
private static final int majorJavaVersion;
static {
javaVersion = System.getProperty("java.version");
// version String should look like "1.4.2_10"
if (javaVersion.contains("1.7.")) {
majorJavaVersion = JAVA_17;
}
else if (javaVersion.contains("1.6.")) {
majorJavaVersion = JAVA_16;
}
else {
// else leave 1.5 as default (it's either 1.5 or unknown)
majorJavaVersion = JAVA_15;
}
}
/**
* Return the full Java version string, as returned by
* <code>System.getProperty("java.version")</code>.
* @return the full Java version string
* @see System#getProperty(String)
*/
public static String getJavaVersion() {
return javaVersion;
}
/**
* Get the major version code. This means we can do things like
* <code>if (getMajorJavaVersion() < JAVA_14)</code>.
* @return a code comparable to the JAVA_XX codes in this class
* @see #JAVA_13
* @see #JAVA_14
* @see #JAVA_15
* @see #JAVA_16
* @see #JAVA_17
*/
public static int getMajorJavaVersion() {
return majorJavaVersion;
}
/**
* Convenience method to determine if the current JVM is at least Java 1.4.
* @return <code>true</code> if the current JVM is at least Java 1.4
* @deprecated as of Spring 3.0 which requires Java 1.5+
* @see #getMajorJavaVersion()
* @see #JAVA_14
* @see #JAVA_15
* @see #JAVA_16
* @see #JAVA_17
*/
@Deprecated
public static boolean isAtLeastJava14() {
return true;
}
/**
* Convenience method to determine if the current JVM is at least
* Java 1.5 (Java 5).
* @return <code>true</code> if the current JVM is at least Java 1.5
* @deprecated as of Spring 3.0 which requires Java 1.5+
* @see #getMajorJavaVersion()
* @see #JAVA_15
* @see #JAVA_16
* @see #JAVA_17
*/
@Deprecated
public static boolean isAtLeastJava15() {
return true;
}
/**
* Convenience method to determine if the current JVM is at least
* Java 1.6 (Java 6).
* @return <code>true</code> if the current JVM is at least Java 1.6
* @deprecated as of Spring 3.0, in favor of reflective checks for
* the specific Java 1.6 classes of interest
* @see #getMajorJavaVersion()
* @see #JAVA_16
* @see #JAVA_17
*/
@Deprecated
public static boolean isAtLeastJava16() {
return (majorJavaVersion >= JAVA_16);
}
}

View File

@@ -0,0 +1,263 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* 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 org.springframework.core;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.asm.ClassReader;
import org.springframework.asm.Label;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;
import org.springframework.asm.Type;
import org.springframework.asm.commons.EmptyVisitor;
import org.springframework.util.ClassUtils;
/**
* Implementation of {@link ParameterNameDiscoverer} that uses the LocalVariableTable
* information in the method attributes to discover parameter names. Returns
* <code>null</code> if the class file was compiled without debug information.
*
* <p>Uses ObjectWeb's ASM library for analyzing class files. Each discoverer
* instance caches the ASM discovered information for each introspected Class, in a
* thread-safe manner. It is recommended to reuse discoverer instances
* as far as possible.
*
* @author Adrian Colyer
* @author Costin Leau
* @author Juergen Hoeller
* @since 2.0
*/
public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer {
private static Log logger = LogFactory.getLog(LocalVariableTableParameterNameDiscoverer.class);
// marker object for classes that do not have any debug info
private static final Map<Member, String[]> NO_DEBUG_INFO_MAP = Collections.emptyMap();
// the cache uses a nested index (value is a map) to keep the top level cache relatively small in size
private final Map<Class<?>, Map<Member, String[]>> parameterNamesCache =
new ConcurrentHashMap<Class<?>, Map<Member, String[]>>();
public String[] getParameterNames(Method method) {
Class<?> declaringClass = method.getDeclaringClass();
Map<Member, String[]> map = this.parameterNamesCache.get(declaringClass);
if (map == null) {
// initialize cache
map = inspectClass(declaringClass);
this.parameterNamesCache.put(declaringClass, map);
}
if (map != NO_DEBUG_INFO_MAP) {
return map.get(method);
}
return null;
}
@SuppressWarnings("unchecked")
public String[] getParameterNames(Constructor ctor) {
Class<?> declaringClass = ctor.getDeclaringClass();
Map<Member, String[]> map = this.parameterNamesCache.get(declaringClass);
if (map == null) {
// initialize cache
map = inspectClass(declaringClass);
this.parameterNamesCache.put(declaringClass, map);
}
if (map != NO_DEBUG_INFO_MAP) {
return map.get(ctor);
}
return null;
}
/**
* Inspects the target class. Exceptions will be logged and a maker map returned
* to indicate the lack of debug information.
*/
private Map<Member, String[]> inspectClass(Class<?> clazz) {
InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz));
if (is == null) {
// We couldn't load the class file, which is not fatal as it
// simply means this method of discovering parameter names won't work.
if (logger.isDebugEnabled()) {
logger.debug("Cannot find '.class' file for class [" + clazz
+ "] - unable to determine constructors/methods parameter names");
}
return NO_DEBUG_INFO_MAP;
}
try {
ClassReader classReader = new ClassReader(is);
Map<Member, String[]> map = new ConcurrentHashMap<Member, String[]>();
classReader.accept(new ParameterNameDiscoveringVisitor(clazz, map), false);
return map;
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Exception thrown while reading '.class' file for class [" + clazz
+ "] - unable to determine constructors/methods parameter names", ex);
}
}
finally {
try {
is.close();
}
catch (IOException ex) {
// ignore
}
}
return NO_DEBUG_INFO_MAP;
}
/**
* Helper class that inspects all methods (constructor included) and then
* attempts to find the parameter names for that member.
*/
private static class ParameterNameDiscoveringVisitor extends EmptyVisitor {
private static final String STATIC_CLASS_INIT = "<clinit>";
private final Class<?> clazz;
private final Map<Member, String[]> memberMap;
public ParameterNameDiscoveringVisitor(Class<?> clazz, Map<Member, String[]> memberMap) {
this.clazz = clazz;
this.memberMap = memberMap;
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// exclude synthetic + bridged && static class initialization
if (!isSyntheticOrBridged(access) && !STATIC_CLASS_INIT.equals(name)) {
return new LocalVariableTableVisitor(clazz, memberMap, name, desc, isStatic(access));
}
return null;
}
private static boolean isSyntheticOrBridged(int access) {
return (((access & Opcodes.ACC_SYNTHETIC) | (access & Opcodes.ACC_BRIDGE)) > 0);
}
private static boolean isStatic(int access) {
return ((access & Opcodes.ACC_STATIC) > 0);
}
}
private static class LocalVariableTableVisitor extends EmptyVisitor {
private static final String CONSTRUCTOR = "<init>";
private final Class<?> clazz;
private final Map<Member, String[]> memberMap;
private final String name;
private final Type[] args;
private final boolean isStatic;
private String[] parameterNames;
private boolean hasLvtInfo = false;
/*
* The nth entry contains the slot index of the LVT table entry holding the
* argument name for the nth parameter.
*/
private final int[] lvtSlotIndex;
public LocalVariableTableVisitor(Class<?> clazz, Map<Member, String[]> map, String name, String desc,
boolean isStatic) {
this.clazz = clazz;
this.memberMap = map;
this.name = name;
// determine args
args = Type.getArgumentTypes(desc);
this.parameterNames = new String[args.length];
this.isStatic = isStatic;
this.lvtSlotIndex = computeLvtSlotIndices(isStatic, args);
}
@Override
public void visitLocalVariable(String name, String description, String signature, Label start, Label end,
int index) {
this.hasLvtInfo = true;
for (int i = 0; i < lvtSlotIndex.length; i++) {
if (lvtSlotIndex[i] == index) {
this.parameterNames[i] = name;
}
}
}
@Override
public void visitEnd() {
if (this.hasLvtInfo || (this.isStatic && this.parameterNames.length == 0)) {
// visitLocalVariable will never be called for static no args methods
// which doesn't use any local variables.
// This means that hasLvtInfo could be false for that kind of methods
// even if the class has local variable info.
memberMap.put(resolveMember(), parameterNames);
}
}
private Member resolveMember() {
ClassLoader loader = clazz.getClassLoader();
Class<?>[] classes = new Class<?>[args.length];
// resolve args
for (int i = 0; i < args.length; i++) {
classes[i] = ClassUtils.resolveClassName(args[i].getClassName(), loader);
}
try {
if (CONSTRUCTOR.equals(name)) {
return clazz.getDeclaredConstructor(classes);
}
return clazz.getDeclaredMethod(name, classes);
} catch (NoSuchMethodException ex) {
throw new IllegalStateException("Method [" + name
+ "] was discovered in the .class file but cannot be resolved in the class object", ex);
}
}
private static int[] computeLvtSlotIndices(boolean isStatic, Type[] paramTypes) {
int[] lvtIndex = new int[paramTypes.length];
int nextIndex = (isStatic ? 0 : 1);
for (int i = 0; i < paramTypes.length; i++) {
lvtIndex[i] = nextIndex;
if (isWideType(paramTypes[i])) {
nextIndex += 2;
} else {
nextIndex++;
}
}
return lvtIndex;
}
private static boolean isWideType(Type aType) {
// float is not a wide type
return (aType == Type.LONG_TYPE || aType == Type.DOUBLE_TYPE);
}
}
}

View File

@@ -0,0 +1,451 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.Map;
import org.springframework.util.Assert;
/**
* Helper class that encapsulates the specification of a method parameter, i.e.
* a Method or Constructor plus a parameter index and a nested type index for
* a declared generic type. Useful as a specification object to pass along.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @author Andy Clement
* @since 2.0
* @see GenericCollectionTypeResolver
*/
public class MethodParameter {
private final Method method;
private final Constructor constructor;
private final int parameterIndex;
private Class<?> parameterType;
private Type genericParameterType;
private Annotation[] parameterAnnotations;
private ParameterNameDiscoverer parameterNameDiscoverer;
private String parameterName;
private int nestingLevel = 1;
/** Map from Integer level to Integer type index */
Map<Integer, Integer> typeIndexesPerLevel;
Map<TypeVariable, Type> typeVariableMap;
private int hash = 0;
/**
* Create a new MethodParameter for the given method, with nesting level 1.
* @param method the Method to specify a parameter for
* @param parameterIndex the index of the parameter
*/
public MethodParameter(Method method, int parameterIndex) {
this(method, parameterIndex, 1);
}
/**
* Create a new MethodParameter for the given method.
* @param method the Method to specify a parameter for
* @param parameterIndex the index of the parameter
* (-1 for the method return type; 0 for the first method parameter,
* 1 for the second method parameter, etc)
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
*/
public MethodParameter(Method method, int parameterIndex, int nestingLevel) {
Assert.notNull(method, "Method must not be null");
this.method = method;
this.parameterIndex = parameterIndex;
this.nestingLevel = nestingLevel;
this.constructor = null;
}
/**
* Create a new MethodParameter for the given constructor, with nesting level 1.
* @param constructor the Constructor to specify a parameter for
* @param parameterIndex the index of the parameter
*/
public MethodParameter(Constructor constructor, int parameterIndex) {
this(constructor, parameterIndex, 1);
}
/**
* Create a new MethodParameter for the given constructor.
* @param constructor the Constructor to specify a parameter for
* @param parameterIndex the index of the parameter
* @param nestingLevel the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List)
*/
public MethodParameter(Constructor constructor, int parameterIndex, int nestingLevel) {
Assert.notNull(constructor, "Constructor must not be null");
this.constructor = constructor;
this.parameterIndex = parameterIndex;
this.nestingLevel = nestingLevel;
this.method = null;
}
/**
* Copy constructor, resulting in an independent MethodParameter object
* based on the same metadata and cache state that the original object was in.
* @param original the original MethodParameter object to copy from
*/
public MethodParameter(MethodParameter original) {
Assert.notNull(original, "Original must not be null");
this.method = original.method;
this.constructor = original.constructor;
this.parameterIndex = original.parameterIndex;
this.parameterType = original.parameterType;
this.genericParameterType = original.genericParameterType;
this.parameterAnnotations = original.parameterAnnotations;
this.parameterNameDiscoverer = original.parameterNameDiscoverer;
this.parameterName = original.parameterName;
this.nestingLevel = original.nestingLevel;
this.typeIndexesPerLevel = original.typeIndexesPerLevel;
this.typeVariableMap = original.typeVariableMap;
this.hash = original.hash;
}
/**
* Return the wrapped Method, if any.
* <p>Note: Either Method or Constructor is available.
* @return the Method, or <code>null</code> if none
*/
public Method getMethod() {
return this.method;
}
/**
* Return the wrapped Constructor, if any.
* <p>Note: Either Method or Constructor is available.
* @return the Constructor, or <code>null</code> if none
*/
public Constructor getConstructor() {
return this.constructor;
}
/**
* Returns the wrapped member.
* @return the member
*/
private Member getMember() {
return this.method != null ? this.method : this.constructor;
}
/**
* Returns the wrapped annotated element.
* @return the annotated element
*/
private AnnotatedElement getAnnotatedElement() {
return this.method != null ? this.method : this.constructor;
}
/**
* Return the class that declares the underlying Method or Constructor.
*/
public Class getDeclaringClass() {
return getMember().getDeclaringClass();
}
/**
* Return the index of the method/constructor parameter.
* @return the parameter index (never negative)
*/
public int getParameterIndex() {
return this.parameterIndex;
}
/**
* Set a resolved (generic) parameter type.
*/
void setParameterType(Class<?> parameterType) {
this.parameterType = parameterType;
}
/**
* Return the type of the method/constructor parameter.
* @return the parameter type (never <code>null</code>)
*/
public Class<?> getParameterType() {
if (this.parameterType == null) {
if (this.parameterIndex < 0) {
this.parameterType = (this.method != null ? this.method.getReturnType() : null);
}
else {
this.parameterType = (this.method != null ?
this.method.getParameterTypes()[this.parameterIndex] :
this.constructor.getParameterTypes()[this.parameterIndex]);
}
}
return this.parameterType;
}
/**
* Return the generic type of the method/constructor parameter.
* @return the parameter type (never <code>null</code>)
*/
public Type getGenericParameterType() {
if (this.genericParameterType == null) {
if (this.parameterIndex < 0) {
this.genericParameterType = (this.method != null ? this.method.getGenericReturnType() : null);
}
else {
this.genericParameterType = (this.method != null ?
this.method.getGenericParameterTypes()[this.parameterIndex] :
this.constructor.getGenericParameterTypes()[this.parameterIndex]);
}
}
return this.genericParameterType;
}
/**
* Return the annotations associated with the target method/constructor itself.
*/
public Annotation[] getMethodAnnotations() {
return getAnnotatedElement().getAnnotations();
}
/**
* Return the method/constructor annotation of the given type, if available.
* @param annotationType the annotation type to look for
* @return the annotation object, or <code>null</code> if not found
*/
@SuppressWarnings("unchecked")
public <T extends Annotation> T getMethodAnnotation(Class<T> annotationType) {
return getAnnotatedElement().getAnnotation(annotationType);
}
/**
* Return the annotations associated with the specific method/constructor parameter.
*/
public Annotation[] getParameterAnnotations() {
if (this.parameterAnnotations == null) {
Annotation[][] annotationArray = (this.method != null ?
this.method.getParameterAnnotations() : this.constructor.getParameterAnnotations());
if (this.parameterIndex >= 0 && this.parameterIndex < annotationArray.length) {
this.parameterAnnotations = annotationArray[this.parameterIndex];
}
else {
this.parameterAnnotations = new Annotation[0];
}
}
return this.parameterAnnotations;
}
/**
* Return the parameter annotation of the given type, if available.
* @param annotationType the annotation type to look for
* @return the annotation object, or <code>null</code> if not found
*/
@SuppressWarnings("unchecked")
public <T extends Annotation> T getParameterAnnotation(Class<T> annotationType) {
Annotation[] anns = getParameterAnnotations();
for (Annotation ann : anns) {
if (annotationType.isInstance(ann)) {
return (T) ann;
}
}
return null;
}
/**
* Return true if the parameter has at least one annotation, false if it has none.
*/
public boolean hasParameterAnnotations() {
return getParameterAnnotations().length != 0;
}
/**
* Return true if the parameter has the given annotation type, and false if it doesn't.
*/
public <T extends Annotation> boolean hasParameterAnnotation(Class<T> annotationType) {
return getParameterAnnotation(annotationType) != null;
}
/**
* Initialize parameter name discovery for this method parameter.
* <p>This method does not actually try to retrieve the parameter name at
* this point; it just allows discovery to happen when the application calls
* {@link #getParameterName()} (if ever).
*/
public void initParameterNameDiscovery(ParameterNameDiscoverer parameterNameDiscoverer) {
this.parameterNameDiscoverer = parameterNameDiscoverer;
}
/**
* Return the name of the method/constructor parameter.
* @return the parameter name (may be <code>null</code> if no
* parameter name metadata is contained in the class file or no
* {@link #initParameterNameDiscovery ParameterNameDiscoverer}
* has been set to begin with)
*/
public String getParameterName() {
if (this.parameterNameDiscoverer != null) {
String[] parameterNames = (this.method != null ?
this.parameterNameDiscoverer.getParameterNames(this.method) :
this.parameterNameDiscoverer.getParameterNames(this.constructor));
if (parameterNames != null) {
this.parameterName = parameterNames[this.parameterIndex];
}
this.parameterNameDiscoverer = null;
}
return this.parameterName;
}
/**
* Increase this parameter's nesting level.
* @see #getNestingLevel()
*/
public void increaseNestingLevel() {
this.nestingLevel++;
}
/**
* Decrease this parameter's nesting level.
* @see #getNestingLevel()
*/
public void decreaseNestingLevel() {
getTypeIndexesPerLevel().remove(this.nestingLevel);
this.nestingLevel--;
}
/**
* Return the nesting level of the target type
* (typically 1; e.g. in case of a List of Lists, 1 would indicate the
* nested List, whereas 2 would indicate the element of the nested List).
*/
public int getNestingLevel() {
return this.nestingLevel;
}
/**
* Set the type index for the current nesting level.
* @param typeIndex the corresponding type index
* (or <code>null</code> for the default type index)
* @see #getNestingLevel()
*/
public void setTypeIndexForCurrentLevel(int typeIndex) {
getTypeIndexesPerLevel().put(this.nestingLevel, typeIndex);
}
/**
* Return the type index for the current nesting level.
* @return the corresponding type index, or <code>null</code>
* if none specified (indicating the default type index)
* @see #getNestingLevel()
*/
public Integer getTypeIndexForCurrentLevel() {
return getTypeIndexForLevel(this.nestingLevel);
}
/**
* Return the type index for the specified nesting level.
* @param nestingLevel the nesting level to check
* @return the corresponding type index, or <code>null</code>
* if none specified (indicating the default type index)
*/
public Integer getTypeIndexForLevel(int nestingLevel) {
return getTypeIndexesPerLevel().get(nestingLevel);
}
/**
* Obtain the (lazily constructed) type-indexes-per-level Map.
*/
private Map<Integer, Integer> getTypeIndexesPerLevel() {
if (this.typeIndexesPerLevel == null) {
this.typeIndexesPerLevel = new HashMap<Integer, Integer>(4);
}
return this.typeIndexesPerLevel;
}
/**
* Create a new MethodParameter for the given method or constructor.
* <p>This is a convenience constructor for scenarios where a
* Method or Constructor reference is treated in a generic fashion.
* @param methodOrConstructor the Method or Constructor to specify a parameter for
* @param parameterIndex the index of the parameter
* @return the corresponding MethodParameter instance
*/
public static MethodParameter forMethodOrConstructor(Object methodOrConstructor, int parameterIndex) {
if (methodOrConstructor instanceof Method) {
return new MethodParameter((Method) methodOrConstructor, parameterIndex);
}
else if (methodOrConstructor instanceof Constructor) {
return new MethodParameter((Constructor) methodOrConstructor, parameterIndex);
}
else {
throw new IllegalArgumentException(
"Given object [" + methodOrConstructor + "] is neither a Method nor a Constructor");
}
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj != null && obj instanceof MethodParameter) {
MethodParameter other = (MethodParameter) obj;
if (this.parameterIndex != other.parameterIndex) {
return false;
}
else if (this.getMember().equals(other.getMember())) {
return true;
}
else {
return false;
}
}
return false;
}
@Override
public int hashCode() {
int result = this.hash;
if (result == 0) {
result = getMember().hashCode();
result = 31 * result + this.parameterIndex;
this.hash = result;
}
return result;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* 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 org.springframework.core;
import org.springframework.util.Assert;
/**
* {@link InheritableThreadLocal} subclass that exposes a specified name
* as {@link #toString()} result (allowing for introspection).
*
* @author Juergen Hoeller
* @since 2.5.2
* @see NamedThreadLocal
*/
public class NamedInheritableThreadLocal<T> extends InheritableThreadLocal<T> {
private final String name;
/**
* Create a new NamedInheritableThreadLocal with the given name.
* @param name a descriptive name for this ThreadLocal
*/
public NamedInheritableThreadLocal(String name) {
Assert.hasText(name, "Name must not be empty");
this.name = name;
}
@Override
public String toString() {
return this.name;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* 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 org.springframework.core;
import org.springframework.util.Assert;
/**
* {@link ThreadLocal} subclass that exposes a specified name
* as {@link #toString()} result (allowing for introspection).
*
* @author Juergen Hoeller
* @since 2.5.2
* @see NamedInheritableThreadLocal
*/
public class NamedThreadLocal<T> extends ThreadLocal<T> {
private final String name;
/**
* Create a new NamedThreadLocal with the given name.
* @param name a descriptive name for this ThreadLocal
*/
public NamedThreadLocal(String name) {
Assert.hasText(name, "Name must not be empty");
this.name = name;
}
@Override
public String toString() {
return this.name;
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core;
/**
* Handy class for wrapping checked <code>Exceptions</code> with a root cause.
*
* <p>This class is <code>abstract</code> to force the programmer to extend
* the class. <code>getMessage</code> will include nested exception
* information; <code>printStackTrace</code> and other like methods will
* delegate to the wrapped exception, if any.
*
* <p>The similarity between this class and the {@link NestedRuntimeException}
* class is unavoidable, as Java forces these two classes to have different
* superclasses (ah, the inflexibility of concrete inheritance!).
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see #getMessage
* @see #printStackTrace
* @see NestedRuntimeException
*/
public abstract class NestedCheckedException extends Exception {
/** Use serialVersionUID from Spring 1.2 for interoperability */
private static final long serialVersionUID = 7100714597678207546L;
static {
// Eagerly load the NestedExceptionUtils class to avoid classloader deadlock
// issues on OSGi when calling getMessage(). Reported by Don Brown; SPR-5607.
NestedExceptionUtils.class.getName();
}
/**
* Construct a <code>NestedCheckedException</code> with the specified detail message.
* @param msg the detail message
*/
public NestedCheckedException(String msg) {
super(msg);
}
/**
* Construct a <code>NestedCheckedException</code> with the specified detail message
* and nested exception.
* @param msg the detail message
* @param cause the nested exception
*/
public NestedCheckedException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Return the detail message, including the message from the nested exception
* if there is one.
*/
@Override
public String getMessage() {
return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
}
/**
* Retrieve the innermost cause of this exception, if any.
* @return the innermost exception, or <code>null</code> if none
*/
public Throwable getRootCause() {
Throwable rootCause = null;
Throwable cause = getCause();
while (cause != null && cause != rootCause) {
rootCause = cause;
cause = cause.getCause();
}
return rootCause;
}
/**
* Retrieve the most specific cause of this exception, that is,
* either the innermost cause (root cause) or this exception itself.
* <p>Differs from {@link #getRootCause()} in that it falls back
* to the present exception if there is no root cause.
* @return the most specific cause (never <code>null</code>)
* @since 2.0.3
*/
public Throwable getMostSpecificCause() {
Throwable rootCause = getRootCause();
return (rootCause != null ? rootCause : this);
}
/**
* Check whether this exception contains an exception of the given type:
* either it is of the given class itself or it contains a nested cause
* of the given type.
* @param exType the exception type to look for
* @return whether there is a nested exception of the specified type
*/
public boolean contains(Class exType) {
if (exType == null) {
return false;
}
if (exType.isInstance(this)) {
return true;
}
Throwable cause = getCause();
if (cause == this) {
return false;
}
if (cause instanceof NestedCheckedException) {
return ((NestedCheckedException) cause).contains(exType);
}
else {
while (cause != null) {
if (exType.isInstance(cause)) {
return true;
}
if (cause.getCause() == cause) {
break;
}
cause = cause.getCause();
}
return false;
}
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* 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 org.springframework.core;
/**
* Helper class for implementing exception classes which are capable of
* holding nested exceptions. Necessary because we can't share a base
* class among different exception types.
*
* <p>Mainly for use within the framework.
*
* @author Juergen Hoeller
* @since 2.0
* @see NestedRuntimeException
* @see NestedCheckedException
* @see NestedIOException
* @see org.springframework.web.util.NestedServletException
*/
public abstract class NestedExceptionUtils {
/**
* Build a message for the given base message and root cause.
* @param message the base message
* @param cause the root cause
* @return the full exception message
*/
public static String buildMessage(String message, Throwable cause) {
if (cause != null) {
StringBuilder sb = new StringBuilder();
if (message != null) {
sb.append(message).append("; ");
}
sb.append("nested exception is ").append(cause);
return sb.toString();
}
else {
return message;
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core;
import java.io.IOException;
/**
* Subclass of {@link IOException} that properly handles a root cause,
* exposing the root cause just like NestedChecked/RuntimeException does.
*
* <p>Proper root cause handling has not been added to standard IOException before
* Java 6, which is why we need to do it ourselves for Java 5 compatibility purposes.
*
* <p>The similarity between this class and the NestedChecked/RuntimeException
* class is unavoidable, as this class needs to derive from IOException.
*
* @author Juergen Hoeller
* @since 2.0
* @see #getMessage
* @see #printStackTrace
* @see org.springframework.core.NestedCheckedException
* @see org.springframework.core.NestedRuntimeException
*/
public class NestedIOException extends IOException {
static {
// Eagerly load the NestedExceptionUtils class to avoid classloader deadlock
// issues on OSGi when calling getMessage(). Reported by Don Brown; SPR-5607.
NestedExceptionUtils.class.getName();
}
/**
* Construct a <code>NestedIOException</code> with the specified detail message.
* @param msg the detail message
*/
public NestedIOException(String msg) {
super(msg);
}
/**
* Construct a <code>NestedIOException</code> with the specified detail message
* and nested exception.
* @param msg the detail message
* @param cause the nested exception
*/
public NestedIOException(String msg, Throwable cause) {
super(msg);
initCause(cause);
}
/**
* Return the detail message, including the message from the nested exception
* if there is one.
*/
@Override
public String getMessage() {
return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core;
/**
* Handy class for wrapping runtime <code>Exceptions</code> with a root cause.
*
* <p>This class is <code>abstract</code> to force the programmer to extend
* the class. <code>getMessage</code> will include nested exception
* information; <code>printStackTrace</code> and other like methods will
* delegate to the wrapped exception, if any.
*
* <p>The similarity between this class and the {@link NestedCheckedException}
* class is unavoidable, as Java forces these two classes to have different
* superclasses (ah, the inflexibility of concrete inheritance!).
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see #getMessage
* @see #printStackTrace
* @see NestedCheckedException
*/
public abstract class NestedRuntimeException extends RuntimeException {
/** Use serialVersionUID from Spring 1.2 for interoperability */
private static final long serialVersionUID = 5439915454935047936L;
static {
// Eagerly load the NestedExceptionUtils class to avoid classloader deadlock
// issues on OSGi when calling getMessage(). Reported by Don Brown; SPR-5607.
NestedExceptionUtils.class.getName();
}
/**
* Construct a <code>NestedRuntimeException</code> with the specified detail message.
* @param msg the detail message
*/
public NestedRuntimeException(String msg) {
super(msg);
}
/**
* Construct a <code>NestedRuntimeException</code> with the specified detail message
* and nested exception.
* @param msg the detail message
* @param cause the nested exception
*/
public NestedRuntimeException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Return the detail message, including the message from the nested exception
* if there is one.
*/
@Override
public String getMessage() {
return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
}
/**
* Retrieve the innermost cause of this exception, if any.
* @return the innermost exception, or <code>null</code> if none
* @since 2.0
*/
public Throwable getRootCause() {
Throwable rootCause = null;
Throwable cause = getCause();
while (cause != null && cause != rootCause) {
rootCause = cause;
cause = cause.getCause();
}
return rootCause;
}
/**
* Retrieve the most specific cause of this exception, that is,
* either the innermost cause (root cause) or this exception itself.
* <p>Differs from {@link #getRootCause()} in that it falls back
* to the present exception if there is no root cause.
* @return the most specific cause (never <code>null</code>)
* @since 2.0.3
*/
public Throwable getMostSpecificCause() {
Throwable rootCause = getRootCause();
return (rootCause != null ? rootCause : this);
}
/**
* Check whether this exception contains an exception of the given type:
* either it is of the given class itself or it contains a nested cause
* of the given type.
* @param exType the exception type to look for
* @return whether there is a nested exception of the specified type
*/
public boolean contains(Class exType) {
if (exType == null) {
return false;
}
if (exType.isInstance(this)) {
return true;
}
Throwable cause = getCause();
if (cause == this) {
return false;
}
if (cause instanceof NestedRuntimeException) {
return ((NestedRuntimeException) cause).contains(exType);
}
else {
while (cause != null) {
if (exType.isInstance(cause)) {
return true;
}
if (cause.getCause() == cause) {
break;
}
cause = cause.getCause();
}
return false;
}
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* {@link Comparator} implementation for {@link Ordered} objects,
* sorting by order value ascending (resp. by priority descending).
*
* <p>Non-<code>Ordered</code> objects are treated as greatest order
* values, thus ending up at the end of the list, in arbitrary order
* (just like same order values of <code>Ordered</code> objects).
*
* @author Juergen Hoeller
* @since 07.04.2003
* @see Ordered
* @see java.util.Collections#sort(java.util.List, java.util.Comparator)
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
*/
public class OrderComparator implements Comparator<Object> {
/**
* Shared default instance of OrderComparator.
*/
public static OrderComparator INSTANCE = new OrderComparator();
public int compare(Object o1, Object o2) {
boolean p1 = (o1 instanceof PriorityOrdered);
boolean p2 = (o2 instanceof PriorityOrdered);
if (p1 && !p2) {
return -1;
}
else if (p2 && !p1) {
return 1;
}
// Direct evaluation instead of Integer.compareTo to avoid unnecessary object creation.
int i1 = getOrder(o1);
int i2 = getOrder(o2);
return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
}
/**
* Determine the order value for the given object.
* <p>The default implementation checks against the {@link Ordered}
* interface. Can be overridden in subclasses.
* @param obj the object to check
* @return the order value, or <code>Ordered.LOWEST_PRECEDENCE</code> as fallback
*/
protected int getOrder(Object obj) {
return (obj instanceof Ordered ? ((Ordered) obj).getOrder() : Ordered.LOWEST_PRECEDENCE);
}
/**
* Sort the given List with a default OrderComparator.
* <p>Optimized to skip sorting for lists with size 0 or 1,
* in order to avoid unnecessary array extraction.
* @param list the List to sort
* @see java.util.Collections#sort(java.util.List, java.util.Comparator)
*/
public static void sort(List<?> list) {
if (list.size() > 1) {
Collections.sort(list, INSTANCE);
}
}
/**
* Sort the given array with a default OrderComparator.
* <p>Optimized to skip sorting for lists with size 0 or 1,
* in order to avoid unnecessary array extraction.
* @param array the array to sort
* @see java.util.Arrays#sort(Object[], java.util.Comparator)
*/
public static void sort(Object[] array) {
if (array.length > 1) {
Arrays.sort(array, INSTANCE);
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core;
/**
* Interface that can be implemented by objects that should be
* orderable, for example in a Collection.
*
* <p>The actual order can be interpreted as prioritization, with
* the first object (with the lowest order value) having the highest
* priority.
*
* <p>Note that there is a 'priority' marker for this interface:
* {@link PriorityOrdered}. Order values expressed by PriorityOrdered
* objects always apply before order values of 'plain' Ordered values.
*
* @author Juergen Hoeller
* @since 07.04.2003
* @see OrderComparator
* @see org.springframework.core.annotation.Order
*/
public interface Ordered {
/**
* Useful constant for the highest precedence value.
* @see java.lang.Integer#MIN_VALUE
*/
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
/**
* Useful constant for the lowest precedence value.
* @see java.lang.Integer#MAX_VALUE
*/
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
/**
* Return the order value of this object, with a
* higher value meaning greater in terms of sorting.
* <p>Normally starting with 0, with <code>Integer.MAX_VALUE</code>
* indicating the greatest value. Same order values will result
* in arbitrary positions for the affected objects.
* <p>Higher values can be interpreted as lower priority. As a
* consequence, the object with the lowest value has highest priority
* (somewhat analogous to Servlet "load-on-startup" values).
* @return the order value
*/
int getOrder();
}

View File

@@ -0,0 +1,155 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.util.FileCopyUtils;
/**
* <code>ClassLoader</code> that does <i>not</i> always delegate to the
* parent loader, as normal class loaders do. This enables, for example,
* instrumentation to be forced in the overriding ClassLoader, or a
* "throwaway" class loading behavior, where selected classes are
* temporarily loaded in the overriding ClassLoader, in order to load
* an instrumented version of the class in the parent ClassLoader later on.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0.1
*/
public class OverridingClassLoader extends DecoratingClassLoader {
/** Packages that are excluded by default */
public static final String[] DEFAULT_EXCLUDED_PACKAGES =
new String[] {"java.", "javax.", "sun.", "oracle."};
private static final String CLASS_FILE_SUFFIX = ".class";
/**
* Create a new OverridingClassLoader for the given class loader.
* @param parent the ClassLoader to build an overriding ClassLoader for
*/
public OverridingClassLoader(ClassLoader parent) {
super(parent);
for (String packageName : DEFAULT_EXCLUDED_PACKAGES) {
excludePackage(packageName);
}
}
@Override
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class result = null;
if (isEligibleForOverriding(name)) {
result = loadClassForOverriding(name);
}
if (result != null) {
if (resolve) {
resolveClass(result);
}
return result;
}
else {
return super.loadClass(name, resolve);
}
}
/**
* Determine whether the specified class is eligible for overriding
* by this class loader.
* @param className the class name to check
* @return whether the specified class is eligible
* @see #isExcluded
*/
protected boolean isEligibleForOverriding(String className) {
return !isExcluded(className);
}
/**
* Load the specified class for overriding purposes in this ClassLoader.
* <p>The default implementation delegates to {@link #findLoadedClass},
* {@link #loadBytesForClass} and {@link #defineClass}.
* @param name the name of the class
* @return the Class object, or <code>null</code> if no class defined for that name
* @throws ClassNotFoundException if the class for the given name couldn't be loaded
*/
protected Class loadClassForOverriding(String name) throws ClassNotFoundException {
Class result = findLoadedClass(name);
if (result == null) {
byte[] bytes = loadBytesForClass(name);
if (bytes != null) {
result = defineClass(name, bytes, 0, bytes.length);
}
}
return result;
}
/**
* Load the defining bytes for the given class,
* to be turned into a Class object through a {@link #defineClass} call.
* <p>The default implementation delegates to {@link #openStreamForClass}
* and {@link #transformIfNecessary}.
* @param name the name of the class
* @return the byte content (with transformers already applied),
* or <code>null</code> if no class defined for that name
* @throws ClassNotFoundException if the class for the given name couldn't be loaded
*/
protected byte[] loadBytesForClass(String name) throws ClassNotFoundException {
InputStream is = openStreamForClass(name);
if (is == null) {
return null;
}
try {
// Load the raw bytes.
byte[] bytes = FileCopyUtils.copyToByteArray(is);
// Transform if necessary and use the potentially transformed bytes.
return transformIfNecessary(name, bytes);
}
catch (IOException ex) {
throw new ClassNotFoundException("Cannot load resource for class [" + name + "]", ex);
}
}
/**
* Open an InputStream for the specified class.
* <p>The default implementation loads a standard class file through
* the parent ClassLoader's <code>getResourceAsStream</code> method.
* @param name the name of the class
* @return the InputStream containing the byte code for the specified class
*/
protected InputStream openStreamForClass(String name) {
String internalName = name.replace('.', '/') + CLASS_FILE_SUFFIX;
return getParent().getResourceAsStream(internalName);
}
/**
* Transformation hook to be implemented by subclasses.
* <p>The default implementation simply returns the given bytes as-is.
* @param name the fully-qualified name of the class being transformed
* @param bytes the raw bytes of the class
* @return the transformed bytes (never <code>null</code>;
* same as the input bytes if the transformation produced no changes)
*/
protected byte[] transformIfNecessary(String name, byte[] bytes) {
return bytes;
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* 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 org.springframework.core;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* Interface to discover parameter names for methods and constructors.
*
* <p>Parameter name discovery is not always possible, but various strategies are
* available to try, such as looking for debug information that may have been
* emitted at compile time, and looking for argname annotation values optionally
* accompanying AspectJ annotated methods.
*
* @author Rod Johnson
* @author Adrian Colyer
* @since 2.0
*/
public interface ParameterNameDiscoverer {
/**
* Return parameter names for this method,
* or <code>null</code> if they cannot be determined.
* @param method method to find parameter names for
* @return an array of parameter names if the names can be resolved,
* or <code>null</code> if they cannot
*/
String[] getParameterNames(Method method);
/**
* Return parameter names for this constructor,
* or <code>null</code> if they cannot be determined.
* @param ctor constructor to find parameter names for
* @return an array of parameter names if the names can be resolved,
* or <code>null</code> if they cannot
*/
String[] getParameterNames(Constructor ctor);
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* 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 org.springframework.core;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* ParameterNameDiscoverer implementation that tries several ParameterNameDiscoverers
* in succession. Those added first in the <code>addDiscoverer</code> method have
* highest priority. If one returns <code>null</code>, the next will be tried.
*
* <p>The default behavior is always to return <code>null</code>
* if no discoverer matches.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
*/
public class PrioritizedParameterNameDiscoverer implements ParameterNameDiscoverer {
private final List<ParameterNameDiscoverer> parameterNameDiscoverers =
new LinkedList<ParameterNameDiscoverer>();
/**
* Add a further ParameterNameDiscoverer to the list of discoverers
* that this PrioritizedParameterNameDiscoverer checks.
*/
public void addDiscoverer(ParameterNameDiscoverer pnd) {
this.parameterNameDiscoverers.add(pnd);
}
public String[] getParameterNames(Method method) {
for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) {
String[] result = pnd.getParameterNames(method);
if (result != null) {
return result;
}
}
return null;
}
public String[] getParameterNames(Constructor ctor) {
for (ParameterNameDiscoverer pnd : this.parameterNameDiscoverers) {
String[] result = pnd.getParameterNames(ctor);
if (result != null) {
return result;
}
}
return null;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core;
/**
* Extension of the {@link Ordered} interface, expressing a 'priority'
* ordering: Order values expressed by PriorityOrdered objects always
* apply before order values of 'plain' Ordered values.
*
* <p>This is primarily a special-purpose interface, used for objects
* where it is particularly important to determine 'prioritized'
* objects first, without even obtaining the remaining objects.
* A typical example: Prioritized post-processors in a Spring
* {@link org.springframework.context.ApplicationContext}.
*
* <p>Note: PriorityOrdered post-processor beans are initialized in
* a special phase, ahead of other post-processor beans. This subtly
* affects their autowiring behavior: They will only be autowired against
* beans which do not require eager initialization for type matching.
*
* @author Juergen Hoeller
* @since 2.5
* @see org.springframework.beans.factory.config.PropertyOverrideConfigurer
* @see org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
*/
public interface PriorityOrdered extends Ordered {
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* 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 org.springframework.core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.StringValueResolver;
/**
* Simple implementation of the {@link AliasRegistry} interface.
* Serves as base class for
* {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}
* implementations.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
public class SimpleAliasRegistry implements AliasRegistry {
/** Map from alias to canonical name */
private final Map<String, String> aliasMap = new ConcurrentHashMap<String, String>();
public void registerAlias(String name, String alias) {
Assert.hasText(name, "'name' must not be empty");
Assert.hasText(alias, "'alias' must not be empty");
if (alias.equals(name)) {
this.aliasMap.remove(alias);
}
else {
if (!allowAliasOverriding()) {
String registeredName = this.aliasMap.get(alias);
if (registeredName != null && !registeredName.equals(name)) {
throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" +
name + "': It is already registered for name '" + registeredName + "'.");
}
}
checkForAliasCircle(name, alias);
this.aliasMap.put(alias, name);
}
}
/**
* Return whether alias overriding is allowed.
* Default is <code>true</code>.
*/
protected boolean allowAliasOverriding() {
return true;
}
public void removeAlias(String alias) {
String name = this.aliasMap.remove(alias);
if (name == null) {
throw new IllegalStateException("No alias '" + alias + "' registered");
}
}
public boolean isAlias(String name) {
return this.aliasMap.containsKey(name);
}
public String[] getAliases(String name) {
List<String> result = new ArrayList<String>();
synchronized (this.aliasMap) {
retrieveAliases(name, result);
}
return StringUtils.toStringArray(result);
}
/**
* Transitively retrieve all aliases for the given name.
* @param name the target name to find aliases for
* @param result the resulting aliases list
*/
private void retrieveAliases(String name, List<String> result) {
for (Map.Entry<String, String> entry : this.aliasMap.entrySet()) {
String registeredName = entry.getValue();
if (registeredName.equals(name)) {
String alias = entry.getKey();
result.add(alias);
retrieveAliases(alias, result);
}
}
}
/**
* Resolve all alias target names and aliases registered in this
* factory, applying the given StringValueResolver to them.
* <p>The value resolver may for example resolve placeholders
* in target bean names and even in alias names.
* @param valueResolver the StringValueResolver to apply
*/
public void resolveAliases(StringValueResolver valueResolver) {
Assert.notNull(valueResolver, "StringValueResolver must not be null");
synchronized (this.aliasMap) {
Map<String, String> aliasCopy = new HashMap<String, String>(this.aliasMap);
for (String alias : aliasCopy.keySet()) {
String registeredName = aliasCopy.get(alias);
String resolvedAlias = valueResolver.resolveStringValue(alias);
String resolvedName = valueResolver.resolveStringValue(registeredName);
if (resolvedAlias.equals(resolvedName)) {
this.aliasMap.remove(alias);
}
else if (!resolvedAlias.equals(alias)) {
String existingName = this.aliasMap.get(resolvedAlias);
if (existingName != null && !existingName.equals(resolvedName)) {
throw new IllegalStateException(
"Cannot register resolved alias '" + resolvedAlias + "' (original: '" + alias +
"') for name '" + resolvedName + "': It is already registered for name '" +
registeredName + "'.");
}
checkForAliasCircle(resolvedName, resolvedAlias);
this.aliasMap.remove(alias);
this.aliasMap.put(resolvedAlias, resolvedName);
}
else if (!registeredName.equals(resolvedName)) {
this.aliasMap.put(alias, resolvedName);
}
}
}
}
/**
* Determine the raw name, resolving aliases to canonical names.
* @param name the user-specified name
* @return the transformed name
*/
public String canonicalName(String name) {
String canonicalName = name;
// Handle aliasing...
String resolvedName;
do {
resolvedName = this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
/**
* Check whether the given name points back to given alias as an alias
* in the other direction, catching a circular reference upfront and
* throwing a corresponding IllegalStateException.
* @param name the candidate name
* @param alias the candidate alias
* @see #registerAlias
*/
protected void checkForAliasCircle(String name, String alias) {
if (alias.equals(canonicalName(name))) {
throw new IllegalStateException("Cannot register alias '" + alias +
"' for name '" + name + "': Circular reference - '" +
name + "' is a direct or indirect alias for '" + alias + "' already");
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* 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 org.springframework.core;
/**
* Interface to be implemented by a reloading-aware ClassLoader
* (e.g. a Groovy-based ClassLoader). Detected for example by
* Spring's CGLIB proxy factory for making a caching decision.
*
* <p>If a ClassLoader does <i>not</i> implement this interface,
* then all of the classes obtained from it should be considered
* as not reloadable (i.e. cacheable).
*
* @author Juergen Hoeller
* @since 2.5.1
*/
public interface SmartClassLoader {
/**
* Determine whether the given class is reloadable (in this ClassLoader).
* <p>Typically used to check whether the result may be cached (for this
* ClassLoader) or whether it should be reobtained every time.
* @param clazz the class to check (usually loaded from this ClassLoader)
* @return whether the class should be expected to appear in a reloaded
* version (with a different <code>Class</code> object) later on
*/
boolean isClassReloadable(Class clazz);
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* 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 org.springframework.core;
/**
* Class that exposes the Spring version. Fetches the
* "Implementation-Version" manifest attribute from the jar file.
*
* <p>Note that some ClassLoaders do not expose the package metadata,
* hence this class might not be able to determine the Spring version
* in all environments. Consider using a reflection-based check instead:
* For example, checking for the presence of a specific Spring 2.0
* method that you intend to call.
*
* @author Juergen Hoeller
* @since 1.1
*/
public class SpringVersion {
/**
* Return the full version string of the present Spring codebase,
* or <code>null</code> if it cannot be determined.
* @see java.lang.Package#getImplementationVersion()
*/
public static String getVersion() {
Package pkg = SpringVersion.class.getPackage();
return (pkg != null ? pkg.getImplementationVersion() : null);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* 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 org.springframework.core.annotation;
import org.springframework.core.OrderComparator;
import org.springframework.core.Ordered;
/**
* {@link java.util.Comparator} implementation that checks
* {@link org.springframework.core.Ordered} as well as the
* {@link Order} annotation, with an order value provided by an
* <code>Ordered</code> instance overriding a statically defined
* annotation value (if any).
*
* @author Juergen Hoeller
* @since 2.0.1
* @see org.springframework.core.Ordered
* @see Order
*/
public class AnnotationAwareOrderComparator extends OrderComparator {
@Override
protected int getOrder(Object obj) {
if (obj instanceof Ordered) {
return ((Ordered) obj).getOrder();
}
if (obj != null) {
Order order = obj.getClass().getAnnotation(Order.class);
if (order != null) {
return order.value();
}
}
return Ordered.LOWEST_PRECEDENCE;
}
}

View File

@@ -0,0 +1,428 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.Assert;
/**
* General utility methods for working with annotations, handling bridge methods (which the compiler
* generates for generic declarations) as well as super methods (for optional &quot;annotation inheritance&quot;).
* Note that none of this is provided by the JDK's introspection facilities themselves.
*
* <p>As a general rule for runtime-retained annotations (e.g. for transaction control, authorization or service
* exposure), always use the lookup methods on this class (e.g., {@link #findAnnotation(Method, Class)}, {@link
* #getAnnotation(Method, Class)}, and {@link #getAnnotations(Method)}) instead of the plain annotation lookup
* methods in the JDK. You can still explicitly choose between lookup on the given class level only ({@link
* #getAnnotation(Method, Class)}) and lookup in the entire inheritance hierarchy of the given method ({@link
* #findAnnotation(Method, Class)}).
*
* @author Rob Harrop
* @author Juergen Hoeller
* @author Sam Brannen
* @author Mark Fisher
* @author Chris Beams
* @since 2.0
* @see java.lang.reflect.Method#getAnnotations()
* @see java.lang.reflect.Method#getAnnotation(Class)
*/
public abstract class AnnotationUtils {
/** The attribute name for annotations with a single element */
static final String VALUE = "value";
private static final Map<Class<?>, Boolean> annotatedInterfaceCache = new WeakHashMap<Class<?>, Boolean>();
/**
* Get a single {@link Annotation} of {@code annotationType} from the supplied
* Method, Constructor or Field. Meta-annotations will be searched if the annotation
* is not declared locally on the supplied element.
* @param ae the Method, Constructor or Field from which to get the annotation
* @param annotationType the annotation class to look for, both locally and as a meta-annotation
* @return the matching annotation or {@code null} if not found
* @since 3.1
*/
public static <T extends Annotation> T getAnnotation(AnnotatedElement ae, Class<T> annotationType) {
T ann = ae.getAnnotation(annotationType);
if (ann == null) {
for (Annotation metaAnn : ae.getAnnotations()) {
ann = metaAnn.annotationType().getAnnotation(annotationType);
if (ann != null) {
break;
}
}
}
return ann;
}
/**
* Get all {@link Annotation Annotations} from the supplied {@link Method}.
* <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
* @param method the method to look for annotations on
* @return the annotations found
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
*/
public static Annotation[] getAnnotations(Method method) {
return BridgeMethodResolver.findBridgedMethod(method).getAnnotations();
}
/**
* Get a single {@link Annotation} of <code>annotationType</code> from the supplied {@link Method}.
* <p>Correctly handles bridge {@link Method Methods} generated by the compiler.
* @param method the method to look for annotations on
* @param annotationType the annotation class to look for
* @return the annotations found
* @see org.springframework.core.BridgeMethodResolver#findBridgedMethod(Method)
*/
public static <A extends Annotation> A getAnnotation(Method method, Class<A> annotationType) {
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
A ann = resolvedMethod.getAnnotation(annotationType);
if (ann == null) {
for (Annotation metaAnn : resolvedMethod.getAnnotations()) {
ann = metaAnn.annotationType().getAnnotation(annotationType);
if (ann != null) {
break;
}
}
}
return ann;
}
/**
* Get a single {@link Annotation} of <code>annotationType</code> from the supplied {@link Method},
* traversing its super methods if no annotation can be found on the given method itself.
* <p>Annotations on methods are not inherited by default, so we need to handle this explicitly.
* @param method the method to look for annotations on
* @param annotationType the annotation class to look for
* @return the annotation found, or <code>null</code> if none found
*/
public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) {
A annotation = getAnnotation(method, annotationType);
Class<?> cl = method.getDeclaringClass();
if (annotation == null) {
annotation = searchOnInterfaces(method, annotationType, cl.getInterfaces());
}
while (annotation == null) {
cl = cl.getSuperclass();
if (cl == null || cl == Object.class) {
break;
}
try {
Method equivalentMethod = cl.getDeclaredMethod(method.getName(), method.getParameterTypes());
annotation = getAnnotation(equivalentMethod, annotationType);
if (annotation == null) {
annotation = searchOnInterfaces(method, annotationType, cl.getInterfaces());
}
}
catch (NoSuchMethodException ex) {
// We're done...
}
}
return annotation;
}
private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>[] ifcs) {
A annotation = null;
for (Class<?> iface : ifcs) {
if (isInterfaceWithAnnotatedMethods(iface)) {
try {
Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes());
annotation = getAnnotation(equivalentMethod, annotationType);
}
catch (NoSuchMethodException ex) {
// Skip this interface - it doesn't have the method...
}
if (annotation != null) {
break;
}
}
}
return annotation;
}
private static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) {
synchronized (annotatedInterfaceCache) {
Boolean flag = annotatedInterfaceCache.get(iface);
if (flag != null) {
return flag;
}
boolean found = false;
for (Method ifcMethod : iface.getMethods()) {
if (ifcMethod.getAnnotations().length > 0) {
found = true;
break;
}
}
annotatedInterfaceCache.put(iface, found);
return found;
}
}
/**
* Find a single {@link Annotation} of <code>annotationType</code> from the supplied {@link Class},
* traversing its interfaces and superclasses if no annotation can be found on the given class itself.
* <p>This method explicitly handles class-level annotations which are not declared as
* {@link java.lang.annotation.Inherited inherited} <i>as well as annotations on interfaces</i>.
* <p>The algorithm operates as follows: Searches for an annotation on the given class and returns
* it if found. Else searches all interfaces that the given class declares, returning the annotation
* from the first matching candidate, if any. Else proceeds with introspection of the superclass
* of the given class, checking the superclass itself; if no annotation found there, proceeds
* with the interfaces that the superclass declares. Recursing up through the entire superclass
* hierarchy if no match is found.
* @param clazz the class to look for annotations on
* @param annotationType the annotation class to look for
* @return the annotation found, or <code>null</code> if none found
*/
public static <A extends Annotation> A findAnnotation(Class<?> clazz, Class<A> annotationType) {
Assert.notNull(clazz, "Class must not be null");
A annotation = clazz.getAnnotation(annotationType);
if (annotation != null) {
return annotation;
}
for (Class<?> ifc : clazz.getInterfaces()) {
annotation = findAnnotation(ifc, annotationType);
if (annotation != null) {
return annotation;
}
}
if (!Annotation.class.isAssignableFrom(clazz)) {
for (Annotation ann : clazz.getAnnotations()) {
annotation = findAnnotation(ann.annotationType(), annotationType);
if (annotation != null) {
return annotation;
}
}
}
Class<?> superClass = clazz.getSuperclass();
if (superClass == null || superClass == Object.class) {
return null;
}
return findAnnotation(superClass, annotationType);
}
/**
* Find the first {@link Class} in the inheritance hierarchy of the specified <code>clazz</code>
* (including the specified <code>clazz</code> itself) which declares an annotation for the
* specified <code>annotationType</code>, or <code>null</code> if not found. If the supplied
* <code>clazz</code> is <code>null</code>, <code>null</code> will be returned.
* <p>If the supplied <code>clazz</code> is an interface, only the interface itself will be checked;
* the inheritance hierarchy for interfaces will not be traversed.
* <p>The standard {@link Class} API does not provide a mechanism for determining which class
* in an inheritance hierarchy actually declares an {@link Annotation}, so we need to handle
* this explicitly.
* @param annotationType the Class object corresponding to the annotation type
* @param clazz the Class object corresponding to the class on which to check for the annotation,
* or <code>null</code>
* @return the first {@link Class} in the inheritance hierarchy of the specified <code>clazz</code>
* which declares an annotation for the specified <code>annotationType</code>, or <code>null</code>
* if not found
* @see Class#isAnnotationPresent(Class)
* @see Class#getDeclaredAnnotations()
*/
public static Class<?> findAnnotationDeclaringClass(Class<? extends Annotation> annotationType, Class<?> clazz) {
Assert.notNull(annotationType, "Annotation type must not be null");
if (clazz == null || clazz.equals(Object.class)) {
return null;
}
return (isAnnotationDeclaredLocally(annotationType, clazz)) ? clazz :
findAnnotationDeclaringClass(annotationType, clazz.getSuperclass());
}
/**
* Determine whether an annotation for the specified <code>annotationType</code> is
* declared locally on the supplied <code>clazz</code>. The supplied {@link Class}
* may represent any type.
* <p>Note: This method does <strong>not</strong> determine if the annotation is
* {@link java.lang.annotation.Inherited inherited}. For greater clarity regarding inherited
* annotations, consider using {@link #isAnnotationInherited(Class, Class)} instead.
* @param annotationType the Class object corresponding to the annotation type
* @param clazz the Class object corresponding to the class on which to check for the annotation
* @return <code>true</code> if an annotation for the specified <code>annotationType</code>
* is declared locally on the supplied <code>clazz</code>
* @see Class#getDeclaredAnnotations()
* @see #isAnnotationInherited(Class, Class)
*/
public static boolean isAnnotationDeclaredLocally(Class<? extends Annotation> annotationType, Class<?> clazz) {
Assert.notNull(annotationType, "Annotation type must not be null");
Assert.notNull(clazz, "Class must not be null");
boolean declaredLocally = false;
for (Annotation annotation : Arrays.asList(clazz.getDeclaredAnnotations())) {
if (annotation.annotationType().equals(annotationType)) {
declaredLocally = true;
break;
}
}
return declaredLocally;
}
/**
* Determine whether an annotation for the specified <code>annotationType</code> is present
* on the supplied <code>clazz</code> and is {@link java.lang.annotation.Inherited inherited}
* i.e., not declared locally for the class).
* <p>If the supplied <code>clazz</code> is an interface, only the interface itself will be checked.
* In accordance with standard meta-annotation semantics, the inheritance hierarchy for interfaces
* will not be traversed. See the {@link java.lang.annotation.Inherited JavaDoc} for the
* &#064;Inherited meta-annotation for further details regarding annotation inheritance.
* @param annotationType the Class object corresponding to the annotation type
* @param clazz the Class object corresponding to the class on which to check for the annotation
* @return <code>true</code> if an annotation for the specified <code>annotationType</code> is present
* on the supplied <code>clazz</code> and is {@link java.lang.annotation.Inherited inherited}
* @see Class#isAnnotationPresent(Class)
* @see #isAnnotationDeclaredLocally(Class, Class)
*/
public static boolean isAnnotationInherited(Class<? extends Annotation> annotationType, Class<?> clazz) {
Assert.notNull(annotationType, "Annotation type must not be null");
Assert.notNull(clazz, "Class must not be null");
return (clazz.isAnnotationPresent(annotationType) && !isAnnotationDeclaredLocally(annotationType, clazz));
}
/**
* Retrieve the given annotation's attributes as a Map, preserving all attribute types as-is.
* @param annotation the annotation to retrieve the attributes for
* @return the Map of annotation attributes, with attribute names as keys and
* corresponding attribute values as values
*/
public static Map<String, Object> getAnnotationAttributes(Annotation annotation) {
return getAnnotationAttributes(annotation, false);
}
/**
* Retrieve the given annotation's attributes as a Map.
* @param annotation the annotation to retrieve the attributes for
* @param classValuesAsString whether to turn Class references into Strings (for compatibility with
* {@link org.springframework.core.type.AnnotationMetadata} or to preserve them as Class references
* @return the Map of annotation attributes, with attribute names as keys and
* corresponding attribute values as values
*/
public static Map<String, Object> getAnnotationAttributes(Annotation annotation, boolean classValuesAsString) {
Map<String, Object> attrs = new HashMap<String, Object>();
Method[] methods = annotation.annotationType().getDeclaredMethods();
for (Method method : methods) {
if (method.getParameterTypes().length == 0 && method.getReturnType() != void.class) {
try {
Object value = method.invoke(annotation);
if (classValuesAsString) {
if (value instanceof Class) {
value = ((Class<?>) value).getName();
}
else if (value instanceof Class[]) {
Class<?>[] clazzArray = (Class[]) value;
String[] newValue = new String[clazzArray.length];
for (int i = 0; i < clazzArray.length; i++) {
newValue[i] = clazzArray[i].getName();
}
value = newValue;
}
}
attrs.put(method.getName(), value);
}
catch (Exception ex) {
throw new IllegalStateException("Could not obtain annotation attribute values", ex);
}
}
}
return attrs;
}
/**
* Retrieve the <em>value</em> of the <code>&quot;value&quot;</code> attribute of a
* single-element Annotation, given an annotation instance.
* @param annotation the annotation instance from which to retrieve the value
* @return the attribute value, or <code>null</code> if not found
* @see #getValue(Annotation, String)
*/
public static Object getValue(Annotation annotation) {
return getValue(annotation, VALUE);
}
/**
* Retrieve the <em>value</em> of a named Annotation attribute, given an annotation instance.
* @param annotation the annotation instance from which to retrieve the value
* @param attributeName the name of the attribute value to retrieve
* @return the attribute value, or <code>null</code> if not found
* @see #getValue(Annotation)
*/
public static Object getValue(Annotation annotation, String attributeName) {
try {
Method method = annotation.annotationType().getDeclaredMethod(attributeName, new Class[0]);
return method.invoke(annotation);
}
catch (Exception ex) {
return null;
}
}
/**
* Retrieve the <em>default value</em> of the <code>&quot;value&quot;</code> attribute
* of a single-element Annotation, given an annotation instance.
* @param annotation the annotation instance from which to retrieve the default value
* @return the default value, or <code>null</code> if not found
* @see #getDefaultValue(Annotation, String)
*/
public static Object getDefaultValue(Annotation annotation) {
return getDefaultValue(annotation, VALUE);
}
/**
* Retrieve the <em>default value</em> of a named Annotation attribute, given an annotation instance.
* @param annotation the annotation instance from which to retrieve the default value
* @param attributeName the name of the attribute value to retrieve
* @return the default value of the named attribute, or <code>null</code> if not found
* @see #getDefaultValue(Class, String)
*/
public static Object getDefaultValue(Annotation annotation, String attributeName) {
return getDefaultValue(annotation.annotationType(), attributeName);
}
/**
* Retrieve the <em>default value</em> of the <code>&quot;value&quot;</code> attribute
* of a single-element Annotation, given the {@link Class annotation type}.
* @param annotationType the <em>annotation type</em> for which the default value should be retrieved
* @return the default value, or <code>null</code> if not found
* @see #getDefaultValue(Class, String)
*/
public static Object getDefaultValue(Class<? extends Annotation> annotationType) {
return getDefaultValue(annotationType, VALUE);
}
/**
* Retrieve the <em>default value</em> of a named Annotation attribute, given the {@link Class annotation type}.
* @param annotationType the <em>annotation type</em> for which the default value should be retrieved
* @param attributeName the name of the attribute value to retrieve.
* @return the default value of the named attribute, or <code>null</code> if not found
* @see #getDefaultValue(Annotation, String)
*/
public static Object getDefaultValue(Class<? extends Annotation> annotationType, String attributeName) {
try {
Method method = annotationType.getDeclaredMethod(attributeName, new Class[0]);
return method.getDefaultValue();
}
catch (Exception ex) {
return null;
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.Ordered;
/**
* Annotation that defines ordering. The value is optional, and represents order value
* as defined in the {@link Ordered} interface. Lower values have higher priority.
* The default value is <code>Ordered.LOWEST_PRECEDENCE</code>, indicating
* lowest priority (losing to any other specified order value).
*
* <p><b>NOTE:</b> Annotation-based ordering is supported for specific kinds of
* components only, e.g. for annotation-based AspectJ aspects. Spring container
* strategies, on the other hand, are typically based on the {@link Ordered}
* interface in order to allow for configurable ordering of each <i>instance</i>.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.core.Ordered
* @see AnnotationAwareOrderComparator
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
public @interface Order {
/**
* The order value. Default is {@link Ordered#LOWEST_PRECEDENCE}.
* @see Ordered#getOrder()
*/
int value() default Ordered.LOWEST_PRECEDENCE;
}

View File

@@ -0,0 +1,8 @@
/**
*
* Core support package for Java 5 annotations.
*
*/
package org.springframework.core.annotation;

View File

@@ -0,0 +1,128 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert;
import java.lang.annotation.Annotation;
import java.util.Collection;
import java.util.Map;
import org.springframework.util.Assert;
/**
* @author Keith Donald
* @since 3.1
*/
abstract class AbstractDescriptor {
private final Class<?> type;
protected AbstractDescriptor(Class<?> type) {
Assert.notNull(type, "Type must not be null");
this.type = type;
}
public Class<?> getType() {
return this.type;
}
public TypeDescriptor getElementTypeDescriptor() {
if (isCollection()) {
Class<?> elementType = resolveCollectionElementType();
return (elementType != null ? new TypeDescriptor(nested(elementType, 0)) : null);
}
else if (isArray()) {
Class<?> elementType = getType().getComponentType();
return new TypeDescriptor(nested(elementType, 0));
}
else {
return null;
}
}
public TypeDescriptor getMapKeyTypeDescriptor() {
if (isMap()) {
Class<?> keyType = resolveMapKeyType();
return keyType != null ? new TypeDescriptor(nested(keyType, 0)) : null;
}
else {
return null;
}
}
public TypeDescriptor getMapValueTypeDescriptor() {
if (isMap()) {
Class<?> valueType = resolveMapValueType();
return valueType != null ? new TypeDescriptor(nested(valueType, 1)) : null;
}
else {
return null;
}
}
public AbstractDescriptor nested() {
if (isCollection()) {
Class<?> elementType = resolveCollectionElementType();
return (elementType != null ? nested(elementType, 0) : null);
}
else if (isArray()) {
return nested(getType().getComponentType(), 0);
}
else if (isMap()) {
Class<?> mapValueType = resolveMapValueType();
return (mapValueType != null ? nested(mapValueType, 1) : null);
}
else if (Object.class.equals(getType())) {
// could be a collection type but we don't know about its element type,
// so let's just assume there is an element type of type Object
return this;
}
else {
throw new IllegalStateException("Not a collection, array, or map: cannot resolve nested value types");
}
}
// subclassing hooks
public abstract Annotation[] getAnnotations();
protected abstract Class<?> resolveCollectionElementType();
protected abstract Class<?> resolveMapKeyType();
protected abstract Class<?> resolveMapValueType();
protected abstract AbstractDescriptor nested(Class<?> type, int typeIndex);
// internal helpers
private boolean isCollection() {
return Collection.class.isAssignableFrom(getType());
}
private boolean isArray() {
return getType().isArray();
}
private boolean isMap() {
return Map.class.isAssignableFrom(getType());
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert;
import java.lang.annotation.Annotation;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
/**
* @author Keith Donald
* @since 3.1
*/
class BeanPropertyDescriptor extends AbstractDescriptor {
private final Property property;
private final MethodParameter methodParameter;
private final Annotation[] annotations;
public BeanPropertyDescriptor(Property property) {
super(property.getType());
this.property = property;
this.methodParameter = property.getMethodParameter();
this.annotations = property.getAnnotations();
}
@Override
public Annotation[] getAnnotations() {
return this.annotations;
}
@Override
protected Class<?> resolveCollectionElementType() {
return GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter);
}
@Override
protected Class<?> resolveMapKeyType() {
return GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter);
}
@Override
protected Class<?> resolveMapValueType() {
return GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter);
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
MethodParameter methodParameter = new MethodParameter(this.methodParameter);
methodParameter.increaseNestingLevel();
methodParameter.setTypeIndexForCurrentLevel(typeIndex);
return new BeanPropertyDescriptor(type, this.property, methodParameter, this.annotations);
}
// internal
private BeanPropertyDescriptor(Class<?> type, Property propertyDescriptor, MethodParameter methodParameter, Annotation[] annotations) {
super(type);
this.property = propertyDescriptor;
this.methodParameter = methodParameter;
this.annotations = annotations;
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert;
import java.lang.annotation.Annotation;
/**
* @author Keith Donald
* @since 3.1
*/
class ClassDescriptor extends AbstractDescriptor {
ClassDescriptor(Class<?> type) {
super(type);
}
@Override
public Annotation[] getAnnotations() {
return TypeDescriptor.EMPTY_ANNOTATION_ARRAY;
}
@Override
protected Class<?> resolveCollectionElementType() {
return null;
}
@Override
protected Class<?> resolveMapKeyType() {
return null;
}
@Override
protected Class<?> resolveMapValueType() {
return null;
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
return new ClassDescriptor(type);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* 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 org.springframework.core.convert;
import org.springframework.core.NestedRuntimeException;
/**
* Base class for exceptions thrown by the conversion system.
*
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("serial")
public abstract class ConversionException extends NestedRuntimeException {
/**
* Construct a new conversion exception.
* @param message the exception message
*/
public ConversionException(String message) {
super(message);
}
/**
* Construct a new conversion exception.
* @param message the exception message
* @param cause the cause
*/
public ConversionException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* 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 org.springframework.core.convert;
import org.springframework.util.ObjectUtils;
/**
* Exception to be thrown when an actual type conversion attempt fails.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 3.0
*/
@SuppressWarnings("serial")
public final class ConversionFailedException extends ConversionException {
private final TypeDescriptor sourceType;
private final TypeDescriptor targetType;
private final Object value;
/**
* Create a new conversion exception.
* @param sourceType the value's original type
* @param targetType the value's target type
* @param value the value we tried to convert
* @param cause the cause of the conversion failure
*/
public ConversionFailedException(TypeDescriptor sourceType, TypeDescriptor targetType, Object value, Throwable cause) {
super("Failed to convert from type " + sourceType + " to type " + targetType + " for value '" + ObjectUtils.nullSafeToString(value) + "'", cause);
this.sourceType = sourceType;
this.targetType = targetType;
this.value = value;
}
/**
* Return the source type we tried to convert the value from.
*/
public TypeDescriptor getSourceType() {
return this.sourceType;
}
/**
* Return the target type we tried to convert the value to.
*/
public TypeDescriptor getTargetType() {
return this.targetType;
}
/**
* Return the offending value.
*/
public Object getValue() {
return this.value;
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert;
/**
* A service interface for type conversion. This is the entry point into the convert system.
* Call {@link #convert(Object, Class)} to perform a thread-safe type conversion using this system.
*
* @author Keith Donald
* @since 3.0
*/
public interface ConversionService {
/**
* Returns true if objects of sourceType can be converted to targetType.
* If this method returns true, it means {@link #convert(Object, Class)} is capable of converting an instance of sourceType to targetType.
* Special note on collections, arrays, and maps types:
* For conversion between collection, array, and map types, this method will return 'true'
* even though a convert invocation may still generate a {@link ConversionException} if the underlying elements are not convertible.
* Callers are expected to handle this exceptional case when working with collections and maps.
* @param sourceType the source type to convert from (may be null if source is null)
* @param targetType the target type to convert to (required)
* @return true if a conversion can be performed, false if not
* @throws IllegalArgumentException if targetType is null
*/
boolean canConvert(Class<?> sourceType, Class<?> targetType);
/**
* Returns true if objects of sourceType can be converted to the targetType.
* The TypeDescriptors provide additional context about the source and target locations where conversion would occur, often object fields or property locations.
* If this method returns true, it means {@link #convert(Object, TypeDescriptor, TypeDescriptor)} is capable of converting an instance of sourceType to targetType.
* Special note on collections, arrays, and maps types:
* For conversion between collection, array, and map types, this method will return 'true'
* even though a convert invocation may still generate a {@link ConversionException} if the underlying elements are not convertible.
* Callers are expected to handle this exceptional case when working with collections and maps.
* @param sourceType context about the source type to convert from (may be null if source is null)
* @param targetType context about the target type to convert to (required)
* @return true if a conversion can be performed between the source and target types, false if not
* @throws IllegalArgumentException if targetType is null
*/
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
/**
* Convert the source to targetType.
* @param source the source object to convert (may be null)
* @param targetType the target type to convert to (required)
* @return the converted object, an instance of targetType
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is null
*/
<T> T convert(Object source, Class<T> targetType);
/**
* Convert the source to targetType.
* The TypeDescriptors provide additional context about the source and target locations where conversion will occur, often object fields or property locations.
* @param source the source object to convert (may be null)
* @param sourceType context about the source type converting from (may be null if source is null)
* @param targetType context about the target type to convert to (required)
* @return the converted object, an instance of {@link TypeDescriptor#getObjectType() targetType}</code>
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is null
* @throws IllegalArgumentException if sourceType is null but source is not null
*/
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* 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 org.springframework.core.convert;
/**
* Thrown when a suitable converter could not be found in a conversion service.
*
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings("serial")
public final class ConverterNotFoundException extends ConversionException {
private final TypeDescriptor sourceType;
private final TypeDescriptor targetType;
/**
* Creates a new conversion executor not found exception.
* @param sourceType the source type requested to convert from
* @param targetType the target type requested to convert to
* @param message a descriptive message
*/
public ConverterNotFoundException(TypeDescriptor sourceType, TypeDescriptor targetType) {
super("No converter found capable of converting from type " + sourceType + " to type " + targetType);
this.sourceType = sourceType;
this.targetType = targetType;
}
/**
* Returns the source type that was requested to convert from.
*/
public TypeDescriptor getSourceType() {
return this.sourceType;
}
/**
* Returns the target type that was requested to convert to.
*/
public TypeDescriptor getTargetType() {
return this.targetType;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.GenericCollectionTypeResolver;
/**
* @author Keith Donald
* @since 3.1
*/
class FieldDescriptor extends AbstractDescriptor {
private final Field field;
private final int nestingLevel;
private Map<Integer, Integer> typeIndexesPerLevel;
public FieldDescriptor(Field field) {
super(field.getType());
this.field = field;
this.nestingLevel = 1;
}
private FieldDescriptor(Class<?> type, Field field, int nestingLevel, int typeIndex, Map<Integer, Integer> typeIndexesPerLevel) {
super(type);
this.field = field;
this.nestingLevel = nestingLevel;
this.typeIndexesPerLevel = typeIndexesPerLevel;
this.typeIndexesPerLevel.put(nestingLevel, typeIndex);
}
@Override
public Annotation[] getAnnotations() {
return TypeDescriptor.nullSafeAnnotations(this.field.getAnnotations());
}
@Override
protected Class<?> resolveCollectionElementType() {
return GenericCollectionTypeResolver.getCollectionFieldType(this.field, this.nestingLevel, this.typeIndexesPerLevel);
}
@Override
protected Class<?> resolveMapKeyType() {
return GenericCollectionTypeResolver.getMapKeyFieldType(this.field, this.nestingLevel, this.typeIndexesPerLevel);
}
@Override
protected Class<?> resolveMapValueType() {
return GenericCollectionTypeResolver.getMapValueFieldType(this.field, this.nestingLevel, this.typeIndexesPerLevel);
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
if (this.typeIndexesPerLevel == null) {
this.typeIndexesPerLevel = new HashMap<Integer, Integer>(4);
}
return new FieldDescriptor(type, this.field, this.nestingLevel + 1, typeIndex, this.typeIndexesPerLevel);
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert;
import java.lang.annotation.Annotation;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
/**
* @author Keith Donald
* @since 3.1
*/
class ParameterDescriptor extends AbstractDescriptor {
private final MethodParameter methodParameter;
public ParameterDescriptor(MethodParameter methodParameter) {
super(methodParameter.getParameterType());
if (methodParameter.getNestingLevel() != 1) {
throw new IllegalArgumentException("MethodParameter argument must have its nestingLevel set to 1");
}
this.methodParameter = methodParameter;
}
private ParameterDescriptor(Class<?> type, MethodParameter methodParameter) {
super(type);
this.methodParameter = methodParameter;
}
@Override
public Annotation[] getAnnotations() {
if (this.methodParameter.getParameterIndex() == -1) {
return TypeDescriptor.nullSafeAnnotations(this.methodParameter.getMethodAnnotations());
}
else {
return TypeDescriptor.nullSafeAnnotations(this.methodParameter.getParameterAnnotations());
}
}
@Override
protected Class<?> resolveCollectionElementType() {
return GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter);
}
@Override
protected Class<?> resolveMapKeyType() {
return GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter);
}
@Override
protected Class<?> resolveMapValueType() {
return GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter);
}
@Override
protected AbstractDescriptor nested(Class<?> type, int typeIndex) {
MethodParameter methodParameter = new MethodParameter(this.methodParameter);
methodParameter.increaseNestingLevel();
methodParameter.setTypeIndexForCurrentLevel(typeIndex);
return new ParameterDescriptor(type, methodParameter);
}
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* A description of a JavaBeans Property that allows us to avoid a dependency on
* <code>java.beans.PropertyDescriptor</code>. The <code>java.beans</code> package
* is not available in a number of environments (e.g. Android, Java ME), so this is
* desirable for portability of Spring's core conversion facility.
*
* <p>Used to build a TypeDescriptor from a property location.
* The built TypeDescriptor can then be used to convert from/to the property type.
*
* @author Keith Donald
* @since 3.1
* @see TypeDescriptor#TypeDescriptor(Property)
* @see TypeDescriptor#nested(Property, int)
*/
public final class Property {
private final Class<?> objectType;
private final Method readMethod;
private final Method writeMethod;
private final String name;
private final MethodParameter methodParameter;
private final Annotation[] annotations;
public Property(Class<?> objectType, Method readMethod, Method writeMethod) {
this.objectType = objectType;
this.readMethod = readMethod;
this.writeMethod = writeMethod;
this.methodParameter = resolveMethodParameter();
this.name = resolveName();
this.annotations = resolveAnnotations();
}
/**
* The object declaring this property, either directly or in a superclass the object extends.
*/
public Class<?> getObjectType() {
return this.objectType;
}
/**
* The name of the property: e.g. 'foo'
*/
public String getName() {
return this.name;
}
/**
* The property type: e.g. <code>java.lang.String</code>
*/
public Class<?> getType() {
return this.methodParameter.getParameterType();
}
/**
* The property getter method: e.g. <code>getFoo()</code>
*/
public Method getReadMethod() {
return this.readMethod;
}
/**
* The property setter method: e.g. <code>setFoo(String)</code>
*/
public Method getWriteMethod() {
return this.writeMethod;
}
// package private
MethodParameter getMethodParameter() {
return this.methodParameter;
}
Annotation[] getAnnotations() {
return this.annotations;
}
// internal helpers
private String resolveName() {
if (this.readMethod != null) {
int index = this.readMethod.getName().indexOf("get");
if (index != -1) {
index += 3;
}
else {
index = this.readMethod.getName().indexOf("is");
if (index == -1) {
throw new IllegalArgumentException("Not a getter method");
}
index += 2;
}
return StringUtils.uncapitalize(this.readMethod.getName().substring(index));
}
else {
int index = this.writeMethod.getName().indexOf("set") + 3;
if (index == -1) {
throw new IllegalArgumentException("Not a setter method");
}
return StringUtils.uncapitalize(this.writeMethod.getName().substring(index));
}
}
private MethodParameter resolveMethodParameter() {
MethodParameter read = resolveReadMethodParameter();
MethodParameter write = resolveWriteMethodParameter();
if (read == null && write == null) {
throw new IllegalStateException("Property is neither readable nor writeable");
}
if (read != null && write != null && !write.getParameterType().isAssignableFrom(read.getParameterType())) {
throw new IllegalStateException("Write parameter is not assignable from read parameter");
}
return read != null ? read : write;
}
private MethodParameter resolveReadMethodParameter() {
if (getReadMethod() == null) {
return null;
}
return resolveParameterType(new MethodParameter(getReadMethod(), -1));
}
private MethodParameter resolveWriteMethodParameter() {
if (getWriteMethod() == null) {
return null;
}
return resolveParameterType(new MethodParameter(getWriteMethod(), 0));
}
private MethodParameter resolveParameterType(MethodParameter parameter) {
// needed to resolve generic property types that parameterized by sub-classes e.g. T getFoo();
GenericTypeResolver.resolveParameterType(parameter, getObjectType());
return parameter;
}
private Annotation[] resolveAnnotations() {
Map<Class<?>, Annotation> annMap = new LinkedHashMap<Class<?>, Annotation>();
Method readMethod = getReadMethod();
if (readMethod != null) {
for (Annotation ann : readMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Method writeMethod = getWriteMethod();
if (writeMethod != null) {
for (Annotation ann : writeMethod.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
Field field = getField();
if (field != null) {
for (Annotation ann : field.getAnnotations()) {
annMap.put(ann.annotationType(), ann);
}
}
return annMap.values().toArray(new Annotation[annMap.size()]);
}
private Field getField() {
String name = getName();
if (!StringUtils.hasLength(name)) {
return null;
}
Class<?> declaringClass = declaringClass();
Field field = ReflectionUtils.findField(declaringClass, name);
if (field == null) {
// Same lenient fallback checking as in CachedIntrospectionResults...
field = ReflectionUtils.findField(declaringClass,
name.substring(0, 1).toLowerCase() + name.substring(1));
if (field == null) {
field = ReflectionUtils.findField(declaringClass,
name.substring(0, 1).toUpperCase() + name.substring(1));
}
}
return field;
}
private Class<?> declaringClass() {
if (getReadMethod() != null) {
return getReadMethod().getDeclaringClass();
}
else {
return getWriteMethod().getDeclaringClass();
}
}
}

View File

@@ -0,0 +1,577 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Context about a type to convert from or to.
*
* @author Keith Donald
* @author Andy Clement
* @author Juergen Hoeller
* @since 3.0
*/
public class TypeDescriptor {
static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
private static final Map<Class<?>, TypeDescriptor> typeDescriptorCache = new HashMap<Class<?>, TypeDescriptor>();
static {
typeDescriptorCache.put(boolean.class, new TypeDescriptor(boolean.class));
typeDescriptorCache.put(Boolean.class, new TypeDescriptor(Boolean.class));
typeDescriptorCache.put(byte.class, new TypeDescriptor(byte.class));
typeDescriptorCache.put(Byte.class, new TypeDescriptor(Byte.class));
typeDescriptorCache.put(char.class, new TypeDescriptor(char.class));
typeDescriptorCache.put(Character.class, new TypeDescriptor(Character.class));
typeDescriptorCache.put(short.class, new TypeDescriptor(short.class));
typeDescriptorCache.put(Short.class, new TypeDescriptor(Short.class));
typeDescriptorCache.put(int.class, new TypeDescriptor(int.class));
typeDescriptorCache.put(Integer.class, new TypeDescriptor(Integer.class));
typeDescriptorCache.put(long.class, new TypeDescriptor(long.class));
typeDescriptorCache.put(Long.class, new TypeDescriptor(Long.class));
typeDescriptorCache.put(float.class, new TypeDescriptor(float.class));
typeDescriptorCache.put(Float.class, new TypeDescriptor(Float.class));
typeDescriptorCache.put(double.class, new TypeDescriptor(double.class));
typeDescriptorCache.put(Double.class, new TypeDescriptor(Double.class));
typeDescriptorCache.put(String.class, new TypeDescriptor(String.class));
}
private final Class<?> type;
private final TypeDescriptor elementTypeDescriptor;
private final TypeDescriptor mapKeyTypeDescriptor;
private final TypeDescriptor mapValueTypeDescriptor;
private final Annotation[] annotations;
/**
* Create a new type descriptor from a {@link MethodParameter}.
* Use this constructor when a source or target conversion point is a constructor parameter, method parameter, or method return value.
* @param methodParameter the method parameter
*/
public TypeDescriptor(MethodParameter methodParameter) {
this(new ParameterDescriptor(methodParameter));
}
/**
* Create a new type descriptor from a {@link Field}.
* Use this constructor when source or target conversion point is a field.
* @param field the field
*/
public TypeDescriptor(Field field) {
this(new FieldDescriptor(field));
}
/**
* Create a new type descriptor from a {@link Property}.
* Use this constructor when a source or target conversion point is a property on a Java class.
* @param property the property
*/
public TypeDescriptor(Property property) {
this(new BeanPropertyDescriptor(property));
}
/**
* Create a new type descriptor from the given type.
* Use this to instruct the conversion system to convert an object to a specific target type, when no type location such as a method parameter or field is available to provide additional conversion context.
* Generally prefer use of {@link #forObject(Object)} for constructing type descriptors from source objects, as it handles the null object case.
* @param type the class
* @return the type descriptor
*/
public static TypeDescriptor valueOf(Class<?> type) {
TypeDescriptor desc = typeDescriptorCache.get(type);
return (desc != null ? desc : new TypeDescriptor(type));
}
/**
* Create a new type descriptor from a java.util.Collection type.
* Useful for converting to typed Collections.
* For example, a List&lt;String&gt; could be converted to a List&lt;EmailAddress&gt; by converting to a targetType built with this method.
* The method call to construct such a TypeDescriptor would look something like: collection(List.class, TypeDescriptor.valueOf(EmailAddress.class));
* @param collectionType the collection type, which must implement {@link Collection}.
* @param elementTypeDescriptor a descriptor for the collection's element type, used to convert collection elements
* @return the collection type descriptor
*/
public static TypeDescriptor collection(Class<?> collectionType, TypeDescriptor elementTypeDescriptor) {
if (!Collection.class.isAssignableFrom(collectionType)) {
throw new IllegalArgumentException("collectionType must be a java.util.Collection");
}
return new TypeDescriptor(collectionType, elementTypeDescriptor);
}
/**
* Create a new type descriptor from a java.util.Map type.
* Useful for Converting to typed Maps.
* For example, a Map&lt;String, String&gt; could be converted to a Map&lt;Id, EmailAddress&gt; by converting to a targetType built with this method:
* The method call to construct such a TypeDescriptor would look something like: map(Map.class, TypeDescriptor.valueOf(Id.class), TypeDescriptor.valueOf(EmailAddress.class));
* @param mapType the map type, which must implement {@link Map}.
* @param keyTypeDescriptor a descriptor for the map's key type, used to convert map keys
* @param valueTypeDescriptor the map's value type, used to convert map values
* @return the map type descriptor
*/
public static TypeDescriptor map(Class<?> mapType, TypeDescriptor keyTypeDescriptor, TypeDescriptor valueTypeDescriptor) {
if (!Map.class.isAssignableFrom(mapType)) {
throw new IllegalArgumentException("mapType must be a java.util.Map");
}
return new TypeDescriptor(mapType, keyTypeDescriptor, valueTypeDescriptor);
}
/**
* Creates a type descriptor for a nested type declared within the method parameter.
* For example, if the methodParameter is a List&lt;String&gt; and the nestingLevel is 1, the nested type descriptor will be String.class.
* If the methodParameter is a List<List<String>> and the nestingLevel is 2, the nested type descriptor will also be a String.class.
* If the methodParameter is a Map<Integer, String> and the nesting level is 1, the nested type descriptor will be String, derived from the map value.
* If the methodParameter is a List<Map<Integer, String>> and the nesting level is 2, the nested type descriptor will be String, derived from the map value.
* Returns null if a nested type cannot be obtained because it was not declared.
* For example, if the method parameter is a List&lt;?&gt;, the nested type descriptor returned will be null.
* @param methodParameter the method parameter with a nestingLevel of 1
* @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the method parameter.
* @return the nested type descriptor at the specified nesting level, or null if it could not be obtained.
* @throws IllegalArgumentException if the nesting level of the input {@link MethodParameter} argument is not 1.
* @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types.
*/
public static TypeDescriptor nested(MethodParameter methodParameter, int nestingLevel) {
if (methodParameter.getNestingLevel() != 1) {
throw new IllegalArgumentException("methodParameter nesting level must be 1: use the nestingLevel parameter to specify the desired nestingLevel for nested type traversal");
}
return nested(new ParameterDescriptor(methodParameter), nestingLevel);
}
/**
* Creates a type descriptor for a nested type declared within the field.
* <p>For example, if the field is a <code>List&lt;String&gt;</code> and the nestingLevel is 1, the nested type descriptor will be <code>String.class</code>.
* If the field is a <code>List&lt;List&lt;String&gt;&gt;</code> and the nestingLevel is 2, the nested type descriptor will also be a <code>String.class</code>.
* If the field is a <code>Map&lt;Integer, String&gt;</code> and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value.
* If the field is a <code>List&lt;Map&lt;Integer, String&gt;&gt;</code> and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value.
* Returns <code>null</code> if a nested type cannot be obtained because it was not declared.
* For example, if the field is a <code>List&lt;?&gt;</code>, the nested type descriptor returned will be <code>null</code>.
* @param field the field
* @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the field.
* @return the nested type descriptor at the specified nestingLevel, or null if it could not be obtained
* @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types.
*/
public static TypeDescriptor nested(Field field, int nestingLevel) {
return nested(new FieldDescriptor(field), nestingLevel);
}
/**
* Creates a type descriptor for a nested type declared within the property.
* <p>For example, if the property is a <code>List&lt;String&gt;</code> and the nestingLevel is 1, the nested type descriptor will be <code>String.class</code>.
* If the property is a <code>List&lt;List&lt;String&gt;&gt;</code> and the nestingLevel is 2, the nested type descriptor will also be a <code>String.class</code>.
* If the property is a <code>Map&lt;Integer, String&gt;</code> and the nestingLevel is 1, the nested type descriptor will be String, derived from the map value.
* If the property is a <code>List&lt;Map&lt;Integer, String&gt;&gt;</code> and the nestingLevel is 2, the nested type descriptor will be String, derived from the map value.
* Returns <code>null</code> if a nested type cannot be obtained because it was not declared.
* For example, if the property is a <code>List&lt;?&gt;</code>, the nested type descriptor returned will be <code>null</code>.
* @param property the property
* @param nestingLevel the nesting level of the collection/array element or map key/value declaration within the property.
* @return the nested type descriptor at the specified nestingLevel, or <code>null</code> if it could not be obtained
* @throws IllegalArgumentException if the types up to the specified nesting level are not of collection, array, or map types.
*/
public static TypeDescriptor nested(Property property, int nestingLevel) {
return nested(new BeanPropertyDescriptor(property), nestingLevel);
}
/**
* Create a new type descriptor for an object.
* Use this factory method to introspect a source object before asking the conversion system to convert it to some another type.
* If the provided object is null, returns null, else calls {@link #valueOf(Class)} to build a TypeDescriptor from the object's class.
* @param object the source object
* @return the type descriptor
*/
public static TypeDescriptor forObject(Object source) {
return (source != null ? valueOf(source.getClass()) : null);
}
/**
* The type of the backing class, method parameter, field, or property described by this TypeDescriptor.
* Returns primitive types as-is.
* See {@link #getObjectType()} for a variation of this operation that resolves primitive types to their corresponding Object types if necessary.
* @return the type, or <code>null</code> if this is {@link TypeDescriptor#NULL}
* @see #getObjectType()
*/
public Class<?> getType() {
return this.type;
}
/**
* Variation of {@link #getType()} that accounts for a primitive type by returning its object wrapper type.
* This is useful for conversion service implementations that wish to normalize to object-based types and not work with primitive types directly.
*/
public Class<?> getObjectType() {
return ClassUtils.resolvePrimitiveIfNecessary(getType());
}
/**
* Narrows this {@link TypeDescriptor} by setting its type to the class of the provided value.
* If the value is null, no narrowing is performed and this TypeDescriptor is returned unchanged.
* Designed to be called by binding frameworks when they read property, field, or method return values.
* Allows such frameworks to narrow a TypeDescriptor built from a declared property, field, or method return value type.
* For example, a field declared as java.lang.Object would be narrowed to java.util.HashMap if it was set to a java.util.HashMap value.
* The narrowed TypeDescriptor can then be used to convert the HashMap to some other type.
* Annotation and nested type context is preserved by the narrowed copy.
* @param value the value to use for narrowing this type descriptor
* @return this TypeDescriptor narrowed (returns a copy with its type updated to the class of the provided value)
*/
public TypeDescriptor narrow(Object value) {
if (value == null) {
return this;
}
return new TypeDescriptor(value.getClass(), this.elementTypeDescriptor,
this.mapKeyTypeDescriptor, this.mapValueTypeDescriptor, this.annotations);
}
/**
* Returns the name of this type: the fully qualified class name.
*/
public String getName() {
return ClassUtils.getQualifiedName(getType());
}
/**
* Is this type a primitive type?
*/
public boolean isPrimitive() {
return getType().isPrimitive();
}
/**
* The annotations associated with this type descriptor, if any.
* @return the annotations, or an empty array if none.
*/
public Annotation[] getAnnotations() {
return this.annotations;
}
/**
* Obtain the annotation associated with this type descriptor of the specified type.
* @return the annotation, or null if no such annotation exists on this type descriptor.
*/
public Annotation getAnnotation(Class<? extends Annotation> annotationType) {
for (Annotation annotation : getAnnotations()) {
if (annotation.annotationType().equals(annotationType)) {
return annotation;
}
}
return null;
}
/**
* Returns true if an object of this type descriptor can be assigned to the location described by the given type descriptor.
* For example, valueOf(String.class).isAssignableTo(valueOf(CharSequence.class)) returns true because a String value can be assigned to a CharSequence variable.
* On the other hand, valueOf(Number.class).isAssignableTo(valueOf(Integer.class)) returns false because, while all Integers are Numbers, not all Numbers are Integers.
* <p>
* For arrays, collections, and maps, element and key/value types are checked if declared.
* For example, a List&lt;String&gt; field value is assignable to a Collection&lt;CharSequence&gt; field, but List&lt;Number&gt; is not assignable to List&lt;Integer&gt;.
* @return true if this type is assignable to the type represented by the provided type descriptor.
* @see #getObjectType()
*/
public boolean isAssignableTo(TypeDescriptor typeDescriptor) {
boolean typesAssignable = typeDescriptor.getObjectType().isAssignableFrom(getObjectType());
if (!typesAssignable) {
return false;
}
if (isArray() && typeDescriptor.isArray()) {
return getElementTypeDescriptor().isAssignableTo(typeDescriptor.getElementTypeDescriptor());
}
else if (isCollection() && typeDescriptor.isCollection()) {
return isNestedAssignable(getElementTypeDescriptor(), typeDescriptor.getElementTypeDescriptor());
}
else if (isMap() && typeDescriptor.isMap()) {
return isNestedAssignable(getMapKeyTypeDescriptor(), typeDescriptor.getMapKeyTypeDescriptor()) &&
isNestedAssignable(getMapValueTypeDescriptor(), typeDescriptor.getMapValueTypeDescriptor());
}
else {
return true;
}
}
// indexable type descriptor operations
/**
* Is this type a {@link Collection} type?
*/
public boolean isCollection() {
return Collection.class.isAssignableFrom(getType());
}
/**
* Is this type an array type?
*/
public boolean isArray() {
return getType().isArray();
}
/**
* If this type is an array, returns the array's component type.
* If this type is a {@link Collection} and it is parameterized, returns the Collection's element type.
* If the Collection is not parameterized, returns null indicating the element type is not declared.
* @return the array component type or Collection element type, or <code>null</code> if this type is a Collection but its element type is not parameterized.
* @throws IllegalStateException if this type is not a java.util.Collection or Array type
*/
public TypeDescriptor getElementTypeDescriptor() {
assertCollectionOrArray();
return this.elementTypeDescriptor;
}
/**
* If this type is a {@link Collection} or an Array, creates a element TypeDescriptor from the provided collection or array element.
* Narrows the {@link #getElementTypeDescriptor() elementType} property to the class of the provided collection or array element.
* For example, if this describes a java.util.List&lt;java.lang.Number&lt; and the element argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
* If this describes a java.util.List&lt;?&gt; and the element argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well.
* Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned.
* @param element the collection or array element
* @return a element type descriptor, narrowed to the type of the provided element
* @throws IllegalStateException if this type is not a java.util.Collection or Array type
* @see #narrow(Object)
*/
public TypeDescriptor elementTypeDescriptor(Object element) {
return narrow(element, getElementTypeDescriptor());
}
// map type descriptor operations
/**
* Is this type a {@link Map} type?
*/
public boolean isMap() {
return Map.class.isAssignableFrom(getType());
}
/**
* If this type is a {@link Map} and its key type is parameterized, returns the map's key type.
* If the Map's key type is not parameterized, returns null indicating the key type is not declared.
* @return the Map key type, or <code>null</code> if this type is a Map but its key type is not parameterized.
* @throws IllegalStateException if this type is not a java.util.Map.
*/
public TypeDescriptor getMapKeyTypeDescriptor() {
assertMap();
return this.mapKeyTypeDescriptor;
}
/**
* If this type is a {@link Map}, creates a mapKey {@link TypeDescriptor} from the provided map key.
* Narrows the {@link #getMapKeyTypeDescriptor() mapKeyType} property to the class of the provided map key.
* For example, if this describes a java.util.Map&lt;java.lang.Number, java.lang.String&lt; and the key argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
* If this describes a java.util.Map&lt;?, ?&gt; and the key argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well.
* Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned.
* @param mapKey the map key
* @return the map key type descriptor
* @throws IllegalStateException if this type is not a java.util.Map.
* @see #narrow(Object)
*/
public TypeDescriptor getMapKeyTypeDescriptor(Object mapKey) {
return narrow(mapKey, getMapKeyTypeDescriptor());
}
/**
* If this type is a {@link Map} and its value type is parameterized, returns the map's value type.
* If the Map's value type is not parameterized, returns null indicating the value type is not declared.
* @return the Map value type, or <code>null</code> if this type is a Map but its value type is not parameterized.
* @throws IllegalStateException if this type is not a java.util.Map.
*/
public TypeDescriptor getMapValueTypeDescriptor() {
assertMap();
return this.mapValueTypeDescriptor;
}
/**
* If this type is a {@link Map}, creates a mapValue {@link TypeDescriptor} from the provided map value.
* Narrows the {@link #getMapValueTypeDescriptor() mapValueType} property to the class of the provided map value.
* For example, if this describes a java.util.Map&lt;java.lang.String, java.lang.Number&lt; and the value argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
* If this describes a java.util.Map&lt;?, ?&gt; and the value argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer as well.
* Annotation and nested type context will be preserved in the narrowed TypeDescriptor that is returned.
* @param mapValue the map value
* @return the map value type descriptor
* @throws IllegalStateException if this type is not a java.util.Map.
*/
public TypeDescriptor getMapValueTypeDescriptor(Object mapValue) {
return narrow(mapValue, getMapValueTypeDescriptor());
}
// deprecations in Spring 3.1
/**
* Returns the value of {@link TypeDescriptor#getType() getType()} for the {@link #getElementTypeDescriptor() elementTypeDescriptor}.
* @deprecated in Spring 3.1 in favor of {@link #getElementTypeDescriptor()}.
* @throws IllegalStateException if this type is not a java.util.Collection or Array type
*/
@Deprecated
public Class<?> getElementType() {
return getElementTypeDescriptor().getType();
}
/**
* Returns the value of {@link TypeDescriptor#getType() getType()} for the {@link #getMapKeyTypeDescriptor() getMapKeyTypeDescriptor}.
* @deprecated in Spring 3.1 in favor of {@link #getMapKeyTypeDescriptor()}.
* @throws IllegalStateException if this type is not a java.util.Map.
*/
@Deprecated
public Class<?> getMapKeyType() {
return getMapKeyTypeDescriptor().getType();
}
/**
* Returns the value of {@link TypeDescriptor#getType() getType()} for the {@link #getMapValueTypeDescriptor() getMapValueTypeDescriptor}.
* @deprecated in Spring 3.1 in favor of {@link #getMapValueTypeDescriptor()}.
* @throws IllegalStateException if this type is not a java.util.Map.
*/
@Deprecated
public Class<?> getMapValueType() {
return getMapValueTypeDescriptor().getType();
}
// package private helpers
TypeDescriptor(AbstractDescriptor descriptor) {
this.type = descriptor.getType();
this.elementTypeDescriptor = descriptor.getElementTypeDescriptor();
this.mapKeyTypeDescriptor = descriptor.getMapKeyTypeDescriptor();
this.mapValueTypeDescriptor = descriptor.getMapValueTypeDescriptor();
this.annotations = descriptor.getAnnotations();
}
static Annotation[] nullSafeAnnotations(Annotation[] annotations) {
return annotations != null ? annotations : EMPTY_ANNOTATION_ARRAY;
}
// internal constructors
private TypeDescriptor(Class<?> type) {
this(new ClassDescriptor(type));
}
private TypeDescriptor(Class<?> collectionType, TypeDescriptor elementTypeDescriptor) {
this(collectionType, elementTypeDescriptor, null, null, EMPTY_ANNOTATION_ARRAY);
}
private TypeDescriptor(Class<?> mapType, TypeDescriptor keyTypeDescriptor, TypeDescriptor valueTypeDescriptor) {
this(mapType, null, keyTypeDescriptor, valueTypeDescriptor, EMPTY_ANNOTATION_ARRAY);
}
private TypeDescriptor(Class<?> type, TypeDescriptor elementTypeDescriptor, TypeDescriptor mapKeyTypeDescriptor,
TypeDescriptor mapValueTypeDescriptor, Annotation[] annotations) {
this.type = type;
this.elementTypeDescriptor = elementTypeDescriptor;
this.mapKeyTypeDescriptor = mapKeyTypeDescriptor;
this.mapValueTypeDescriptor = mapValueTypeDescriptor;
this.annotations = annotations;
}
private static TypeDescriptor nested(AbstractDescriptor descriptor, int nestingLevel) {
for (int i = 0; i < nestingLevel; i++) {
descriptor = descriptor.nested();
if (descriptor == null) {
return null;
}
}
return new TypeDescriptor(descriptor);
}
// internal helpers
private void assertCollectionOrArray() {
if (!isCollection() && !isArray()) {
throw new IllegalStateException("Not a java.util.Collection or Array");
}
}
private void assertMap() {
if (!isMap()) {
throw new IllegalStateException("Not a java.util.Map");
}
}
private TypeDescriptor narrow(Object value, TypeDescriptor typeDescriptor) {
if (typeDescriptor != null) {
return typeDescriptor.narrow(value);
}
else {
return (value != null ? new TypeDescriptor(value.getClass(), null, null, null, this.annotations) : null);
}
}
private boolean isNestedAssignable(TypeDescriptor nestedTypeDescriptor, TypeDescriptor otherNestedTypeDescriptor) {
if (nestedTypeDescriptor == null || otherNestedTypeDescriptor == null) {
return true;
}
return nestedTypeDescriptor.isAssignableTo(otherNestedTypeDescriptor);
}
private String wildcard(TypeDescriptor typeDescriptor) {
return typeDescriptor != null ? typeDescriptor.toString() : "?";
}
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof TypeDescriptor)) {
return false;
}
TypeDescriptor other = (TypeDescriptor) obj;
boolean annotatedTypeEquals = ObjectUtils.nullSafeEquals(getType(), other.getType()) && ObjectUtils.nullSafeEquals(getAnnotations(), other.getAnnotations());
if (!annotatedTypeEquals) {
return false;
}
if (isCollection() || isArray()) {
return ObjectUtils.nullSafeEquals(getElementTypeDescriptor(), other.getElementTypeDescriptor());
}
else if (isMap()) {
return ObjectUtils.nullSafeEquals(getMapKeyTypeDescriptor(), other.getMapKeyTypeDescriptor()) && ObjectUtils.nullSafeEquals(getMapValueTypeDescriptor(), other.getMapValueTypeDescriptor());
}
else {
return true;
}
}
public int hashCode() {
return getType().hashCode();
}
public String toString() {
StringBuilder builder = new StringBuilder();
Annotation[] anns = getAnnotations();
for (Annotation ann : anns) {
builder.append("@").append(ann.annotationType().getName()).append(' ');
}
builder.append(ClassUtils.getQualifiedName(getType()));
if (isMap()) {
builder.append("<").append(wildcard(getMapKeyTypeDescriptor()));
builder.append(", ").append(wildcard(getMapValueTypeDescriptor())).append(">");
}
else if (isCollection()) {
builder.append("<").append(wildcard(getElementTypeDescriptor())).append(">");
}
return builder.toString();
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.converter;
import org.springframework.core.convert.TypeDescriptor;
/**
* A generic converter that conditionally executes.
*
* <p>Applies a rule that determines if a converter between a set of
* {@link #getConvertibleTypes() convertible types} matches given a client request to
* convert between a source field of convertible type S and a target field of convertible type T.
*
* <p>Often used to selectively match custom conversion logic based on the presence of
* a field or class-level characteristic, such as an annotation or method. For example,
* when converting from a String field to a Date field, an implementation might return
* <code>true</code> if the target field has also been annotated with <code>@DateTimeFormat</code>.
*
* <p>As another example, when converting from a String field to an Account field,
* an implementation might return true if the target Account class defines a
* <code>public static findAccount(String)</code> method.
*
* @author Keith Donald
* @since 3.0
*/
public interface ConditionalGenericConverter extends GenericConverter {
/**
* Should the converter from <code>sourceType</code> to <code>targetType</code>
* currently under consideration be selected?
* @param sourceType the type descriptor of the field we are converting from
* @param targetType the type descriptor of the field we are converting to
* @return true if conversion should be performed, false otherwise
*/
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.converter;
/**
* A converter converts a source object of type S to a target of type T.
* Implementations of this interface are thread-safe and can be shared.
*
* @author Keith Donald
* @since 3.0
*/
public interface Converter<S, T> {
/**
* Convert the source of type S to target type T.
* @param source the source object to convert, which must be an instance of S
* @return the converted object, which must be an instance of T
* @throws IllegalArgumentException if the source could not be converted to the desired target type
*/
T convert(S source);
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.converter;
/**
* A factory for "ranged" converters that can convert objects from S to subtypes of R.
*
* @author Keith Donald
* @since 3.0
* @param <S> The source type converters created by this factory can convert from
* @param <R> The target range (or base) type converters created by this factory can convert to;
* for example {@link Number} for a set of number subtypes.
*/
public interface ConverterFactory<S, R> {
/**
* Get the converter to convert from S to target type T, where T is also an instance of R.
* @param <T> the target type
* @param targetType the target type to convert to
* @return A converter from S to T
*/
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.converter;
/**
* For registering converters with a type conversion system.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 3.0
*/
public interface ConverterRegistry {
/**
* Add a plain converter to this registry.
* The convertible sourceType/targetType pair is derived from the Converter's parameterized types.
* @throws IllegalArgumentException if the parameterized types could not be resolved
*/
void addConverter(Converter<?, ?> converter);
/**
* Add a plain converter to this registry.
* The convertible sourceType/targetType pair is specified explicitly.
* Allows for a Converter to be reused for multiple distinct pairs without having to create a Converter class for each pair.
* @since 3.1
*/
void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter);
/**
* Add a generic converter to this registry.
*/
void addConverter(GenericConverter converter);
/**
* Add a ranged converter factory to this registry.
* The convertible sourceType/rangeType pair is derived from the ConverterFactory's parameterized types.
* @throws IllegalArgumentException if the parameterized types could not be resolved.
*/
void addConverterFactory(ConverterFactory<?, ?> converterFactory);
/**
* Remove any converters from sourceType to targetType.
* @param sourceType the source type
* @param targetType the target type
*/
void removeConvertible(Class<?> sourceType, Class<?> targetType);
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.converter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
import java.util.Set;
/**
* Generic converter interface for converting between two or more types.
*
* <p>This is the most flexible of the Converter SPI interfaces, but also the most complex.
* It is flexible in that a GenericConverter may support converting between multiple source/target
* type pairs (see {@link #getConvertibleTypes()}. In addition, GenericConverter implementations
* have access to source/target {@link TypeDescriptor field context} during the type conversion process.
* This allows for resolving source and target field metadata such as annotations and generics
* information, which can be used influence the conversion logic.
*
* <p>This interface should generally not be used when the simpler {@link Converter} or
* {@link ConverterFactory} interfaces are sufficient.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 3.0
* @see TypeDescriptor
* @see Converter
* @see ConverterFactory
*/
public interface GenericConverter {
/**
* Return the source and target types which this converter can convert between.
* <p>Each entry is a convertible source-to-target type pair.
*/
Set<ConvertiblePair> getConvertibleTypes();
/**
* Convert the source to the targetType described by the TypeDescriptor.
* @param source the source object to convert (may be null)
* @param sourceType the type descriptor of the field we are converting from
* @param targetType the type descriptor of the field we are converting to
* @return the converted object
*/
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
/**
* Holder for a source-to-target class pair.
*/
public static final class ConvertiblePair {
private final Class<?> sourceType;
private final Class<?> targetType;
/**
* Create a new source-to-target pair.
* @param sourceType the source type
* @param targetType the target type
*/
public ConvertiblePair(Class<?> sourceType, Class<?> targetType) {
Assert.notNull(sourceType, "Source type must not be null");
Assert.notNull(targetType, "Target type must not be null");
this.sourceType = sourceType;
this.targetType = targetType;
}
public Class<?> getSourceType() {
return this.sourceType;
}
public Class<?> getTargetType() {
return this.targetType;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || obj.getClass() != ConvertiblePair.class) {
return false;
}
ConvertiblePair other = (ConvertiblePair) obj;
return this.sourceType.equals(other.sourceType) && this.targetType.equals(other.targetType);
}
@Override
public int hashCode() {
return this.sourceType.hashCode() * 31 + this.targetType.hashCode();
}
}
}

View File

@@ -0,0 +1,8 @@
/**
*
* SPI to implement Converters for the type conversion system.
*
*/
package org.springframework.core.convert.converter;

View File

@@ -0,0 +1,8 @@
/**
*
* Type conversion system API.
*
*/
package org.springframework.core.convert;

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.util.ObjectUtils;
/**
* Converts an Array to another Array.
* First adapts the source array to a List, then delegates to {@link CollectionToArrayConverter} to perform the target array conversion.
*
* @author Keith Donald
* @since 3.0
*/
final class ArrayToArrayConverter implements ConditionalGenericConverter {
private final CollectionToArrayConverter helperConverter;
public ArrayToArrayConverter(ConversionService conversionService) {
this.helperConverter = new CollectionToArrayConverter(conversionService);
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object[].class, Object[].class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.matches(sourceType, targetType);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.convert(Arrays.asList(ObjectUtils.toObjectArray(source)), sourceType, targetType);
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
/**
* Converts an Array to a Collection.
*
* <p>First, creates a new Collection of the requested targetType.
* Then adds each array element to the target collection.
* Will perform an element conversion from the source component type to the collection's parameterized type if necessary.
*
* @author Keith Donald
* @since 3.0
*/
final class ArrayToCollectionConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public ArrayToCollectionConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object[].class, Collection.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(
sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService);
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
int length = Array.getLength(source);
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), length);
if (targetType.getElementTypeDescriptor() == null) {
for (int i = 0; i < length; i++) {
Object sourceElement = Array.get(source, i);
target.add(sourceElement);
}
}
else {
for (int i = 0; i < length; i++) {
Object sourceElement = Array.get(source, i);
Object targetElement = this.conversionService.convert(sourceElement,
sourceType.elementTypeDescriptor(sourceElement), targetType.getElementTypeDescriptor());
target.add(targetElement);
}
}
return target;
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
/**
* Converts an Array to an Object by returning the first array element after converting it to the desired targetType.
*
* @author Keith Donald
* @since 3.0
*/
final class ArrayToObjectConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public ArrayToObjectConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object[].class, Object.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType, this.conversionService);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
if (sourceType.isAssignableTo(targetType)) {
return source;
}
if (Array.getLength(source) == 0) {
return null;
}
Object firstElement = Array.get(source, 0);
return this.conversionService.convert(firstElement, sourceType.elementTypeDescriptor(firstElement), targetType);
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Arrays;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.util.ObjectUtils;
/**
* Converts an Array to a comma-delimited String.
* This implementation first adapts the source Array to a List, then delegates to {@link CollectionToStringConverter} to perform the target String conversion.
*
* @author Keith Donald
* @since 3.0
*/
final class ArrayToStringConverter implements ConditionalGenericConverter {
private final CollectionToStringConverter helperConverter;
public ArrayToStringConverter(ConversionService conversionService) {
this.helperConverter = new CollectionToStringConverter(conversionService);
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object[].class, String.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.matches(sourceType, targetType);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.helperConverter.convert(Arrays.asList(ObjectUtils.toObjectArray(source)), sourceType, targetType);
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.util.NumberUtils;
/**
* Converts from a Character to any JDK-standard Number implementation.
*
* <p>Support Number classes including Byte, Short, Integer, Float, Double, Long, BigInteger, BigDecimal. This class
* delegates to {@link NumberUtils#convertNumberToTargetClass(Number, Class)} to perform the conversion.
*
* @author Keith Donald
* @since 3.0
* @see java.lang.Byte
* @see java.lang.Short
* @see java.lang.Integer
* @see java.lang.Long
* @see java.math.BigInteger
* @see java.lang.Float
* @see java.lang.Double
* @see java.math.BigDecimal
* @see NumberUtils
*/
final class CharacterToNumberFactory implements ConverterFactory<Character, Number> {
public <T extends Number> Converter<Character, T> getConverter(Class<T> targetType) {
return new CharacterToNumber<T>(targetType);
}
private static final class CharacterToNumber<T extends Number> implements Converter<Character, T> {
private final Class<T> targetType;
public CharacterToNumber(Class<T> targetType) {
this.targetType = targetType;
}
public T convert(Character source) {
return NumberUtils.convertNumberToTargetClass((short) source.charValue(), this.targetType);
}
}
}

View File

@@ -0,0 +1,69 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
/**
* Converts a Collection to an array.
*
* <p>First, creates a new array of the requested targetType with a length equal to the
* size of the source Collection. Then sets each collection element into the array.
* Will perform an element conversion from the collection's parameterized type to the
* array's component type if necessary.
*
* @author Keith Donald
* @since 3.0
*/
final class CollectionToArrayConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public CollectionToArrayConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Collection.class, Object[].class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Collection<?> sourceCollection = (Collection<?>) source;
Object array = Array.newInstance(targetType.getElementTypeDescriptor().getType(), sourceCollection.size());
int i = 0;
for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement, sourceType.elementTypeDescriptor(sourceElement), targetType.getElementTypeDescriptor());
Array.set(array, i++, targetElement);
}
return array;
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
/**
* Converts from a Collection to another Collection.
*
* <p>First, creates a new Collection of the requested targetType with a size equal to the
* size of the source Collection. Then copies each element in the source collection to the
* target collection. Will perform an element conversion from the source collection's
* parameterized type to the target collection's parameterized type if necessary.
*
* @author Keith Donald
* @since 3.0
*/
final class CollectionToCollectionConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public CollectionToCollectionConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Collection.class, Collection.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(
sourceType.getElementTypeDescriptor(), targetType.getElementTypeDescriptor(), this.conversionService);
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
boolean copyRequired = !targetType.getType().isInstance(source);
Collection<?> sourceCollection = (Collection<?>) source;
if (!copyRequired && sourceCollection.isEmpty()) {
return sourceCollection;
}
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), sourceCollection.size());
if (targetType.getElementTypeDescriptor() == null) {
for (Object element : sourceCollection) {
target.add(element);
}
}
else {
for (Object sourceElement : sourceCollection) {
Object targetElement = this.conversionService.convert(sourceElement,
sourceType.elementTypeDescriptor(sourceElement), targetType.getElementTypeDescriptor());
target.add(targetElement);
if (sourceElement != targetElement) {
copyRequired = true;
}
}
}
return (copyRequired ? target : source);
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
/**
* Converts a Collection to an Object by returning the first collection element after converting it to the desired targetType.
*
* @author Keith Donald
* @since 3.0
*/
final class CollectionToObjectConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public CollectionToObjectConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Collection.class, Object.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType, this.conversionService);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
if (sourceType.isAssignableTo(targetType)) {
return source;
}
Collection<?> sourceCollection = (Collection<?>) source;
if (sourceCollection.size() == 0) {
return null;
}
Object firstElement = sourceCollection.iterator().next();
return this.conversionService.convert(firstElement, sourceType.elementTypeDescriptor(firstElement), targetType);
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
/**
* Converts a Collection to a comma-delimited String.
*
* @author Keith Donald
* @since 3.0
*/
final class CollectionToStringConverter implements ConditionalGenericConverter {
private static final String DELIMITER = ",";
private final ConversionService conversionService;
public CollectionToStringConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Collection.class, String.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(sourceType.getElementTypeDescriptor(), targetType, this.conversionService);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Collection<?> sourceCollection = (Collection<?>) source;
if (sourceCollection.size() == 0) {
return "";
}
StringBuilder sb = new StringBuilder();
int i = 0;
for (Object sourceElement : sourceCollection) {
if (i > 0) {
sb.append(DELIMITER);
}
Object targetElement = this.conversionService.convert(sourceElement, sourceType.elementTypeDescriptor(sourceElement), targetType);
sb.append(targetElement);
i++;
}
return sb.toString();
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.ConverterRegistry;
/**
* Configuration interface to be implemented by most if not all {@link ConversionService}
* types. Consolidates the read-only operations exposed by {@link ConversionService} and
* the mutating operations of {@link ConverterRegistry} to allow for convenient ad-hoc
* addition and removal of {@link org.springframework.core.convert.converter.Converter
* Converters} through. The latter is particularly useful when working against a
* {@link org.springframework.core.env.ConfigurableEnvironment ConfigurableEnvironment}
* instance in application context bootstrapping code.
*
* @author Chris Beams
* @since 3.1
* @see org.springframework.core.env.ConfigurablePropertyResolver#getConversionService()
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.context.ConfigurableApplicationContext#getEnvironment()
*/
public interface ConfigurableConversionService extends ConversionService, ConverterRegistry {
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Set;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.converter.GenericConverter;
/**
* A factory for common {@link org.springframework.core.convert.ConversionService}
* configurations.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Chris Beams
* @since 3.0
*/
public abstract class ConversionServiceFactory {
/**
* Register the given Converter objects with the given target ConverterRegistry.
* @param converters the converter objects: implementing {@link Converter},
* {@link ConverterFactory}, or {@link GenericConverter}
* @param registry the target registry
*/
public static void registerConverters(Set<?> converters, ConverterRegistry registry) {
if (converters != null) {
for (Object converter : converters) {
if (converter instanceof GenericConverter) {
registry.addConverter((GenericConverter) converter);
}
else if (converter instanceof Converter<?, ?>) {
registry.addConverter((Converter<?, ?>) converter);
}
else if (converter instanceof ConverterFactory<?, ?>) {
registry.addConverterFactory((ConverterFactory<?, ?>) converter);
}
else {
throw new IllegalArgumentException("Each converter object must implement one of the " +
"Converter, ConverterFactory, or GenericConverter interfaces");
}
}
}
}
/**
* Create a new default GenericConversionService instance that can be safely modified.
* @deprecated in Spring 3.1 in favor of {@link DefaultConversionService#DefaultConversionService()}
*/
public static GenericConversionService createDefaultConversionService() {
return new DefaultConversionService();
}
/**
* Populate the given GenericConversionService instance with the set of default converters.
* @deprecated in Spring 3.1 in favor of {@link DefaultConversionService#addDefaultConverters(ConverterRegistry)}
*/
public static void addDefaultConverters(GenericConversionService conversionService) {
DefaultConversionService.addDefaultConverters(conversionService);
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.GenericConverter;
/**
* Internal utilities for the conversion package.
*
* @author Keith Donald
* @since 3.0
*/
abstract class ConversionUtils {
public static Object invokeConverter(GenericConverter converter, Object source, TypeDescriptor sourceType,
TypeDescriptor targetType) {
try {
return converter.convert(source, sourceType, targetType);
}
catch (ConversionFailedException ex) {
throw ex;
}
catch (Exception ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
}
}
public static boolean canConvertElements(TypeDescriptor sourceElementType, TypeDescriptor targetElementType, ConversionService conversionService) {
if (targetElementType == null) {
// yes
return true;
}
if (sourceElementType == null) {
// maybe
return true;
}
if (conversionService.canConvert(sourceElementType, targetElementType)) {
// yes
return true;
}
else if (sourceElementType.getType().isAssignableFrom(targetElementType.getType())) {
// maybe;
return true;
}
else {
// no;
return false;
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.beans.PropertyEditorSupport;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
/**
* Adapter that exposes a {@link java.beans.PropertyEditor} for any given
* {@link org.springframework.core.convert.ConversionService} and specific target type.
*
* @author Juergen Hoeller
* @since 3.0
*/
public class ConvertingPropertyEditorAdapter extends PropertyEditorSupport {
private final ConversionService conversionService;
private final TypeDescriptor targetDescriptor;
private final boolean canConvertToString;
/**
* Create a new ConvertingPropertyEditorAdapter for a given
* {@link org.springframework.core.convert.ConversionService}
* and the given target type.
* @param conversionService the ConversionService to delegate to
* @param targetDescriptor the target type to convert to
*/
public ConvertingPropertyEditorAdapter(ConversionService conversionService, TypeDescriptor targetDescriptor) {
Assert.notNull(conversionService, "ConversionService must not be null");
Assert.notNull(targetDescriptor, "TypeDescriptor must not be null");
this.conversionService = conversionService;
this.targetDescriptor = targetDescriptor;
this.canConvertToString = conversionService.canConvert(this.targetDescriptor, TypeDescriptor.valueOf(String.class));
}
@Override
public void setAsText(String text) throws IllegalArgumentException {
setValue(this.conversionService.convert(text, TypeDescriptor.valueOf(String.class), this.targetDescriptor));
}
@Override
public String getAsText() {
if (this.canConvertToString) {
return (String) this.conversionService.convert(getValue(), this.targetDescriptor, TypeDescriptor.valueOf(String.class));
}
else {
return null;
}
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Locale;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.ConverterRegistry;
/**
* A specialization of {@link GenericConversionService} configured by default with
* converters appropriate for most environments.
*
* <p>Designed for direct instantiation but also exposes the static
* {@link #addDefaultConverters(ConverterRegistry)} utility method for ad hoc use against any
* {@code ConverterRegistry} instance.
*
* @author Chris Beams
* @since 3.1
*/
public class DefaultConversionService extends GenericConversionService {
/**
* Create a new {@code DefaultConversionService} with the set of
* {@linkplain DefaultConversionService#addDefaultConverters(ConverterRegistry) default converters}.
*/
public DefaultConversionService() {
addDefaultConverters(this);
}
// static utility methods
/**
* Add converters appropriate for most environments.
* @param converterRegistry the registry of converters to add to (must also be castable to ConversionService)
* @throws ClassCastException if the converterRegistry could not be cast to a ConversionService
*/
public static void addDefaultConverters(ConverterRegistry converterRegistry) {
addScalarConverters(converterRegistry);
addCollectionConverters(converterRegistry);
addFallbackConverters(converterRegistry);
}
// internal helpers
private static void addScalarConverters(ConverterRegistry converterRegistry) {
converterRegistry.addConverter(new StringToBooleanConverter());
converterRegistry.addConverter(Boolean.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new StringToNumberConverterFactory());
converterRegistry.addConverter(Number.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverterFactory(new NumberToNumberConverterFactory());
converterRegistry.addConverter(new StringToCharacterConverter());
converterRegistry.addConverter(Character.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new NumberToCharacterConverter());
converterRegistry.addConverterFactory(new CharacterToNumberFactory());
converterRegistry.addConverterFactory(new StringToEnumConverterFactory());
converterRegistry.addConverter(Enum.class, String.class, new EnumToStringConverter());
converterRegistry.addConverter(new StringToLocaleConverter());
converterRegistry.addConverter(Locale.class, String.class, new ObjectToStringConverter());
converterRegistry.addConverter(new PropertiesToStringConverter());
converterRegistry.addConverter(new StringToPropertiesConverter());
}
private static void addCollectionConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
converterRegistry.addConverter(new ArrayToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToCollectionConverter(conversionService));
converterRegistry.addConverter(new MapToMapConverter(conversionService));
converterRegistry.addConverter(new ArrayToStringConverter(conversionService));
converterRegistry.addConverter(new StringToArrayConverter(conversionService));
converterRegistry.addConverter(new ArrayToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToArrayConverter(conversionService));
converterRegistry.addConverter(new CollectionToStringConverter(conversionService));
converterRegistry.addConverter(new StringToCollectionConverter(conversionService));
converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
}
private static void addFallbackConverters(ConverterRegistry converterRegistry) {
ConversionService conversionService = (ConversionService) converterRegistry;
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter(conversionService));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
/**
* Simply calls {@link Enum#name()} to convert a source Enum to a String.
* @author Keith Donald
* @since 3.0
*/
final class EnumToStringConverter implements Converter<Enum<?>, String> {
public String convert(Enum<?> source) {
return source.name();
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.io.StringWriter;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
/**
* Simply calls {@link Object#toString()} to convert any supported Object to a String.
* Supports CharSequence, StringWriter, and any class with a String constructor or <code>valueOf(String)</code> method.
*
* <p>Used by the default ConversionService as a fallback if there are no other explicit
* to-String converters registered.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 3.0
*/
final class FallbackObjectToStringConverter implements ConditionalGenericConverter {
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object.class, String.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
Class<?> sourceClass = sourceType.getObjectType();
return CharSequence.class.isAssignableFrom(sourceClass) || StringWriter.class.isAssignableFrom(sourceClass) ||
ObjectToObjectConverter.hasValueOfMethodOrConstructor(sourceClass, String.class);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return (source != null ? source.toString() : null);
}
}

View File

@@ -0,0 +1,644 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.core.convert.converter.ConverterRegistry;
import org.springframework.core.convert.converter.GenericConverter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Base {@link ConversionService} implementation suitable for use in most environments.
* Indirectly implements {@link ConverterRegistry} as registration API through the
* {@link ConfigurableConversionService} interface.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Chris Beams
* @since 3.0
*/
public class GenericConversionService implements ConfigurableConversionService {
private static final GenericConverter NO_OP_CONVERTER = new GenericConverter() {
public Set<ConvertiblePair> getConvertibleTypes() {
return null;
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
return source;
}
public String toString() {
return "NO_OP";
}
};
private static final GenericConverter NO_MATCH = new GenericConverter() {
public Set<ConvertiblePair> getConvertibleTypes() {
throw new UnsupportedOperationException();
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
throw new UnsupportedOperationException();
}
public String toString() {
return "NO_MATCH";
}
};
private final Map<Class<?>, Map<Class<?>, MatchableConverters>> converters =
new HashMap<Class<?>, Map<Class<?>, MatchableConverters>>(36);
private final Map<ConverterCacheKey, GenericConverter> converterCache =
new ConcurrentHashMap<ConverterCacheKey, GenericConverter>();
// implementing ConverterRegistry
public void addConverter(Converter<?, ?> converter) {
GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converter, Converter.class);
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to the determine sourceType <S> and targetType <T> which " +
"your Converter<S, T> converts between; declare these generic types.");
}
addConverter(new ConverterAdapter(typeInfo, converter));
}
public void addConverter(Class<?> sourceType, Class<?> targetType, Converter<?, ?> converter) {
GenericConverter.ConvertiblePair typeInfo = new GenericConverter.ConvertiblePair(sourceType, targetType);
addConverter(new ConverterAdapter(typeInfo, converter));
}
public void addConverter(GenericConverter converter) {
Set<GenericConverter.ConvertiblePair> convertibleTypes = converter.getConvertibleTypes();
for (GenericConverter.ConvertiblePair convertibleType : convertibleTypes) {
getMatchableConverters(convertibleType.getSourceType(), convertibleType.getTargetType()).add(converter);
}
invalidateCache();
}
public void addConverterFactory(ConverterFactory<?, ?> converterFactory) {
GenericConverter.ConvertiblePair typeInfo = getRequiredTypeInfo(converterFactory, ConverterFactory.class);
if (typeInfo == null) {
throw new IllegalArgumentException("Unable to the determine sourceType <S> and targetRangeType R which " +
"your ConverterFactory<S, R> converts between; declare these generic types.");
}
addConverter(new ConverterFactoryAdapter(typeInfo, converterFactory));
}
public void removeConvertible(Class<?> sourceType, Class<?> targetType) {
getSourceConverterMap(sourceType).remove(targetType);
invalidateCache();
}
// implementing ConversionService
public boolean canConvert(Class<?> sourceType, Class<?> targetType) {
if (targetType == null) {
throw new IllegalArgumentException("The targetType to convert to cannot be null");
}
return canConvert(sourceType != null ? TypeDescriptor.valueOf(sourceType) : null, TypeDescriptor.valueOf(targetType));
}
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType == null) {
throw new IllegalArgumentException("The targetType to convert to cannot be null");
}
if (sourceType == null) {
return true;
}
GenericConverter converter = getConverter(sourceType, targetType);
return (converter != null);
}
@SuppressWarnings("unchecked")
public <T> T convert(Object source, Class<T> targetType) {
if (targetType == null) {
throw new IllegalArgumentException("The targetType to convert to cannot be null");
}
return (T) convert(source, TypeDescriptor.forObject(source), TypeDescriptor.valueOf(targetType));
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType == null) {
throw new IllegalArgumentException("The targetType to convert to cannot be null");
}
if (sourceType == null) {
Assert.isTrue(source == null, "The source must be [null] if sourceType == [null]");
return handleResult(sourceType, targetType, convertNullSource(sourceType, targetType));
}
if (source != null && !sourceType.getObjectType().isInstance(source)) {
throw new IllegalArgumentException("The source to convert from must be an instance of " +
sourceType + "; instead it was a " + source.getClass().getName());
}
GenericConverter converter = getConverter(sourceType, targetType);
if (converter != null) {
Object result = ConversionUtils.invokeConverter(converter, source, sourceType, targetType);
return handleResult(sourceType, targetType, result);
}
else {
return handleConverterNotFound(source, sourceType, targetType);
}
}
/**
* Convenience operation for converting a source object to the specified targetType, where the targetType is a descriptor that provides additional conversion context.
* Simply delegates to {@link #convert(Object, TypeDescriptor, TypeDescriptor)} and encapsulates the construction of the sourceType descriptor using {@link TypeDescriptor#forObject(Object)}.
* @param source the source object
* @param targetType the target type
* @return the converted value
* @throws ConversionException if a conversion exception occurred
* @throws IllegalArgumentException if targetType is null
* @throws IllegalArgumentException if sourceType is null but source is not null
*/
public Object convert(Object source, TypeDescriptor targetType) {
return convert(source, TypeDescriptor.forObject(source), targetType);
}
public String toString() {
List<String> converterStrings = new ArrayList<String>();
for (Map<Class<?>, MatchableConverters> targetConverters : this.converters.values()) {
for (MatchableConverters matchable : targetConverters.values()) {
converterStrings.add(matchable.toString());
}
}
Collections.sort(converterStrings);
StringBuilder builder = new StringBuilder();
builder.append("ConversionService converters = ").append("\n");
for (String converterString : converterStrings) {
builder.append("\t");
builder.append(converterString);
builder.append("\n");
}
return builder.toString();
}
// subclassing hooks
/**
* Template method to convert a null source.
* <p>Default implementation returns <code>null</code>.
* Subclasses may override to return custom null objects for specific target types.
* @param sourceType the sourceType to convert from
* @param targetType the targetType to convert to
* @return the converted null object
*/
protected Object convertNullSource(TypeDescriptor sourceType, TypeDescriptor targetType) {
return null;
}
/**
* Hook method to lookup the converter for a given sourceType/targetType pair.
* First queries this ConversionService's converter cache.
* On a cache miss, then performs an exhaustive search for a matching converter.
* If no converter matches, returns the default converter.
* Subclasses may override.
* @param sourceType the source type to convert from
* @param targetType the target type to convert to
* @return the generic converter that will perform the conversion, or <code>null</code> if no suitable converter was found
* @see #getDefaultConverter(TypeDescriptor, TypeDescriptor)
*/
protected GenericConverter getConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
ConverterCacheKey key = new ConverterCacheKey(sourceType, targetType);
GenericConverter converter = this.converterCache.get(key);
if (converter != null) {
return (converter != NO_MATCH ? converter : null);
}
else {
converter = findConverterForClassPair(sourceType, targetType);
if (converter == null) {
converter = getDefaultConverter(sourceType, targetType);
}
if (converter != null) {
this.converterCache.put(key, converter);
return converter;
}
else {
this.converterCache.put(key, NO_MATCH);
return null;
}
}
}
/**
* Return the default converter if no converter is found for the given sourceType/targetType pair.
* Returns a NO_OP Converter if the sourceType is assignable to the targetType.
* Returns <code>null</code> otherwise, indicating no suitable converter could be found.
* Subclasses may override.
* @param sourceType the source type to convert from
* @param targetType the target type to convert to
* @return the default generic converter that will perform the conversion
*/
protected GenericConverter getDefaultConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
return (sourceType.isAssignableTo(targetType) ? NO_OP_CONVERTER : null);
}
// internal helpers
private GenericConverter.ConvertiblePair getRequiredTypeInfo(Object converter, Class<?> genericIfc) {
Class<?>[] args = GenericTypeResolver.resolveTypeArguments(converter.getClass(), genericIfc);
return (args != null ? new GenericConverter.ConvertiblePair(args[0], args[1]) : null);
}
private MatchableConverters getMatchableConverters(Class<?> sourceType, Class<?> targetType) {
Map<Class<?>, MatchableConverters> sourceMap = getSourceConverterMap(sourceType);
MatchableConverters matchable = sourceMap.get(targetType);
if (matchable == null) {
matchable = new MatchableConverters();
sourceMap.put(targetType, matchable);
}
return matchable;
}
private void invalidateCache() {
this.converterCache.clear();
}
private Map<Class<?>, MatchableConverters> getSourceConverterMap(Class<?> sourceType) {
Map<Class<?>, MatchableConverters> sourceMap = converters.get(sourceType);
if (sourceMap == null) {
sourceMap = new HashMap<Class<?>, MatchableConverters>();
this.converters.put(sourceType, sourceMap);
}
return sourceMap;
}
private GenericConverter findConverterForClassPair(TypeDescriptor sourceType, TypeDescriptor targetType) {
Class<?> sourceObjectType = sourceType.getObjectType();
if (sourceObjectType.isInterface()) {
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
classQueue.addFirst(sourceObjectType);
while (!classQueue.isEmpty()) {
Class<?> currentClass = classQueue.removeLast();
Map<Class<?>, MatchableConverters> converters = getTargetConvertersForSource(currentClass);
GenericConverter converter = getMatchingConverterForTarget(sourceType, targetType, converters);
if (converter != null) {
return converter;
}
Class<?>[] interfaces = currentClass.getInterfaces();
for (Class<?> ifc : interfaces) {
classQueue.addFirst(ifc);
}
}
Map<Class<?>, MatchableConverters> objectConverters = getTargetConvertersForSource(Object.class);
return getMatchingConverterForTarget(sourceType, targetType, objectConverters);
}
else if (sourceObjectType.isArray()) {
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
classQueue.addFirst(sourceObjectType);
while (!classQueue.isEmpty()) {
Class<?> currentClass = classQueue.removeLast();
Map<Class<?>, MatchableConverters> converters = getTargetConvertersForSource(currentClass);
GenericConverter converter = getMatchingConverterForTarget(sourceType, targetType, converters);
if (converter != null) {
return converter;
}
Class<?> componentType = ClassUtils.resolvePrimitiveIfNecessary(currentClass.getComponentType());
if (componentType.getSuperclass() != null) {
classQueue.addFirst(Array.newInstance(componentType.getSuperclass(), 0).getClass());
}
else if (componentType.isInterface()) {
classQueue.addFirst(Object[].class);
}
}
return null;
}
else {
HashSet<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
classQueue.addFirst(sourceObjectType);
while (!classQueue.isEmpty()) {
Class<?> currentClass = classQueue.removeLast();
Map<Class<?>, MatchableConverters> converters = getTargetConvertersForSource(currentClass);
GenericConverter converter = getMatchingConverterForTarget(sourceType, targetType, converters);
if (converter != null) {
return converter;
}
Class<?> superClass = currentClass.getSuperclass();
if (superClass != null && superClass != Object.class) {
classQueue.addFirst(superClass);
}
for (Class<?> interfaceType : currentClass.getInterfaces()) {
addInterfaceHierarchy(interfaceType, interfaces);
}
}
for (Class<?> interfaceType : interfaces) {
Map<Class<?>, MatchableConverters> converters = getTargetConvertersForSource(interfaceType);
GenericConverter converter = getMatchingConverterForTarget(sourceType, targetType, converters);
if (converter != null) {
return converter;
}
}
Map<Class<?>, MatchableConverters> objectConverters = getTargetConvertersForSource(Object.class);
return getMatchingConverterForTarget(sourceType, targetType, objectConverters);
}
}
private Map<Class<?>, MatchableConverters> getTargetConvertersForSource(Class<?> sourceType) {
Map<Class<?>, MatchableConverters> converters = this.converters.get(sourceType);
if (converters == null) {
converters = Collections.emptyMap();
}
return converters;
}
private GenericConverter getMatchingConverterForTarget(TypeDescriptor sourceType, TypeDescriptor targetType,
Map<Class<?>, MatchableConverters> converters) {
Class<?> targetObjectType = targetType.getObjectType();
if (targetObjectType.isInterface()) {
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
classQueue.addFirst(targetObjectType);
while (!classQueue.isEmpty()) {
Class<?> currentClass = classQueue.removeLast();
MatchableConverters matchable = converters.get(currentClass);
GenericConverter converter = matchConverter(matchable, sourceType, targetType);
if (converter != null) {
return converter;
}
Class<?>[] interfaces = currentClass.getInterfaces();
for (Class<?> ifc : interfaces) {
classQueue.addFirst(ifc);
}
}
return matchConverter(converters.get(Object.class), sourceType, targetType);
}
else if (targetObjectType.isArray()) {
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
classQueue.addFirst(targetObjectType);
while (!classQueue.isEmpty()) {
Class<?> currentClass = classQueue.removeLast();
MatchableConverters matchable = converters.get(currentClass);
GenericConverter converter = matchConverter(matchable, sourceType, targetType);
if (converter != null) {
return converter;
}
Class<?> componentType = ClassUtils.resolvePrimitiveIfNecessary(currentClass.getComponentType());
if (componentType.getSuperclass() != null) {
classQueue.addFirst(Array.newInstance(componentType.getSuperclass(), 0).getClass());
}
else if (componentType.isInterface()) {
classQueue.addFirst(Object[].class);
}
}
return null;
}
else {
Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
LinkedList<Class<?>> classQueue = new LinkedList<Class<?>>();
classQueue.addFirst(targetObjectType);
while (!classQueue.isEmpty()) {
Class<?> currentClass = classQueue.removeLast();
MatchableConverters matchable = converters.get(currentClass);
GenericConverter converter = matchConverter(matchable, sourceType, targetType);
if (converter != null) {
return converter;
}
Class<?> superClass = currentClass.getSuperclass();
if (superClass != null && superClass != Object.class) {
classQueue.addFirst(superClass);
}
for (Class<?> interfaceType : currentClass.getInterfaces()) {
addInterfaceHierarchy(interfaceType, interfaces);
}
}
for (Class<?> interfaceType : interfaces) {
MatchableConverters matchable = converters.get(interfaceType);
GenericConverter converter = matchConverter(matchable, sourceType, targetType);
if (converter != null) {
return converter;
}
}
return matchConverter(converters.get(Object.class), sourceType, targetType);
}
}
private void addInterfaceHierarchy(Class<?> interfaceType, Set<Class<?>> interfaces) {
interfaces.add(interfaceType);
for (Class<?> inheritedInterface : interfaceType.getInterfaces()) {
addInterfaceHierarchy(inheritedInterface, interfaces);
}
}
private GenericConverter matchConverter(
MatchableConverters matchable, TypeDescriptor sourceFieldType, TypeDescriptor targetFieldType) {
if (matchable == null) {
return null;
}
return matchable.matchConverter(sourceFieldType, targetFieldType);
}
private Object handleConverterNotFound(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
assertNotPrimitiveTargetType(sourceType, targetType);
return source;
}
else if (sourceType.isAssignableTo(targetType) && targetType.getObjectType().isInstance(source)) {
return source;
}
else {
throw new ConverterNotFoundException(sourceType, targetType);
}
}
private Object handleResult(TypeDescriptor sourceType, TypeDescriptor targetType, Object result) {
if (result == null) {
assertNotPrimitiveTargetType(sourceType, targetType);
}
return result;
}
private void assertNotPrimitiveTargetType(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType.isPrimitive()) {
throw new ConversionFailedException(sourceType, targetType, null,
new IllegalArgumentException("A null value cannot be assigned to a primitive type"));
}
}
@SuppressWarnings("unchecked")
private final class ConverterAdapter implements GenericConverter {
private final ConvertiblePair typeInfo;
private final Converter<Object, Object> converter;
public ConverterAdapter(ConvertiblePair typeInfo, Converter<?, ?> converter) {
this.converter = (Converter<Object, Object>) converter;
this.typeInfo = typeInfo;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(this.typeInfo);
}
public boolean matchesTargetType(TypeDescriptor targetType) {
return this.typeInfo.getTargetType().equals(targetType.getObjectType());
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converter.convert(source);
}
public String toString() {
return this.typeInfo.getSourceType().getName() + " -> " + this.typeInfo.getTargetType().getName() +
" : " + this.converter.toString();
}
}
@SuppressWarnings("unchecked")
private final class ConverterFactoryAdapter implements GenericConverter {
private final ConvertiblePair typeInfo;
private final ConverterFactory<Object, Object> converterFactory;
public ConverterFactoryAdapter(ConvertiblePair typeInfo, ConverterFactory<?, ?> converterFactory) {
this.converterFactory = (ConverterFactory<Object, Object>) converterFactory;
this.typeInfo = typeInfo;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(this.typeInfo);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return convertNullSource(sourceType, targetType);
}
return this.converterFactory.getConverter(targetType.getObjectType()).convert(source);
}
public String toString() {
return this.typeInfo.getSourceType().getName() + " -> " + this.typeInfo.getTargetType().getName() +
" : " + this.converterFactory.toString();
}
}
private static class MatchableConverters {
private LinkedList<ConditionalGenericConverter> conditionalConverters;
private GenericConverter defaultConverter;
public void add(GenericConverter converter) {
if (converter instanceof ConditionalGenericConverter) {
if (this.conditionalConverters == null) {
this.conditionalConverters = new LinkedList<ConditionalGenericConverter>();
}
this.conditionalConverters.addFirst((ConditionalGenericConverter) converter);
}
else {
this.defaultConverter = converter;
}
}
public GenericConverter matchConverter(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (this.conditionalConverters != null) {
for (ConditionalGenericConverter conditional : this.conditionalConverters) {
if (conditional.matches(sourceType, targetType)) {
return conditional;
}
}
}
if (this.defaultConverter instanceof ConverterAdapter) {
ConverterAdapter adapter = (ConverterAdapter) this.defaultConverter;
if (!adapter.matchesTargetType(targetType)) {
return null;
}
}
return this.defaultConverter;
}
public String toString() {
if (this.conditionalConverters != null) {
StringBuilder builder = new StringBuilder();
for (Iterator<ConditionalGenericConverter> it = this.conditionalConverters.iterator(); it.hasNext();) {
builder.append(it.next());
if (it.hasNext()) {
builder.append(", ");
}
}
if (this.defaultConverter != null) {
builder.append(", ").append(this.defaultConverter);
}
return builder.toString();
}
else {
return this.defaultConverter.toString();
}
}
}
private static final class ConverterCacheKey {
private final TypeDescriptor sourceType;
private final TypeDescriptor targetType;
public ConverterCacheKey(TypeDescriptor sourceType, TypeDescriptor targetType) {
this.sourceType = sourceType;
this.targetType = targetType;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ConverterCacheKey)) {
return false;
}
ConverterCacheKey otherKey = (ConverterCacheKey) other;
return this.sourceType.equals(otherKey.sourceType) && this.targetType.equals(otherKey.targetType);
}
public int hashCode() {
return this.sourceType.hashCode() * 29 + this.targetType.hashCode();
}
public String toString() {
return "ConverterCacheKey [sourceType = " + this.sourceType + ", targetType = " + this.targetType + "]";
}
}
}

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* Converts an entity identifier to a entity reference by calling a static finder method
* on the target entity type.
*
* <p>For this converter to match, the finder method must be public, static, have the signature
* <code>find[EntityName]([IdType])</code>, and return an instance of the desired entity type.
*
* @author Keith Donald
* @since 3.0
*/
final class IdToEntityConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public IdToEntityConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object.class, Object.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
Method finder = getFinder(targetType.getType());
return finder != null && this.conversionService.canConvert(sourceType, TypeDescriptor.valueOf(finder.getParameterTypes()[0]));
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Method finder = getFinder(targetType.getType());
Object id = this.conversionService.convert(source, sourceType, TypeDescriptor.valueOf(finder.getParameterTypes()[0]));
return ReflectionUtils.invokeMethod(finder, source, id);
}
private Method getFinder(Class<?> entityClass) {
String finderMethod = "find" + getEntityName(entityClass);
Method[] methods = entityClass.getDeclaredMethods();
for (Method method : methods) {
if (Modifier.isStatic(method.getModifiers()) && method.getParameterTypes().length == 1 && method.getReturnType().equals(entityClass)) {
if (method.getName().equals(finderMethod)) {
return method;
}
}
}
return null;
}
private String getEntityName(Class<?> entityClass) {
String shortName = ClassUtils.getShortName(entityClass);
int lastDot = shortName.lastIndexOf('.');
if (lastDot != -1) {
return shortName.substring(lastDot + 1);
}
else {
return shortName;
}
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
/**
* Converts a Map to another Map.
*
* <p>First, creates a new Map of the requested targetType with a size equal to the
* size of the source Map. Then copies each element in the source map to the target map.
* Will perform a conversion from the source maps's parameterized K,V types to the target
* map's parameterized types K,V if necessary.
*
* @author Keith Donald
* @since 3.0
*/
final class MapToMapConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public MapToMapConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Map.class, Map.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return canConvertKey(sourceType, targetType) && canConvertValue(sourceType, targetType);
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
boolean copyRequired = !targetType.getType().isInstance(source);
Map<Object, Object> sourceMap = (Map<Object, Object>) source;
if (!copyRequired && sourceMap.isEmpty()) {
return sourceMap;
}
Map<Object, Object> targetMap = CollectionFactory.createMap(targetType.getType(), sourceMap.size());
for (Map.Entry<Object, Object> entry : sourceMap.entrySet()) {
Object sourceKey = entry.getKey();
Object sourceValue = entry.getValue();
Object targetKey = convertKey(sourceKey, sourceType, targetType.getMapKeyTypeDescriptor());
Object targetValue = convertValue(sourceValue, sourceType, targetType.getMapValueTypeDescriptor());
targetMap.put(targetKey, targetValue);
if (sourceKey != targetKey || sourceValue != targetValue) {
copyRequired = true;
}
}
return (copyRequired ? targetMap : sourceMap);
}
// internal helpers
private boolean canConvertKey(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(sourceType.getMapKeyTypeDescriptor(),
targetType.getMapKeyTypeDescriptor(), this.conversionService);
}
private boolean canConvertValue(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(sourceType.getMapValueTypeDescriptor(),
targetType.getMapValueTypeDescriptor(), this.conversionService);
}
private Object convertKey(Object sourceKey, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType == null) {
return sourceKey;
}
return this.conversionService.convert(sourceKey, sourceType.getMapKeyTypeDescriptor(sourceKey), targetType);
}
private Object convertValue(Object sourceValue, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType == null) {
return sourceValue;
}
return this.conversionService.convert(sourceValue, sourceType.getMapValueTypeDescriptor(sourceValue), targetType);
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
/**
* Converts from any JDK-standard Number implementation to a Character.
*
* @author Keith Donald
* @since 3.0
* @see java.lang.Character
* @see java.lang.Short
* @see java.lang.Integer
* @see java.lang.Long
* @see java.math.BigInteger
* @see java.lang.Float
* @see java.lang.Double
* @see java.math.BigDecimal
*/
final class NumberToCharacterConverter implements Converter<Number, Character> {
public Character convert(Number source) {
return (char) source.shortValue();
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.util.NumberUtils;
/**
* Converts from any JDK-standard Number implementation to any other JDK-standard Number implementation.
*
* <p>Support Number classes including Byte, Short, Integer, Float, Double, Long, BigInteger, BigDecimal. This class
* delegates to {@link NumberUtils#convertNumberToTargetClass(Number, Class)} to perform the conversion.
*
* @author Keith Donald
* @since 3.0
* @see java.lang.Byte
* @see java.lang.Short
* @see java.lang.Integer
* @see java.lang.Long
* @see java.math.BigInteger
* @see java.lang.Float
* @see java.lang.Double
* @see java.math.BigDecimal
* @see NumberUtils
*/
final class NumberToNumberConverterFactory implements ConverterFactory<Number, Number> {
public <T extends Number> Converter<Number, T> getConverter(Class<T> targetType) {
return new NumberToNumber<T>(targetType);
}
private final static class NumberToNumber<T extends Number> implements Converter<Number, T> {
private final Class<T> targetType;
public NumberToNumber(Class<T> targetType) {
this.targetType = targetType;
}
public T convert(Number source) {
return NumberUtils.convertNumberToTargetClass(source, this.targetType);
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
/**
* Converts an Object to a single-element Array containing the Object.
* Will convert the Object to the target Array's component type if necessary.
*
* @author Keith Donald
* @since 3.0
*/
final class ObjectToArrayConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public ObjectToArrayConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object.class, Object[].class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(sourceType, targetType.getElementTypeDescriptor(), this.conversionService);
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Object target = Array.newInstance(targetType.getElementTypeDescriptor().getType(), 1);
Object targetElement = this.conversionService.convert(source, sourceType, targetType.getElementTypeDescriptor());
Array.set(target, 0, targetElement);
return target;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
/**
* Converts an Object to a single-element Collection containing the Object.
* Will convert the Object to the target Collection's parameterized type if necessary.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 3.0
*/
final class ObjectToCollectionConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public ObjectToCollectionConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object.class, Collection.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return ConversionUtils.canConvertElements(sourceType, targetType.getElementTypeDescriptor(), this.conversionService);
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), 1);
if (targetType.getElementTypeDescriptor() == null || targetType.getElementTypeDescriptor().isCollection()) {
target.add(source);
}
else {
Object singleElement = this.conversionService.convert(source, sourceType, targetType.getElementTypeDescriptor());
target.add(singleElement);
}
return target;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* Generic Converter that attempts to convert a source Object to a target type
* by delegating to methods on the target type.
*
* <p>Calls the static <code>valueOf(sourceType)</code> method on the target type
* to perform the conversion, if such a method exists. Else calls the target type's
* Constructor that accepts a single sourceType argument, if such a Constructor exists.
* Else throws a ConversionFailedException.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 3.0
*/
final class ObjectToObjectConverter implements ConditionalGenericConverter {
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(Object.class, Object.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return !sourceType.equals(targetType) && hasValueOfMethodOrConstructor(targetType.getType(), sourceType.getType());
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
Class<?> sourceClass = sourceType.getType();
Class<?> targetClass = targetType.getType();
Method method = getValueOfMethodOn(targetClass, sourceClass);
try {
if (method != null) {
ReflectionUtils.makeAccessible(method);
return method.invoke(null, source);
}
else {
Constructor<?> constructor = getConstructor(targetClass, sourceClass);
if (constructor != null) {
return constructor.newInstance(source);
}
}
}
catch (InvocationTargetException ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex.getTargetException());
}
catch (Throwable ex) {
throw new ConversionFailedException(sourceType, targetType, source, ex);
}
throw new IllegalStateException("No static valueOf(" + sourceClass.getName() +
") method or Constructor(" + sourceClass.getName() + ") exists on " + targetClass.getName());
}
static boolean hasValueOfMethodOrConstructor(Class<?> clazz, Class<?> sourceParameterType) {
return getValueOfMethodOn(clazz, sourceParameterType) != null || getConstructor(clazz, sourceParameterType) != null;
}
private static Method getValueOfMethodOn(Class<?> clazz, Class<?> sourceParameterType) {
return ClassUtils.getStaticMethod(clazz, "valueOf", sourceParameterType);
}
private static Constructor<?> getConstructor(Class<?> clazz, Class<?> sourceParameterType) {
return ClassUtils.getConstructorIfAvailable(clazz, sourceParameterType);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
/**
* Simply calls {@link Object#toString()} to convert a source Object to a String.
* @author Keith Donald
* @since 3.0
*/
final class ObjectToStringConverter implements Converter<Object, String> {
public String convert(Object source) {
return source.toString();
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;
import org.springframework.core.convert.converter.Converter;
/**
* Converts from a Properties to a String by calling {@link Properties#store(java.io.OutputStream, String)}.
* Decodes with the ISO-8859-1 charset before returning the String.
*
* @author Keith Donald
* @since 3.0
*/
final class PropertiesToStringConverter implements Converter<Properties, String> {
public String convert(Properties source) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
source.store(os, null);
return os.toString("ISO-8859-1");
}
catch (IOException ex) {
// Should never happen.
throw new IllegalArgumentException("Failed to store [" + source + "] into String", ex);
}
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.util.StringUtils;
/**
* Converts a comma-delimited String to an Array.
* Only matches if String.class can be converted to the target array element type.
*
* @author Keith Donald
* @since 3.0
*/
final class StringToArrayConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public StringToArrayConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, Object[].class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
}
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
String string = (String) source;
String[] fields = StringUtils.commaDelimitedListToStringArray(string);
Object target = Array.newInstance(targetType.getElementTypeDescriptor().getType(), fields.length);
for (int i = 0; i < fields.length; i++) {
String sourceElement = fields[i];
Object targetElement = this.conversionService.convert(sourceElement.trim(), sourceType, targetType.getElementTypeDescriptor());
Array.set(target, i, targetElement);
}
return target;
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.HashSet;
import java.util.Set;
import org.springframework.core.convert.converter.Converter;
/**
* Converts String to a Boolean.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 3.0
*/
final class StringToBooleanConverter implements Converter<String, Boolean> {
private static final Set<String> trueValues = new HashSet<String>(4);
private static final Set<String> falseValues = new HashSet<String>(4);
static {
trueValues.add("true");
trueValues.add("on");
trueValues.add("yes");
trueValues.add("1");
falseValues.add("false");
falseValues.add("off");
falseValues.add("no");
falseValues.add("0");
}
public Boolean convert(String source) {
String value = source.trim();
if ("".equals(value)) {
return null;
}
value = value.toLowerCase();
if (trueValues.contains(value)) {
return Boolean.TRUE;
}
else if (falseValues.contains(value)) {
return Boolean.FALSE;
}
else {
throw new IllegalArgumentException("Invalid boolean value '" + source + "'");
}
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to a Character.
*
* @author Keith Donald
* @since 3.0
*/
final class StringToCharacterConverter implements Converter<String, Character> {
public Character convert(String source) {
if (source.length() == 0) {
return null;
}
if (source.length() > 1) {
throw new IllegalArgumentException(
"Can only convert a [String] with length of 1 to a [Character]; string value '" + source + "' has length of " + source.length());
}
return source.charAt(0);
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Collection;
import java.util.Collections;
import java.util.Set;
import org.springframework.core.CollectionFactory;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.util.StringUtils;
/**
* Converts a comma-delimited String to a Collection.
* If the target collection element type is declared, only matches if String.class can be converted to it.
*
* @author Keith Donald
* @since 3.0
*/
final class StringToCollectionConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;
public StringToCollectionConverter(ConversionService conversionService) {
this.conversionService = conversionService;
}
public Set<ConvertiblePair> getConvertibleTypes() {
return Collections.singleton(new ConvertiblePair(String.class, Collection.class));
}
public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (targetType.getElementTypeDescriptor() != null) {
return this.conversionService.canConvert(sourceType, targetType.getElementTypeDescriptor());
} else {
return true;
}
}
@SuppressWarnings("unchecked")
public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) {
if (source == null) {
return null;
}
String string = (String) source;
String[] fields = StringUtils.commaDelimitedListToStringArray(string);
Collection<Object> target = CollectionFactory.createCollection(targetType.getType(), fields.length);
if (targetType.getElementTypeDescriptor() == null) {
for (String field : fields) {
target.add(field.trim());
}
} else {
for (String field : fields) {
Object targetElement = this.conversionService.convert(field.trim(), sourceType, targetType.getElementTypeDescriptor());
target.add(targetElement);
}
}
return target;
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
/**
* Converts from a String to a java.lang.Enum by calling {@link Enum#valueOf(Class, String)}.
*
* @author Keith Donald
* @since 3.0
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnum(targetType);
}
private class StringToEnum<T extends Enum> implements Converter<String, T> {
private final Class<T> enumType;
public StringToEnum(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
if (source.length() == 0) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.util.Locale;
import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;
/**
* Converts a String to a Locale.
*
* @author Keith Donald
* @since 3.0
*/
final class StringToLocaleConverter implements Converter<String, Locale> {
public Locale convert(String source) {
return StringUtils.parseLocaleString(source);
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.util.NumberUtils;
/**
* Converts from a String any JDK-standard Number implementation.
*
* <p>Support Number classes including Byte, Short, Integer, Float, Double, Long, BigInteger, BigDecimal. This class
* delegates to {@link NumberUtils#parseNumber(String, Class)} to perform the conversion.
*
* @author Keith Donald
* @since 3.0
* @see java.lang.Byte
* @see java.lang.Short
* @see java.lang.Integer
* @see java.lang.Long
* @see java.math.BigInteger
* @see java.lang.Float
* @see java.lang.Double
* @see java.math.BigDecimal
* @see NumberUtils
*/
final class StringToNumberConverterFactory implements ConverterFactory<String, Number> {
public <T extends Number> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToNumber<T>(targetType);
}
private static final class StringToNumber<T extends Number> implements Converter<String, T> {
private final Class<T> targetType;
public StringToNumber(Class<T> targetType) {
this.targetType = targetType;
}
public T convert(String source) {
if (source.length() == 0) {
return null;
}
return NumberUtils.parseNumber(source, this.targetType);
}
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.convert.support;
import java.io.ByteArrayInputStream;
import java.util.Properties;
import org.springframework.core.convert.converter.Converter;
/**
* Converts a String to a Properties by calling Properties#load(java.io.InputStream).
* Uses ISO-8559-1 encoding required by Properties.
*
* @author Keith Donald
* @since 3.0
*/
final class StringToPropertiesConverter implements Converter<String, Properties> {
public Properties convert(String source) {
try {
Properties props = new Properties();
// Must use the ISO-8859-1 encoding because Properties.load(stream) expects it.
props.load(new ByteArrayInputStream(source.getBytes("ISO-8859-1")));
return props;
}
catch (Exception ex) {
// Should never happen.
throw new IllegalArgumentException("Failed to parse [" + source + "] into Properties", ex);
}
}
}

View File

@@ -0,0 +1,8 @@
/**
*
* Default implementation of the type conversion system.
*
*/
package org.springframework.core.convert.support;

View File

@@ -0,0 +1,138 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.enums;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.CachingMapDecorator;
import org.springframework.util.ClassUtils;
/**
* Abstract base class for {@link LabeledEnumResolver} implementations,
* caching all retrieved {@link LabeledEnum} instances.
*
* <p>Subclasses need to implement the template method
* {@link #findLabeledEnums(Class)}.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 1.2.2
* @see #findLabeledEnums(Class)
* @deprecated as of Spring 3.0, in favor of Java 5 enums.
*/
@Deprecated
public abstract class AbstractCachingLabeledEnumResolver implements LabeledEnumResolver {
protected transient final Log logger = LogFactory.getLog(getClass());
private final LabeledEnumCache labeledEnumCache = new LabeledEnumCache();
public Set<LabeledEnum> getLabeledEnumSet(Class type) throws IllegalArgumentException {
return new TreeSet<LabeledEnum>(getLabeledEnumMap(type).values());
}
public Map<Comparable, LabeledEnum> getLabeledEnumMap(Class type) throws IllegalArgumentException {
Assert.notNull(type, "No type specified");
return this.labeledEnumCache.get(type);
}
public LabeledEnum getLabeledEnumByCode(Class type, Comparable code) throws IllegalArgumentException {
Assert.notNull(code, "No enum code specified");
Map<Comparable, LabeledEnum> typeEnums = getLabeledEnumMap(type);
LabeledEnum codedEnum = typeEnums.get(code);
if (codedEnum == null) {
throw new IllegalArgumentException(
"No enumeration with code '" + code + "'" + " of type [" + type.getName() +
"] exists: this is likely a configuration error. " +
"Make sure the code value matches a valid instance's code property!");
}
return codedEnum;
}
public LabeledEnum getLabeledEnumByLabel(Class type, String label) throws IllegalArgumentException {
Map<Comparable, LabeledEnum> typeEnums = getLabeledEnumMap(type);
for (LabeledEnum value : typeEnums.values()) {
if (value.getLabel().equalsIgnoreCase(label)) {
return value;
}
}
throw new IllegalArgumentException(
"No enumeration with label '" + label + "' of type [" + type +
"] exists: this is likely a configuration error. " +
"Make sure the label string matches a valid instance's label property!");
}
/**
* Template method to be implemented by subclasses.
* Supposed to find all LabeledEnum instances for the given type.
* @param type the enum type
* @return the Set of LabeledEnum instances
* @see org.springframework.core.enums.LabeledEnum
*/
protected abstract Set<LabeledEnum> findLabeledEnums(Class type);
/**
* Inner cache class that implements lazy building of LabeledEnum Maps.
*/
private class LabeledEnumCache extends CachingMapDecorator<Class, Map<Comparable, LabeledEnum>> {
public LabeledEnumCache() {
super(true);
}
@Override
protected Map<Comparable, LabeledEnum> create(Class key) {
Set<LabeledEnum> typeEnums = findLabeledEnums(key);
if (typeEnums == null || typeEnums.isEmpty()) {
throw new IllegalArgumentException(
"Unsupported labeled enumeration type '" + key + "': " +
"make sure you've properly defined this enumeration! " +
"If it is static, are the class and its fields public/static/final?");
}
Map<Comparable, LabeledEnum> typeEnumMap = new HashMap<Comparable, LabeledEnum>(typeEnums.size());
for (LabeledEnum labeledEnum : typeEnums) {
typeEnumMap.put(labeledEnum.getCode(), labeledEnum);
}
return Collections.unmodifiableMap(typeEnumMap);
}
@Override
protected boolean useWeakValue(Class key, Map<Comparable, LabeledEnum> value) {
if (!ClassUtils.isCacheSafe(key, AbstractCachingLabeledEnumResolver.this.getClass().getClassLoader())) {
if (logger != null && logger.isDebugEnabled()) {
logger.debug("Not strongly caching class [" + key.getName() + "] because it is not cache-safe");
}
return true;
}
else {
return false;
}
}
}
}

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.enums;
/**
* Base class for labeled enum instances that aren't static.
*
* @author Keith Donald
* @since 1.2.6
* @deprecated as of Spring 3.0, in favor of Java 5 enums.
*/
public abstract class AbstractGenericLabeledEnum extends AbstractLabeledEnum {
/**
* A descriptive label for the enum.
*/
private final String label;
/**
* Create a new StaticLabeledEnum instance.
* @param label the label; if <code>null</code>), the enum's code
* will be used as label
*/
protected AbstractGenericLabeledEnum(String label) {
this.label = label;
}
public String getLabel() {
if (this.label != null) {
return label;
}
else {
return getCode().toString();
}
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.enums;
/**
* Abstract base superclass for LabeledEnum implementations.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Sam Brannen
* @since 1.2.2
* @deprecated as of Spring 3.0, in favor of Java 5 enums.
*/
@Deprecated
public abstract class AbstractLabeledEnum implements LabeledEnum {
/**
* Create a new AbstractLabeledEnum instance.
*/
protected AbstractLabeledEnum() {
}
public Class getType() {
// Could be coded as getClass().isAnonymousClass() on JDK 1.5
boolean isAnonymous = (getClass().getDeclaringClass() == null && getClass().getName().indexOf('$') != -1);
return (isAnonymous ? getClass().getSuperclass() : getClass());
}
public int compareTo(Object obj) {
if (!(obj instanceof LabeledEnum)) {
throw new ClassCastException("You may only compare LabeledEnums");
}
LabeledEnum that = (LabeledEnum) obj;
if (!this.getType().equals(that.getType())) {
throw new ClassCastException("You may only compare LabeledEnums of the same type");
}
return this.getCode().compareTo(that.getCode());
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof LabeledEnum)) {
return false;
}
LabeledEnum other = (LabeledEnum) obj;
return (this.getType().equals(other.getType()) && this.getCode().equals(other.getCode()));
}
@Override
public int hashCode() {
return (getType().hashCode() * 29 + getCode().hashCode());
}
@Override
public String toString() {
return getLabel();
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.enums;
import java.io.Serializable;
import java.util.Comparator;
import org.springframework.util.comparator.CompoundComparator;
import org.springframework.util.comparator.NullSafeComparator;
/**
* An interface for objects that represent a labeled enumeration.
* Each such enum instance has the following characteristics:
*
* <ul>
* <li>A type that identifies the enum's class.
* For example: <code>com.mycompany.util.FileFormat</code>.</li>
*
* <li>A code that uniquely identifies the enum within the context of its type.
* For example: &quot;CSV&quot;. Different classes of codes are possible
* (e.g., Character, Integer, String).</li>
*
* <li>A descriptive label. For example: "the CSV File Format".</li>
* </ul>
*
* @author Keith Donald
* @since 1.2.2
* @deprecated as of Spring 3.0, in favor of Java 5 enums.
*/
@Deprecated
public interface LabeledEnum extends Comparable, Serializable {
/**
* Return this enumeration's type.
*/
Class getType();
/**
* Return this enumeration's code.
* <p>Each code should be unique within enumerations of the same type.
*/
Comparable getCode();
/**
* Return a descriptive, optional label.
*/
String getLabel();
// Constants for standard enum ordering (Comparator implementations)
/**
* Shared Comparator instance that sorts enumerations by <code>CODE_ORDER</code>.
*/
Comparator CODE_ORDER = new Comparator() {
public int compare(Object o1, Object o2) {
Comparable c1 = ((LabeledEnum) o1).getCode();
Comparable c2 = ((LabeledEnum) o2).getCode();
return c1.compareTo(c2);
}
};
/**
* Shared Comparator instance that sorts enumerations by <code>LABEL_ORDER</code>.
*/
Comparator LABEL_ORDER = new Comparator() {
public int compare(Object o1, Object o2) {
LabeledEnum e1 = (LabeledEnum) o1;
LabeledEnum e2 = (LabeledEnum) o2;
Comparator comp = new NullSafeComparator(String.CASE_INSENSITIVE_ORDER, true);
return comp.compare(e1.getLabel(), e2.getLabel());
}
};
/**
* Shared Comparator instance that sorts enumerations by <code>LABEL_ORDER</code>,
* then <code>CODE_ORDER</code>.
*/
Comparator DEFAULT_ORDER =
new CompoundComparator(new Comparator[] { LABEL_ORDER, CODE_ORDER });
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.enums;
import java.util.Map;
import java.util.Set;
/**
* Interface for looking up <code>LabeledEnum</code> instances.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 1.2.2
* @deprecated as of Spring 3.0, in favor of Java 5 enums.
*/
@Deprecated
public interface LabeledEnumResolver {
/**
* Return a set of enumerations of a particular type. Each element in the
* set should be an instance of LabeledEnum.
* @param type the enum type
* @return a set of localized enumeration instances for the provided type
* @throws IllegalArgumentException if the type is not supported
*/
public Set getLabeledEnumSet(Class type) throws IllegalArgumentException;
/**
* Return a map of enumerations of a particular type. Each element in the
* map should be a key/value pair, where the key is the enum code, and the
* value is the <code>LabeledEnum</code> instance.
* @param type the enum type
* @return a Map of localized enumeration instances,
* with enum code as key and <code>LabeledEnum</code> instance as value
* @throws IllegalArgumentException if the type is not supported
*/
public Map getLabeledEnumMap(Class type) throws IllegalArgumentException;
/**
* Resolve a single <code>LabeledEnum</code> by its identifying code.
* @param type the enum type
* @param code the enum code
* @return the enum
* @throws IllegalArgumentException if the code did not map to a valid instance
*/
public LabeledEnum getLabeledEnumByCode(Class type, Comparable code) throws IllegalArgumentException;
/**
* Resolve a single <code>LabeledEnum</code> by its identifying code.
* @param type the enum type
* @param label the enum label
* @return the enum
* @throws IllegalArgumentException if the label did not map to a valid instance
*/
public LabeledEnum getLabeledEnumByLabel(Class type, String label) throws IllegalArgumentException;
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.enums;
import org.springframework.util.Assert;
/**
* Implementation of LabeledEnum which uses a letter as the code type.
*
* <p>Should almost always be subclassed, but for some simple situations it may be
* used directly. Note that you will not be able to use unique type-based functionality
* like <code>LabeledEnumResolver.getLabeledEnumSet(type)</code> in this case.
*
* @author Keith Donald
* @since 1.2.2
* @deprecated as of Spring 3.0, in favor of Java 5 enums.
*/
@Deprecated
public class LetterCodedLabeledEnum extends AbstractGenericLabeledEnum {
/**
* The unique code of this enum.
*/
private final Character code;
/**
* Create a new LetterCodedLabeledEnum instance.
* @param code the letter code
* @param label the label (can be <code>null</code>)
*/
public LetterCodedLabeledEnum(char code, String label) {
super(label);
Assert.isTrue(Character.isLetter(code),
"The code '" + code + "' is invalid: it must be a letter");
this.code = new Character(code);
}
public Comparable getCode() {
return code;
}
/**
* Return the letter code of this LabeledEnum instance.
*/
public char getLetterCode() {
return ((Character) getCode()).charValue();
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* 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 org.springframework.core.enums;
/**
* Implementation of LabeledEnum which uses Short as the code type.
*
* <p>Should almost always be subclassed, but for some simple situations it may be
* used directly. Note that you will not be able to use unique type-based functionality
* like <code>LabeledEnumResolver.getLabeledEnumSet(type)</code> in this case.
*
* @author Keith Donald
* @since 1.2.2
* @deprecated as of Spring 3.0, in favor of Java 5 enums.
*/
@Deprecated
public class ShortCodedLabeledEnum extends AbstractGenericLabeledEnum {
/**
* The unique code of this enum.
*/
private final Short code;
/**
* Create a new ShortCodedLabeledEnum instance.
* @param code the short code
* @param label the label (can be <code>null</code>)
*/
public ShortCodedLabeledEnum(int code, String label) {
super(label);
this.code = new Short((short) code);
}
public Comparable getCode() {
return code;
}
/**
* Return the short code of this LabeledEnum instance.
*/
public short getShortCode() {
return ((Short) getCode()).shortValue();
}
}

Some files were not shown because too many files have changed in this diff Show More