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:
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 + "]");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 "annotation inheritance").
|
||||
* 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
|
||||
* @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>"value"</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>"value"</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>"value"</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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Core support package for Java 5 annotations.
|
||||
*
|
||||
*/
|
||||
package org.springframework.core.annotation;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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<String> could be converted to a List<EmailAddress> 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<String, String> could be converted to a Map<Id, EmailAddress> 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<String> 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<?>, 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<String></code> and the nestingLevel is 1, the nested type descriptor will be <code>String.class</code>.
|
||||
* If the field is a <code>List<List<String>></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<Integer, String></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<Map<Integer, String>></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<?></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<String></code> and the nestingLevel is 1, the nested type descriptor will be <code>String.class</code>.
|
||||
* If the property is a <code>List<List<String>></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<Integer, String></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<Map<Integer, String>></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<?></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<String> field value is assignable to a Collection<CharSequence> field, but List<Number> is not assignable to List<Integer>.
|
||||
* @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<java.lang.Number< and the element argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
|
||||
* If this describes a java.util.List<?> 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<java.lang.Number, java.lang.String< and the key argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
|
||||
* If this describes a java.util.Map<?, ?> 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<java.lang.String, java.lang.Number< and the value argument is a java.lang.Integer, the returned TypeDescriptor will be java.lang.Integer.
|
||||
* If this describes a java.util.Map<?, ?> 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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* SPI to implement Converters for the type conversion system.
|
||||
*
|
||||
*/
|
||||
package org.springframework.core.convert.converter;
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Type conversion system API.
|
||||
*
|
||||
*/
|
||||
package org.springframework.core.convert;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 + "'");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
/**
|
||||
*
|
||||
* Default implementation of the type conversion system.
|
||||
*
|
||||
*/
|
||||
package org.springframework.core.convert.support;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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: "CSV". 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 });
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user