Moved over initial version of core bundle

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@108 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Arjen Poutsma 2008-10-22 13:55:20 +00:00
parent a0e7cf11a8
commit 3b9b46b480
137 changed files with 19915 additions and 4 deletions

View File

@ -21,9 +21,13 @@
<dependencies>
<!-- compile dependencies -->
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1" conf="compile->runtime" />
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="optional->runtime" />
<dependency org="org.objectweb.asm" name="com.springsource.org.objectweb.asm" rev="2.2.3" conf="optional->compile" />
<dependency org="org.objectweb.asm" name="com.springsource.org.objectweb.asm.commons" rev="2.2.3" conf="optional->compile" />
<dependency org="edu.emory.mathcs.backport" name="com.springsource.edu.emory.mathcs.backport" rev="3.0.0" conf="optional->compile" />
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.collections" rev="3.2.0" conf="optional->compile" />
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.attributes" rev="2.2.0" conf="optional->compile" />
<!-- test dependencies -->
<dependency org="org.apache.log4j" name="com.springsource.org.apache.log4j" rev="1.2.15" conf="test->runtime"/>
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.4.0" conf="test->runtime" />
</dependencies>

View File

@ -0,0 +1,59 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
/**
* Common interface for managing aliases. Serves as super-interface for
* {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
public interface AliasRegistry {
/**
* Given a name, register an alias for it.
* @param name the canonical name
* @param alias the alias to be registered
* @throws IllegalStateException if the alias is already in use
* and may not be overridden
*/
void registerAlias(String name, String alias);
/**
* Remove the specified alias from this registry.
* @param alias the alias to remove
* @throws IllegalStateException if no such alias was found
*/
void removeAlias(String alias);
/**
* Determine whether this given name is defines as an alias
* (as opposed to the name of an actually registered component).
* @param beanName the bean name to check
* @return whether the given name is an alias
*/
boolean isAlias(String beanName);
/**
* Return the aliases for the given name, if defined.
* @param name the name to check for aliases
* @return the aliases, or an empty array if none
*/
String[] getAliases(String name);
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
/**
* Interface defining a generic contract for attaching and accessing metadata
* to/from arbitrary objects.
*
* @author Rob Harrop
* @since 2.0
*/
public interface AttributeAccessor {
/**
* Set the attribute defined by <code>name</code> to the supplied <code>value</code>.
* If <code>value</code> is <code>null</code>, the attribute is {@link #removeAttribute removed}.
* <p>In general, users should take care to prevent overlaps with other
* metadata attributes by using fully-qualified names, perhaps using
* class or package names as prefix.
* @param name the unique attribute key
* @param value the attribute value to be attached
*/
void setAttribute(String name, Object value);
/**
* Get the value of the attribute identified by <code>name</code>.
* Return <code>null</code> if the attribute doesn't exist.
* @param name the unique attribute key
* @return the current value of the attribute, if any
*/
Object getAttribute(String name);
/**
* Remove the attribute identified by <code>name</code> and return its value.
* Return <code>null</code> if no attribute under <code>name</code> is found.
* @param name the unique attribute key
* @return the last value of the attribute, if any
*/
Object removeAttribute(String name);
/**
* Return <code>true</code> if the attribute identified by <code>name</code> exists.
* Otherwise return <code>false</code>.
* @param name the unique attribute key
*/
boolean hasAttribute(String name);
/**
* Return the names of all attributes.
*/
String[] attributeNames();
}

View File

@ -0,0 +1,102 @@
/*
* 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;
import java.io.Serializable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
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 attributes = new LinkedHashMap();
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() {
Set attributeNames = this.attributes.keySet();
return (String[]) attributeNames.toArray(new String[attributeNames.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 (int i = 0; i < attributeNames.length; i++) {
String attributeName = attributeNames[i];
setAttribute(attributeName, source.getAttribute(attributeName));
}
}
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);
}
public int hashCode() {
return this.attributes.hashCode();
}
}

View File

@ -0,0 +1,206 @@
/*
* 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.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
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.
*
* <p>Only usable on JDK 1.5 and higher. Use an appropriate {@link JdkVersion}
* check before calling this class if a fallback for JDK 1.4 is desirable.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
* @see JdkVersion
*/
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.
* @throws IllegalStateException if no bridged {@link Method} can 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 candidateMethods = new ArrayList();
Method[] methods = ReflectionUtils.getAllDeclaredMethods(bridgeMethod.getDeclaringClass());
for (int i = 0; i < methods.length; i++) {
Method candidateMethod = methods[i];
if (isBridgedCandidateFor(candidateMethod, bridgeMethod)) {
candidateMethods.add(candidateMethod);
}
}
Method result;
// Now perform simple quick checks.
if (candidateMethods.size() == 1) {
result = (Method) candidateMethods.get(0);
}
else {
result = searchCandidates(candidateMethods, bridgeMethod);
}
if (result == null) {
throw new IllegalStateException(
"Unable to locate bridged method for bridge method '" + bridgeMethod + "'");
}
return result;
}
/**
* 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 candidateMethods, Method bridgeMethod) {
Map typeParameterMap = GenericTypeResolver.getTypeVariableMap(bridgeMethod.getDeclaringClass());
for (int i = 0; i < candidateMethods.size(); i++) {
Method candidateMethod = (Method) candidateMethods.get(i);
if (isBridgeMethodFor(bridgeMethod, candidateMethod, typeParameterMap)) {
return candidateMethod;
}
}
return 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 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 (int i = 0; i < interfaces.length; i++) {
Class anInterface = interfaces[i];
Method method = searchForMatch(anInterface, 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 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.
if (!candidateParameter.equals(GenericTypeResolver.resolveType(genericParameter, typeVariableMap))) {
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());
}
}

View File

@ -0,0 +1,370 @@
/*
* 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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
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.NavigableMap;
import java.util.NavigableSet;
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.apache.commons.collections.map.CaseInsensitiveMap;
import org.apache.commons.collections.map.ListOrderedMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.ClassUtils;
/**
* Factory for collections, being aware of Commons Collection 3.x's extended
* collections as well as of JDK 1.5+ concurrent collections and backport-concurrent
* collections. Mainly for internal use within the framework.
*
* <p>The goal of this class is to avoid runtime dependencies on JDK 1.5+ and
* Commons Collections 3.x, simply using the best collection implementation
* that is available at runtime. As of Spring 2.5, JDK 1.4 is required,
* so former adapter methods for JDK 1.3/1.4 always return the JDK 1.4
* collections now. The adapter methods are still kept for supporting
* Spring-based applications/frameworks which were built to support JDK 1.3.
*
* @author Juergen Hoeller
* @since 1.1.1
*/
public abstract class CollectionFactory {
private static final Log logger = LogFactory.getLog(CollectionFactory.class);
/** Whether the Commons Collections 3.x library is present on the classpath */
private static final boolean commonsCollections3Available =
ClassUtils.isPresent("org.apache.commons.collections.map.CaseInsensitiveMap",
CollectionFactory.class.getClassLoader());
/** Whether the backport-concurrent library is present on the classpath */
private static final boolean backportConcurrentAvailable =
ClassUtils.isPresent("edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap",
CollectionFactory.class.getClassLoader());
private static final Set approximableCollectionTypes = new HashSet(10);
private static final Set approximableMapTypes = new HashSet(6);
static {
approximableCollectionTypes.add(Collection.class);
approximableCollectionTypes.add(List.class);
approximableCollectionTypes.add(Set.class);
approximableCollectionTypes.add(SortedSet.class);
approximableMapTypes.add(Map.class);
approximableMapTypes.add(SortedMap.class);
if (JdkVersion.isAtLeastJava16()) {
approximableCollectionTypes.add(NavigableSet.class);
approximableMapTypes.add(NavigableMap.class);
}
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
*/
public static Set createLinkedSetIfPossible(int initialCapacity) {
return new LinkedHashSet(initialCapacity);
}
/**
* Create a copy-on-write Set (allowing for synchronization-less iteration),
* requiring JDK >= 1.5 or the backport-concurrent library on the classpath.
* Prefers a JDK 1.5+ CopyOnWriteArraySet to its backport-concurrent equivalent.
* Throws an IllegalStateException if no copy-on-write Set is available.
* @return the new Set instance
* @throws IllegalStateException if no copy-on-write Set is available
* @see java.util.concurrent.ConcurrentHashMap
* @see edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap
*/
public static Set createCopyOnWriteSet() {
if (JdkVersion.isAtLeastJava15()) {
logger.trace("Creating [java.util.concurrent.CopyOnWriteArraySet]");
return JdkConcurrentCollectionFactory.createCopyOnWriteArraySet();
}
else if (backportConcurrentAvailable) {
logger.trace("Creating [edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArraySet]");
return BackportConcurrentCollectionFactory.createCopyOnWriteArraySet();
}
else {
throw new IllegalStateException("Cannot create CopyOnWriteArraySet - " +
"neither JDK 1.5 nor backport-concurrent available on the classpath");
}
}
/**
* 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
*/
public static Map createLinkedMapIfPossible(int initialCapacity) {
return new LinkedHashMap(initialCapacity);
}
/**
* Create a linked case-insensitive Map if possible: if Commons Collections
* 3.x is available, a CaseInsensitiveMap with ListOrderedMap decorator will
* be created. Else, a JDK {@link java.util.LinkedHashMap} will be used.
* @param initialCapacity the initial capacity of the Map
* @return the new Map instance
* @see org.apache.commons.collections.map.CaseInsensitiveMap
* @see org.apache.commons.collections.map.ListOrderedMap
*/
public static Map createLinkedCaseInsensitiveMapIfPossible(int initialCapacity) {
if (commonsCollections3Available) {
logger.trace("Creating [org.apache.commons.collections.map.ListOrderedMap/CaseInsensitiveMap]");
return CommonsCollectionFactory.createListOrderedCaseInsensitiveMap(initialCapacity);
}
else {
logger.debug("Falling back to [java.util.LinkedHashMap] for linked case-insensitive map");
return new LinkedHashMap(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
*/
public static Map createIdentityMapIfPossible(int initialCapacity) {
return new IdentityHashMap(initialCapacity);
}
/**
* Create a concurrent Map if possible: that is, if running on JDK >= 1.5
* or if the backport-concurrent library is available. Prefers a JDK 1.5+
* ConcurrentHashMap to its backport-concurrent equivalent. Falls back
* to a plain synchronized HashMap if no concurrent Map is available.
* @param initialCapacity the initial capacity of the Map
* @return the new Map instance
* @see java.util.concurrent.ConcurrentHashMap
* @see edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap
*/
public static Map createConcurrentMapIfPossible(int initialCapacity) {
if (JdkVersion.isAtLeastJava15()) {
logger.trace("Creating [java.util.concurrent.ConcurrentHashMap]");
return JdkConcurrentCollectionFactory.createConcurrentHashMap(initialCapacity);
}
else if (backportConcurrentAvailable) {
logger.trace("Creating [edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap]");
return BackportConcurrentCollectionFactory.createConcurrentHashMap(initialCapacity);
}
else {
logger.debug("Falling back to plain synchronized [java.util.HashMap] for concurrent map");
return Collections.synchronizedMap(new HashMap(initialCapacity));
}
}
/**
* Create a concurrent Map with a dedicated {@link ConcurrentMap} interface,
* requiring JDK >= 1.5 or the backport-concurrent library on the classpath.
* Prefers a JDK 1.5+ ConcurrentHashMap to its backport-concurrent equivalent.
* Throws an IllegalStateException if no concurrent Map is available.
* @param initialCapacity the initial capacity of the Map
* @return the new ConcurrentMap instance
* @throws IllegalStateException if no concurrent Map is available
* @see java.util.concurrent.ConcurrentHashMap
* @see edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap
*/
public static ConcurrentMap createConcurrentMap(int initialCapacity) {
if (JdkVersion.isAtLeastJava15()) {
logger.trace("Creating [java.util.concurrent.ConcurrentHashMap]");
return new JdkConcurrentHashMap(initialCapacity);
}
else if (backportConcurrentAvailable) {
logger.trace("Creating [edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap]");
return new BackportConcurrentHashMap(initialCapacity);
}
else {
throw new IllegalStateException("Cannot create ConcurrentHashMap - " +
"neither JDK 1.5 nor backport-concurrent available on the classpath");
}
}
/**
* 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
*/
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);
}
}
/**
* 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 collection instance
* @see java.util.TreeMap
* @see java.util.LinkedHashMap
*/
public static Map createApproximateMap(Object map, int initialCapacity) {
if (map instanceof SortedMap) {
return new TreeMap(((SortedMap) map).comparator());
}
else {
return new LinkedHashMap(initialCapacity);
}
}
/**
* Actual creation of Commons Collections.
* In separate inner class to avoid runtime dependency on Commons Collections 3.x.
*/
private static abstract class CommonsCollectionFactory {
private static Map createListOrderedCaseInsensitiveMap(int initialCapacity) {
// Commons Collections does not support initial capacity of 0.
return ListOrderedMap.decorate(new CaseInsensitiveMap(initialCapacity == 0 ? 1 : initialCapacity));
}
}
/**
* Actual creation of JDK 1.5+ concurrent Collections.
* In separate inner class to avoid runtime dependency on JDK 1.5.
*/
private static abstract class JdkConcurrentCollectionFactory {
private static Set createCopyOnWriteArraySet() {
return new CopyOnWriteArraySet();
}
private static Map createConcurrentHashMap(int initialCapacity) {
return new ConcurrentHashMap(initialCapacity);
}
}
/**
* Actual creation of backport-concurrent Collections.
* In separate inner class to avoid runtime dependency on the backport-concurrent library.
*/
private static abstract class BackportConcurrentCollectionFactory {
private static Set createCopyOnWriteArraySet() {
return new edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArraySet();
}
private static Map createConcurrentHashMap(int initialCapacity) {
return new edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap(initialCapacity);
}
}
/**
* ConcurrentMap adapter for the JDK ConcurrentHashMap class.
*/
private static class JdkConcurrentHashMap extends ConcurrentHashMap implements ConcurrentMap {
public JdkConcurrentHashMap(int initialCapacity) {
super(initialCapacity);
}
}
/**
* ConcurrentMap adapter for the backport-concurrent ConcurrentHashMap class.
*/
private static class BackportConcurrentHashMap
extends edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap
implements ConcurrentMap {
public BackportConcurrentHashMap(int initialCapacity) {
super(initialCapacity);
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
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
*/
public interface ConcurrentMap extends Map {
Object putIfAbsent(Object key, Object value);
boolean remove(Object key, Object value);
boolean replace(Object key, Object oldValue, Object newValue);
Object replace(Object key, Object value);
}

View File

@ -0,0 +1,126 @@
/*
* 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.IOException;
import java.io.InputStream;
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;
/**
* 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 {
super(in);
this.classLoader = classLoader;
}
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);
}
}
protected Class resolveProxyClass(String[] interfaces) throws IOException, ClassNotFoundException {
if (this.classLoader != null) {
// Use the specified ClassLoader to resolve local proxy classes.
Class[] resolvedInterfaces = new Class[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
try {
resolvedInterfaces[i] = ClassUtils.forName(interfaces[i], this.classLoader);
}
catch (ClassNotFoundException ex) {
resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex);
}
}
try {
return Proxy.getProxyClass(this.classLoader, resolvedInterfaces);
}
catch (IllegalArgumentException ex) {
throw new ClassNotFoundException(null, ex);
}
}
else {
// Use ObjectInputStream's default ClassLoader...
try {
return super.resolveProxyClass(interfaces);
}
catch (ClassNotFoundException ex) {
Class[] resolvedInterfaces = new Class[interfaces.length];
for (int i = 0; i < interfaces.length; i++) {
resolvedInterfaces[i] = resolveFallbackIfPossible(interfaces[i], ex);
}
return Proxy.getProxyClass(getFallbackClassLoader(), resolvedInterfaces);
}
}
}
/**
* Resolve the given class name against a fallback class loader.
* <p>The default implementation simply rethrows the original exception,
* since there is no fallback available.
* @param className the class name to resolve
* @param ex the original exception thrown when attempting to load the class
* @return the newly resolved class (never <code>null</code>)
*/
protected Class resolveFallbackIfPossible(String className, ClassNotFoundException ex)
throws IOException, ClassNotFoundException{
throw ex;
}
/**
* Return the fallback ClassLoader to use when no ClassLoader was specified
* and ObjectInputStream's own default ClassLoader failed.
* <p>The default implementation simply returns <code>null</code>.
*/
protected ClassLoader getFallbackClassLoader() throws IOException {
return null;
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
/**
* Exception thrown when the {@link Constants} class is asked for
* an invalid constant name.
*
* @author Rod Johnson
* @since 28.04.2003
* @see org.springframework.core.Constants
*/
public class ConstantException extends IllegalArgumentException {
/**
* Thrown when an invalid constant name is requested.
* @param className name of the class containing the constant definitions
* @param field invalid constant name
* @param message description of the problem
*/
public ConstantException(String className, String field, String message) {
super("Field '" + field + "' " + message + " in class [" + className + "]");
}
/**
* Thrown when an invalid constant value is looked up.
* @param className name of the class containing the constant definitions
* @param namePrefix prefix of the searched constant names
* @param value the looked up constant value
*/
public ConstantException(String className, String namePrefix, Object value) {
super("No '" + namePrefix + "' field with value '" + value + "' found in class [" + className + "]");
}
}

View File

@ -0,0 +1,346 @@
/*
* 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;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
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 fieldCache = new HashMap();
/**
* 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 (int i = 0; i < fields.length; i++) {
Field field = fields[i];
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 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 getNames(String namePrefix) {
String prefixToUse = (namePrefix != null ? namePrefix.trim().toUpperCase(Locale.ENGLISH) : "");
Set names = new HashSet();
for (Iterator it = this.fieldCache.keySet().iterator(); it.hasNext();) {
String code = (String) it.next();
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 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 names = new HashSet();
for (Iterator it = this.fieldCache.keySet().iterator(); it.hasNext();) {
String code = (String) it.next();
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 getValues(String namePrefix) {
String prefixToUse = (namePrefix != null ? namePrefix.trim().toUpperCase(Locale.ENGLISH) : "");
Set values = new HashSet();
for (Iterator it = this.fieldCache.keySet().iterator(); it.hasNext();) {
String code = (String) it.next();
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 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 getValuesForSuffix(String nameSuffix) {
String suffixToUse = (nameSuffix != null ? nameSuffix.trim().toUpperCase(Locale.ENGLISH) : "");
Set values = new HashSet();
for (Iterator it = this.fieldCache.keySet().iterator(); it.hasNext();) {
String code = (String) it.next();
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 (Iterator it = this.fieldCache.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
String key = (String) entry.getKey();
if (key.startsWith(prefixToUse) && entry.getValue().equals(value)) {
return key;
}
}
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 (Iterator it = this.fieldCache.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
String key = (String) entry.getKey();
if (key.endsWith(suffixToUse) && entry.getValue().equals(value)) {
return key;
}
}
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) {
StringBuffer parsedPrefix = new StringBuffer();
for(int i = 0; i < propertyName.length(); i++) {
char c = propertyName.charAt(i);
if (Character.isUpperCase(c)) {
parsedPrefix.append("_");
parsedPrefix.append(c);
}
else {
parsedPrefix.append(Character.toUpperCase(c));
}
}
return parsedPrefix.toString();
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
/**
* Interface to be implemented by objects that can return information about
* the current call stack. Useful in AOP (as in AspectJ cflow concept)
* but not AOP-specific.
*
* @author Rod Johnson
* @since 02.02.2004
*/
public interface ControlFlow {
/**
* Detect whether we're under the given class,
* according to the current stack trace.
* @param clazz the clazz to look for
*/
boolean under(Class clazz);
/**
* Detect whether we're under the given class and method,
* according to the current stack trace.
* @param clazz the clazz to look for
* @param methodName the name of the method to look for
*/
boolean under(Class clazz, String methodName);
/**
* Detect whether the current stack trace contains the given token.
* @param token the token to look for
*/
boolean underToken(String token);
}

View File

@ -0,0 +1,118 @@
/*
* 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;
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;
}
public String toString() {
StringBuffer sb = new StringBuffer("Jdk14ControlFlow: ");
for (int i = 0; i < this.stack.length; i++) {
if (i > 0) {
sb.append("\n\t@");
}
sb.append(this.stack[i]);
}
return sb.toString();
}
}
}

View File

@ -0,0 +1,307 @@
/*
* 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.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 ignoredInterfaces = new HashSet();
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 = null;
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 = null;
boolean pluralize = false;
if (parameter.getParameterType().isArray()) {
valueClass = parameter.getParameterType().getComponentType();
pluralize = true;
}
else if (Collection.class.isAssignableFrom(parameter.getParameterType())) {
if (JdkVersion.isAtLeastJava15()) {
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 = null;
boolean pluralize = false;
if (resolvedType.isArray()) {
valueClass = resolvedType.getComponentType();
pluralize = true;
}
else if (Collection.class.isAssignableFrom(resolvedType)) {
if (JdkVersion.isAtLeastJava15()) {
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.indexOf("-") == -1) {
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 (int i = 0; i < chars.length; i++) {
char c = chars[i];
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 (int i = 0; i < ifcs.length; i++) {
Class ifc = ifcs[i];
if (!ignoredInterfaces.contains(ifc)) {
return ifc;
}
}
}
else if (valueClass.getName().lastIndexOf('$') != -1 && valueClass.getDeclaringClass() == null) {
// '$' in the class name but no inner class -
// assuming it's a special subclass (e.g. by OpenJPA)
valueClass = valueClass.getSuperclass();
}
return valueClass;
}
/**
* Pluralize the given name.
*/
private static String pluralize(String name) {
return name + PLURAL_SUFFIX;
}
/**
* Retrieves the <code>Class</code> of an element in the <code>Collection</code>.
* The exact element for which the <code>Class</code> is retreived will depend
* on the concrete <code>Collection</code> implementation.
*/
private static Object peekAhead(Collection collection) {
Iterator it = collection.iterator();
if (!it.hasNext()) {
throw new IllegalStateException(
"Unable to peek ahead in non-empty collection - no element found");
}
Object value = it.next();
if (value == null) {
throw new IllegalStateException(
"Unable to peek ahead in non-empty collection - only null element found");
}
return value;
}
}

View File

@ -0,0 +1,108 @@
/*
* 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.Iterator;
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 excludedPackages = new HashSet();
private final Set excludedClasses = new HashSet();
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 (Iterator it = this.excludedPackages.iterator(); it.hasNext();) {
String packageName = (String) it.next();
if (className.startsWith(packageName)) {
return true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
/**
* Interface that can be implemented by exceptions etc that are error coded.
* The error code is a String, rather than a number, so it can be given
* user-readable values, such as "object.failureDescription".
*
* <p>An error code can be resolved by a MessageSource, for example.
*
* @author Rod Johnson
* @see org.springframework.context.MessageSource
*/
public interface ErrorCoded {
/**
* Return the error code associated with this failure.
* The GUI can render this any way it pleases, allowing for localization etc.
* @return a String error code associated with this failure,
* or <code>null</code> if not error-coded
*/
String getErrorCode();
}

View File

@ -0,0 +1,426 @@
/*
* 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.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).
*
* <p>Only usable on Java 5. Use an appropriate {@link JdkVersion} check
* before calling this class, if a fallback for JDK 1.4 is desirable.
*
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.beans.BeanWrapper
*/
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 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 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 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, 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, 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, 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, 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, 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, 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(methodParam, GenericTypeResolver.getTargetType(methodParam),
source, typeIndex, 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, int nestingLevel) {
return extractType(null, field.getGenericType(), source, typeIndex, 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(null, method.getGenericReturnType(), source, typeIndex, nestingLevel, 1);
}
/**
* Extract the generic type from the given Type object.
* @param methodParam the method parameter specification
* @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(
MethodParameter methodParam, Type type, Class source, int typeIndex, int nestingLevel, int currentLevel) {
Type resolvedType = type;
if (type instanceof TypeVariable && methodParam != null && methodParam.typeVariableMap != null) {
Type mappedType = (Type) methodParam.typeVariableMap.get(type);
if (mappedType != null) {
resolvedType = mappedType;
}
}
if (resolvedType instanceof ParameterizedType) {
return extractTypeFromParameterizedType(
methodParam, (ParameterizedType) resolvedType, source, typeIndex, nestingLevel, currentLevel);
}
else if (resolvedType instanceof Class) {
return extractTypeFromClass(methodParam, (Class) resolvedType, source, typeIndex, nestingLevel, currentLevel);
}
else {
return null;
}
}
/**
* Extract the generic type from the given ParameterizedType object.
* @param methodParam the method parameter specification
* @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(MethodParameter methodParam,
ParameterizedType ptype, Class source, int typeIndex, 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 = (methodParam != null ? methodParam.getTypeIndexForLevel(nextLevel) : null);
// Default is last parameter type: Collection element or Map value.
int indexToUse = (currentTypeIndex != null ? currentTypeIndex.intValue() : paramTypes.length - 1);
Type paramType = paramTypes[indexToUse];
return extractType(methodParam, paramType, source, typeIndex, nestingLevel, nextLevel);
}
if (source != null && !source.isAssignableFrom(rawType)) {
return null;
}
Class fromSuperclassOrInterface =
extractTypeFromClass(methodParam, rawType, source, typeIndex, nestingLevel, currentLevel);
if (fromSuperclassOrInterface != null) {
return fromSuperclassOrInterface;
}
if (paramTypes == null || typeIndex >= paramTypes.length) {
return null;
}
Type paramType = paramTypes[typeIndex];
if (paramType instanceof TypeVariable && methodParam != null && methodParam.typeVariableMap != null) {
Type mappedType = (Type) methodParam.typeVariableMap.get(paramType);
if (mappedType != null) {
paramType = mappedType;
}
}
if (paramType instanceof WildcardType) {
Type[] lowerBounds = ((WildcardType) paramType).getLowerBounds();
if (lowerBounds != null && lowerBounds.length > 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(null, clazz, source, typeIndex, 1, 1);
}
/**
* Extract the generic type from the given Class object.
* @param methodParam the method parameter specification
* @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(
MethodParameter methodParam, Class clazz, Class source, int typeIndex, int nestingLevel, int currentLevel) {
if (clazz.getName().startsWith("java.util.")) {
return null;
}
if (clazz.getSuperclass() != null && isIntrospectionCandidate(clazz.getSuperclass())) {
return extractType(methodParam, clazz.getGenericSuperclass(), source, typeIndex, nestingLevel, currentLevel);
}
Type[] ifcs = clazz.getGenericInterfaces();
if (ifcs != null) {
for (int i = 0; i < ifcs.length; i++) {
Type ifc = ifcs[i];
Type rawType = ifc;
if (ifc instanceof ParameterizedType) {
rawType = ((ParameterizedType) ifc).getRawType();
}
if (rawType instanceof Class && isIntrospectionCandidate((Class) rawType)) {
return extractType(methodParam, ifc, source, typeIndex, nestingLevel, currentLevel);
}
}
}
return null;
}
/**
* Determine whether the given class is a potential candidate
* that defines generic collection or map types.
* @param clazz the class to check
* @return whether the given class is assignable to Collection or Map
*/
private static boolean isIntrospectionCandidate(Class clazz) {
return (Collection.class.isAssignableFrom(clazz) || Map.class.isAssignableFrom(clazz));
}
}

View File

@ -0,0 +1,262 @@
/*
* 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.GenericArrayType;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
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.
*
* <p>Only usable on Java 5. Use an appropriate JdkVersion check before
* calling this class, if a fallback for JDK 1.4 is desirable.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 2.5.2
* @see GenericCollectionTypeResolver
* @see JdkVersion
*/
public abstract class GenericTypeResolver {
/** Cache from Class to TypeVariable Map */
private static final Map typeVariableCache = Collections.synchronizedMap(new WeakHashMap());
/**
* 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 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 typeVariableMap = getTypeVariableMap(clazz);
Type rawType = getRawType(genericType, typeVariableMap);
return (rawType instanceof Class ? (Class) rawType : method.getReturnType());
}
/**
* 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
*/
static Class resolveType(Type genericType, Map 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 typeVariableMap) {
Type resolvedType = genericType;
if (genericType instanceof TypeVariable) {
TypeVariable tv = (TypeVariable) genericType;
resolvedType = (Type) 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.
*/
static Map getTypeVariableMap(Class clazz) {
Map typeVariableMap = (Map) typeVariableCache.get(clazz);
if (typeVariableMap == null) {
typeVariableMap = new HashMap();
// 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, 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 typeVariableMap) {
for (int i = 0; i < genericInterfaces.length; i++) {
Type genericInterface = genericInterfaces[i];
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 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 = (Type) typeVariableMap.get(typeVariableArgument);
if (resolvedType == null) {
resolvedType = extractBoundForTypeVariable(typeVariableArgument);
}
typeVariableMap.put(variable, resolvedType);
}
}
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
/**
* Interface to be implemented by transparent resource proxies that need to be
* considered as equal to the underlying resource, for example for consistent
* lookup key comparisons. Note that this interface does imply such special
* semantics and does not constitute a general-purpose mixin!
*
* <p>Such wrappers will automatically be unwrapped for key comparisons in
* {@link org.springframework.transaction.support.TransactionSynchronizationManager}.
*
* <p>Only fully transparent proxies, e.g. for redirection or service lookups,
* are supposed to implement this interface. Proxies that decorate the target
* object with new behavior, such as AOP proxies, do <i>not</i> qualify here!
*
* @author Juergen Hoeller
* @since 2.5.4
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public interface InfrastructureProxy {
/**
* Return the underlying resource (never <code>null</code>).
*/
Object getWrappedObject();
}

View File

@ -0,0 +1,143 @@
/*
* 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;
/**
* Internal helper class used to find the Java/JDK version
* that Spring is operating on, to allow for automatically
* adapting to the present platform's capabilities.
*
* <p>Note that Spring requires JVM 1.4 or higher, as of Spring 2.5.
*
* @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.indexOf("1.7.") != -1) {
majorJavaVersion = JAVA_17;
}
else if (javaVersion.indexOf("1.6.") != -1) {
majorJavaVersion = JAVA_16;
}
else if (javaVersion.indexOf("1.5.") != -1) {
majorJavaVersion = JAVA_15;
}
else {
// else leave 1.4 as default (it's either 1.4 or unknown)
majorJavaVersion = JAVA_14;
}
}
/**
* 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
* @see #getMajorJavaVersion()
* @see #JAVA_14
* @see #JAVA_15
* @see #JAVA_16
* @see #JAVA_17
*/
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
* @see #getMajorJavaVersion()
* @see #JAVA_15
* @see #JAVA_16
* @see #JAVA_17
*/
public static boolean isAtLeastJava15() {
return getMajorJavaVersion() >= JAVA_15;
}
/**
* 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
* @see #getMajorJavaVersion()
* @see #JAVA_16
* @see #JAVA_17
*/
public static boolean isAtLeastJava16() {
return getMajorJavaVersion() >= JAVA_16;
}
}

View File

@ -0,0 +1,278 @@
/*
* 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.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.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 ClassReader for each introspected Class, in a
* thread-safe manner. It is recommended to reuse discoverer instances
* as far as possible.
*
* @author Adrian Colyer
* @author Juergen Hoeller
* @since 2.0
*/
public class LocalVariableTableParameterNameDiscoverer implements ParameterNameDiscoverer {
private static Log logger = LogFactory.getLog(LocalVariableTableParameterNameDiscoverer.class);
private final Map parameterNamesCache = CollectionFactory.createConcurrentMapIfPossible(16);
private final Map classReaderCache = new HashMap();
public String[] getParameterNames(Method method) {
String[] paramNames = (String[]) this.parameterNamesCache.get(method);
if (paramNames == null) {
try {
paramNames = visitMethod(method).getParameterNames();
if (paramNames != null) {
this.parameterNamesCache.put(method, paramNames);
}
}
catch (IOException ex) {
// 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("IOException whilst attempting to read '.class' file for class [" +
method.getDeclaringClass().getName() +
"] - unable to determine parameter names for method: " + method, ex);
}
}
}
return paramNames;
}
public String[] getParameterNames(Constructor ctor) {
String[] paramNames = (String[]) this.parameterNamesCache.get(ctor);
if (paramNames == null) {
try {
paramNames = visitConstructor(ctor).getParameterNames();
if (paramNames != null) {
this.parameterNamesCache.put(ctor, paramNames);
}
}
catch (IOException ex) {
// 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("IOException whilst attempting to read '.class' file for class [" +
ctor.getDeclaringClass().getName() +
"] - unable to determine parameter names for constructor: " + ctor, ex);
}
}
}
return paramNames;
}
/**
* Visit the given method and discover its parameter names.
*/
private ParameterNameDiscoveringVisitor visitMethod(Method method) throws IOException {
ClassReader classReader = getClassReader(method.getDeclaringClass());
FindMethodParameterNamesClassVisitor classVisitor = new FindMethodParameterNamesClassVisitor(method);
classReader.accept(classVisitor, false);
return classVisitor;
}
/**
* Visit the given constructor and discover its parameter names.
*/
private ParameterNameDiscoveringVisitor visitConstructor(Constructor ctor) throws IOException {
ClassReader classReader = getClassReader(ctor.getDeclaringClass());
FindConstructorParameterNamesClassVisitor classVisitor = new FindConstructorParameterNamesClassVisitor(ctor);
classReader.accept(classVisitor, false);
return classVisitor;
}
/**
* Obtain a (cached) ClassReader for the given class.
*/
private ClassReader getClassReader(Class clazz) throws IOException {
synchronized (this.classReaderCache) {
ClassReader classReader = (ClassReader) this.classReaderCache.get(clazz);
if (classReader == null) {
InputStream is = clazz.getResourceAsStream(ClassUtils.getClassFileName(clazz));
if (is == null) {
throw new FileNotFoundException("Class file for class [" + clazz.getName() + "] not found");
}
try {
classReader = new ClassReader(is);
this.classReaderCache.put(clazz, classReader);
}
finally {
is.close();
}
}
return classReader;
}
}
/**
* Helper class that looks for a given member name and descriptor, and then
* attempts to find the parameter names for that member.
*/
private static abstract class ParameterNameDiscoveringVisitor extends EmptyVisitor {
private String methodNameToMatch;
private String descriptorToMatch;
private int numParamsExpected;
/*
* The nth entry contains the slot index of the LVT table entry holding the
* argument name for the nth parameter.
*/
private int[] lvtSlotIndex;
private String[] parameterNames;
public ParameterNameDiscoveringVisitor(String name, boolean isStatic, Class[] paramTypes) {
this.methodNameToMatch = name;
this.numParamsExpected = paramTypes.length;
computeLvtSlotIndices(isStatic, paramTypes);
}
public void setDescriptorToMatch(String descriptor) {
this.descriptorToMatch = descriptor;
}
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (name.equals(this.methodNameToMatch) && desc.equals(this.descriptorToMatch)) {
return new LocalVariableTableVisitor(this, isStatic(access));
}
else {
// Not interested in this method...
return null;
}
}
private boolean isStatic(int access) {
return ((access & Opcodes.ACC_STATIC) > 0);
}
public String[] getParameterNames() {
return this.parameterNames;
}
private void computeLvtSlotIndices(boolean isStatic, Class[] paramTypes) {
this.lvtSlotIndex = new int[paramTypes.length];
int nextIndex = (isStatic ? 0 : 1);
for (int i = 0; i < paramTypes.length; i++) {
this.lvtSlotIndex[i] = nextIndex;
if (isWideType(paramTypes[i])) {
nextIndex += 2;
}
else {
nextIndex++;
}
}
}
private boolean isWideType(Class aType) {
return (aType == Long.TYPE || aType == Double.TYPE);
}
}
private static class FindMethodParameterNamesClassVisitor extends ParameterNameDiscoveringVisitor {
public FindMethodParameterNamesClassVisitor(Method method) {
super(method.getName(), Modifier.isStatic(method.getModifiers()), method.getParameterTypes());
setDescriptorToMatch(Type.getMethodDescriptor(method));
}
}
private static class FindConstructorParameterNamesClassVisitor extends ParameterNameDiscoveringVisitor {
public FindConstructorParameterNamesClassVisitor(Constructor ctor) {
super("<init>", false, ctor.getParameterTypes());
Type[] pTypes = new Type[ctor.getParameterTypes().length];
for (int i = 0; i < pTypes.length; i++) {
pTypes[i] = Type.getType(ctor.getParameterTypes()[i]);
}
setDescriptorToMatch(Type.getMethodDescriptor(Type.VOID_TYPE, pTypes));
}
}
private static class LocalVariableTableVisitor extends EmptyVisitor {
private final ParameterNameDiscoveringVisitor memberVisitor;
private final boolean isStatic;
private String[] parameterNames;
private boolean hasLvtInfo = false;
public LocalVariableTableVisitor(ParameterNameDiscoveringVisitor memberVisitor, boolean isStatic) {
this.memberVisitor = memberVisitor;
this.isStatic = isStatic;
this.parameterNames = new String[memberVisitor.numParamsExpected];
}
public void visitLocalVariable(
String name, String description, String signature, Label start, Label end, int index) {
this.hasLvtInfo = true;
int[] lvtSlotIndices = this.memberVisitor.lvtSlotIndex;
for (int i = 0; i < lvtSlotIndices.length; i++) {
if (lvtSlotIndices[i] == index) {
this.parameterNames[i] = name;
}
}
}
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.
this.memberVisitor.parameterNames = this.parameterNames;
}
}
}
}

View File

@ -0,0 +1,328 @@
/*
* 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.HashMap;
import java.util.Map;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* 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.
*
* <p>Used by {@link GenericCollectionTypeResolver},
* {@link org.springframework.beans.BeanWrapperImpl} and
* {@link org.springframework.beans.factory.support.AbstractBeanFactory}.
*
* <p>Note that this class does not depend on JDK 1.5 API artifacts, in order
* to remain compatible with JDK 1.4. Concrete generic type resolution
* via JDK 1.5 API happens in {@link GenericCollectionTypeResolver} only.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 2.0
* @see GenericCollectionTypeResolver
*/
public class MethodParameter {
private static final Method methodParameterAnnotationsMethod =
ClassUtils.getMethodIfAvailable(Method.class, "getParameterAnnotations", new Class[0]);
private static final Method constructorParameterAnnotationsMethod =
ClassUtils.getMethodIfAvailable(Constructor.class, "getParameterAnnotations", new Class[0]);
private Method method;
private Constructor constructor;
private final int parameterIndex;
private Class parameterType;
private Object[] parameterAnnotations;
private ParameterNameDiscoverer parameterNameDiscoverer;
private String parameterName;
private int nestingLevel = 1;
/** Map from Integer level to Integer type index */
private Map typeIndexesPerLevel;
Map typeVariableMap;
/**
* 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;
}
/**
* 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;
}
/**
* 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.parameterAnnotations = original.parameterAnnotations;
this.typeVariableMap = original.typeVariableMap;
}
/**
* 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;
}
/**
* 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) {
this.parameterType = (this.method != null ?
this.method.getParameterTypes()[this.parameterIndex] :
this.constructor.getParameterTypes()[this.parameterIndex]);
}
return this.parameterType;
}
/**
* Return the annotations associated with the method/constructor parameter.
* @return the parameter annotations, or <code>null</code> if there is
* no annotation support (on JDK < 1.5). The return value is an Object array
* instead of an Annotation array simply for compatibility with older JDKs;
* feel free to cast it to <code>Annotation[]</code> on JDK 1.5 or higher.
*/
public Object[] getParameterAnnotations() {
if (this.parameterAnnotations != null) {
return this.parameterAnnotations;
}
if (methodParameterAnnotationsMethod == null) {
return null;
}
Object[][] annotationArray = (this.method != null ?
((Object[][]) ReflectionUtils.invokeMethod(methodParameterAnnotationsMethod, this.method)) :
((Object[][]) ReflectionUtils.invokeMethod(constructorParameterAnnotationsMethod, this.constructor)));
this.parameterAnnotations = annotationArray[this.parameterIndex];
return this.parameterAnnotations;
}
/**
* 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(new Integer(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(new Integer(this.nestingLevel), new Integer(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 (Integer) getTypeIndexesPerLevel().get(new Integer(nestingLevel));
}
/**
* Obtain the (lazily constructed) type-indexes-per-level Map.
*/
private Map getTypeIndexesPerLevel() {
if (this.typeIndexesPerLevel == null) {
this.typeIndexesPerLevel = new HashMap(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");
}
}
}

View File

@ -0,0 +1,47 @@
/*
* 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 extends InheritableThreadLocal {
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;
}
public String toString() {
return this.name;
}
}

View File

@ -0,0 +1,47 @@
/*
* 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 extends ThreadLocal {
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;
}
public String toString() {
return this.name;
}
}

View File

@ -0,0 +1,133 @@
/*
* 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;
/**
* 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;
/**
* 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.
*/
public String getMessage() {
return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
}
/**
* Retrieve the innermost cause of this exception, if any.
* @return the innermost exception, or <code>null</code> if none
*/
public Throwable getRootCause() {
Throwable rootCause = null;
Throwable cause = getCause();
while (cause != null && cause != rootCause) {
rootCause = cause;
cause = cause.getCause();
}
return rootCause;
}
/**
* Retrieve the most specific cause of this exception, that is,
* either the innermost cause (root cause) or this exception itself.
* <p>Differs from {@link #getRootCause()} in that it falls back
* to the present exception if there is no root cause.
* @return the most specific cause (never <code>null</code>)
* @since 2.0.3
*/
public Throwable getMostSpecificCause() {
Throwable rootCause = getRootCause();
return (rootCause != null ? rootCause : this);
}
/**
* Check whether this exception contains an exception of the given type:
* either it is of the given class itself or it contains a nested cause
* of the given type.
* @param exType the exception type to look for
* @return whether there is a nested exception of the specified type
*/
public boolean contains(Class exType) {
if (exType == null) {
return false;
}
if (exType.isInstance(this)) {
return true;
}
Throwable cause = getCause();
if (cause == this) {
return false;
}
if (cause instanceof NestedCheckedException) {
return ((NestedCheckedException) cause).contains(exType);
}
else {
while (cause != null) {
if (exType.isInstance(cause)) {
return true;
}
if (cause.getCause() == cause) {
break;
}
cause = cause.getCause();
}
return false;
}
}
}

View File

@ -0,0 +1,55 @@
/*
* Copyright 2002-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;
/**
* 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) {
StringBuffer buf = new StringBuffer();
if (message != null) {
buf.append(message).append("; ");
}
buf.append("nested exception is ").append(cause);
return buf.toString();
}
else {
return message;
}
}
}

View File

@ -0,0 +1,66 @@
/*
* 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;
import java.io.IOException;
/**
* Subclass of IOException that properly handles a root cause,
* exposing the root cause just like NestedChecked/RuntimeException does.
*
* <p>The similarity between this class and the NestedChecked/RuntimeException
* class is unavoidable, as this class needs to derive from IOException
* and cannot derive from NestedCheckedException.
*
* @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 {
/**
* 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.
*/
public String getMessage() {
return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
}
}

View File

@ -0,0 +1,134 @@
/*
* 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;
/**
* 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;
/**
* 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.
*/
public String getMessage() {
return NestedExceptionUtils.buildMessage(super.getMessage(), getCause());
}
/**
* Retrieve the innermost cause of this exception, if any.
* @return the innermost exception, or <code>null</code> if none
* @since 2.0
*/
public Throwable getRootCause() {
Throwable rootCause = null;
Throwable cause = getCause();
while (cause != null && cause != rootCause) {
rootCause = cause;
cause = cause.getCause();
}
return rootCause;
}
/**
* Retrieve the most specific cause of this exception, that is,
* either the innermost cause (root cause) or this exception itself.
* <p>Differs from {@link #getRootCause()} in that it falls back
* to the present exception if there is no root cause.
* @return the most specific cause (never <code>null</code>)
* @since 2.0.3
*/
public Throwable getMostSpecificCause() {
Throwable rootCause = getRootCause();
return (rootCause != null ? rootCause : this);
}
/**
* Check whether this exception contains an exception of the given type:
* either it is of the given class itself or it contains a nested cause
* of the given type.
* @param exType the exception type to look for
* @return whether there is a nested exception of the specified type
*/
public boolean contains(Class exType) {
if (exType == null) {
return false;
}
if (exType.isInstance(this)) {
return true;
}
Throwable cause = getCause();
if (cause == this) {
return false;
}
if (cause instanceof NestedRuntimeException) {
return ((NestedRuntimeException) cause).contains(exType);
}
else {
while (cause != null) {
if (exType.isInstance(cause)) {
return true;
}
if (cause.getCause() == cause) {
break;
}
cause = cause.getCause();
}
return false;
}
}
}

View File

@ -0,0 +1,64 @@
/*
* 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;
import java.util.Comparator;
/**
* {@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 {
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);
}
}

View File

@ -0,0 +1,69 @@
/*
* 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 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 or 1, with {@link #LOWEST_PRECEDENCE}
* indicating greatest. Same order values will result in arbitrary
* positions for the affected objects.
* <p>Higher value can be interpreted as lower priority,
* consequently the first object has highest priority
* (somewhat analogous to Servlet "load-on-startup" values).
* <p>Note that order values below 0 are reserved for framework
* purposes. Application-specified values should always be 0 or
* greater, with only framework components (internal or third-party)
* supposed to use lower values.
* @return the order value
* @see #LOWEST_PRECEDENCE
*/
int getOrder();
}

View File

@ -0,0 +1,154 @@
/*
* 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.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 (int i = 0; i < DEFAULT_EXCLUDED_PACKAGES.length; i++) {
excludePackage(DEFAULT_EXCLUDED_PACKAGES[i]);
}
}
protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
Class result = null;
if (isEligibleForOverriding(name)) {
result = loadClassForOverriding(name);
}
if (result != null) {
if (resolve) {
resolveClass(result);
}
return result;
}
else {
return super.loadClass(name, resolve);
}
}
/**
* Determine whether the specified class is eligible for overriding
* by this class loader.
* @param className the class name to check
* @return whether the specified class is eligible
* @see #isExcluded
*/
protected boolean isEligibleForOverriding(String className) {
return !isExcluded(className);
}
/**
* Load the specified class for overriding purposes in this ClassLoader.
* <p>The default implementation delegates to {@link #findLoadedClass},
* {@link #loadBytesForClass} and {@link #defineClass}.
* @param name the name of the class
* @return the Class object, or <code>null</code> if no class defined for that name
* @throws ClassNotFoundException if the class for the given name couldn't be loaded
*/
protected Class loadClassForOverriding(String name) throws ClassNotFoundException {
Class result = findLoadedClass(name);
if (result == null) {
byte[] bytes = loadBytesForClass(name);
if (bytes != null) {
result = defineClass(name, bytes, 0, bytes.length);
}
}
return result;
}
/**
* Load the defining bytes for the given class,
* to be turned into a Class object through a {@link #defineClass} call.
* <p>The default implementation delegates to {@link #openStreamForClass}
* and {@link #transformIfNecessary}.
* @param name the name of the class
* @return the byte content (with transformers already applied),
* or <code>null</code> if no class defined for that name
* @throws ClassNotFoundException if the class for the given name couldn't be loaded
*/
protected byte[] loadBytesForClass(String name) throws ClassNotFoundException {
InputStream is = openStreamForClass(name);
if (is == null) {
return null;
}
try {
// Load the raw bytes.
byte[] bytes = FileCopyUtils.copyToByteArray(is);
// Transform if necessary and use the potentially transformed bytes.
return transformIfNecessary(name, bytes);
}
catch (IOException ex) {
throw new ClassNotFoundException("Cannot load resource for class [" + name + "]", ex);
}
}
/**
* Open an InputStream for the specified class.
* <p>The default implementation loads a standard class file through
* the parent ClassLoader's <code>getResourceAsStream</code> method.
* @param name the name of the class
* @return the InputStream containing the byte code for the specified class
*/
protected InputStream openStreamForClass(String name) {
String internalName = name.replace('.', '/') + CLASS_FILE_SUFFIX;
return getParent().getResourceAsStream(internalName);
}
/**
* Transformation hook to be implemented by subclasses.
* <p>The default implementation simply returns the given bytes as-is.
* @param name the fully-qualified name of the class being transformed
* @param bytes the raw bytes of the class
* @return the transformed bytes (never <code>null</code>;
* same as the input bytes if the transformation produced no changes)
*/
protected byte[] transformIfNecessary(String name, byte[] bytes) {
return bytes;
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* Interface to discover parameter names for methods and constructors.
*
* <p>Parameter name discovery is not always possible, but various strategies are
* available to try, such as looking for debug information that may have been
* emitted at compile time, and looking for argname annotation values optionally
* accompanying AspectJ annotated methods.
*
* @author Rod Johnson
* @author Adrian Colyer
* @since 2.0
*/
public interface ParameterNameDiscoverer {
/**
* Return parameter names for this method,
* or <code>null</code> if they cannot be determined.
* @param method method to find parameter names for
* @return an array of parameter names if the names can be resolved,
* or <code>null</code> if they cannot
*/
String[] getParameterNames(Method method);
/**
* Return parameter names for this constructor,
* or <code>null</code> if they cannot be determined.
* @param ctor constructor to find parameter names for
* @return an array of parameter names if the names can be resolved,
* or <code>null</code> if they cannot
*/
String[] getParameterNames(Constructor ctor);
}

View File

@ -0,0 +1,73 @@
/*
* 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;
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 parameterNameDiscoverers = new LinkedList();
/**
* 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 (Iterator it = this.parameterNameDiscoverers.iterator(); it.hasNext(); ) {
ParameterNameDiscoverer pnd = (ParameterNameDiscoverer) it.next();
String[] result = pnd.getParameterNames(method);
if (result != null) {
return result;
}
}
return null;
}
public String[] getParameterNames(Constructor ctor) {
for (Iterator it = this.parameterNameDiscoverers.iterator(); it.hasNext(); ) {
ParameterNameDiscoverer pnd = (ParameterNameDiscoverer) it.next();
String[] result = pnd.getParameterNames(ctor);
if (result != null) {
return result;
}
}
return null;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
/**
* 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-postprocessor beans. This
* subtly affects their autowiring behavior: They will only be
* autowired against beans which do not require eager initialization
* for type matching.
*
* @author Juergen Hoeller
* @since 2.5
* @see org.springframework.beans.factory.config.PropertyOverrideConfigurer
* @see org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
*/
public interface PriorityOrdered extends Ordered {
}

View File

@ -0,0 +1,225 @@
/*
* 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.Method;
import java.lang.reflect.Modifier;
import java.util.LinkedList;
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.ReflectionUtils;
/**
* Helper implementation for a reflective visitor.
* Mainly for internal use within the framework.
*
* <p>To use, call <code>invokeVisit</code>, passing a Visitor object
* and the data argument to accept (double-dispatch). For example:
*
* <pre>
* public String styleValue(Object value) {
* reflectiveVistorSupport.invokeVisit(this, value)
* }
*
* // visit call back will be invoked via reflection
* String visit(&lt;valueType&gt; arg) {
* // process argument of type &lt;valueType&gt;
* }
* </pre>
*
* See the {@link org.springframework.core.style.DefaultValueStyler} class
* for a concrete usage of this visitor helper.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 1.2.2
* @deprecated as of Spring 2.5, to be removed in Spring 3.0
*/
public class ReflectiveVisitorHelper {
private static final String VISIT_METHOD = "visit";
private static final String VISIT_NULL = "visitNull";
private static final Log logger = LogFactory.getLog(ReflectiveVisitorHelper.class);
private final CachingMapDecorator visitorClassVisitMethods = new CachingMapDecorator() {
public Object create(Object key) {
return new ClassVisitMethods((Class) key);
}
};
/**
* Use reflection to call the appropriate <code>visit</code> method
* on the provided visitor, passing in the specified argument.
* @param visitor the visitor encapsulating the logic to process the argument
* @param argument the argument to dispatch
* @throws IllegalArgumentException if the visitor parameter is null
*/
public Object invokeVisit(Object visitor, Object argument) {
Assert.notNull(visitor, "The visitor to visit is required");
// Perform call back on the visitor through reflection.
Method method = getMethod(visitor.getClass(), argument);
if (method == null) {
if (logger.isWarnEnabled()) {
logger.warn("No method found by reflection for visitor class [" + visitor.getClass().getName()
+ "] and argument of type [" + (argument != null ? argument.getClass().getName() : "") + "]");
}
return null;
}
try {
Object[] args = null;
if (argument != null) {
args = new Object[] {argument};
}
if (!Modifier.isPublic(method.getModifiers())) {
method.setAccessible(true);
}
return method.invoke(visitor, args);
}
catch (Exception ex) {
ReflectionUtils.handleReflectionException(ex);
throw new IllegalStateException("Should never get here");
}
}
/**
* Determines the most appropriate visit method for the
* given visitor class and argument.
*/
private Method getMethod(Class visitorClass, Object argument) {
ClassVisitMethods visitMethods = (ClassVisitMethods) this.visitorClassVisitMethods.get(visitorClass);
return visitMethods.getVisitMethod(argument != null ? argument.getClass() : null);
}
/**
* Internal class caching visitor methods by argument class.
*/
private static class ClassVisitMethods {
private final Class visitorClass;
private final CachingMapDecorator visitMethodCache = new CachingMapDecorator() {
public Object create(Object argumentClazz) {
if (argumentClazz == null) {
return findNullVisitorMethod();
}
Method method = findVisitMethod((Class) argumentClazz);
if (method == null) {
method = findDefaultVisitMethod();
}
return method;
}
};
public ClassVisitMethods(Class visitorClass) {
this.visitorClass = visitorClass;
}
private Method findNullVisitorMethod() {
for (Class clazz = this.visitorClass; clazz != null; clazz = clazz.getSuperclass()) {
try {
return clazz.getDeclaredMethod(VISIT_NULL, (Class[]) null);
}
catch (NoSuchMethodException ex) {
}
}
return findDefaultVisitMethod();
}
private Method findDefaultVisitMethod() {
final Class[] args = {Object.class};
for (Class clazz = this.visitorClass; clazz != null; clazz = clazz.getSuperclass()) {
try {
return clazz.getDeclaredMethod(VISIT_METHOD, args);
}
catch (NoSuchMethodException ex) {
}
}
if (logger.isWarnEnabled()) {
logger.warn("No default '" + VISIT_METHOD + "' method found. Returning <null>.");
}
return null;
}
/**
* Gets a cached visitor method for the specified argument type.
*/
private Method getVisitMethod(Class argumentClass) {
return (Method) this.visitMethodCache.get(argumentClass);
}
/**
* Traverses class hierarchy looking for applicable visit() method.
*/
private Method findVisitMethod(Class rootArgumentType) {
if (rootArgumentType == Object.class) {
return null;
}
LinkedList classQueue = new LinkedList();
classQueue.addFirst(rootArgumentType);
while (!classQueue.isEmpty()) {
Class argumentType = (Class) classQueue.removeLast();
// Check for a visit method on the visitor class matching this
// argument type.
try {
if (logger.isTraceEnabled()) {
logger.trace("Looking for method " + VISIT_METHOD + "(" + argumentType + ")");
}
return findVisitMethod(this.visitorClass, argumentType);
}
catch (NoSuchMethodException e) {
// Queue up the argument super class if it's not of type Object.
if (!argumentType.isInterface() && (argumentType.getSuperclass() != Object.class)) {
classQueue.addFirst(argumentType.getSuperclass());
}
// Queue up argument's implemented interfaces.
Class[] interfaces = argumentType.getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
classQueue.addFirst(interfaces[i]);
}
}
}
// No specific method found -> return the default.
return findDefaultVisitMethod();
}
private Method findVisitMethod(Class visitorClass, Class argumentType) throws NoSuchMethodException {
try {
return visitorClass.getDeclaredMethod(VISIT_METHOD, new Class[] {argumentType});
}
catch (NoSuchMethodException ex) {
// Try visitorClass superclasses.
if (visitorClass.getSuperclass() != Object.class) {
return findVisitMethod(visitorClass.getSuperclass(), argumentType);
}
else {
throw ex;
}
}
}
}
}

View File

@ -0,0 +1,147 @@
/*
* 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.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
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 aliasMap = CollectionFactory.createConcurrentMapIfPossible(16);
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 = (String) 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 + "'.");
}
}
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 = (String) 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 aliases = new ArrayList();
synchronized (this.aliasMap) {
for (Iterator it = this.aliasMap.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
String registeredName = (String) entry.getValue();
if (registeredName.equals(name)) {
aliases.add(entry.getKey());
}
}
}
return StringUtils.toStringArray(aliases);
}
/**
* 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 aliasCopy = new HashMap(this.aliasMap);
for (Iterator it = aliasCopy.keySet().iterator(); it.hasNext();) {
String alias = (String) it.next();
String registeredName = (String) aliasCopy.get(alias);
String resolvedAlias = valueResolver.resolveStringValue(alias);
String resolvedName = valueResolver.resolveStringValue(registeredName);
if (!resolvedAlias.equals(alias)) {
String existingName = (String) 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 + "'.");
}
this.aliasMap.put(resolvedAlias, resolvedName);
this.aliasMap.remove(alias);
}
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 = null;
do {
resolvedName = (String) this.aliasMap.get(canonicalName);
if (resolvedName != null) {
canonicalName = resolvedName;
}
}
while (resolvedName != null);
return canonicalName;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
/**
* Interface to be implemented by a reloading-aware ClassLoader
* (e.g. a Groovy-based ClassLoader). Detected for example by
* Spring's CGLIB proxy factory for making a caching decision.
*
* <p>If a ClassLoader does <i>not</i> implement this interface,
* then all of the classes obtained from it should be considered
* as not reloadable (i.e. cacheable).
*
* @author Juergen Hoeller
* @since 2.5.1
*/
public interface SmartClassLoader {
/**
* Determine whether the given class is reloadable (in this ClassLoader).
* <p>Typically used to check whether the result may be cached (for this
* ClassLoader) or whether it should be reobtained every time.
* @param clazz the class to check (usually loaded from this ClassLoader)
* @return whether the class should be expected to appear in a reloaded
* version (with a different <code>Class</code> object) later on
*/
boolean isClassReloadable(Class clazz);
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
/**
* Class that exposes the Spring version. Fetches the
* "Implementation-Version" manifest attribute from the jar file.
*
* <p>Note that some ClassLoaders do not expose the package metadata,
* hence this class might not be able to determine the Spring version
* in all environments. Consider using a reflection-based check instead:
* For example, checking for the presence of a specific Spring 2.0
* method that you intend to call.
*
* @author Juergen Hoeller
* @since 1.1
*/
public class SpringVersion {
/**
* Return the full version string of the present Spring codebase,
* or <code>null</code> if it cannot be determined.
* @see java.lang.Package#getImplementationVersion()
*/
public static String getVersion() {
Package pkg = SpringVersion.class.getPackage();
return (pkg != null ? pkg.getImplementationVersion() : null);
}
}

View File

@ -0,0 +1,129 @@
/*
* 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.enums;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
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)
*/
public abstract class AbstractCachingLabeledEnumResolver implements LabeledEnumResolver {
protected transient final Log logger = LogFactory.getLog(getClass());
private final CachingMapDecorator labeledEnumCache = new CachingMapDecorator(true) {
protected Object create(Object key) {
Class enumType = (Class) key;
Set typeEnums = findLabeledEnums(enumType);
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 typeEnumMap = new HashMap(typeEnums.size());
for (Iterator it = typeEnums.iterator(); it.hasNext();) {
LabeledEnum labeledEnum = (LabeledEnum) it.next();
typeEnumMap.put(labeledEnum.getCode(), labeledEnum);
}
return Collections.unmodifiableMap(typeEnumMap);
}
protected boolean useWeakValue(Object key, Object value) {
Class enumType = (Class) key;
if (!ClassUtils.isCacheSafe(enumType, AbstractCachingLabeledEnumResolver.this.getClass().getClassLoader())) {
if (logger.isDebugEnabled()) {
logger.debug("Not strongly caching class [" + enumType.getName() + "] because it is not cache-safe");
}
return true;
}
else {
return false;
}
}
};
public Set getLabeledEnumSet(Class type) throws IllegalArgumentException {
return new TreeSet(getLabeledEnumMap(type).values());
}
public Map getLabeledEnumMap(Class type) throws IllegalArgumentException {
Assert.notNull(type, "No type specified");
return (Map) this.labeledEnumCache.get(type);
}
public LabeledEnum getLabeledEnumByCode(Class type, Comparable code) throws IllegalArgumentException {
Assert.notNull(code, "No enum code specified");
Map typeEnums = getLabeledEnumMap(type);
LabeledEnum codedEnum = (LabeledEnum) 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 typeEnums = getLabeledEnumMap(type);
Iterator it = typeEnums.values().iterator();
while (it.hasNext()) {
LabeledEnum value = (LabeledEnum) it.next();
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 findLabeledEnums(Class type);
}

View File

@ -0,0 +1,52 @@
/*
* 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.enums;
/**
* Base class for labeled enum instances that aren't static.
*
* @author Keith Donald
* @since 1.2.6
*/
public abstract class AbstractGenericLabeledEnum extends AbstractLabeledEnum {
/**
* A descriptive label for the enum.
*/
private final String label;
/**
* Create a new StaticLabeledEnum instance.
* @param label the label; if <code>null</code>), the enum's code
* will be used as label
*/
protected AbstractGenericLabeledEnum(String label) {
this.label = label;
}
public String getLabel() {
if (this.label != null) {
return label;
}
else {
return getCode().toString();
}
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.enums;
/**
* Abstract base superclass for LabeledEnum implementations.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Sam Brannen
* @since 1.2.2
*/
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());
}
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()));
}
public int hashCode() {
return (getType().hashCode() * 29 + getCode().hashCode());
}
public String toString() {
return getLabel();
}
}

View File

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

View File

@ -0,0 +1,69 @@
/*
* 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.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
*/
public interface LabeledEnumResolver {
/**
* Return a set of enumerations of a particular type. Each element in the
* set should be an instance of LabeledEnum.
* @param type the enum type
* @return a set of localized enumeration instances for the provided type
* @throws IllegalArgumentException if the type is not supported
*/
public Set getLabeledEnumSet(Class type) throws IllegalArgumentException;
/**
* Return a map of enumerations of a particular type. Each element in the
* map should be a key/value pair, where the key is the enum code, and the
* value is the <code>LabeledEnum</code> instance.
* @param type the enum type
* @return a Map of localized enumeration instances,
* with enum code as key and <code>LabeledEnum</code> instance as value
* @throws IllegalArgumentException if the type is not supported
*/
public Map getLabeledEnumMap(Class type) throws IllegalArgumentException;
/**
* Resolve a single <code>LabeledEnum</code> by its identifying code.
* @param type the enum type
* @param code the enum code
* @return the enum
* @throws IllegalArgumentException if the code did not map to a valid instance
*/
public LabeledEnum getLabeledEnumByCode(Class type, Comparable code) throws IllegalArgumentException;
/**
* Resolve a single <code>LabeledEnum</code> by its identifying code.
* @param type the enum type
* @param label the enum label
* @return the enum
* @throws IllegalArgumentException if the label did not map to a valid instance
*/
public LabeledEnum getLabeledEnumByLabel(Class type, String label) throws IllegalArgumentException;
}

View File

@ -0,0 +1,63 @@
/*
* 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.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
*/
public class LetterCodedLabeledEnum extends AbstractGenericLabeledEnum {
/**
* The unique code of this enum.
*/
private final Character code;
/**
* Create a new LetterCodedLabeledEnum instance.
* @param code the letter code
* @param label the label (can be <code>null</code>)
*/
public LetterCodedLabeledEnum(char code, String label) {
super(label);
Assert.isTrue(Character.isLetter(code),
"The code '" + code + "' is invalid: it must be a letter");
this.code = new Character(code);
}
public Comparable getCode() {
return code;
}
/**
* Return the letter code of this LabeledEnum instance.
*/
public char getLetterCode() {
return ((Character) getCode()).charValue();
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.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
*/
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();
}
}

View File

@ -0,0 +1,100 @@
/*
* 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.enums;
/**
* Base class for static type-safe labeled enum instances.
*
* Usage example:
*
* <pre>
* public class FlowSessionStatus extends StaticLabeledEnum {
*
* // public static final instances!
* public static FlowSessionStatus CREATED = new FlowSessionStatus(0, &quot;Created&quot;);
* public static FlowSessionStatus ACTIVE = new FlowSessionStatus(1, &quot;Active&quot;);
* public static FlowSessionStatus PAUSED = new FlowSessionStatus(2, &quot;Paused&quot;);
* public static FlowSessionStatus SUSPENDED = new FlowSessionStatus(3, &quot;Suspended&quot;);
* public static FlowSessionStatus ENDED = new FlowSessionStatus(4, &quot;Ended&quot;);
*
* // private constructor!
* private FlowSessionStatus(int code, String label) {
* super(code, label);
* }
*
* // custom behavior
* }</pre>
*
* @author Keith Donald
* @since 1.2.6
*/
public abstract class StaticLabeledEnum extends AbstractLabeledEnum {
/**
* The unique code of the enum.
*/
private final Short code;
/**
* A descriptive label for the enum.
*/
private final transient String label;
/**
* Create a new StaticLabeledEnum instance.
* @param code the short code
* @param label the label (can be <code>null</code>)
*/
protected StaticLabeledEnum(int code, String label) {
this.code = new Short((short) code);
if (label != null) {
this.label = label;
}
else {
this.label = this.code.toString();
}
}
public Comparable getCode() {
return code;
}
public String getLabel() {
return label;
}
/**
* Return the code of this LabeledEnum instance as a short.
*/
public short shortValue() {
return ((Number) getCode()).shortValue();
}
//---------------------------------------------------------------------
// Serialization support
//---------------------------------------------------------------------
/**
* Return the resolved type safe static enum instance.
*/
protected Object readResolve() {
return StaticLabeledEnumResolver.instance().getLabeledEnumByCode(getType(), getCode());
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.enums;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Set;
import java.util.TreeSet;
import org.springframework.util.Assert;
/**
* {@link LabeledEnumResolver} that resolves statically defined enumerations.
* Static implies all enum instances were defined within Java code,
* implementing the type-safe enum pattern.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 1.2.2
*/
public class StaticLabeledEnumResolver extends AbstractCachingLabeledEnumResolver {
/**
* Shared <code>StaticLabeledEnumResolver</code> singleton instance.
*/
private static final StaticLabeledEnumResolver INSTANCE = new StaticLabeledEnumResolver();
/**
* Return the shared <code>StaticLabeledEnumResolver</code> singleton instance.
* Mainly for resolving unique StaticLabeledEnum references on deserialization.
* @see StaticLabeledEnum
*/
public static StaticLabeledEnumResolver instance() {
return INSTANCE;
}
protected Set findLabeledEnums(Class type) {
Set typeEnums = new TreeSet();
Field[] fields = type.getFields();
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
if (Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) {
if (type.isAssignableFrom(field.getType())) {
try {
Object value = field.get(null);
Assert.isTrue(value instanceof LabeledEnum, "Field value must be a LabeledEnum instance");
typeEnums.add(value);
}
catch (IllegalAccessException ex) {
logger.warn("Unable to access field value: " + field, ex);
}
}
}
}
return typeEnums;
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.enums;
import org.springframework.util.Assert;
/**
* Implementation of LabeledEnum which uses a String 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) in this case.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 1.2.2
* @see org.springframework.core.enums.LabeledEnumResolver#getLabeledEnumSet(Class)
*/
public class StringCodedLabeledEnum extends AbstractGenericLabeledEnum {
/**
* The unique code of this enum.
*/
private final String code;
/**
* Create a new StringCodedLabeledEnum instance.
* @param code the String code
* @param label the label (can be <code>null</code>)
*/
public StringCodedLabeledEnum(String code, String label) {
super(label);
Assert.notNull(code, "'code' must not be null");
this.code = code;
}
public Comparable getCode() {
return this.code;
}
/**
* Return the String code of this LabeledEnum instance.
*/
public String getStringCode() {
return (String) getCode();
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Interfaces and classes for type-safe enum support on JDK >= 1.3.
This enum abstraction support codes and labels.
</body>
</html>

View File

@ -0,0 +1,177 @@
/*
* 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.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.springframework.core.NestedIOException;
import org.springframework.util.ResourceUtils;
/**
* Convenience base class for {@link Resource} implementations,
* pre-implementing typical behavior.
*
* <p>The "exists" method will check whether a File or InputStream can
* be opened; "isOpen" will always return false; "getURL" and "getFile"
* throw an exception; and "toString" will return the description.
*
* @author Juergen Hoeller
* @since 28.12.2003
*/
public abstract class AbstractResource implements Resource {
/**
* This implementation checks whether a File can be opened,
* falling back to whether an InputStream can be opened.
* This will cover both directories and content resources.
*/
public boolean exists() {
// Try file existence: can we find the file in the file system?
try {
return getFile().exists();
}
catch (IOException ex) {
// Fall back to stream existence: can we open the stream?
try {
InputStream is = getInputStream();
is.close();
return true;
}
catch (Throwable isEx) {
return false;
}
}
}
/**
* This implementation always returns <code>true</code>.
*/
public boolean isReadable() {
return true;
}
/**
* This implementation always returns <code>false</code>.
*/
public boolean isOpen() {
return false;
}
/**
* This implementation throws a FileNotFoundException, assuming
* that the resource cannot be resolved to a URL.
*/
public URL getURL() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to URL");
}
/**
* This implementation builds a URI based on the URL returned
* by {@link #getURL()}.
*/
public URI getURI() throws IOException {
URL url = getURL();
try {
return ResourceUtils.toURI(url);
}
catch (URISyntaxException ex) {
throw new NestedIOException("Invalid URI [" + url + "]", ex);
}
}
/**
* This implementation throws a FileNotFoundException, assuming
* that the resource cannot be resolved to an absolute file path.
*/
public File getFile() throws IOException {
throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
}
/**
* This implementation checks the timestamp of the underlying File,
* if available.
* @see #getFileForLastModifiedCheck()
*/
public long lastModified() throws IOException {
long lastModified = getFileForLastModifiedCheck().lastModified();
if (lastModified == 0L) {
throw new FileNotFoundException(getDescription() +
" cannot be resolved in the file system for resolving its last-modified timestamp");
}
return lastModified;
}
/**
* Determine the File to use for timestamp checking.
* <p>The default implementation delegates to {@link #getFile()}.
* @return the File to use for timestamp checking (never <code>null</code>)
* @throws IOException if the resource cannot be resolved as absolute
* file path, i.e. if the resource is not available in a file system
*/
protected File getFileForLastModifiedCheck() throws IOException {
return getFile();
}
/**
* This implementation throws a FileNotFoundException, assuming
* that relative resources cannot be created for this resource.
*/
public Resource createRelative(String relativePath) throws IOException {
throw new FileNotFoundException("Cannot create a relative resource for " + getDescription());
}
/**
* This implementation always throws IllegalStateException,
* assuming that the resource does not carry a filename.
*/
public String getFilename() throws IllegalStateException {
throw new IllegalStateException(getDescription() + " does not carry a filename");
}
/**
* This implementation returns the description of this resource.
* @see #getDescription()
*/
public String toString() {
return getDescription();
}
/**
* This implementation compares description strings.
* @see #getDescription()
*/
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof Resource && ((Resource) obj).getDescription().equals(getDescription())));
}
/**
* This implementation returns the description's hash code.
* @see #getDescription()
*/
public int hashCode() {
return getDescription().hashCode();
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.io;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
/**
* {@link Resource} implementation for a given byte array.
* Creates a ByteArrayInputStreams for the given byte array.
*
* <p>Useful for loading content from any given byte array,
* without having to resort to a single-use {@link InputStreamResource}.
* Particularly useful for creating mail attachments from local content,
* where JavaMail needs to be able to read the stream multiple times.
*
* @author Juergen Hoeller
* @since 1.2.3
* @see java.io.ByteArrayInputStream
* @see InputStreamResource
* @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource)
*/
public class ByteArrayResource extends AbstractResource {
private final byte[] byteArray;
private final String description;
/**
* Create a new ByteArrayResource.
* @param byteArray the byte array to wrap
*/
public ByteArrayResource(byte[] byteArray) {
this(byteArray, "resource loaded from byte array");
}
/**
* Create a new ByteArrayResource.
* @param byteArray the byte array to wrap
* @param description where the byte array comes from
*/
public ByteArrayResource(byte[] byteArray, String description) {
if (byteArray == null) {
throw new IllegalArgumentException("Byte array must not be null");
}
this.byteArray = byteArray;
this.description = (description != null ? description : "");
}
/**
* Return the underlying byte array.
*/
public final byte[] getByteArray() {
return this.byteArray;
}
/**
* This implementation always returns <code>true</code>.
*/
public boolean exists() {
return true;
}
/**
* This implementation returns a ByteArrayInputStream for the
* underlying byte array.
* @see java.io.ByteArrayInputStream
*/
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(this.byteArray);
}
/**
* This implementation returns the passed-in description, if any.
*/
public String getDescription() {
return this.description;
}
/**
* This implementation compares the underlying byte array.
* @see java.util.Arrays#equals(byte[], byte[])
*/
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof ByteArrayResource && Arrays.equals(((ByteArrayResource) obj).byteArray, this.byteArray)));
}
/**
* This implementation returns the hash code based on the
* underlying byte array.
*/
public int hashCode() {
return (byte[].class.hashCode() * 29 * this.byteArray.length);
}
}

View File

@ -0,0 +1,244 @@
/*
* 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.io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
/**
* {@link Resource} implementation for class path resources.
* Uses either a given ClassLoader or a given Class for loading resources.
*
* <p>Supports resolution as <code>java.io.File</code> if the class path
* resource resides in the file system, but not for resources in a JAR.
* Always supports resolution as URL.
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see java.lang.ClassLoader#getResourceAsStream(String)
* @see java.lang.Class#getResourceAsStream(String)
*/
public class ClassPathResource extends AbstractResource {
private final String path;
private ClassLoader classLoader;
private Class clazz;
/**
* Create a new ClassPathResource for ClassLoader usage.
* A leading slash will be removed, as the ClassLoader
* resource access methods will not accept it.
* <p>The thread context class loader will be used for
* loading the resource.
* @param path the absolute path within the class path
* @see java.lang.ClassLoader#getResourceAsStream(String)
* @see org.springframework.util.ClassUtils#getDefaultClassLoader()
*/
public ClassPathResource(String path) {
this(path, (ClassLoader) null);
}
/**
* Create a new ClassPathResource for ClassLoader usage.
* A leading slash will be removed, as the ClassLoader
* resource access methods will not accept it.
* @param path the absolute path within the classpath
* @param classLoader the class loader to load the resource with,
* or <code>null</code> for the thread context class loader
* @see java.lang.ClassLoader#getResourceAsStream(String)
*/
public ClassPathResource(String path, ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
this.path = pathToUse;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
/**
* Create a new ClassPathResource for Class usage.
* The path can be relative to the given class,
* or absolute within the classpath via a leading slash.
* @param path relative or absolute path within the class path
* @param clazz the class to load resources with
* @see java.lang.Class#getResourceAsStream
*/
public ClassPathResource(String path, Class clazz) {
Assert.notNull(path, "Path must not be null");
this.path = StringUtils.cleanPath(path);
this.clazz = clazz;
}
/**
* Create a new ClassPathResource with optional ClassLoader and Class.
* Only for internal usage.
* @param path relative or absolute path within the classpath
* @param classLoader the class loader to load the resource with, if any
* @param clazz the class to load resources with, if any
*/
protected ClassPathResource(String path, ClassLoader classLoader, Class clazz) {
this.path = StringUtils.cleanPath(path);
this.classLoader = classLoader;
this.clazz = clazz;
}
/**
* Return the path for this resource (as resource path within the class path).
*/
public final String getPath() {
return this.path;
}
/**
* Return the ClassLoader that this resource will be obtained from.
*/
public final ClassLoader getClassLoader() {
return (this.classLoader != null ? this.classLoader : this.clazz.getClassLoader());
}
/**
* This implementation opens an InputStream for the given class path resource.
* @see java.lang.ClassLoader#getResourceAsStream(String)
* @see java.lang.Class#getResourceAsStream(String)
*/
public InputStream getInputStream() throws IOException {
InputStream is = null;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else {
is = this.classLoader.getResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(
getDescription() + " cannot be opened because it does not exist");
}
return is;
}
/**
* This implementation returns a URL for the underlying class path resource.
* @see java.lang.ClassLoader#getResource(String)
* @see java.lang.Class#getResource(String)
*/
public URL getURL() throws IOException {
URL url = null;
if (this.clazz != null) {
url = this.clazz.getResource(this.path);
}
else {
url = this.classLoader.getResource(this.path);
}
if (url == null) {
throw new FileNotFoundException(
getDescription() + " cannot be resolved to URL because it does not exist");
}
return url;
}
/**
* This implementation returns a File reference for the underlying class path
* resource, provided that it refers to a file in the file system.
* @see org.springframework.util.ResourceUtils#getFile(java.net.URL, String)
*/
public File getFile() throws IOException {
return ResourceUtils.getFile(getURL(), getDescription());
}
/**
* This implementation determines the underlying File
* (or jar file, in case of a resource in a jar/zip).
*/
protected File getFileForLastModifiedCheck() throws IOException {
URL url = getURL();
if (ResourceUtils.isJarURL(url)) {
URL actualUrl = ResourceUtils.extractJarFileURL(url);
return ResourceUtils.getFile(actualUrl);
}
else {
return ResourceUtils.getFile(url, getDescription());
}
}
/**
* This implementation creates a ClassPathResource, applying the given path
* relative to the path of the underlying resource of this descriptor.
* @see org.springframework.util.StringUtils#applyRelativePath(String, String)
*/
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
return new ClassPathResource(pathToUse, this.classLoader, this.clazz);
}
/**
* This implementation returns the name of the file that this class path
* resource refers to.
* @see org.springframework.util.StringUtils#getFilename(String)
*/
public String getFilename() {
return StringUtils.getFilename(this.path);
}
/**
* This implementation returns a description that includes the class path location.
*/
public String getDescription() {
return "class path resource [" + this.path + "]";
}
/**
* This implementation compares the underlying class path locations.
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof ClassPathResource) {
ClassPathResource otherRes = (ClassPathResource) obj;
return (this.path.equals(otherRes.path) &&
ObjectUtils.nullSafeEquals(this.classLoader, otherRes.classLoader) &&
ObjectUtils.nullSafeEquals(this.clazz, otherRes.clazz));
}
return false;
}
/**
* This implementation returns the hash code of the underlying
* class path location.
*/
public int hashCode() {
return this.path.hashCode();
}
}

View File

@ -0,0 +1,40 @@
/*
* 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.io;
/**
* Extended interface for a resource that is loaded from an enclosing
* 'context', e.g. from a {@link javax.servlet.ServletContext} or a
* {@link javax.portlet.PortletContext} but also from plain classpath paths
* or relative file system paths (specified without an explicit prefix,
* hence applying relative to the local {@link ResourceLoader}'s context).
*
* @author Juergen Hoeller
* @since 2.5
* @see org.springframework.web.context.support.ServletContextResource
* @see org.springframework.web.portlet.context.PortletContextResource
*/
public interface ContextResource extends Resource {
/**
* Return the path within the enclosing 'context'.
* <p>This is typically path relative to a context-specific root directory,
* e.g. a ServletContext root or a PortletContext root.
*/
String getPathWithinContext();
}

View File

@ -0,0 +1,143 @@
/*
* 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.io;
import java.net.MalformedURLException;
import java.net.URL;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Default implementation of the {@link ResourceLoader} interface.
* Used by {@link ResourceEditor}, and serves as base class for
* {@link org.springframework.context.support.AbstractApplicationContext}.
* Can also be used standalone.
*
* <p>Will return a {@link UrlResource} if the location value is a URL,
* and a {@link ClassPathResource} if it is a non-URL path or a
* "classpath:" pseudo-URL.
*
* @author Juergen Hoeller
* @since 10.03.2004
* @see FileSystemResourceLoader
* @see org.springframework.context.support.ClassPathXmlApplicationContext
*/
public class DefaultResourceLoader implements ResourceLoader {
private ClassLoader classLoader;
/**
* Create a new DefaultResourceLoader.
* <p>ClassLoader access will happen using the thread context class loader
* at the time of this ResourceLoader's initialization.
* @see java.lang.Thread#getContextClassLoader()
*/
public DefaultResourceLoader() {
this.classLoader = ClassUtils.getDefaultClassLoader();
}
/**
* Create a new DefaultResourceLoader.
* @param classLoader the ClassLoader to load class path resources with, or <code>null</code>
* for using the thread context class loader at the time of actual resource access
*/
public DefaultResourceLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* Specify the ClassLoader to load class path resources with, or <code>null</code>
* for using the thread context class loader at the time of actual resource access.
* <p>The default is that ClassLoader access will happen using the thread context
* class loader at the time of this ResourceLoader's initialization.
*/
public void setClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
/**
* Return the ClassLoader to load class path resources with,
* or <code>null</code> if using the thread context class loader on actual access
* (applying to the thread that constructs the ClassPathResource object).
* <p>Will get passed to ClassPathResource's constructor for all
* ClassPathResource objects created by this resource loader.
* @see ClassPathResource
*/
public ClassLoader getClassLoader() {
return this.classLoader;
}
public Resource getResource(String location) {
Assert.notNull(location, "Location must not be null");
if (location.startsWith(CLASSPATH_URL_PREFIX)) {
return new ClassPathResource(location.substring(CLASSPATH_URL_PREFIX.length()), getClassLoader());
}
else {
try {
// Try to parse the location as a URL...
URL url = new URL(location);
return new UrlResource(url);
}
catch (MalformedURLException ex) {
// No URL -> resolve as resource path.
return getResourceByPath(location);
}
}
}
/**
* Return a Resource handle for the resource at the given path.
* <p>The default implementation supports class path locations. This should
* be appropriate for standalone implementations but can be overridden,
* e.g. for implementations targeted at a Servlet container.
* @param path the path to the resource
* @return the corresponding Resource handle
* @see ClassPathResource
* @see org.springframework.context.support.FileSystemXmlApplicationContext#getResourceByPath
* @see org.springframework.web.context.support.XmlWebApplicationContext#getResourceByPath
*/
protected Resource getResourceByPath(String path) {
return new ClassPathContextResource(path, getClassLoader());
}
/**
* ClassPathResource that explicitly expresses a context-relative path
* through implementing the ContextResource interface.
*/
private static class ClassPathContextResource extends ClassPathResource implements ContextResource {
public ClassPathContextResource(String path, ClassLoader classLoader) {
super(path, classLoader);
}
public String getPathWithinContext() {
return getPath();
}
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(getPath(), relativePath);
return new ClassPathContextResource(pathToUse, getClassLoader());
}
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.io;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
* Simple {@link Resource} implementation that holds a resource description
* but does not point to an actually readable resource.
*
* <p>To be used as placeholder if a <code>Resource</code> argument is
* expected by an API but not necessarily used for actual reading.
*
* @author Juergen Hoeller
* @since 1.2.6
*/
public class DescriptiveResource extends AbstractResource {
private final String description;
/**
* Create a new DescriptiveResource.
* @param description the resource description
*/
public DescriptiveResource(String description) {
this.description = (description != null ? description : "");
}
public boolean exists() {
return false;
}
public boolean isReadable() {
return false;
}
public InputStream getInputStream() throws IOException {
throw new FileNotFoundException(
getDescription() + " cannot be opened because it does not point to a readable resource");
}
public String getDescription() {
return this.description;
}
/**
* This implementation compares the underlying description String.
*/
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof DescriptiveResource && ((DescriptiveResource) obj).description.equals(this.description)));
}
/**
* This implementation returns the hash code of the underlying description String.
*/
public int hashCode() {
return this.description.hashCode();
}
}

View File

@ -0,0 +1,177 @@
/*
* 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.io;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* {@link Resource} implementation for <code>java.io.File</code> handles.
* Obviously supports resolution as File, and also as URL.
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see java.io.File
*/
public class FileSystemResource extends AbstractResource {
private final File file;
private final String path;
/**
* Create a new FileSystemResource from a File handle.
* <p>Note: When building relative resources via {@link #createRelative},
* the relative path will apply <i>at the same directory level</i>:
* e.g. new File("C:/dir1"), relative path "dir2" -> "C:/dir2"!
* If you prefer to have relative paths built underneath the given root
* directory, use the {@link #FileSystemResource(String) constructor with a file path}
* to append a trailing slash to the root path: "C:/dir1/", which
* indicates this directory as root for all relative paths.
* @param file a File handle
*/
public FileSystemResource(File file) {
Assert.notNull(file, "File must not be null");
this.file = file;
this.path = StringUtils.cleanPath(file.getPath());
}
/**
* Create a new FileSystemResource from a file path.
* <p>Note: When building relative resources via {@link #createRelative},
* it makes a difference whether the specified resource base path here
* ends with a slash or not. In the case of "C:/dir1/", relative paths
* will be built underneath that root: e.g. relative path "dir2" ->
* "C:/dir1/dir2". In the case of "C:/dir1", relative paths will apply
* at the same directory level: relative path "dir2" -> "C:/dir2".
* @param path a file path
*/
public FileSystemResource(String path) {
Assert.notNull(path, "Path must not be null");
this.file = new File(path);
this.path = StringUtils.cleanPath(path);
}
/**
* Return the file path for this resource.
*/
public final String getPath() {
return this.path;
}
/**
* This implementation returns whether the underlying file exists.
* @see java.io.File#exists()
*/
public boolean exists() {
return this.file.exists();
}
/**
* This implementation checks whether the underlying file is marked as readable
* (and corresponds to an actual file with content, not to a directory).
* @see java.io.File#canRead()
* @see java.io.File#isDirectory()
*/
public boolean isReadable() {
return (this.file.canRead() && !this.file.isDirectory());
}
/**
* This implementation opens a FileInputStream for the underlying file.
* @see java.io.FileInputStream
*/
public InputStream getInputStream() throws IOException {
return new FileInputStream(this.file);
}
/**
* This implementation returns a URL for the underlying file.
* @see java.io.File#toURI()
*/
public URL getURL() throws IOException {
return this.file.toURI().toURL();
}
/**
* This implementation returns a URI for the underlying file.
* @see java.io.File#toURI()
*/
public URI getURI() throws IOException {
return this.file.toURI();
}
/**
* This implementation returns the underlying File reference.
*/
public File getFile() {
return this.file;
}
/**
* This implementation creates a FileSystemResource, applying the given path
* relative to the path of the underlying file of this resource descriptor.
* @see org.springframework.util.StringUtils#applyRelativePath(String, String)
*/
public Resource createRelative(String relativePath) {
String pathToUse = StringUtils.applyRelativePath(this.path, relativePath);
return new FileSystemResource(pathToUse);
}
/**
* This implementation returns the name of the file.
* @see java.io.File#getName()
*/
public String getFilename() {
return this.file.getName();
}
/**
* This implementation returns a description that includes the absolute
* path of the file.
* @see java.io.File#getAbsolutePath()
*/
public String getDescription() {
return "file [" + this.file.getAbsolutePath() + "]";
}
/**
* This implementation compares the underlying File references.
*/
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof FileSystemResource && this.path.equals(((FileSystemResource) obj).path)));
}
/**
* This implementation returns the hash code of the underlying File reference.
*/
public int hashCode() {
return this.path.hashCode();
}
}

View File

@ -0,0 +1,72 @@
/*
* 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.io;
/**
* {@link ResourceLoader} implementation that resolves plain paths as
* file system resources rather than as class path resources
* (the latter is {@link DefaultResourceLoader}'s default strategy).
*
* <p><b>NOTE:</b> Plain paths will always be interpreted as relative
* to the current VM working directory, even if they start with a slash.
* (This is consistent with the semantics in a Servlet container.)
* <b>Use an explicit "file:" prefix to enforce an absolute file path.</b>
*
* <p>{@link org.springframework.context.support.FileSystemXmlApplicationContext}
* is a full-fledged ApplicationContext implementation that provides
* the same resource path resolution strategy.
*
* @author Juergen Hoeller
* @since 1.1.3
* @see DefaultResourceLoader
* @see org.springframework.context.support.FileSystemXmlApplicationContext
*/
public class FileSystemResourceLoader extends DefaultResourceLoader {
/**
* Resolve resource paths as file system paths.
* <p>Note: Even if a given path starts with a slash, it will get
* interpreted as relative to the current VM working directory.
* @param path the path to the resource
* @return the corresponding Resource handle
* @see FileSystemResource
* @see org.springframework.web.context.support.ServletContextResourceLoader#getResourceByPath
*/
protected Resource getResourceByPath(String path) {
if (path != null && path.startsWith("/")) {
path = path.substring(1);
}
return new FileSystemContextResource(path);
}
/**
* FileSystemResource that explicitly expresses a context-relative path
* through implementing the ContextResource interface.
*/
private static class FileSystemContextResource extends FileSystemResource implements ContextResource {
public FileSystemContextResource(String path) {
super(path);
}
public String getPathWithinContext() {
return getPath();
}
}
}

View File

@ -0,0 +1,121 @@
/*
* 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.io;
import java.io.IOException;
import java.io.InputStream;
/**
* {@link Resource} implementation for a given InputStream. Should only
* be used if no specific Resource implementation is applicable.
* In particular, prefer {@link ByteArrayResource} or any of the
* file-based Resource implementations where possible.
*
* <p>In contrast to other Resource implementations, this is a descriptor
* for an <i>already opened</i> resource - therefore returning "true" from
* <code>isOpen()</code>. Do not use it if you need to keep the resource
* descriptor somewhere, or if you need to read a stream multiple times.
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see ByteArrayResource
* @see ClassPathResource
* @see FileSystemResource
* @see UrlResource
*/
public class InputStreamResource extends AbstractResource {
private final InputStream inputStream;
private final String description;
private boolean read = false;
/**
* Create a new InputStreamResource.
* @param inputStream the InputStream to use
*/
public InputStreamResource(InputStream inputStream) {
this(inputStream, "resource loaded through InputStream");
}
/**
* Create a new InputStreamResource.
* @param inputStream the InputStream to use
* @param description where the InputStream comes from
*/
public InputStreamResource(InputStream inputStream, String description) {
if (inputStream == null) {
throw new IllegalArgumentException("InputStream must not be null");
}
this.inputStream = inputStream;
this.description = (description != null ? description : "");
}
/**
* This implementation always returns <code>true</code>.
*/
public boolean exists() {
return true;
}
/**
* This implementation always returns <code>true</code>.
*/
public boolean isOpen() {
return true;
}
/**
* This implementation throws IllegalStateException if attempting to
* read the underlying stream multiple times.
*/
public InputStream getInputStream() throws IOException, IllegalStateException {
if (this.read) {
throw new IllegalStateException("InputStream has already been read - " +
"do not use InputStreamResource if a stream needs to be read multiple times");
}
this.read = true;
return this.inputStream;
}
/**
* This implementation returns the passed-in description, if any.
*/
public String getDescription() {
return this.description;
}
/**
* This implementation compares the underlying InputStream.
*/
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof InputStreamResource && ((InputStreamResource) obj).inputStream.equals(this.inputStream)));
}
/**
* This implementation returns the hash code of the underlying InputStream.
*/
public int hashCode() {
return this.inputStream.hashCode();
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.io;
import java.io.IOException;
import java.io.InputStream;
/**
* Simple interface for objects that are sources for an {@link InputStream}.
*
* <p>This is the base interface for Spring's more extensive {@link Resource} interface.
*
* <p>For single-use streams, {@link InputStreamResource} can be used for any
* given <code>InputStream</code>. Spring's {@link ByteArrayResource} or any
* file-based <code>Resource</code> implementation can be used as a concrete
* instance, allowing one to read the underlying content stream multiple times.
* This makes this interface useful as an abstract content source for mail
* attachments, for example.
*
* @author Juergen Hoeller
* @since 20.01.2004
* @see java.io.InputStream
* @see Resource
* @see InputStreamResource
* @see ByteArrayResource
*/
public interface InputStreamSource {
/**
* Return an {@link InputStream}.
* <p>It is expected that each call creates a <i>fresh</i> stream.
* <p>This requirement is particularly important when you consider an API such
* as JavaMail, which needs to be able to read the stream multiple times when
* creating mail attachments. For such a use case, it is <i>required</i>
* that each <code>getInputStream()</code> call returns a fresh stream.
* @throws IOException if the stream could not be opened
* @see org.springframework.mail.javamail.MimeMessageHelper#addAttachment(String, InputStreamSource)
*/
InputStream getInputStream() throws IOException;
}

View File

@ -0,0 +1,124 @@
/*
* 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.io;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
/**
* Interface for a resource descriptor that abstracts from the actual
* type of underlying resource, such as a file or class path resource.
*
* <p>An InputStream can be opened for every resource if it exists in
* physical form, but a URL or File handle can just be returned for
* certain resources. The actual behavior is implementation-specific.
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see #getInputStream()
* @see #getURL()
* @see #getURI()
* @see #getFile()
* @see FileSystemResource
* @see ClassPathResource
* @see UrlResource
* @see ByteArrayResource
* @see InputStreamResource
* @see org.springframework.web.context.support.ServletContextResource
*/
public interface Resource extends InputStreamSource {
/**
* Return whether this resource actually exists in physical form.
* <p>This method performs a definitive existence check, whereas the
* existence of a <code>Resource</code> handle only guarantees a
* valid descriptor handle.
*/
boolean exists();
/**
* Return whether the contents of this resource can be read,
* e.g. via {@link #getInputStream()} or {@link #getFile()}.
* <p>Will be <code>true</code> for typical resource descriptors;
* note that actual content reading may still fail when attempted.
* However, a value of <code>false</code> is a definitive indication
* that the resource content cannot be read.
*/
boolean isReadable();
/**
* Return whether this resource represents a handle with an open
* stream. If true, the InputStream cannot be read multiple times,
* and must be read and closed to avoid resource leaks.
* <p>Will be <code>false</code> for typical resource descriptors.
*/
boolean isOpen();
/**
* Return a URL handle for this resource.
* @throws IOException if the resource cannot be resolved as URL,
* i.e. if the resource is not available as descriptor
*/
URL getURL() throws IOException;
/**
* Return a URI handle for this resource.
* @throws IOException if the resource cannot be resolved as URI,
* i.e. if the resource is not available as descriptor
*/
URI getURI() throws IOException;
/**
* Return a File handle for this resource.
* @throws IOException if the resource cannot be resolved as absolute
* file path, i.e. if the resource is not available in a file system
*/
File getFile() throws IOException;
/**
* Determine the last-modified timestamp for this resource.
* @throws IOException if the resource cannot be resolved
* (in the file system or as some other known physical resource type)
*/
long lastModified() throws IOException;
/**
* Create a resource relative to this resource.
* @param relativePath the relative path (relative to this resource)
* @return the resource handle for the relative resource
* @throws IOException if the relative resource cannot be determined
*/
Resource createRelative(String relativePath) throws IOException;
/**
* Return a filename for this resource, i.e. typically the last
* part of the path: for example, "myfile.txt".
*/
String getFilename();
/**
* Return a description for this resource,
* to be used for error output when working with the resource.
* <p>Implementations are also encouraged to return this value
* from their <code>toString</code> method.
* @see java.lang.Object#toString()
*/
String getDescription();
}

View File

@ -0,0 +1,106 @@
/*
* 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.io;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
import org.springframework.util.SystemPropertyUtils;
/**
* {@link java.beans.PropertyEditor Editor} for {@link Resource}
* descriptors, to automatically convert <code>String</code> locations
* e.g. <code>"file:C:/myfile.txt"</code> or
* <code>"classpath:myfile.txt"</code>) to <code>Resource</code>
* properties instead of using a <code>String</code> location property.
*
* <p>The path may contain <code>${...}</code> placeholders, to be resolved
* as system properties: e.g. <code>${user.dir}</code>.
*
* <p>Delegates to a {@link ResourceLoader} to do the heavy lifting,
* by default using a {@link DefaultResourceLoader}.
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see Resource
* @see ResourceLoader
* @see DefaultResourceLoader
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders
* @see System#getProperty(String)
*/
public class ResourceEditor extends PropertyEditorSupport {
private final ResourceLoader resourceLoader;
/**
* Create a new instance of the {@link ResourceEditor} class
* using a {@link DefaultResourceLoader}.
*/
public ResourceEditor() {
this(new DefaultResourceLoader());
}
/**
* Create a new instance of the {@link ResourceEditor} class
* using the given {@link ResourceLoader}.
* @param resourceLoader the <code>ResourceLoader</code> to use
*/
public ResourceEditor(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
}
public void setAsText(String text) {
if (StringUtils.hasText(text)) {
String locationToUse = resolvePath(text).trim();
setValue(this.resourceLoader.getResource(locationToUse));
}
else {
setValue(null);
}
}
/**
* Resolve the given path, replacing placeholders with
* corresponding system property values if necessary.
* @param path the original file path
* @return the resolved file path
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders
*/
protected String resolvePath(String path) {
return SystemPropertyUtils.resolvePlaceholders(path);
}
public String getAsText() {
Resource value = (Resource) getValue();
try {
// Try to determine URL for resource.
return (value != null ? value.getURL().toExternalForm() : "");
}
catch (IOException ex) {
// Couldn't determine resource URL - return null to indicate
// that there is no appropriate text representation.
return null;
}
}
}

View File

@ -0,0 +1,77 @@
/*
* 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.io;
import org.springframework.util.ResourceUtils;
/**
* Strategy interface for loading resources (e.. class path or file system
* resources). An {@link org.springframework.context.ApplicationContext}
* is required to provide this functionality, plus extended
* {@link org.springframework.core.io.support.ResourcePatternResolver} support.
*
* <p>{@link DefaultResourceLoader} is a standalone implementation that is
* usable outside an ApplicationContext, also used by {@link ResourceEditor}.
*
* <p>Bean properties of type Resource and Resource array can be populated
* from Strings when running in an ApplicationContext, using the particular
* context's resource loading strategy.
*
* @author Juergen Hoeller
* @since 10.03.2004
* @see Resource
* @see org.springframework.core.io.support.ResourcePatternResolver
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.ResourceLoaderAware
*/
public interface ResourceLoader {
/** Pseudo URL prefix for loading from the class path: "classpath:" */
String CLASSPATH_URL_PREFIX = ResourceUtils.CLASSPATH_URL_PREFIX;
/**
* Return a Resource handle for the specified resource.
* The handle should always be a reusable resource descriptor,
* allowing for multiple {@link Resource#getInputStream()} calls.
* <p><ul>
* <li>Must support fully qualified URLs, e.g. "file:C:/test.dat".
* <li>Must support classpath pseudo-URLs, e.g. "classpath:test.dat".
* <li>Should support relative file paths, e.g. "WEB-INF/test.dat".
* (This will be implementation-specific, typically provided by an
* ApplicationContext implementation.)
* </ul>
* <p>Note that a Resource handle does not imply an existing resource;
* you need to invoke {@link Resource#exists} to check for existence.
* @param location the resource location
* @return a corresponding Resource handle
* @see #CLASSPATH_URL_PREFIX
* @see org.springframework.core.io.Resource#exists
* @see org.springframework.core.io.Resource#getInputStream
*/
Resource getResource(String location);
/**
* Expose the ClassLoader used by this ResourceLoader.
* <p>Clients which need to access the ClassLoader directly can do so
* in a uniform manner with the ResourceLoader, rather than relying
* on the thread context ClassLoader.
* @return the ClassLoader (never <code>null</code>)
*/
ClassLoader getClassLoader();
}

View File

@ -0,0 +1,218 @@
/*
* 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.io;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
/**
* {@link Resource} implementation for <code>java.net.URL</code> locators.
* Obviously supports resolution as URL, and also as File in case of
* the "file:" protocol.
*
* @author Juergen Hoeller
* @since 28.12.2003
* @see java.net.URL
*/
public class UrlResource extends AbstractResource {
/**
* Original URL, used for actual access.
*/
private final URL url;
/**
* Cleaned URL (with normalized path), used for comparisons.
*/
private final URL cleanedUrl;
/**
* Original URI, if available; used for URI and File access.
*/
private final URI uri;
/**
* Create a new UrlResource.
* @param url a URL
*/
public UrlResource(URL url) {
Assert.notNull(url, "URL must not be null");
this.url = url;
this.cleanedUrl = getCleanedUrl(this.url, url.toString());
this.uri = null;
}
/**
* Create a new UrlResource.
* @param uri a URI
* @throws MalformedURLException if the given URL path is not valid
*/
public UrlResource(URI uri) throws MalformedURLException {
Assert.notNull(uri, "URI must not be null");
this.url = uri.toURL();
this.cleanedUrl = getCleanedUrl(this.url, uri.toString());
this.uri = uri;
}
/**
* Create a new UrlResource.
* @param path a URL path
* @throws MalformedURLException if the given URL path is not valid
*/
public UrlResource(String path) throws MalformedURLException {
Assert.notNull(path, "Path must not be null");
this.url = new URL(path);
this.cleanedUrl = getCleanedUrl(this.url, path);
this.uri = null;
}
/**
* Determine a cleaned URL for the given original URL.
* @param originalUrl the original URL
* @param originalPath the original URL path
* @return the cleaned URL
* @see org.springframework.util.StringUtils#cleanPath
*/
private URL getCleanedUrl(URL originalUrl, String originalPath) {
try {
return new URL(StringUtils.cleanPath(originalPath));
}
catch (MalformedURLException ex) {
// Cleaned URL path cannot be converted to URL
// -> take original URL.
return originalUrl;
}
}
/**
* This implementation opens an InputStream for the given URL.
* It sets the "UseCaches" flag to <code>false</code>,
* mainly to avoid jar file locking on Windows.
* @see java.net.URL#openConnection()
* @see java.net.URLConnection#setUseCaches(boolean)
* @see java.net.URLConnection#getInputStream()
*/
public InputStream getInputStream() throws IOException {
URLConnection con = this.url.openConnection();
con.setUseCaches(false);
return con.getInputStream();
}
/**
* This implementation returns the underlying URL reference.
*/
public URL getURL() throws IOException {
return this.url;
}
/**
* This implementation returns the underlying URI directly,
* if possible.
*/
public URI getURI() throws IOException {
if (this.uri != null) {
return this.uri;
}
else {
return super.getURI();
}
}
/**
* This implementation returns a File reference for the underlying URL/URI,
* provided that it refers to a file in the file system.
* @see org.springframework.util.ResourceUtils#getFile(java.net.URL, String)
*/
public File getFile() throws IOException {
if (this.uri != null) {
return ResourceUtils.getFile(this.uri, getDescription());
}
else {
return ResourceUtils.getFile(this.url, getDescription());
}
}
/**
* This implementation determines the underlying File
* (or jar file, in case of a resource in a jar/zip).
*/
protected File getFileForLastModifiedCheck() throws IOException {
if (ResourceUtils.isJarURL(this.url)) {
URL actualUrl = ResourceUtils.extractJarFileURL(this.url);
return ResourceUtils.getFile(actualUrl);
}
else {
return getFile();
}
}
/**
* This implementation creates a UrlResource, applying the given path
* relative to the path of the underlying URL of this resource descriptor.
* @see java.net.URL#URL(java.net.URL, String)
*/
public Resource createRelative(String relativePath) throws MalformedURLException {
if (relativePath.startsWith("/")) {
relativePath = relativePath.substring(1);
}
return new UrlResource(new URL(this.url, relativePath));
}
/**
* This implementation returns the name of the file that this URL refers to.
* @see java.net.URL#getFile()
* @see java.io.File#getName()
*/
public String getFilename() {
return new File(this.url.getFile()).getName();
}
/**
* This implementation returns a description that includes the URL.
*/
public String getDescription() {
return "URL [" + this.url + "]";
}
/**
* This implementation compares the underlying URL references.
*/
public boolean equals(Object obj) {
return (obj == this ||
(obj instanceof UrlResource && this.cleanedUrl.equals(((UrlResource) obj).cleanedUrl)));
}
/**
* This implementation returns the hash code of the underlying URL reference.
*/
public int hashCode() {
return this.cleanedUrl.hashCode();
}
}

View File

@ -0,0 +1,7 @@
<html>
<body>
Generic abstraction for (file-based) resources, used throughout the framework.
</body>
</html>

View File

@ -0,0 +1,117 @@
/*
* 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.io.support;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Holder that combines a {@link org.springframework.core.io.Resource}
* with a specific encoding to be used for reading from the resource.
*
* <p>Used as argument for operations that support to read content with
* a specific encoding (usually through a <code>java.io.Reader</code>.
*
* @author Juergen Hoeller
* @since 1.2.6
* @see java.io.Reader
*/
public class EncodedResource {
private final Resource resource;
private final String encoding;
/**
* Create a new EncodedResource for the given Resource,
* not specifying a specific encoding.
* @param resource the Resource to hold
*/
public EncodedResource(Resource resource) {
this(resource, null);
}
/**
* Create a new EncodedResource for the given Resource,
* using the specified encoding.
* @param resource the Resource to hold
* @param encoding the encoding to use for reading from the resource
*/
public EncodedResource(Resource resource, String encoding) {
Assert.notNull(resource, "Resource must not be null");
this.resource = resource;
this.encoding = encoding;
}
/**
* Return the Resource held.
*/
public final Resource getResource() {
return this.resource;
}
/**
* Return the encoding to use for reading from the resource,
* or <code>null</code> if none specified.
*/
public final String getEncoding() {
return this.encoding;
}
/**
* Open a <code>java.io.Reader</code> for the specified resource,
* using the specified encoding (if any).
* @throws IOException if opening the Reader failed
*/
public Reader getReader() throws IOException {
if (this.encoding != null) {
return new InputStreamReader(this.resource.getInputStream(), this.encoding);
}
else {
return new InputStreamReader(this.resource.getInputStream());
}
}
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof EncodedResource) {
EncodedResource otherRes = (EncodedResource) obj;
return (this.resource.equals(otherRes.resource) &&
ObjectUtils.nullSafeEquals(this.encoding, otherRes.encoding));
}
return false;
}
public int hashCode() {
return this.resource.hashCode();
}
public String toString() {
return this.resource.toString();
}
}

View File

@ -0,0 +1,129 @@
/*
* 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.io.support;
import java.util.Locale;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
/**
* Helper class for loading a localized resource,
* specified through name, extension and current locale.
*
* @author Juergen Hoeller
* @since 1.2.5
*/
public class LocalizedResourceHelper {
/** The default separator to use inbetween file name parts: an underscore */
public static final String DEFAULT_SEPARATOR = "_";
private final ResourceLoader resourceLoader;
private String separator = DEFAULT_SEPARATOR;
/**
* Create a new LocalizedResourceHelper with a DefaultResourceLoader.
* @see org.springframework.core.io.DefaultResourceLoader
*/
public LocalizedResourceHelper() {
this.resourceLoader = new DefaultResourceLoader();
}
/**
* Create a new LocalizedResourceHelper with the given ResourceLoader.
* @param resourceLoader the ResourceLoader to use
*/
public LocalizedResourceHelper(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
}
/**
* Set the separator to use inbetween file name parts.
* Default is an underscore ("_").
*/
public void setSeparator(String separator) {
this.separator = (separator != null ? separator : DEFAULT_SEPARATOR);
}
/**
* Find the most specific localized resource for the given name,
* extension and locale:
* <p>The file will be searched with locations in the following order,
* similar to <code>java.util.ResourceBundle</code>'s search order:
* <ul>
* <li>[name]_[language]_[country]_[variant][extension]
* <li>[name]_[language]_[country][extension]
* <li>[name]_[language][extension]
* <li>[name][extension]
* </ul>
* <p>If none of the specific files can be found, a resource
* descriptor for the default location will be returned.
* @param name the name of the file, without localization part nor extension
* @param extension the file extension (e.g. ".xls")
* @param locale the current locale (may be <code>null</code>)
* @return the most specific localized resource found
* @see java.util.ResourceBundle
*/
public Resource findLocalizedResource(String name, String extension, Locale locale) {
Assert.notNull(name, "Name must not be null");
Assert.notNull(extension, "Extension must not be null");
Resource resource = null;
if (locale != null) {
String lang = locale.getLanguage();
String country = locale.getCountry();
String variant = locale.getVariant();
// Check for file with language, country and variant localization.
if (variant.length() > 0) {
String location =
name + this.separator + lang + this.separator + country + this.separator + variant + extension;
resource = this.resourceLoader.getResource(location);
}
// Check for file with language and country localization.
if ((resource == null || !resource.exists()) && country.length() > 0) {
String location = name + this.separator + lang + this.separator + country + extension;
resource = this.resourceLoader.getResource(location);
}
// Check for document with language localization.
if ((resource == null || !resource.exists()) && lang.length() > 0) {
String location = name + this.separator + lang + extension;
resource = this.resourceLoader.getResource(location);
}
}
// Check for document without localization.
if (resource == null || !resource.exists()) {
String location = name + extension;
resource = this.resourceLoader.getResource(location);
}
return resource;
}
}

View File

@ -0,0 +1,605 @@
/*
* 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.io.support;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.UrlResource;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.PathMatcher;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
/**
* A {@link ResourcePatternResolver} implementation that is able to resolve a
* specified resource location path into one or more matching Resources.
* The source path may be a simple path which has a one-to-one mapping to a
* target {@link org.springframework.core.io.Resource}, or alternatively
* may contain the special "<code>classpath*:</code>" prefix and/or
* internal Ant-style regular expressions (matched using Spring's
* {@link org.springframework.util.AntPathMatcher} utility).
* Both of the latter are effectively wildcards.
*
* <p><b>No Wildcards:</b>
*
* <p>In the simple case, if the specified location path does not start with the
* <code>"classpath*:</code>" prefix, and does not contain a PathMatcher pattern,
* this resolver will simply return a single resource via a
* <code>getResource()</code> call on the underlying <code>ResourceLoader</code>.
* Examples are real URLs such as "<code>file:C:/context.xml</code>", pseudo-URLs
* such as "<code>classpath:/context.xml</code>", and simple unprefixed paths
* such as "<code>/WEB-INF/context.xml</code>". The latter will resolve in a
* fashion specific to the underlying <code>ResourceLoader</code> (e.g.
* <code>ServletContextResource</code> for a <code>WebApplicationContext</code>).
*
* <p><b>Ant-style Patterns:</b>
*
* <p>When the path location contains an Ant-style pattern, e.g.:
* <pre>
* /WEB-INF/*-context.xml
* com/mycompany/**&#47;applicationContext.xml
* file:C:/some/path/*-context.xml
* classpath:com/mycompany/**&#47;applicationContext.xml</pre>
* the resolver follows a more complex but defined procedure to try to resolve
* the wildcard. It produces a <code>Resource</code> for the path up to the last
* non-wildcard segment and obtains a <code>URL</code> from it. If this URL is
* not a "<code>jar:</code>" URL or container-specific variant (e.g.
* "<code>zip:</code>" in WebLogic, "<code>wsjar</code>" in WebSphere", etc.),
* then a <code>java.io.File</code> is obtained from it, and used to resolve the
* wildcard by walking the filesystem. In the case of a jar URL, the resolver
* either gets a <code>java.net.JarURLConnection</code> from it, or manually parse
* the jar URL, and then traverse the contents of the jar file, to resolve the
* wildcards.
*
* <p><b>Implications on portability:</b>
*
* <p>If the specified path is already a file URL (either explicitly, or
* implicitly because the base <code>ResourceLoader</code> is a filesystem one,
* then wildcarding is guaranteed to work in a completely portable fashion.
*
* <p>If the specified path is a classpath location, then the resolver must
* obtain the last non-wildcard path segment URL via a
* <code>Classloader.getResource()</code> call. Since this is just a
* node of the path (not the file at the end) it is actually undefined
* (in the ClassLoader Javadocs) exactly what sort of a URL is returned in
* this case. In practice, it is usually a <code>java.io.File</code> representing
* the directory, where the classpath resource resolves to a filesystem
* location, or a jar URL of some sort, where the classpath resource resolves
* to a jar location. Still, there is a portability concern on this operation.
*
* <p>If a jar URL is obtained for the last non-wildcard segment, the resolver
* must be able to get a <code>java.net.JarURLConnection</code> from it, or
* manually parse the jar URL, to be able to walk the contents of the jar,
* and resolve the wildcard. This will work in most environments, but will
* fail in others, and it is strongly recommended that the wildcard
* resolution of resources coming from jars be thoroughly tested in your
* specific environment before you rely on it.
*
* <p><b><code>classpath*:</code> Prefix:</b>
*
* <p>There is special support for retrieving multiple class path resources with
* the same name, via the "<code>classpath*:</code>" prefix. For example,
* "<code>classpath*:META-INF/beans.xml</code>" will find all "beans.xml"
* files in the class path, be it in "classes" directories or in JAR files.
* This is particularly useful for autodetecting config files of the same name
* at the same location within each jar file. Internally, this happens via a
* <code>ClassLoader.getResources()</code> call, and is completely portable.
*
* <p>The "classpath*:" prefix can also be combined with a PathMatcher pattern in
* the rest of the location path, for example "classpath*:META-INF/*-beans.xml".
* In this case, the resolution strategy is fairly simple: a
* <code>ClassLoader.getResources()</code> call is used on the last non-wildcard
* path segment to get all the matching resources in the class loader hierarchy,
* and then off each resource the same PathMatcher resolution strategy described
* above is used for the wildcard subpath.
*
* <p><b>Other notes:</b>
*
* <p><b>WARNING:</b> Note that "<code>classpath*:</code>" when combined with
* Ant-style patterns will only work reliably with at least one root directory
* before the pattern starts, unless the actual target files reside in the file
* system. This means that a pattern like "<code>classpath*:*.xml</code>" will
* <i>not</i> retrieve files from the root of jar files but rather only from the
* root of expanded directories. This originates from a limitation in the JDK's
* <code>ClassLoader.getResources()</code> method which only returns file system
* locations for a passed-in empty String (indicating potential roots to search).
*
* <p><b>WARNING:</b> Ant-style patterns with "classpath:" resources are not
* guaranteed to find matching resources if the root package to search is available
* in multiple class path locations. This is because a resource such as<pre>
* com/mycompany/package1/service-context.xml
* </pre>may be in only one location, but when a path such as<pre>
* classpath:com/mycompany/**&#47;service-context.xml
* </pre>is used to try to resolve it, the resolver will work off the (first) URL
* returned by <code>getResource("com/mycompany");</code>. If this base package
* node exists in multiple classloader locations, the actual end resource may
* not be underneath. Therefore, preferably, use "<code>classpath*:<code>" with the same
* Ant-style pattern in such a case, which will search <i>all</i> class path
* locations that contain the root package.
*
* @author Juergen Hoeller
* @author Colin Sampaleanu
* @since 1.0.2
* @see #CLASSPATH_ALL_URL_PREFIX
* @see org.springframework.util.AntPathMatcher
* @see org.springframework.core.io.ResourceLoader#getResource(String)
* @see java.lang.ClassLoader#getResources(String)
*/
public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
private static final Log logger = LogFactory.getLog(PathMatchingResourcePatternResolver.class);
private static Method equinoxResolveMethod;
static {
// Detect Equinox OSGi (e.g. on WebSphere 6.1)
try {
Class fileLocatorClass = PathMatchingResourcePatternResolver.class.getClassLoader().loadClass(
"org.eclipse.core.runtime.FileLocator");
equinoxResolveMethod = fileLocatorClass.getMethod("resolve", new Class[] {URL.class});
logger.debug("Found Equinox FileLocator for OSGi bundle URL resolution");
}
catch (Throwable ex) {
equinoxResolveMethod = null;
}
}
private final ResourceLoader resourceLoader;
private PathMatcher pathMatcher = new AntPathMatcher();
/**
* Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
* <p>ClassLoader access will happen via the thread context class loader.
* @see org.springframework.core.io.DefaultResourceLoader
*/
public PathMatchingResourcePatternResolver() {
this.resourceLoader = new DefaultResourceLoader();
}
/**
* Create a new PathMatchingResourcePatternResolver with a DefaultResourceLoader.
* @param classLoader the ClassLoader to load classpath resources with,
* or <code>null</code> for using the thread context class loader
* @see org.springframework.core.io.DefaultResourceLoader
*/
public PathMatchingResourcePatternResolver(ClassLoader classLoader) {
this.resourceLoader = new DefaultResourceLoader(classLoader);
}
/**
* Create a new PathMatchingResourcePatternResolver.
* <p>ClassLoader access will happen via the thread context class loader.
* @param resourceLoader the ResourceLoader to load root directories and
* actual resources with
*/
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
this.resourceLoader = resourceLoader;
}
/**
* Return the ResourceLoader that this pattern resolver works with.
*/
public ResourceLoader getResourceLoader() {
return this.resourceLoader;
}
/**
* Return the ClassLoader that this pattern resolver works with
* (never <code>null</code>).
*/
public ClassLoader getClassLoader() {
return getResourceLoader().getClassLoader();
}
/**
* Set the PathMatcher implementation to use for this
* resource pattern resolver. Default is AntPathMatcher.
* @see org.springframework.util.AntPathMatcher
*/
public void setPathMatcher(PathMatcher pathMatcher) {
Assert.notNull(pathMatcher, "PathMatcher must not be null");
this.pathMatcher = pathMatcher;
}
/**
* Return the PathMatcher that this resource pattern resolver uses.
*/
public PathMatcher getPathMatcher() {
return this.pathMatcher;
}
public Resource getResource(String location) {
return getResourceLoader().getResource(location);
}
public Resource[] getResources(String locationPattern) throws IOException {
Assert.notNull(locationPattern, "Location pattern must not be null");
if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
// a class path resource (multiple resources for same name possible)
if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
// a class path resource pattern
return findPathMatchingResources(locationPattern);
}
else {
// all class path resources with the given name
return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
}
}
else {
// Only look for a pattern after a prefix here
// (to not get fooled by a pattern symbol in a strange prefix).
int prefixEnd = locationPattern.indexOf(":") + 1;
if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
// a file pattern
return findPathMatchingResources(locationPattern);
}
else {
// a single resource with the given name
return new Resource[] {getResourceLoader().getResource(locationPattern)};
}
}
}
/**
* Find all class location resources with the given location via the ClassLoader.
* @param location the absolute path within the classpath
* @return the result as Resource array
* @throws IOException in case of I/O errors
* @see java.lang.ClassLoader#getResources
* @see #convertClassLoaderURL
*/
protected Resource[] findAllClassPathResources(String location) throws IOException {
String path = location;
if (path.startsWith("/")) {
path = path.substring(1);
}
Enumeration resourceUrls = getClassLoader().getResources(path);
Set result = new LinkedHashSet(16);
while (resourceUrls.hasMoreElements()) {
URL url = (URL) resourceUrls.nextElement();
result.add(convertClassLoaderURL(url));
}
return (Resource[]) result.toArray(new Resource[result.size()]);
}
/**
* Convert the given URL as returned from the ClassLoader into a Resource object.
* <p>The default implementation simply creates a UrlResource instance.
* @param url a URL as returned from the ClassLoader
* @return the corresponding Resource object
* @see java.lang.ClassLoader#getResources
* @see org.springframework.core.io.Resource
*/
protected Resource convertClassLoaderURL(URL url) {
return new UrlResource(url);
}
/**
* Find all resources that match the given location pattern via the
* Ant-style PathMatcher. Supports resources in jar files and zip files
* and in the file system.
* @param locationPattern the location pattern to match
* @return the result as Resource array
* @throws IOException in case of I/O errors
* @see #doFindPathMatchingJarResources
* @see #doFindPathMatchingFileResources
* @see org.springframework.util.PathMatcher
*/
protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
String rootDirPath = determineRootDir(locationPattern);
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);
Set result = new LinkedHashSet(16);
for (int i = 0; i < rootDirResources.length; i++) {
Resource rootDirResource = resolveRootDirResource(rootDirResources[i]);
if (isJarResource(rootDirResource)) {
result.addAll(doFindPathMatchingJarResources(rootDirResource, subPattern));
}
else {
result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
}
}
if (logger.isDebugEnabled()) {
logger.debug("Resolved location pattern [" + locationPattern + "] to resources " + result);
}
return (Resource[]) result.toArray(new Resource[result.size()]);
}
/**
* Determine the root directory for the given location.
* <p>Used for determining the starting point for file matching,
* resolving the root directory location to a <code>java.io.File</code>
* and passing it into <code>retrieveMatchingFiles</code>, with the
* remainder of the location as pattern.
* <p>Will return "/WEB-INF" for the pattern "/WEB-INF/*.xml",
* for example.
* @param location the location to check
* @return the part of the location that denotes the root directory
* @see #retrieveMatchingFiles
*/
protected String determineRootDir(String location) {
int prefixEnd = location.indexOf(":") + 1;
int rootDirEnd = location.length();
while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
}
if (rootDirEnd == 0) {
rootDirEnd = prefixEnd;
}
return location.substring(0, rootDirEnd);
}
/**
* Resolve the specified resource for path matching.
* <p>The default implementation detects an Equinox OSGi "bundleresource:"
* / "bundleentry:" URL and resolves it into a standard jar file URL that
* can be traversed using Spring's standard jar file traversal algorithm.
* @param original the resource to resolfe
* @return the resolved resource (may be identical to the passed-in resource)
* @throws IOException in case of resolution failure
*/
protected Resource resolveRootDirResource(Resource original) throws IOException {
if (equinoxResolveMethod != null) {
URL url = original.getURL();
if (url.getProtocol().startsWith("bundle")) {
return new UrlResource((URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, new Object[] {url}));
}
}
return original;
}
/**
* Return whether the given resource handle indicates a jar resource
* that the <code>doFindPathMatchingJarResources</code> method can handle.
* <p>The default implementation checks against the URL protocols
* "jar", "zip" and "wsjar" (the latter are used by BEA WebLogic Server
* and IBM WebSphere, respectively, but can be treated like jar files).
* @param resource the resource handle to check
* (usually the root directory to start path matching from)
* @see #doFindPathMatchingJarResources
* @see org.springframework.util.ResourceUtils#isJarURL
*/
protected boolean isJarResource(Resource resource) throws IOException {
return ResourceUtils.isJarURL(resource.getURL());
}
/**
* Find all resources in jar files that match the given location pattern
* via the Ant-style PathMatcher.
* @param rootDirResource the root directory as Resource
* @param subPattern the sub pattern to match (below the root directory)
* @return the Set of matching Resource instances
* @throws IOException in case of I/O errors
* @see java.net.JarURLConnection
* @see org.springframework.util.PathMatcher
*/
protected Set doFindPathMatchingJarResources(Resource rootDirResource, String subPattern) throws IOException {
URLConnection con = rootDirResource.getURL().openConnection();
JarFile jarFile = null;
String jarFileUrl = null;
String rootEntryPath = null;
boolean newJarFile = false;
if (con instanceof JarURLConnection) {
// Should usually be the case for traditional JAR files.
JarURLConnection jarCon = (JarURLConnection) con;
jarCon.setUseCaches(false);
jarFile = jarCon.getJarFile();
jarFileUrl = jarCon.getJarFileURL().toExternalForm();
JarEntry jarEntry = jarCon.getJarEntry();
rootEntryPath = (jarEntry != null ? jarEntry.getName() : "");
}
else {
// No JarURLConnection -> need to resort to URL file parsing.
// We'll assume URLs of the format "jar:path!/entry", with the protocol
// being arbitrary as long as following the entry format.
// We'll also handle paths with and without leading "file:" prefix.
String urlFile = rootDirResource.getURL().getFile();
int separatorIndex = urlFile.indexOf(ResourceUtils.JAR_URL_SEPARATOR);
if (separatorIndex != -1) {
jarFileUrl = urlFile.substring(0, separatorIndex);
rootEntryPath = urlFile.substring(separatorIndex + ResourceUtils.JAR_URL_SEPARATOR.length());
jarFile = getJarFile(jarFileUrl);
}
else {
jarFile = new JarFile(urlFile);
jarFileUrl = urlFile;
rootEntryPath = "";
}
newJarFile = true;
}
try {
if (logger.isDebugEnabled()) {
logger.debug("Looking for matching resources in jar file [" + jarFileUrl + "]");
}
if (!"".equals(rootEntryPath) && !rootEntryPath.endsWith("/")) {
// Root entry path must end with slash to allow for proper matching.
// The Sun JRE does not return a slash here, but BEA JRockit does.
rootEntryPath = rootEntryPath + "/";
}
Set result = new LinkedHashSet(8);
for (Enumeration entries = jarFile.entries(); entries.hasMoreElements();) {
JarEntry entry = (JarEntry) entries.nextElement();
String entryPath = entry.getName();
if (entryPath.startsWith(rootEntryPath)) {
String relativePath = entryPath.substring(rootEntryPath.length());
if (getPathMatcher().match(subPattern, relativePath)) {
result.add(rootDirResource.createRelative(relativePath));
}
}
}
return result;
}
finally {
// Close jar file, but only if freshly obtained -
// not from JarURLConnection, which might cache the file reference.
if (newJarFile) {
jarFile.close();
}
}
}
/**
* Resolve the given jar file URL into a JarFile object.
*/
protected JarFile getJarFile(String jarFileUrl) throws IOException {
if (jarFileUrl.startsWith(ResourceUtils.FILE_URL_PREFIX)) {
try {
return new JarFile(ResourceUtils.toURI(jarFileUrl).getSchemeSpecificPart());
}
catch (URISyntaxException ex) {
// Fallback for URLs that are not valid URIs (should hardly ever happen).
return new JarFile(jarFileUrl.substring(ResourceUtils.FILE_URL_PREFIX.length()));
}
}
else {
return new JarFile(jarFileUrl);
}
}
/**
* Find all resources in the file system that match the given location pattern
* via the Ant-style PathMatcher.
* @param rootDirResource the root directory as Resource
* @param subPattern the sub pattern to match (below the root directory)
* @return the Set of matching Resource instances
* @throws IOException in case of I/O errors
* @see #retrieveMatchingFiles
* @see org.springframework.util.PathMatcher
*/
protected Set doFindPathMatchingFileResources(Resource rootDirResource, String subPattern) throws IOException {
File rootDir = null;
try {
rootDir = rootDirResource.getFile().getAbsoluteFile();
}
catch (IOException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot search for matching files underneath " + rootDirResource +
" because it does not correspond to a directory in the file system", ex);
}
return Collections.EMPTY_SET;
}
return doFindMatchingFileSystemResources(rootDir, subPattern);
}
/**
* Find all resources in the file system that match the given location pattern
* via the Ant-style PathMatcher.
* @param rootDir the root directory in the file system
* @param subPattern the sub pattern to match (below the root directory)
* @return the Set of matching Resource instances
* @throws IOException in case of I/O errors
* @see #retrieveMatchingFiles
* @see org.springframework.util.PathMatcher
*/
protected Set doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
}
Set matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
Set result = new LinkedHashSet(matchingFiles.size());
for (Iterator it = matchingFiles.iterator(); it.hasNext();) {
File file = (File) it.next();
result.add(new FileSystemResource(file));
}
return result;
}
/**
* Retrieve files that match the given path pattern,
* checking the given directory and its subdirectories.
* @param rootDir the directory to start from
* @param pattern the pattern to match against,
* relative to the root directory
* @return the Set of matching File instances
* @throws IOException if directory contents could not be retrieved
*/
protected Set retrieveMatchingFiles(File rootDir, String pattern) throws IOException {
if (!rootDir.isDirectory()) {
throw new IllegalArgumentException("Resource path [" + rootDir + "] does not denote a directory");
}
String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/");
if (!pattern.startsWith("/")) {
fullPattern += "/";
}
fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/");
Set result = new LinkedHashSet(8);
doRetrieveMatchingFiles(fullPattern, rootDir, result);
return result;
}
/**
* Recursively retrieve files that match the given pattern,
* adding them to the given result list.
* @param fullPattern the pattern to match against,
* with preprended root directory path
* @param dir the current directory
* @param result the Set of matching File instances to add to
* @throws IOException if directory contents could not be retrieved
*/
protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set result) throws IOException {
if (logger.isDebugEnabled()) {
logger.debug("Searching directory [" + dir.getAbsolutePath() +
"] for files matching pattern [" + fullPattern + "]");
}
File[] dirContents = dir.listFiles();
if (dirContents == null) {
throw new IOException("Could not retrieve contents of directory [" + dir.getAbsolutePath() + "]");
}
for (int i = 0; i < dirContents.length; i++) {
File content = dirContents[i];
String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
doRetrieveMatchingFiles(fullPattern, content, result);
}
if (getPathMatcher().match(fullPattern, currPath)) {
result.add(content);
}
}
}
}

View File

@ -0,0 +1,214 @@
/*
* 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.io.support;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.Resource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.DefaultPropertiesPersister;
import org.springframework.util.PropertiesPersister;
/**
* Base class for JavaBean-style components that need to load properties
* from one or more resources. Supports local properties as well, with
* configurable overriding.
*
* @author Juergen Hoeller
* @since 1.2.2
*/
public abstract class PropertiesLoaderSupport {
public static final String XML_FILE_EXTENSION = ".xml";
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private Properties[] localProperties;
private Resource[] locations;
private boolean localOverride = false;
private boolean ignoreResourceNotFound = false;
private String fileEncoding;
private PropertiesPersister propertiesPersister = new DefaultPropertiesPersister();
/**
* Set local properties, e.g. via the "props" tag in XML bean definitions.
* These can be considered defaults, to be overridden by properties
* loaded from files.
*/
public void setProperties(Properties properties) {
this.localProperties = new Properties[] {properties};
}
/**
* Set local properties, e.g. via the "props" tag in XML bean definitions,
* allowing for merging multiple properties sets into one.
*/
public void setPropertiesArray(Properties[] propertiesArray) {
this.localProperties = propertiesArray;
}
/**
* Set a location of a properties file to be loaded.
* <p>Can point to a classic properties file or to an XML file
* that follows JDK 1.5's properties XML format.
*/
public void setLocation(Resource location) {
this.locations = new Resource[] {location};
}
/**
* Set locations of properties files to be loaded.
* <p>Can point to classic properties files or to XML files
* that follow JDK 1.5's properties XML format.
* <p>Note: Properties defined in later files will override
* properties defined earlier files, in case of overlapping keys.
* Hence, make sure that the most specific files are the last
* ones in the given list of locations.
*/
public void setLocations(Resource[] locations) {
this.locations = locations;
}
/**
* Set whether local properties override properties from files.
* <p>Default is "false": Properties from files override local defaults.
* Can be switched to "true" to let local properties override defaults
* from files.
*/
public void setLocalOverride(boolean localOverride) {
this.localOverride = localOverride;
}
/**
* Set if failure to find the property resource should be ignored.
* <p>"true" is appropriate if the properties file is completely optional.
* Default is "false".
*/
public void setIgnoreResourceNotFound(boolean ignoreResourceNotFound) {
this.ignoreResourceNotFound = ignoreResourceNotFound;
}
/**
* Set the encoding to use for parsing properties files.
* <p>Default is none, using the <code>java.util.Properties</code>
* default encoding.
* <p>Only applies to classic properties files, not to XML files.
* @see org.springframework.util.PropertiesPersister#load
*/
public void setFileEncoding(String encoding) {
this.fileEncoding = encoding;
}
/**
* Set the PropertiesPersister to use for parsing properties files.
* The default is DefaultPropertiesPersister.
* @see org.springframework.util.DefaultPropertiesPersister
*/
public void setPropertiesPersister(PropertiesPersister propertiesPersister) {
this.propertiesPersister =
(propertiesPersister != null ? propertiesPersister : new DefaultPropertiesPersister());
}
/**
* Return a merged Properties instance containing both the
* loaded properties and properties set on this FactoryBean.
*/
protected Properties mergeProperties() throws IOException {
Properties result = new Properties();
if (this.localOverride) {
// Load properties from file upfront, to let local properties override.
loadProperties(result);
}
if (this.localProperties != null) {
for (int i = 0; i < this.localProperties.length; i++) {
CollectionUtils.mergePropertiesIntoMap(this.localProperties[i], result);
}
}
if (!this.localOverride) {
// Load properties from file afterwards, to let those properties override.
loadProperties(result);
}
return result;
}
/**
* Load properties into the given instance.
* @param props the Properties instance to load into
* @throws java.io.IOException in case of I/O errors
* @see #setLocations
*/
protected void loadProperties(Properties props) throws IOException {
if (this.locations != null) {
for (int i = 0; i < this.locations.length; i++) {
Resource location = this.locations[i];
if (logger.isInfoEnabled()) {
logger.info("Loading properties file from " + location);
}
InputStream is = null;
try {
is = location.getInputStream();
if (location.getFilename().endsWith(XML_FILE_EXTENSION)) {
this.propertiesPersister.loadFromXml(props, is);
}
else {
if (this.fileEncoding != null) {
this.propertiesPersister.load(props, new InputStreamReader(is, this.fileEncoding));
}
else {
this.propertiesPersister.load(props, is);
}
}
}
catch (IOException ex) {
if (this.ignoreResourceNotFound) {
if (logger.isWarnEnabled()) {
logger.warn("Could not load properties from " + location + ": " + ex.getMessage());
}
}
else {
throw ex;
}
}
finally {
if (is != null) {
is.close();
}
}
}
}
}
}

View File

@ -0,0 +1,122 @@
/*
* 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.io.support;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.Properties;
import org.springframework.core.io.Resource;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Convenient utility methods for loading of <code>java.util.Properties</code>,
* performing standard handling of input streams.
*
* <p>For more configurable properties loading, including the option of a
* customized encoding, consider using the PropertiesLoaderSupport class.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 2.0
* @see PropertiesLoaderSupport
*/
public abstract class PropertiesLoaderUtils {
/**
* Load properties from the given resource.
* @param resource the resource to load from
* @return the populated Properties instance
* @throws IOException if loading failed
*/
public static Properties loadProperties(Resource resource) throws IOException {
Properties props = new Properties();
fillProperties(props, resource);
return props;
}
/**
* Fill the given properties from the given resource.
* @param props the Properties instance to fill
* @param resource the resource to load from
* @throws IOException if loading failed
*/
public static void fillProperties(Properties props, Resource resource) throws IOException {
InputStream is = resource.getInputStream();
try {
props.load(is);
}
finally {
is.close();
}
}
/**
* Load all properties from the given class path resource,
* using the default class loader.
* <p>Merges properties if more than one resource of the same name
* found in the class path.
* @param resourceName the name of the class path resource
* @return the populated Properties instance
* @throws IOException if loading failed
*/
public static Properties loadAllProperties(String resourceName) throws IOException {
return loadAllProperties(resourceName, null);
}
/**
* Load all properties from the given class path resource,
* using the given class loader.
* <p>Merges properties if more than one resource of the same name
* found in the class path.
* @param resourceName the name of the class path resource
* @param classLoader the ClassLoader to use for loading
* (or <code>null</code> to use the default class loader)
* @return the populated Properties instance
* @throws IOException if loading failed
*/
public static Properties loadAllProperties(String resourceName, ClassLoader classLoader) throws IOException {
Assert.notNull(resourceName, "Resource name must not be null");
ClassLoader clToUse = classLoader;
if (clToUse == null) {
clToUse = ClassUtils.getDefaultClassLoader();
}
Properties properties = new Properties();
Enumeration urls = clToUse.getResources(resourceName);
while (urls.hasMoreElements()) {
URL url = (URL) urls.nextElement();
InputStream is = null;
try {
URLConnection con = url.openConnection();
con.setUseCaches(false);
is = con.getInputStream();
properties.load(is);
}
finally {
if (is != null) {
is.close();
}
}
}
return properties;
}
}

View File

@ -0,0 +1,148 @@
/*
* 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.io.support;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.springframework.core.io.Resource;
import org.springframework.util.SystemPropertyUtils;
/**
* Editor for {@link org.springframework.core.io.Resource} arrays, to
* automatically convert <code>String</code> location patterns
* (e.g. <code>"file:C:/my*.txt"</code> or <code>"classpath*:myfile.txt"</code>)
* to <code>Resource</code> array properties. Can also translate a collection
* or array of location patterns into a merged Resource array.
*
* <p>The path may contain <code>${...}</code> placeholders, to be resolved
* as system properties: e.g. <code>${user.dir}</code>.
*
* <p>Delegates to a {@link ResourcePatternResolver},
* by default using a {@link PathMatchingResourcePatternResolver}.
*
* @author Juergen Hoeller
* @since 1.1.2
* @see org.springframework.core.io.Resource
* @see ResourcePatternResolver
* @see PathMatchingResourcePatternResolver
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders
* @see System#getProperty(String)
*/
public class ResourceArrayPropertyEditor extends PropertyEditorSupport {
private final ResourcePatternResolver resourcePatternResolver;
/**
* Create a new ResourceArrayPropertyEditor with a default
* PathMatchingResourcePatternResolver.
* @see PathMatchingResourcePatternResolver
*/
public ResourceArrayPropertyEditor() {
this.resourcePatternResolver = new PathMatchingResourcePatternResolver();
}
/**
* Create a new ResourceArrayPropertyEditor with the given ResourcePatternResolver.
* @param resourcePatternResolver the ResourcePatternResolver to use
*/
public ResourceArrayPropertyEditor(ResourcePatternResolver resourcePatternResolver) {
this.resourcePatternResolver = resourcePatternResolver;
}
/**
* Treat the given text as location pattern and convert it to a Resource array.
*/
public void setAsText(String text) {
String pattern = resolvePath(text).trim();
try {
setValue(this.resourcePatternResolver.getResources(pattern));
}
catch (IOException ex) {
throw new IllegalArgumentException(
"Could not resolve resource location pattern [" + pattern + "]: " + ex.getMessage());
}
}
/**
* Treat the given value as collection or array and convert it to a Resource array.
* Considers String elements as location patterns, and takes Resource elements as-is.
*/
public void setValue(Object value) throws IllegalArgumentException {
if (value instanceof Collection || (value instanceof Object[] && !(value instanceof Resource[]))) {
Collection input = (value instanceof Collection ? (Collection) value : Arrays.asList((Object[]) value));
List merged = new ArrayList();
for (Iterator it = input.iterator(); it.hasNext();) {
Object element = it.next();
if (element instanceof String) {
// A location pattern: resolve it into a Resource array.
// Might point to a single resource or to multiple resources.
String pattern = resolvePath((String) element).trim();
try {
Resource[] resources = this.resourcePatternResolver.getResources(pattern);
for (int i = 0; i < resources.length; i++) {
Resource resource = resources[i];
if (!merged.contains(resource)) {
merged.add(resource);
}
}
}
catch (IOException ex) {
throw new IllegalArgumentException(
"Could not resolve resource location pattern [" + pattern + "]: " + ex.getMessage());
}
}
else if (element instanceof Resource) {
// A Resource object: add it to the result.
if (!merged.contains(element)) {
merged.add(element);
}
}
else {
throw new IllegalArgumentException("Cannot convert element [" + element + "] to [" +
Resource.class.getName() + "]: only location String and Resource object supported");
}
}
super.setValue(merged.toArray(new Resource[merged.size()]));
}
else {
// An arbitrary value: probably a String or a Resource array.
// setAsText will be called for a String; a Resource array will be used as-is.
super.setValue(value);
}
}
/**
* Resolve the given path, replacing placeholders with
* corresponding system property values if necessary.
* @param path the original file path
* @return the resolved file path
* @see org.springframework.util.SystemPropertyUtils#resolvePlaceholders
*/
protected String resolvePath(String path) {
return SystemPropertyUtils.resolvePlaceholders(path);
}
}

View File

@ -0,0 +1,76 @@
/*
* 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.io.support;
import java.io.IOException;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
/**
* Strategy interface for resolving a location pattern (for example,
* an Ant-style path pattern) into Resource objects.
*
* <p>This is an extension to the {@link org.springframework.core.io.ResourceLoader}
* interface. A passed-in ResourceLoader (for example, an
* {@link org.springframework.context.ApplicationContext} passed in via
* {@link org.springframework.context.ResourceLoaderAware} when running in a context)
* can be checked whether it implements this extended interface too.
*
* <p>{@link PathMatchingResourcePatternResolver} is a standalone implementation
* that is usable outside an ApplicationContext, also used by
* {@link ResourceArrayPropertyEditor} for populating Resource array bean properties.
*
* <p>Can be used with any sort of location pattern (e.g. "/WEB-INF/*-context.xml"):
* Input patterns have to match the strategy implementation. This interface just
* specifies the conversion method rather than a specific pattern format.
*
* <p>This interface also suggests a new resource prefix "classpath*:" for all
* matching resources from the class path. Note that the resource location is
* expected to be a path without placeholders in this case (e.g. "/beans.xml");
* JAR files or classes directories can contain multiple files of the same name.
*
* @author Juergen Hoeller
* @since 1.0.2
* @see org.springframework.core.io.Resource
* @see org.springframework.core.io.ResourceLoader
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.ResourceLoaderAware
*/
public interface ResourcePatternResolver extends ResourceLoader {
/**
* Pseudo URL prefix for all matching resources from the class path: "classpath*:"
* This differs from ResourceLoader's classpath URL prefix in that it
* retrieves all matching resources for a given name (e.g. "/beans.xml"),
* for example in the root of all deployed JAR files.
* @see org.springframework.core.io.ResourceLoader#CLASSPATH_URL_PREFIX
*/
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";
/**
* Resolve the given location pattern into Resource objects.
* <p>Overlapping resource entries that point to the same physical
* resource should be avoided, as far as possible. The result should
* have set semantics.
* @param locationPattern the location pattern to resolve
* @return the corresponding Resource objects
* @throws IOException in case of I/O errors
*/
Resource[] getResources(String locationPattern) throws IOException;
}

View File

@ -0,0 +1,71 @@
/*
* 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.io.support;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.Assert;
import org.springframework.util.ResourceUtils;
/**
* Utility class for determining whether a given URL is a resource
* location that can be loaded via a ResourcePatternResolver.
*
* <p>Callers will usually assume that a location is a relative path
* if the {@link #isUrl(String)} method returns <code>false</code>.
*
* @author Juergen Hoeller
* @since 1.2.3
*/
public abstract class ResourcePatternUtils {
/**
* Return whether the given resource location is a URL: either a
* special "classpath" or "classpath*" pseudo URL or a standard URL.
* @param resourceLocation the location String to check
* @return whether the location qualifies as a URL
* @see ResourcePatternResolver#CLASSPATH_ALL_URL_PREFIX
* @see org.springframework.util.ResourceUtils#CLASSPATH_URL_PREFIX
* @see org.springframework.util.ResourceUtils#isUrl(String)
* @see java.net.URL
*/
public static boolean isUrl(String resourceLocation) {
return (resourceLocation != null &&
(resourceLocation.startsWith(ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX) ||
ResourceUtils.isUrl(resourceLocation)));
}
/**
* Return a default ResourcePatternResolver for the given ResourceLoader.
* <p>This might be the ResourceLoader itself, if it implements the
* ResourcePatternResolver extension, or a PathMatchingResourcePatternResolver
* built on the given ResourceLoader.
* @param resourceLoader the ResourceLoader to build a pattern resolver for
* (not <code>null</code>)
* @return the ResourcePatternResolver
* @see PathMatchingResourcePatternResolver
*/
public static ResourcePatternResolver getResourcePatternResolver(ResourceLoader resourceLoader) {
Assert.notNull(resourceLoader, "ResourceLoader must not be null");
if (resourceLoader instanceof ResourcePatternResolver) {
return (ResourcePatternResolver) resourceLoader;
}
else {
return new PathMatchingResourcePatternResolver(resourceLoader);
}
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Support classes for Spring's resource abstraction.
Includes a ResourcePatternResolver mechanism.
</body>
</html>

View File

@ -0,0 +1,8 @@
<html>
<body>
Provides basic classes for exception handling and version detection,
and other core helpers that are not specific to any part of the framework.
</body>
</html>

View File

@ -0,0 +1,98 @@
/*
* 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.style;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Spring's default <code>toString()</code> styler.
*
* <p>This class is used by {@link ToStringCreator} to style <code>toString()</code>
* output in a consistent manner according to Spring conventions.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 1.2.2
*/
public class DefaultToStringStyler implements ToStringStyler {
private final ValueStyler valueStyler;
/**
* Create a new DefaultToStringStyler.
* @param valueStyler the ValueStyler to use
*/
public DefaultToStringStyler(ValueStyler valueStyler) {
Assert.notNull(valueStyler, "ValueStyler must not be null");
this.valueStyler = valueStyler;
}
/**
* Return the ValueStyler used by this ToStringStyler.
*/
protected final ValueStyler getValueStyler() {
return this.valueStyler;
}
public void styleStart(StringBuffer buffer, Object obj) {
if (!obj.getClass().isArray()) {
buffer.append('[').append(ClassUtils.getShortName(obj.getClass()));
styleIdentityHashCode(buffer, obj);
}
else {
buffer.append('[');
styleIdentityHashCode(buffer, obj);
buffer.append(' ');
styleValue(buffer, obj);
}
}
private void styleIdentityHashCode(StringBuffer buffer, Object obj) {
buffer.append('@');
buffer.append(ObjectUtils.getIdentityHexString(obj));
}
public void styleEnd(StringBuffer buffer, Object o) {
buffer.append(']');
}
public void styleField(StringBuffer buffer, String fieldName, Object value) {
styleFieldStart(buffer, fieldName);
styleValue(buffer, value);
styleFieldEnd(buffer, fieldName);
}
protected void styleFieldStart(StringBuffer buffer, String fieldName) {
buffer.append(' ').append(fieldName).append(" = ");
}
protected void styleFieldEnd(StringBuffer buffer, String fieldName) {
}
public void styleValue(StringBuffer buffer, Object value) {
buffer.append(this.valueStyler.style(value));
}
public void styleFieldSeparator(StringBuffer buffer) {
buffer.append(',');
}
}

View File

@ -0,0 +1,148 @@
/*
* 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.style;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Converts objects to String form, generally for debugging purposes,
* using Spring's <code>toString</code> styling conventions.
*
* <p>Uses the reflective visitor pattern underneath the hood to nicely
* encapsulate styling algorithms for each type of styled object.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 1.2.2
*/
public class DefaultValueStyler implements ValueStyler {
private static final String EMPTY = "[empty]";
private static final String NULL = "[null]";
private static final String COLLECTION = "collection";
private static final String SET = "set";
private static final String LIST = "list";
private static final String MAP = "map";
private static final String ARRAY = "array";
public String style(Object value) {
if (value == null) {
return NULL;
}
else if (value instanceof String) {
return "\'" + value + "\'";
}
else if (value instanceof Class) {
return ClassUtils.getShortName((Class) value);
}
else if (value instanceof Method) {
Method method = (Method) value;
return method.getName() + "@" + ClassUtils.getShortName(method.getDeclaringClass());
}
else if (value instanceof Map) {
return style((Map) value);
}
else if (value instanceof Map.Entry) {
return style((Map.Entry) value);
}
else if (value instanceof Collection) {
return style((Collection) value);
}
else if (value.getClass().isArray()) {
return styleArray(ObjectUtils.toObjectArray(value));
}
else {
return String.valueOf(value);
}
}
private String style(Map value) {
StringBuffer buffer = new StringBuffer(value.size() * 8 + 16);
buffer.append(MAP + "[");
for (Iterator it = value.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
buffer.append(style(entry));
if (it.hasNext()) {
buffer.append(',').append(' ');
}
}
if (value.isEmpty()) {
buffer.append(EMPTY);
}
buffer.append("]");
return buffer.toString();
}
private String style(Map.Entry value) {
return style(value.getKey()) + " -> " + style(value.getValue());
}
private String style(Collection value) {
StringBuffer buffer = new StringBuffer(value.size() * 8 + 16);
buffer.append(getCollectionTypeString(value)).append('[');
for (Iterator i = value.iterator(); i.hasNext();) {
buffer.append(style(i.next()));
if (i.hasNext()) {
buffer.append(',').append(' ');
}
}
if (value.isEmpty()) {
buffer.append(EMPTY);
}
buffer.append("]");
return buffer.toString();
}
private String getCollectionTypeString(Collection value) {
if (value instanceof List) {
return LIST;
}
else if (value instanceof Set) {
return SET;
}
else {
return COLLECTION;
}
}
private String styleArray(Object[] array) {
StringBuffer buffer = new StringBuffer(array.length * 8 + 16);
buffer.append(ARRAY + "<" + ClassUtils.getShortName(array.getClass().getComponentType()) + ">[");
for (int i = 0; i < array.length - 1; i++) {
buffer.append(style(array[i]));
buffer.append(',').append(' ');
}
if (array.length > 0) {
buffer.append(style(array[array.length - 1]));
}
else {
buffer.append(EMPTY);
}
buffer.append("]");
return buffer.toString();
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.style;
/**
* Simple utility class to allow for convenient access to value
* styling logic, mainly to support descriptive logging messages.
*
* <p>For more sophisticated needs, use the {@link ValueStyler} abstraction
* directly. This class simply uses a shared {@link DefaultValueStyler}
* instance underneath.
*
* @author Keith Donald
* @since 1.2.2
* @see ValueStyler
* @see DefaultValueStyler
*/
public abstract class StylerUtils {
/**
* Default ValueStyler instance used by the <code>style</code> method.
* Also available for the {@link ToStringCreator} class in this package.
*/
static final ValueStyler DEFAULT_VALUE_STYLER = new DefaultValueStyler();
/**
* Style the specified value according to default conventions.
* @param value the Object value to style
* @return the styled String
* @see DefaultValueStyler
*/
public static String style(Object value) {
return DEFAULT_VALUE_STYLER.style(value);
}
}

View File

@ -0,0 +1,188 @@
/*
* 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.style;
import org.springframework.util.Assert;
/**
* Utility class that builds pretty-printing <code>toString()</code> methods
* with pluggable styling conventions. By default, ToStringCreator adheres
* to Spring's <code>toString()</code> styling conventions.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 1.2.2
*/
public class ToStringCreator {
/**
* Default ToStringStyler instance used by this ToStringCreator.
*/
private static final ToStringStyler DEFAULT_TO_STRING_STYLER =
new DefaultToStringStyler(StylerUtils.DEFAULT_VALUE_STYLER);
private StringBuffer buffer = new StringBuffer(512);
private ToStringStyler styler;
private Object object;
private boolean styledFirstField;
/**
* Create a ToStringCreator for the given object.
* @param obj the object to be stringified
*/
public ToStringCreator(Object obj) {
this(obj, (ToStringStyler) null);
}
/**
* Create a ToStringCreator for the given object, using the provided style.
* @param obj the object to be stringified
* @param styler the ValueStyler encapsulating pretty-print instructions
*/
public ToStringCreator(Object obj, ValueStyler styler) {
this(obj, new DefaultToStringStyler(styler != null ? styler : StylerUtils.DEFAULT_VALUE_STYLER));
}
/**
* Create a ToStringCreator for the given object, using the provided style.
* @param obj the object to be stringified
* @param styler the ToStringStyler encapsulating pretty-print instructions
*/
public ToStringCreator(Object obj, ToStringStyler styler) {
Assert.notNull(obj, "The object to be styled must not be null");
this.object = obj;
this.styler = (styler != null ? styler : DEFAULT_TO_STRING_STYLER);
this.styler.styleStart(this.buffer, this.object);
}
/**
* Append a byte field value.
* @param fieldName the name of the field, usually the member variable name
* @param value the field value
* @return this, to support call-chaining
*/
public ToStringCreator append(String fieldName, byte value) {
return append(fieldName, new Byte(value));
}
/**
* Append a short field value.
* @param fieldName the name of the field, usually the member variable name
* @param value the field value
* @return this, to support call-chaining
*/
public ToStringCreator append(String fieldName, short value) {
return append(fieldName, new Short(value));
}
/**
* Append a integer field value.
* @param fieldName the name of the field, usually the member variable name
* @param value the field value
* @return this, to support call-chaining
*/
public ToStringCreator append(String fieldName, int value) {
return append(fieldName, new Integer(value));
}
/**
* Append a float field value.
* @param fieldName the name of the field, usually the member variable name
* @param value the field value
* @return this, to support call-chaining
*/
public ToStringCreator append(String fieldName, float value) {
return append(fieldName, new Float(value));
}
/**
* Append a double field value.
* @param fieldName the name of the field, usually the member variable name
* @param value the field value
* @return this, to support call-chaining
*/
public ToStringCreator append(String fieldName, double value) {
return append(fieldName, new Double(value));
}
/**
* Append a long field value.
* @param fieldName the name of the field, usually the member variable name
* @param value the field value
* @return this, to support call-chaining
*/
public ToStringCreator append(String fieldName, long value) {
return append(fieldName, new Long(value));
}
/**
* Append a boolean field value.
* @param fieldName the name of the field, usually the member variable name
* @param value the field value
* @return this, to support call-chaining
*/
public ToStringCreator append(String fieldName, boolean value) {
return append(fieldName, value ? Boolean.TRUE : Boolean.FALSE);
}
/**
* Append a field value.
* @param fieldName the name of the field, usually the member variable name
* @param value the field value
* @return this, to support call-chaining
*/
public ToStringCreator append(String fieldName, Object value) {
printFieldSeparatorIfNecessary();
this.styler.styleField(this.buffer, fieldName, value);
return this;
}
private void printFieldSeparatorIfNecessary() {
if (this.styledFirstField) {
this.styler.styleFieldSeparator(this.buffer);
}
else {
this.styledFirstField = true;
}
}
/**
* Append the provided value.
* @param value The value to append
* @return this, to support call-chaining.
*/
public ToStringCreator append(Object value) {
this.styler.styleValue(this.buffer, value);
return this;
}
/**
* Return the String representation that this ToStringCreator built.
*/
public String toString() {
this.styler.styleEnd(this.buffer, this.object);
return this.buffer.toString();
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.style;
/**
* A strategy interface for pretty-printing <code>toString()</code> methods.
* Encapsulates the print algorithms; some other object such as a builder
* should provide the workflow.
*
* @author Keith Donald
* @since 1.2.2
*/
public interface ToStringStyler {
/**
* Style a <code>toString()</code>'ed object before its fields are styled.
* @param buffer the buffer to print to
* @param obj the object to style
*/
void styleStart(StringBuffer buffer, Object obj);
/**
* Style a <code>toString()</code>'ed object after it's fields are styled.
* @param buffer the buffer to print to
* @param obj the object to style
*/
void styleEnd(StringBuffer buffer, Object obj);
/**
* Style a field value as a string.
* @param buffer the buffer to print to
* @param fieldName the he name of the field
* @param value the field value
*/
void styleField(StringBuffer buffer, String fieldName, Object value);
/**
* Style the given value.
* @param buffer the buffer to print to
* @param value the field value
*/
void styleValue(StringBuffer buffer, Object value);
/**
* Style the field separator.
* @param buffer buffer to print to
*/
void styleFieldSeparator(StringBuffer buffer);
}

View File

@ -0,0 +1,35 @@
/*
* 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.style;
/**
* Strategy that encapsulates value String styling algorithms
* according to Spring conventions.
*
* @author Keith Donald
* @since 1.2.2
*/
public interface ValueStyler {
/**
* Style the given value, returning a String representation.
* @param value the Object value to style
* @return the styled String
*/
String style(Object value);
}

View File

@ -0,0 +1,7 @@
<html>
<body>
Support for styling values as Strings, with ToStringCreator as central class.
</body>
</html>

View File

@ -0,0 +1,55 @@
/*
* 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.task;
/**
* Extended interface for asynchronous {@link TaskExecutor} implementations,
* offering an overloaded {@link #execute(Runnable, long)} variant with
* start timeout parameter.
*
* <p>Implementing this interface also indicates that the {@link #execute(Runnable)}
* method will not execute its Runnable in the caller's thread but rather
* asynchronously in some other thread (at least usually).
*
* @author Juergen Hoeller
* @since 2.0.3
* @see SimpleAsyncTaskExecutor
* @see org.springframework.scheduling.SchedulingTaskExecutor
*/
public interface AsyncTaskExecutor extends TaskExecutor {
/** Constant that indicates immediate execution */
long TIMEOUT_IMMEDIATE = 0;
/** Constant that indicates no time limit */
long TIMEOUT_INDEFINITE = Long.MAX_VALUE;
/**
* Execute the given <code>task</code>.
* @param task the <code>Runnable</code> to execute (never <code>null</code>)
* @param startTimeout the time duration within which the task is supposed to start.
* This is intended as a hint to the executor, allowing for preferred handling
* of immediate tasks. Typical values are {@link #TIMEOUT_IMMEDIATE} or
* {@link #TIMEOUT_INDEFINITE} (the default as used by {@link #execute(Runnable)}).
* @throws TaskTimeoutException in case of the task being rejected because
* of the timeout (i.e. it cannot be started in time)
* @throws TaskRejectedException if the given task was not accepted
*/
void execute(Runnable task, long startTimeout);
}

View File

@ -0,0 +1,200 @@
/*
* 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.task;
import java.io.Serializable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrencyThrottleSupport;
import org.springframework.util.CustomizableThreadCreator;
/**
* TaskExecutor implementation that fires up a new Thread for each task,
* executing it asynchronously.
*
* <p>Supports limiting concurrent threads through the "concurrencyLimit"
* bean property. By default, the number of concurrent threads is unlimited.
*
* <p><b>NOTE: This implementation does not reuse threads!</b> Consider a
* thread-pooling TaskExecutor implementation instead, in particular for
* executing a large number of short-lived tasks.
*
* @author Juergen Hoeller
* @since 2.0
* @see #setConcurrencyLimit
* @see SyncTaskExecutor
* @see org.springframework.scheduling.timer.TimerTaskExecutor
* @see org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
* @see org.springframework.scheduling.commonj.WorkManagerTaskExecutor
*/
public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator implements AsyncTaskExecutor, Serializable {
/**
* Default thread name prefix: "SimpleAsyncTaskExecutor-".
* @deprecated as of Spring 2.0.3, since the default thread name prefix
* is now taken from the concrete class (could be a subclass)
*/
public static final String DEFAULT_THREAD_NAME_PREFIX =
ClassUtils.getShortName(SimpleAsyncTaskExecutor.class) + "-";
/**
* Permit any number of concurrent invocations: that is, don't throttle concurrency.
*/
public static final int UNBOUNDED_CONCURRENCY = ConcurrencyThrottleSupport.UNBOUNDED_CONCURRENCY;
/**
* Switch concurrency 'off': that is, don't allow any concurrent invocations.
*/
public static final int NO_CONCURRENCY = ConcurrencyThrottleSupport.NO_CONCURRENCY;
/**
* Internal concurrency throttle used by this executor.
*/
private final ConcurrencyThrottleAdapter concurrencyThrottle = new ConcurrencyThrottleAdapter();
/**
* Create a new SimpleAsyncTaskExecutor with default thread name prefix.
*/
public SimpleAsyncTaskExecutor() {
super();
}
/**
* Create a new SimpleAsyncTaskExecutor with the given thread name prefix.
* @param threadNamePrefix the prefix to use for the names of newly created threads
*/
public SimpleAsyncTaskExecutor(String threadNamePrefix) {
super(threadNamePrefix);
}
/**
* Set the maximum number of parallel accesses allowed.
* -1 indicates no concurrency limit at all.
* <p>In principle, this limit can be changed at runtime,
* although it is generally designed as a config time setting.
* NOTE: Do not switch between -1 and any concrete limit at runtime,
* as this will lead to inconsistent concurrency counts: A limit
* of -1 effectively turns off concurrency counting completely.
* @see #UNBOUNDED_CONCURRENCY
*/
public void setConcurrencyLimit(int concurrencyLimit) {
this.concurrencyThrottle.setConcurrencyLimit(concurrencyLimit);
}
/**
* Return the maximum number of parallel accesses allowed.
*/
public int getConcurrencyLimit() {
return this.concurrencyThrottle.getConcurrencyLimit();
}
/**
* Return whether this throttle is currently active.
* @return <code>true</code> if the concurrency limit for this instance is active
* @see #getConcurrencyLimit()
* @see #setConcurrencyLimit
*/
public boolean isThrottleActive() {
return this.concurrencyThrottle.isThrottleActive();
}
/**
* Executes the given task, within a concurrency throttle
* if configured (through the superclass's settings).
* @see #doExecute(Runnable)
*/
public void execute(Runnable task) {
execute(task, TIMEOUT_INDEFINITE);
}
/**
* Executes the given task, within a concurrency throttle
* if configured (through the superclass's settings).
* <p>Executes urgent tasks (with 'immediate' timeout) directly,
* bypassing the concurrency throttle (if active). All other
* tasks are subject to throttling.
* @see #TIMEOUT_IMMEDIATE
* @see #doExecute(Runnable)
*/
public void execute(Runnable task, long startTimeout) {
Assert.notNull(task, "Runnable must not be null");
if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {
this.concurrencyThrottle.beforeAccess();
doExecute(new ConcurrencyThrottlingRunnable(task));
}
else {
doExecute(task);
}
}
/**
* Template method for the actual execution of a task.
* <p>The default implementation creates a new Thread and starts it.
* @param task the Runnable to execute
* @see #createThread
* @see java.lang.Thread#start()
*/
protected void doExecute(Runnable task) {
createThread(task).start();
}
/**
* Subclass of the general ConcurrencyThrottleSupport class,
* making <code>beforeAccess()</code> and <code>afterAccess()</code>
* visible to the surrounding class.
*/
private static class ConcurrencyThrottleAdapter extends ConcurrencyThrottleSupport {
protected void beforeAccess() {
super.beforeAccess();
}
protected void afterAccess() {
super.afterAccess();
}
}
/**
* This Runnable calls <code>afterAccess()</code> after the
* target Runnable has finished its execution.
*/
private class ConcurrencyThrottlingRunnable implements Runnable {
private final Runnable target;
public ConcurrencyThrottlingRunnable(Runnable target) {
this.target = target;
}
public void run() {
try {
this.target.run();
}
finally {
concurrencyThrottle.afterAccess();
}
}
}
}

View File

@ -0,0 +1,52 @@
/*
* 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.task;
import org.springframework.util.Assert;
import java.io.Serializable;
/**
* <code>TaskExecutor</code> implementation that executes each task
* <i>synchronously</i> in the calling thread.
*
* <p>Mainly intended for testing scenarios.
*
* <p>Execution in the calling thread does have the advantage of participating
* in it's thread context, for example the thread context class loader or the
* thread's current transaction association. That said, in many cases,
* asynchronous execution will be preferable: choose an asynchronous
* <code>TaskExecutor</code> instead for such scenarios.
*
* @author Juergen Hoeller
* @see SimpleAsyncTaskExecutor
* @see org.springframework.scheduling.timer.TimerTaskExecutor
* @since 2.0
*/
public class SyncTaskExecutor implements TaskExecutor, Serializable {
/**
* Executes the given <code>task</code> synchronously, through direct
* invocation of it's {@link Runnable#run() run()} method.
* @throws IllegalArgumentException if the given <code>task</code> is <code>null</code>
*/
public void execute(Runnable task) {
Assert.notNull(task, "Runnable must not be null");
task.run();
}
}

View File

@ -0,0 +1,47 @@
/*
* 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.task;
/**
* Simple task executor interface that abstracts the execution
* of a {@link Runnable}.
*
* <p>Implementations can use all sorts of different execution strategies,
* such as: synchronous, asynchronous, using a thread pool, and more.
*
* <p>Equivalent to JDK 1.5's {@link java.util.concurrent.Executor}
* interface. Separate mainly for compatibility with JDK 1.4.
* Implementations can simply implement the JDK 1.5 <code>Executor</code>
* interface as well, as it defines the exact same method signature.
*
* @author Juergen Hoeller
* @since 2.0
* @see java.util.concurrent.Executor
*/
public interface TaskExecutor {
/**
* Execute the given <code>task</code>.
* <p>The call might return immediately if the implementation uses
* an asynchronous execution strategy, or might block in the case
* of synchronous execution.
* @param task the <code>Runnable</code> to execute (never <code>null</code>)
* @throws TaskRejectedException if the given task was not accepted
*/
void execute(Runnable task);
}

View File

@ -0,0 +1,53 @@
/*
* 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.task;
import org.springframework.core.NestedRuntimeException;
/**
* Exception thrown when a {@link TaskExecutor} rejects to accept
* a given task for execution.
*
* @author Juergen Hoeller
* @since 2.0.1
* @see TaskExecutor#execute(Runnable)
* @see TaskTimeoutException
*/
public class TaskRejectedException extends NestedRuntimeException {
/**
* Create a new <code>TaskRejectedException</code>
* with the specified detail message and no root cause.
* @param msg the detail message
*/
public TaskRejectedException(String msg) {
super(msg);
}
/**
* Create a new <code>TaskRejectedException</code>
* with the specified detail message and the given root cause.
* @param msg the detail message
* @param cause the root cause (usually from using an underlying
* API such as the <code>java.util.concurrent</code> package)
* @see java.util.concurrent.RejectedExecutionException
*/
public TaskRejectedException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@ -0,0 +1,51 @@
/*
* 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.task;
/**
* Exception thrown when a {@link AsyncTaskExecutor} rejects to accept
* a given task for execution because of the specified timeout.
*
* @author Juergen Hoeller
* @since 2.0.3
* @see AsyncTaskExecutor#execute(Runnable, long)
* @see TaskRejectedException
*/
public class TaskTimeoutException extends TaskRejectedException {
/**
* Create a new <code>TaskTimeoutException</code>
* with the specified detail message and no root cause.
* @param msg the detail message
*/
public TaskTimeoutException(String msg) {
super(msg);
}
/**
* Create a new <code>TaskTimeoutException</code>
* with the specified detail message and the given root cause.
* @param msg the detail message
* @param cause the root cause (usually from using an underlying
* API such as the <code>java.util.concurrent</code> package)
* @see java.util.concurrent.RejectedExecutionException
*/
public TaskTimeoutException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
This package defines Spring's core TaskExecutor abstraction,
and provides SyncTaskExecutor and SimpleAsyncTaskExecutor implementations.
</body>
</html>

View File

@ -0,0 +1,60 @@
/*
* 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.task.support;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.TaskRejectedException;
import org.springframework.util.Assert;
/**
* Adapter that exposes the {@link java.util.concurrent.Executor java.util.concurrent.Executor}
* interface for any Spring {@link org.springframework.core.task.TaskExecutor}.
* Follows the JDK executor contract for exception handling.
*
* @author Juergen Hoeller
* @since 2.5
* @see java.util.concurrent.Executor
* @see org.springframework.core.task.TaskExecutor
*/
public class ConcurrentExecutorAdapter implements Executor {
private final TaskExecutor taskExecutor;
/**
* Create a new ConcurrentExecutorAdapter for the given Spring TaskExecutor.
* @param taskExecutor the Spring TaskExecutor to wrap
*/
public ConcurrentExecutorAdapter(TaskExecutor taskExecutor) {
Assert.notNull(taskExecutor, "TaskExecutor must not be null");
this.taskExecutor = taskExecutor;
}
public void execute(Runnable command) {
try {
this.taskExecutor.execute(command);
}
catch (TaskRejectedException ex) {
throw new RejectedExecutionException(ex.getMessage(), ex);
}
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Support classes for Spring's TaskExecutor abstraction.
Includes an adapter for the JDK 1.5 Executor interface.
</body>
</html>

View File

@ -0,0 +1,98 @@
/*
* 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.metadata;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
/**
* Interface for accessing attributes at runtime. This is a facade,
* which can accommodate any attributes API such as Jakarta Commons Attributes,
* or (possibly in future) a Spring attributes implementation.
*
* <p>The purpose of using this interface is to decouple Spring code from any
* specific attributes implementation. Even once JSR-175 is available, there
* is still value in such a facade interface, as it allows for hierarchical
* attribute sources: for example, an XML file or properties file might override
* some attributes defined in source-level metadata with JSR-175 or another framework.
*
* @author Mark Pollack
* @author Rod Johnson
* @since 30.09.2003
* @see org.springframework.metadata.commons.CommonsAttributes
*/
public interface Attributes {
/**
* Return the class attributes of the target class.
* @param targetClass the class that contains attribute information
* @return a collection of attributes, possibly an empty collection, never <code>null</code>
*/
Collection getAttributes(Class targetClass);
/**
* Return the class attributes of the target class of a given type.
* <p>The class attributes are filtered by providing a <code>Class</code>
* reference to indicate the type to filter on. This is useful if you know
* the type of the attribute you are looking for and don't want to sort
* through the unfiltered Collection yourself.
* @param targetClass the class that contains attribute information
* @param filter specify that only this type of class should be returned
* @return return only the Collection of attributes that are of the filter type
*/
Collection getAttributes(Class targetClass, Class filter);
/**
* Return the method attributes of the target method.
* @param targetMethod the method that contains attribute information
* @return a Collection of attributes, possibly an empty Collection, never <code>null</code>
*/
Collection getAttributes(Method targetMethod);
/**
* Return the method attributes of the target method of a given type.
* <p>The method attributes are filtered by providing a <code>Class</code>
* reference to indicate the type to filter on. This is useful if you know
* the type of the attribute you are looking for and don't want to sort
* through the unfiltered Collection yourself.
* @param targetMethod the method that contains attribute information
* @param filter specify that only this type of class should be returned
* @return a Collection of attributes, possibly an empty Collection, never <code>null</code>
*/
Collection getAttributes(Method targetMethod, Class filter);
/**
* Return the field attributes of the target field.
* @param targetField the field that contains attribute information
* @return a Collection of attribute, possibly an empty Collection, never <code>null</code>
*/
Collection getAttributes(Field targetField);
/**
* Return the field attributes of the target method of a given type.
* <p>The field attributes are filtered by providing a <code>Class</code>
* reference to indicate the type to filter on. This is useful if you know
* the type of the attribute you are looking for and don't want to sort
* through the unfiltered Collection yourself.
* @param targetField the field that contains attribute information
* @param filter specify that only this type of class should be returned
* @return a Collection of attributes, possibly an empty Collection, never <code>null</code>
*/
Collection getAttributes(Field targetField, Class filter);
}

View File

@ -0,0 +1,87 @@
/*
* 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.metadata.commons;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Collection;
import org.springframework.metadata.Attributes;
/**
* Implementation of the Spring Attributes facade for Commons Attributes.
*
* <p>Please see the
* <a href="http://jakarta.apache.org/commons/sandbox/attributes">
* Commons Attributes documentation</a> for information on how to use the
* attribute compiler.
*
* <p>As of December 2003, follow the Javadocs to the AttributeCompiler class
* to see how the Ant task works. Note that you need to put the following jars
* in your $ANT_HOME/lib directory for the Common Attributes compiler to work:
* <ul>
* <li>Commons Attributes compiler jar
* <li>the xjavadoc Jar (from XDoclet)
* <li>commons-collection.jar (from Jakarta Commons)
* </ul>
*
* <p>You need to perform the attribute compilation step before compiling your source.
*
* <p>See build.xml in the tests for package org.springframework.aop.autoproxy.metadata
* for an example of the required Ant scripting. The header of this build script
* includes some quick, and hopefully useful, hints on using Commons Attributes.
* The source files in the same package (TxClass and TxClassWithClassAttribute)
* illustrate attribute usage in source files.
*
* <p>The Spring Framework project does not provide support usage of specific
* attributes implementations. Please refer to the appropriate site and mailing
* list of the attributes implementation.
*
* @author Rod Johnson
*/
public class CommonsAttributes implements Attributes {
/*
* Commons Attributes caches attributes, so we don't need to cache here
* as well.
*/
public Collection getAttributes(Class targetClass) {
return org.apache.commons.attributes.Attributes.getAttributes(targetClass);
}
public Collection getAttributes(Class targetClass, Class filter) {
return org.apache.commons.attributes.Attributes.getAttributes(targetClass, filter);
}
public Collection getAttributes(Method targetMethod) {
return org.apache.commons.attributes.Attributes.getAttributes(targetMethod);
}
public Collection getAttributes(Method targetMethod, Class filter) {
return org.apache.commons.attributes.Attributes.getAttributes(targetMethod, filter);
}
public Collection getAttributes(Field targetField) {
return org.apache.commons.attributes.Attributes.getAttributes(targetField);
}
public Collection getAttributes(Field targetField, Class filter) {
return org.apache.commons.attributes.Attributes.getAttributes(targetField, filter);
}
}

View File

@ -0,0 +1,8 @@
<html>
<body>
Attributes wrapper for
<a href="http://jakarta.apache.org/commons/sandbox/attributes">Commons Attributes</a>.
</body>
</html>

View File

@ -0,0 +1,8 @@
<html>
<body>
Package defining a facade for accessing source-level
metadata attributes at runtime.
</body>
</html>

View File

@ -0,0 +1,411 @@
/*
* 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.util;
/**
* PathMatcher implementation for Ant-style path patterns.
* Examples are provided below.
*
* <p>Part of this mapping code has been kindly borrowed from
* <a href="http://ant.apache.org">Apache Ant</a>.
*
* <p>The mapping matches URLs using the following rules:<br>
* <ul>
* <li>? matches one character</li>
* <li>* matches zero or more characters</li>
* <li>** matches zero or more 'directories' in a path</li>
* </ul>
*
* <p>Some examples:<br>
* <ul>
* <li><code>com/t?st.jsp</code> - matches <code>com/test.jsp</code> but also
* <code>com/tast.jsp</code> or <code>com/txst.jsp</code></li>
* <li><code>com/*.jsp</code> - matches all <code>.jsp</code> files in the
* <code>com</code> directory</li>
* <li><code>com/&#42;&#42;/test.jsp</code> - matches all <code>test.jsp</code>
* files underneath the <code>com</code> path</li>
* <li><code>org/springframework/&#42;&#42;/*.jsp</code> - matches all <code>.jsp</code>
* files underneath the <code>org/springframework</code> path</li>
* <li><code>org/&#42;&#42;/servlet/bla.jsp</code> - matches
* <code>org/springframework/servlet/bla.jsp</code> but also
* <code>org/springframework/testing/servlet/bla.jsp</code> and
* <code>org/servlet/bla.jsp</code></li>
* </ul>
*
* @author Alef Arendsen
* @author Juergen Hoeller
* @author Rob Harrop
* @since 16.07.2003
*/
public class AntPathMatcher implements PathMatcher {
/** Default path separator: "/" */
public static final String DEFAULT_PATH_SEPARATOR = "/";
private String pathSeparator = DEFAULT_PATH_SEPARATOR;
/**
* Set the path separator to use for pattern parsing.
* Default is "/", as in Ant.
*/
public void setPathSeparator(String pathSeparator) {
this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR);
}
public boolean isPattern(String path) {
return (path.indexOf('*') != -1 || path.indexOf('?') != -1);
}
public boolean match(String pattern, String path) {
return doMatch(pattern, path, true);
}
public boolean matchStart(String pattern, String path) {
return doMatch(pattern, path, false);
}
/**
* Actually match the given <code>path</code> against the given <code>pattern</code>.
* @param pattern the pattern to match against
* @param path the path String to test
* @param fullMatch whether a full pattern match is required
* (else a pattern match as far as the given base path goes is sufficient)
* @return <code>true</code> if the supplied <code>path</code> matched,
* <code>false</code> if it didn't
*/
protected boolean doMatch(String pattern, String path, boolean fullMatch) {
if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) {
return false;
}
String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);
String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator);
int pattIdxStart = 0;
int pattIdxEnd = pattDirs.length - 1;
int pathIdxStart = 0;
int pathIdxEnd = pathDirs.length - 1;
// Match all elements up to the first **
while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
String patDir = pattDirs[pattIdxStart];
if ("**".equals(patDir)) {
break;
}
if (!matchStrings(patDir, pathDirs[pathIdxStart])) {
return false;
}
pattIdxStart++;
pathIdxStart++;
}
if (pathIdxStart > pathIdxEnd) {
// Path is exhausted, only match if rest of pattern is * or **'s
if (pattIdxStart > pattIdxEnd) {
return (pattern.endsWith(this.pathSeparator) ?
path.endsWith(this.pathSeparator) : !path.endsWith(this.pathSeparator));
}
if (!fullMatch) {
return true;
}
if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") &&
path.endsWith(this.pathSeparator)) {
return true;
}
for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
if (!pattDirs[i].equals("**")) {
return false;
}
}
return true;
}
else if (pattIdxStart > pattIdxEnd) {
// String not exhausted, but pattern is. Failure.
return false;
}
else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) {
// Path start definitely matches due to "**" part in pattern.
return true;
}
// up to last '**'
while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) {
String patDir = pattDirs[pattIdxEnd];
if (patDir.equals("**")) {
break;
}
if (!matchStrings(patDir, pathDirs[pathIdxEnd])) {
return false;
}
pattIdxEnd--;
pathIdxEnd--;
}
if (pathIdxStart > pathIdxEnd) {
// String is exhausted
for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
if (!pattDirs[i].equals("**")) {
return false;
}
}
return true;
}
while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) {
int patIdxTmp = -1;
for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) {
if (pattDirs[i].equals("**")) {
patIdxTmp = i;
break;
}
}
if (patIdxTmp == pattIdxStart + 1) {
// '**/**' situation, so skip one
pattIdxStart++;
continue;
}
// Find the pattern between padIdxStart & padIdxTmp in str between
// strIdxStart & strIdxEnd
int patLength = (patIdxTmp - pattIdxStart - 1);
int strLength = (pathIdxEnd - pathIdxStart + 1);
int foundIdx = -1;
strLoop:
for (int i = 0; i <= strLength - patLength; i++) {
for (int j = 0; j < patLength; j++) {
String subPat = (String) pattDirs[pattIdxStart + j + 1];
String subStr = (String) pathDirs[pathIdxStart + i + j];
if (!matchStrings(subPat, subStr)) {
continue strLoop;
}
}
foundIdx = pathIdxStart + i;
break;
}
if (foundIdx == -1) {
return false;
}
pattIdxStart = patIdxTmp;
pathIdxStart = foundIdx + patLength;
}
for (int i = pattIdxStart; i <= pattIdxEnd; i++) {
if (!pattDirs[i].equals("**")) {
return false;
}
}
return true;
}
/**
* Tests whether or not a string matches against a pattern.
* The pattern may contain two special characters:<br>
* '*' means zero or more characters<br>
* '?' means one and only one character
* @param pattern pattern to match against.
* Must not be <code>null</code>.
* @param str string which must be matched against the pattern.
* Must not be <code>null</code>.
* @return <code>true</code> if the string matches against the
* pattern, or <code>false</code> otherwise.
*/
private boolean matchStrings(String pattern, String str) {
char[] patArr = pattern.toCharArray();
char[] strArr = str.toCharArray();
int patIdxStart = 0;
int patIdxEnd = patArr.length - 1;
int strIdxStart = 0;
int strIdxEnd = strArr.length - 1;
char ch;
boolean containsStar = false;
for (int i = 0; i < patArr.length; i++) {
if (patArr[i] == '*') {
containsStar = true;
break;
}
}
if (!containsStar) {
// No '*'s, so we make a shortcut
if (patIdxEnd != strIdxEnd) {
return false; // Pattern and string do not have the same size
}
for (int i = 0; i <= patIdxEnd; i++) {
ch = patArr[i];
if (ch != '?') {
if (ch != strArr[i]) {
return false;// Character mismatch
}
}
}
return true; // String matches against pattern
}
if (patIdxEnd == 0) {
return true; // Pattern contains only '*', which matches anything
}
// Process characters before first star
while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
if (ch != '?') {
if (ch != strArr[strIdxStart]) {
return false;// Character mismatch
}
}
patIdxStart++;
strIdxStart++;
}
if (strIdxStart > strIdxEnd) {
// All characters in the string are used. Check if only '*'s are
// left in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
// Process characters after last star
while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
if (ch != '?') {
if (ch != strArr[strIdxEnd]) {
return false;// Character mismatch
}
}
patIdxEnd--;
strIdxEnd--;
}
if (strIdxStart > strIdxEnd) {
// All characters in the string are used. Check if only '*'s are
// left in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
// process pattern between stars. padIdxStart and patIdxEnd point
// always to a '*'.
while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
int patIdxTmp = -1;
for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
if (patArr[i] == '*') {
patIdxTmp = i;
break;
}
}
if (patIdxTmp == patIdxStart + 1) {
// Two stars next to each other, skip the first one.
patIdxStart++;
continue;
}
// Find the pattern between padIdxStart & padIdxTmp in str between
// strIdxStart & strIdxEnd
int patLength = (patIdxTmp - patIdxStart - 1);
int strLength = (strIdxEnd - strIdxStart + 1);
int foundIdx = -1;
strLoop:
for (int i = 0; i <= strLength - patLength; i++) {
for (int j = 0; j < patLength; j++) {
ch = patArr[patIdxStart + j + 1];
if (ch != '?') {
if (ch != strArr[strIdxStart + i + j]) {
continue strLoop;
}
}
}
foundIdx = strIdxStart + i;
break;
}
if (foundIdx == -1) {
return false;
}
patIdxStart = patIdxTmp;
strIdxStart = foundIdx + patLength;
}
// All characters in the string are used. Check if only '*'s are left
// in the pattern. If so, we succeeded. Otherwise failure.
for (int i = patIdxStart; i <= patIdxEnd; i++) {
if (patArr[i] != '*') {
return false;
}
}
return true;
}
/**
* Given a pattern and a full path, determine the pattern-mapped part.
* <p>For example:
* <ul>
* <li>'<code>/docs/cvs/commit.html</code>' and '<code>/docs/cvs/commit.html</code> -> ''</li>
* <li>'<code>/docs/*</code>' and '<code>/docs/cvs/commit</code> -> '<code>cvs/commit</code>'</li>
* <li>'<code>/docs/cvs/*.html</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>commit.html</code>'</li>
* <li>'<code>/docs/**</code>' and '<code>/docs/cvs/commit</code> -> '<code>cvs/commit</code>'</li>
* <li>'<code>/docs/**\/*.html</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>cvs/commit.html</code>'</li>
* <li>'<code>/*.html</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>docs/cvs/commit.html</code>'</li>
* <li>'<code>*.html</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>/docs/cvs/commit.html</code>'</li>
* <li>'<code>*</code>' and '<code>/docs/cvs/commit.html</code> -> '<code>/docs/cvs/commit.html</code>'</li>
* </ul>
* <p>Assumes that {@link #match} returns <code>true</code> for '<code>pattern</code>'
* and '<code>path</code>', but does <strong>not</strong> enforce this.
*/
public String extractPathWithinPattern(String pattern, String path) {
String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator);
String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator);
StringBuffer buffer = new StringBuffer();
// Add any path parts that have a wildcarded pattern part.
int puts = 0;
for (int i = 0; i < patternParts.length; i++) {
String patternPart = patternParts[i];
if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) {
if (puts > 0 || (i == 0 && !pattern.startsWith(this.pathSeparator))) {
buffer.append(this.pathSeparator);
}
buffer.append(pathParts[i]);
puts++;
}
}
// Append any trailing path parts.
for (int i = patternParts.length; i < pathParts.length; i++) {
if (puts > 0 || i > 0) {
buffer.append(this.pathSeparator);
}
buffer.append(pathParts[i]);
}
return buffer.toString();
}
}

View File

@ -0,0 +1,401 @@
/*
* 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.util;
import java.util.Collection;
import java.util.Map;
/**
* Assertion utility class that assists in validating arguments.
* Useful for identifying programmer errors early and clearly at runtime.
*
* <p>For example, if the contract of a public method states it does not
* allow <code>null</code> arguments, Assert can be used to validate that
* contract. Doing this clearly indicates a contract violation when it
* occurs and protects the class's invariants.
*
* <p>Typically used to validate method arguments rather than configuration
* properties, to check for cases that are usually programmer errors rather than
* configuration errors. In contrast to config initialization code, there is
* usally no point in falling back to defaults in such methods.
*
* <p>This class is similar to JUnit's assertion library. If an argument value is
* deemed invalid, an {@link IllegalArgumentException} is thrown (typically).
* For example:
*
* <pre class="code">
* Assert.notNull(clazz, "The class must not be null");
* Assert.isTrue(i > 0, "The value must be greater than zero");</pre>
*
* Mainly for internal use within the framework; consider Jakarta's Commons Lang
* >= 2.0 for a more comprehensive suite of assertion utilities.
*
* @author Keith Donald
* @author Juergen Hoeller
* @author Colin Sampaleanu
* @author Rob Harrop
* @since 1.1.2
*/
public abstract class Assert {
/**
* Assert a boolean expression, throwing <code>IllegalArgumentException</code>
* if the test result is <code>false</code>.
* <pre class="code">Assert.isTrue(i &gt; 0, "The value must be greater than zero");</pre>
* @param expression a boolean expression
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if expression is <code>false</code>
*/
public static void isTrue(boolean expression, String message) {
if (!expression) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert a boolean expression, throwing <code>IllegalArgumentException</code>
* if the test result is <code>false</code>.
* <pre class="code">Assert.isTrue(i &gt; 0);</pre>
* @param expression a boolean expression
* @throws IllegalArgumentException if expression is <code>false</code>
*/
public static void isTrue(boolean expression) {
isTrue(expression, "[Assertion failed] - this expression must be true");
}
/**
* Assert that an object is <code>null</code> .
* <pre class="code">Assert.isNull(value, "The value must be null");</pre>
* @param object the object to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object is not <code>null</code>
*/
public static void isNull(Object object, String message) {
if (object != null) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that an object is <code>null</code> .
* <pre class="code">Assert.isNull(value);</pre>
* @param object the object to check
* @throws IllegalArgumentException if the object is not <code>null</code>
*/
public static void isNull(Object object) {
isNull(object, "[Assertion failed] - the object argument must be null");
}
/**
* Assert that an object is not <code>null</code> .
* <pre class="code">Assert.notNull(clazz, "The class must not be null");</pre>
* @param object the object to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object is <code>null</code>
*/
public static void notNull(Object object, String message) {
if (object == null) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that an object is not <code>null</code> .
* <pre class="code">Assert.notNull(clazz);</pre>
* @param object the object to check
* @throws IllegalArgumentException if the object is <code>null</code>
*/
public static void notNull(Object object) {
notNull(object, "[Assertion failed] - this argument is required; it must not be null");
}
/**
* Assert that the given String is not empty; that is,
* it must not be <code>null</code> and not the empty String.
* <pre class="code">Assert.hasLength(name, "Name must not be empty");</pre>
* @param text the String to check
* @param message the exception message to use if the assertion fails
* @see StringUtils#hasLength
*/
public static void hasLength(String text, String message) {
if (!StringUtils.hasLength(text)) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that the given String is not empty; that is,
* it must not be <code>null</code> and not the empty String.
* <pre class="code">Assert.hasLength(name);</pre>
* @param text the String to check
* @see StringUtils#hasLength
*/
public static void hasLength(String text) {
hasLength(text,
"[Assertion failed] - this String argument must have length; it must not be null or empty");
}
/**
* Assert that the given String has valid text content; that is, it must not
* be <code>null</code> and must contain at least one non-whitespace character.
* <pre class="code">Assert.hasText(name, "'name' must not be empty");</pre>
* @param text the String to check
* @param message the exception message to use if the assertion fails
* @see StringUtils#hasText
*/
public static void hasText(String text, String message) {
if (!StringUtils.hasText(text)) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that the given String has valid text content; that is, it must not
* be <code>null</code> and must contain at least one non-whitespace character.
* <pre class="code">Assert.hasText(name, "'name' must not be empty");</pre>
* @param text the String to check
* @see StringUtils#hasText
*/
public static void hasText(String text) {
hasText(text,
"[Assertion failed] - this String argument must have text; it must not be null, empty, or blank");
}
/**
* Assert that the given text does not contain the given substring.
* <pre class="code">Assert.doesNotContain(name, "rod", "Name must not contain 'rod'");</pre>
* @param textToSearch the text to search
* @param substring the substring to find within the text
* @param message the exception message to use if the assertion fails
*/
public static void doesNotContain(String textToSearch, String substring, String message) {
if (StringUtils.hasLength(textToSearch) && StringUtils.hasLength(substring) &&
textToSearch.indexOf(substring) != -1) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that the given text does not contain the given substring.
* <pre class="code">Assert.doesNotContain(name, "rod");</pre>
* @param textToSearch the text to search
* @param substring the substring to find within the text
*/
public static void doesNotContain(String textToSearch, String substring) {
doesNotContain(textToSearch, substring,
"[Assertion failed] - this String argument must not contain the substring [" + substring + "]");
}
/**
* Assert that an array has elements; that is, it must not be
* <code>null</code> and must have at least one element.
* <pre class="code">Assert.notEmpty(array, "The array must have elements");</pre>
* @param array the array to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object array is <code>null</code> or has no elements
*/
public static void notEmpty(Object[] array, String message) {
if (ObjectUtils.isEmpty(array)) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that an array has elements; that is, it must not be
* <code>null</code> and must have at least one element.
* <pre class="code">Assert.notEmpty(array);</pre>
* @param array the array to check
* @throws IllegalArgumentException if the object array is <code>null</code> or has no elements
*/
public static void notEmpty(Object[] array) {
notEmpty(array, "[Assertion failed] - this array must not be empty: it must contain at least 1 element");
}
/**
* Assert that an array has no null elements.
* Note: Does not complain if the array is empty!
* <pre class="code">Assert.noNullElements(array, "The array must have non-null elements");</pre>
* @param array the array to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the object array contains a <code>null</code> element
*/
public static void noNullElements(Object[] array, String message) {
if (array != null) {
for (int i = 0; i < array.length; i++) {
if (array[i] == null) {
throw new IllegalArgumentException(message);
}
}
}
}
/**
* Assert that an array has no null elements.
* Note: Does not complain if the array is empty!
* <pre class="code">Assert.noNullElements(array);</pre>
* @param array the array to check
* @throws IllegalArgumentException if the object array contains a <code>null</code> element
*/
public static void noNullElements(Object[] array) {
noNullElements(array, "[Assertion failed] - this array must not contain any null elements");
}
/**
* Assert that a collection has elements; that is, it must not be
* <code>null</code> and must have at least one element.
* <pre class="code">Assert.notEmpty(collection, "Collection must have elements");</pre>
* @param collection the collection to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the collection is <code>null</code> or has no elements
*/
public static void notEmpty(Collection collection, String message) {
if (CollectionUtils.isEmpty(collection)) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that a collection has elements; that is, it must not be
* <code>null</code> and must have at least one element.
* <pre class="code">Assert.notEmpty(collection, "Collection must have elements");</pre>
* @param collection the collection to check
* @throws IllegalArgumentException if the collection is <code>null</code> or has no elements
*/
public static void notEmpty(Collection collection) {
notEmpty(collection,
"[Assertion failed] - this collection must not be empty: it must contain at least 1 element");
}
/**
* Assert that a Map has entries; that is, it must not be <code>null</code>
* and must have at least one entry.
* <pre class="code">Assert.notEmpty(map, "Map must have entries");</pre>
* @param map the map to check
* @param message the exception message to use if the assertion fails
* @throws IllegalArgumentException if the map is <code>null</code> or has no entries
*/
public static void notEmpty(Map map, String message) {
if (CollectionUtils.isEmpty(map)) {
throw new IllegalArgumentException(message);
}
}
/**
* Assert that a Map has entries; that is, it must not be <code>null</code>
* and must have at least one entry.
* <pre class="code">Assert.notEmpty(map);</pre>
* @param map the map to check
* @throws IllegalArgumentException if the map is <code>null</code> or has no entries
*/
public static void notEmpty(Map map) {
notEmpty(map, "[Assertion failed] - this map must not be empty; it must contain at least one entry");
}
/**
* Assert that the provided object is an instance of the provided class.
* <pre class="code">Assert.instanceOf(Foo.class, foo);</pre>
* @param clazz the required class
* @param obj the object to check
* @throws IllegalArgumentException if the object is not an instance of clazz
* @see Class#isInstance
*/
public static void isInstanceOf(Class clazz, Object obj) {
isInstanceOf(clazz, obj, "");
}
/**
* Assert that the provided object is an instance of the provided class.
* <pre class="code">Assert.instanceOf(Foo.class, foo);</pre>
* @param type the type to check against
* @param obj the object to check
* @param message a message which will be prepended to the message produced by
* the function itself, and which may be used to provide context. It should
* normally end in a ": " or ". " so that the function generate message looks
* ok when prepended to it.
* @throws IllegalArgumentException if the object is not an instance of clazz
* @see Class#isInstance
*/
public static void isInstanceOf(Class type, Object obj, String message) {
notNull(type, "Type to check against must not be null");
if (!type.isInstance(obj)) {
throw new IllegalArgumentException(message +
"Object of class [" + (obj != null ? obj.getClass().getName() : "null") +
"] must be an instance of " + type);
}
}
/**
* Assert that <code>superType.isAssignableFrom(subType)</code> is <code>true</code>.
* <pre class="code">Assert.isAssignable(Number.class, myClass);</pre>
* @param superType the super type to check
* @param subType the sub type to check
* @throws IllegalArgumentException if the classes are not assignable
*/
public static void isAssignable(Class superType, Class subType) {
isAssignable(superType, subType, "");
}
/**
* Assert that <code>superType.isAssignableFrom(subType)</code> is <code>true</code>.
* <pre class="code">Assert.isAssignable(Number.class, myClass);</pre>
* @param superType the super type to check against
* @param subType the sub type to check
* @param message a message which will be prepended to the message produced by
* the function itself, and which may be used to provide context. It should
* normally end in a ": " or ". " so that the function generate message looks
* ok when prepended to it.
* @throws IllegalArgumentException if the classes are not assignable
*/
public static void isAssignable(Class superType, Class subType, String message) {
notNull(superType, "Type to check against must not be null");
if (subType == null || !superType.isAssignableFrom(subType)) {
throw new IllegalArgumentException(message + subType + " is not assignable to " + superType);
}
}
/**
* Assert a boolean expression, throwing <code>IllegalStateException</code>
* if the test result is <code>false</code>. Call isTrue if you wish to
* throw IllegalArgumentException on an assertion failure.
* <pre class="code">Assert.state(id == null, "The id property must not already be initialized");</pre>
* @param expression a boolean expression
* @param message the exception message to use if the assertion fails
* @throws IllegalStateException if expression is <code>false</code>
*/
public static void state(boolean expression, String message) {
if (!expression) {
throw new IllegalStateException(message);
}
}
/**
* Assert a boolean expression, throwing {@link IllegalStateException}
* if the test result is <code>false</code>.
* <p>Call {@link #isTrue(boolean)} if you wish to
* throw {@link IllegalArgumentException} on an assertion failure.
* <pre class="code">Assert.state(id == null);</pre>
* @param expression a boolean expression
* @throws IllegalStateException if the supplied expression is <code>false</code>
*/
public static void state(boolean expression) {
state(expression, "[Assertion failed] - this state invariant must be true");
}
}

View File

@ -0,0 +1,274 @@
/*
* 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.util;
import java.io.Serializable;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* Simple {@link List} wrapper class that allows for elements to be
* automatically populated as they are requested. This is particularly
* useful for data binding to {@link List Lists}, allowing for elements
* to be created and added to the {@link List} in a "just in time" fashion.
*
* <p>Note: This class is not thread-safe. To create a thread-safe version,
* use the {@link java.util.Collections#synchronizedList} utility methods.
*
* <p>Inspired by <code>LazyList</code> from Commons Collections.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
*/
public class AutoPopulatingList implements List, Serializable {
/**
* The {@link List} that all operations are eventually delegated to.
*/
private final List backingList;
/**
* The {@link ElementFactory} to use to create new {@link List} elements
* on demand.
*/
private final ElementFactory elementFactory;
/**
* Creates a new <code>AutoPopulatingList</code> that is backed by a standard
* {@link ArrayList} and adds new instances of the supplied {@link Class element Class}
* to the backing {@link List} on demand.
*/
public AutoPopulatingList(Class elementClass) {
this(new ArrayList(), elementClass);
}
/**
* Creates a new <code>AutoPopulatingList</code> that is backed by the supplied {@link List}
* and adds new instances of the supplied {@link Class element Class} to the backing
* {@link List} on demand.
*/
public AutoPopulatingList(List backingList, Class elementClass) {
this(backingList, new ReflectiveElementFactory(elementClass));
}
/**
* Creates a new <code>AutoPopulatingList</code> that is backed by a standard
* {@link ArrayList} and creates new elements on demand using the supplied {@link ElementFactory}.
*/
public AutoPopulatingList(ElementFactory elementFactory) {
this(new ArrayList(), elementFactory);
}
/**
* Creates a new <code>AutoPopulatingList</code> that is backed by the supplied {@link List}
* and creates new elements on demand using the supplied {@link ElementFactory}.
*/
public AutoPopulatingList(List backingList, ElementFactory elementFactory) {
Assert.notNull(backingList, "Backing List must not be null");
Assert.notNull(elementFactory, "Element factory must not be null");
this.backingList = backingList;
this.elementFactory = elementFactory;
}
public void add(int index, Object element) {
this.backingList.add(index, element);
}
public boolean add(Object o) {
return this.backingList.add(o);
}
public boolean addAll(Collection c) {
return this.backingList.addAll(c);
}
public boolean addAll(int index, Collection c) {
return this.backingList.addAll(index, c);
}
public void clear() {
this.backingList.clear();
}
public boolean contains(Object o) {
return this.backingList.contains(o);
}
public boolean containsAll(Collection c) {
return this.backingList.containsAll(c);
}
public boolean equals(Object o) {
return this.backingList.equals(o);
}
/**
* Get the element at the supplied index, creating it if there is
* no element at that index.
*/
public Object get(int index) {
int backingListSize = this.backingList.size();
Object element = null;
if (index < backingListSize) {
element = this.backingList.get(index);
if (element == null) {
element = this.elementFactory.createElement(index);
this.backingList.set(index, element);
}
}
else {
for (int x = backingListSize; x < index; x++) {
this.backingList.add(null);
}
element = this.elementFactory.createElement(index);
this.backingList.add(element);
}
return element;
}
public int hashCode() {
return this.backingList.hashCode();
}
public int indexOf(Object o) {
return this.backingList.indexOf(o);
}
public boolean isEmpty() {
return this.backingList.isEmpty();
}
public Iterator iterator() {
return this.backingList.iterator();
}
public int lastIndexOf(Object o) {
return this.backingList.lastIndexOf(o);
}
public ListIterator listIterator() {
return this.backingList.listIterator();
}
public ListIterator listIterator(int index) {
return this.backingList.listIterator(index);
}
public Object remove(int index) {
return this.backingList.remove(index);
}
public boolean remove(Object o) {
return this.backingList.remove(o);
}
public boolean removeAll(Collection c) {
return this.backingList.removeAll(c);
}
public boolean retainAll(Collection c) {
return this.backingList.retainAll(c);
}
public Object set(int index, Object element) {
return this.backingList.set(index, element);
}
public int size() {
return this.backingList.size();
}
public List subList(int fromIndex, int toIndex) {
return this.backingList.subList(fromIndex, toIndex);
}
public Object[] toArray() {
return this.backingList.toArray();
}
public Object[] toArray(Object[] a) {
return this.backingList.toArray(a);
}
/**
* Factory interface for creating elements for an index-based access
* data structure such as a {@link java.util.List}.
*/
public interface ElementFactory {
/**
* Create the element for the supplied index.
* @return the element object
* @throws ElementInstantiationException if the instantiation process failed
* (any exception thrown by a target constructor should be propagated as-is)
*/
Object createElement(int index) throws ElementInstantiationException;
}
/**
* Exception to be thrown from ElementFactory.
*/
public static class ElementInstantiationException extends RuntimeException {
public ElementInstantiationException(String msg) {
super(msg);
}
}
/**
* Reflective implementation of the ElementFactory interface,
* using <code>Class.newInstance()</code> on a given element class.
* @see java.lang.Class#newInstance()
*/
private static class ReflectiveElementFactory implements ElementFactory, Serializable {
private final Class elementClass;
public ReflectiveElementFactory(Class elementClass) {
Assert.notNull(elementClass, "Element clas must not be null");
Assert.isTrue(!elementClass.isInterface(), "Element class must not be an interface type");
Assert.isTrue(!Modifier.isAbstract(elementClass.getModifiers()), "Element class cannot be an abstract class");
this.elementClass = elementClass;
}
public Object createElement(int index) {
try {
return this.elementClass.newInstance();
}
catch (InstantiationException ex) {
throw new ElementInstantiationException("Unable to instantiate element class [" +
this.elementClass.getName() + "]. Root cause is " + ex);
}
catch (IllegalAccessException ex) {
throw new ElementInstantiationException("Cannot access element class [" +
this.elementClass.getName() + "]. Root cause is " + ex);
}
}
}
}

View File

@ -0,0 +1,276 @@
/*
* 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.util;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
/**
* A simple decorator for a Map, encapsulating the workflow for caching
* expensive values in a target Map. Supports caching weak or strong keys.
*
* <p>This class is also an abstract template. Caching Map implementations
* should subclass and override the <code>create(key)</code> method which
* encapsulates expensive creation of a new object.
*
* @author Keith Donald
* @author Juergen Hoeller
* @since 1.2.2
*/
public class CachingMapDecorator implements Map, Serializable {
protected static Object NULL_VALUE = new Object();
private final Map targetMap;
private final boolean synchronize;
private final boolean weak;
/**
* Create a CachingMapDecorator with strong keys,
* using an underlying synchronized Map.
*/
public CachingMapDecorator() {
this(false);
}
/**
* Create a CachingMapDecorator,
* using an underlying synchronized Map.
* @param weak whether to use weak references for keys and values
*/
public CachingMapDecorator(boolean weak) {
Map internalMap = weak ? (Map) new WeakHashMap() : new HashMap();
this.targetMap = Collections.synchronizedMap(internalMap);
this.synchronize = true;
this.weak = weak;
}
/**
* Create a CachingMapDecorator with initial size,
* using an underlying synchronized Map.
* @param weak whether to use weak references for keys and values
* @param size the initial cache size
*/
public CachingMapDecorator(boolean weak, int size) {
Map internalMap = weak ? (Map) new WeakHashMap(size) : new HashMap(size);
this.targetMap = Collections.synchronizedMap(internalMap);
this.synchronize = true;
this.weak = weak;
}
/**
* Create a CachingMapDecorator for the given Map.
* <p>The passed-in Map won't get synchronized explicitly,
* so make sure to pass in a properly synchronized Map, if desired.
* @param targetMap the Map to decorate
*/
public CachingMapDecorator(Map targetMap) {
this(targetMap, false, false);
}
/**
* Create a CachingMapDecorator for the given Map.
* <p>The passed-in Map won't get synchronized explicitly unless
* you specify "synchronize" as "true".
* @param targetMap the Map to decorate
* @param synchronize whether to synchronize on the given Map
* @param weak whether to use weak references for values
*/
public CachingMapDecorator(Map targetMap, boolean synchronize, boolean weak) {
Assert.notNull(targetMap, "Target Map is required");
this.targetMap = (synchronize ? Collections.synchronizedMap(targetMap) : targetMap);
this.synchronize = synchronize;
this.weak = weak;
}
public int size() {
return this.targetMap.size();
}
public boolean isEmpty() {
return this.targetMap.isEmpty();
}
public boolean containsKey(Object key) {
return this.targetMap.containsKey(key);
}
public boolean containsValue(Object value) {
Object valueToCheck = value;
if (valueToCheck == null) {
valueToCheck = NULL_VALUE;
}
if (this.synchronize) {
synchronized (this.targetMap) {
return containsValueOrReference(valueToCheck);
}
}
else {
return containsValueOrReference(valueToCheck);
}
}
private boolean containsValueOrReference(Object value) {
if (this.targetMap.containsValue(value)) {
return true;
}
for (Iterator it = this.targetMap.values().iterator(); it.hasNext();) {
Object mapVal = it.next();
if (mapVal instanceof Reference && value.equals(((Reference) mapVal).get())) {
return true;
}
}
return false;
}
public Object remove(Object key) {
return this.targetMap.remove(key);
}
public void putAll(Map map) {
this.targetMap.putAll(map);
}
public void clear() {
this.targetMap.clear();
}
public Set keySet() {
if (this.synchronize) {
synchronized (this.targetMap) {
return new LinkedHashSet(this.targetMap.keySet());
}
}
else {
return new LinkedHashSet(this.targetMap.keySet());
}
}
public Collection values() {
if (this.synchronize) {
synchronized (this.targetMap) {
return valuesCopy();
}
}
else {
return valuesCopy();
}
}
private Collection valuesCopy() {
LinkedList values = new LinkedList();
for (Iterator it = this.targetMap.values().iterator(); it.hasNext();) {
Object value = it.next();
values.add(value instanceof Reference ? ((Reference) value).get() : value);
}
return values;
}
public Set entrySet() {
if (this.synchronize) {
synchronized (this.targetMap) {
return new LinkedHashSet(this.targetMap.entrySet());
}
}
else {
return new LinkedHashSet(this.targetMap.entrySet());
}
}
/**
* Put an object into the cache, possibly wrapping it with a weak
* reference.
* @see #useWeakValue(Object, Object)
*/
public Object put(Object key, Object value) {
Object newValue = value;
if (newValue == null) {
newValue = NULL_VALUE;
}
if (useWeakValue(key, newValue)) {
newValue = new WeakReference(newValue);
}
return this.targetMap.put(key, newValue);
}
/**
* Decide whether use a weak reference for the value of
* the given key-value pair.
* @param key the candidate key
* @param value the candidate value
* @return <code>true</code> in order to use a weak reference;
* <code>false</code> otherwise.
*/
protected boolean useWeakValue(Object key, Object value) {
return this.weak;
}
/**
* Get value for key.
* Creates and caches value if it doesn't already exist in the cache.
* <p>This implementation is <i>not</i> synchronized: This is highly
* concurrent but does not guarantee unique instances in the cache,
* as multiple values for the same key could get created in parallel.
* Consider overriding this method to synchronize it, if desired.
* @see #create(Object)
*/
public Object get(Object key) {
Object value = this.targetMap.get(key);
if (value instanceof Reference) {
value = ((Reference) value).get();
}
if (value == null) {
value = create(key);
if (value != null) {
put(key, value);
}
}
return (value == NULL_VALUE ? null : value);
}
/**
* Create a value to cache for the given key.
* Called by <code>get</code> if there is no value cached already.
* @param key the cache key
* @see #get(Object)
*/
protected Object create(Object key) {
return null;
}
public String toString() {
return "CachingMapDecorator [" + getClass().getName() + "]:" + this.targetMap;
}
}

View File

@ -0,0 +1,100 @@
/*
* 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.util;
/**
* Utility class for diagnostic purposes, to analyze the
* ClassLoader hierarchy for any given object or class loader.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 02 April 2001
* @deprecated as of Spring 2.5, to be removed in Spring 3.0
* @see java.lang.ClassLoader
*/
public abstract class ClassLoaderUtils {
/**
* Show the class loader hierarchy for this class.
* Uses default line break and tab text characters.
* @param obj object to analyze loader hierarchy for
* @param role a description of the role of this class in the application
* (e.g., "servlet" or "EJB reference")
* @return a String showing the class loader hierarchy for this class
*/
public static String showClassLoaderHierarchy(Object obj, String role) {
return showClassLoaderHierarchy(obj, role, "\n", "\t");
}
/**
* Show the class loader hierarchy for this class.
* @param obj object to analyze loader hierarchy for
* @param role a description of the role of this class in the application
* (e.g., "servlet" or "EJB reference")
* @param lineBreak line break
* @param tabText text to use to set tabs
* @return a String showing the class loader hierarchy for this class
*/
public static String showClassLoaderHierarchy(Object obj, String role, String lineBreak, String tabText) {
String s = "object of " + obj.getClass() + ": role is " + role + lineBreak;
return s + showClassLoaderHierarchy(obj.getClass().getClassLoader(), lineBreak, tabText, 0);
}
/**
* Show the class loader hierarchy for the given class loader.
* Uses default line break and tab text characters.
* @param cl class loader to analyze hierarchy for
* @return a String showing the class loader hierarchy for this class
*/
public static String showClassLoaderHierarchy(ClassLoader cl) {
return showClassLoaderHierarchy(cl, "\n", "\t");
}
/**
* Show the class loader hierarchy for the given class loader.
* @param cl class loader to analyze hierarchy for
* @param lineBreak line break
* @param tabText text to use to set tabs
* @return a String showing the class loader hierarchy for this class
*/
public static String showClassLoaderHierarchy(ClassLoader cl, String lineBreak, String tabText) {
return showClassLoaderHierarchy(cl, lineBreak, tabText, 0);
}
/**
* Show the class loader hierarchy for the given class loader.
* @param cl class loader to analyze hierarchy for
* @param lineBreak line break
* @param tabText text to use to set tabs
* @param indent nesting level (from 0) of this loader; used in pretty printing
* @return a String showing the class loader hierarchy for this class
*/
private static String showClassLoaderHierarchy(ClassLoader cl, String lineBreak, String tabText, int indent) {
if (cl == null) {
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
return "context class loader=[" + ccl + "] hashCode=" + ccl.hashCode();
}
StringBuffer buf = new StringBuffer();
for (int i = 0; i < indent; i++) {
buf.append(tabText);
}
buf.append("[").append(cl).append("] hashCode=").append(cl.hashCode()).append(lineBreak);
ClassLoader parent = cl.getParent();
return buf.toString() + showClassLoaderHierarchy(parent, lineBreak, tabText, indent + 1);
}
}

View File

@ -0,0 +1,982 @@
/*
* 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.util;
import java.beans.Introspector;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Miscellaneous class utility methods. Mainly for internal use within the
* framework; consider Jakarta's Commons Lang for a more comprehensive suite
* of class utilities.
*
* @author Keith Donald
* @author Rob Harrop
* @author Juergen Hoeller
* @since 1.1
* @see TypeUtils
* @see ReflectionUtils
*/
public abstract class ClassUtils {
/** Suffix for array class names: "[]" */
public static final String ARRAY_SUFFIX = "[]";
/** Prefix for internal array class names: "[L" */
private static final String INTERNAL_ARRAY_PREFIX = "[L";
/** The package separator character '.' */
private static final char PACKAGE_SEPARATOR = '.';
/** The inner class separator character '$' */
private static final char INNER_CLASS_SEPARATOR = '$';
/** The CGLIB class separator character "$$" */
public static final String CGLIB_CLASS_SEPARATOR = "$$";
/** The ".class" file suffix */
public static final String CLASS_FILE_SUFFIX = ".class";
/**
* Map with primitive wrapper type as key and corresponding primitive
* type as value, for example: Integer.class -> int.class.
*/
private static final Map primitiveWrapperTypeMap = new HashMap(8);
/**
* Map with primitive type name as key and corresponding primitive
* type as value, for example: "int" -> "int.class".
*/
private static final Map primitiveTypeNameMap = new HashMap(16);
static {
primitiveWrapperTypeMap.put(Boolean.class, boolean.class);
primitiveWrapperTypeMap.put(Byte.class, byte.class);
primitiveWrapperTypeMap.put(Character.class, char.class);
primitiveWrapperTypeMap.put(Double.class, double.class);
primitiveWrapperTypeMap.put(Float.class, float.class);
primitiveWrapperTypeMap.put(Integer.class, int.class);
primitiveWrapperTypeMap.put(Long.class, long.class);
primitiveWrapperTypeMap.put(Short.class, short.class);
Set primitiveTypeNames = new HashSet(16);
primitiveTypeNames.addAll(primitiveWrapperTypeMap.values());
primitiveTypeNames.addAll(Arrays.asList(new Class[] {
boolean[].class, byte[].class, char[].class, double[].class,
float[].class, int[].class, long[].class, short[].class}));
for (Iterator it = primitiveTypeNames.iterator(); it.hasNext();) {
Class primitiveClass = (Class) it.next();
primitiveTypeNameMap.put(primitiveClass.getName(), primitiveClass);
}
}
/**
* Return the default ClassLoader to use: typically the thread context
* ClassLoader, if available; the ClassLoader that loaded the ClassUtils
* class will be used as fallback.
* <p>Call this method if you intend to use the thread context ClassLoader
* in a scenario where you absolutely need a non-null ClassLoader reference:
* for example, for class path resource loading (but not necessarily for
* <code>Class.forName</code>, which accepts a <code>null</code> ClassLoader
* reference as well).
* @return the default ClassLoader (never <code>null</code>)
* @see java.lang.Thread#getContextClassLoader()
*/
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back to system class loader...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
}
return cl;
}
/**
* Override the thread context ClassLoader with the environment's bean ClassLoader
* if necessary, i.e. if the bean ClassLoader is not equivalent to the thread
* context ClassLoader already.
* @param classLoaderToUse the actual ClassLoader to use for the thread context
* @return the original thread context ClassLoader, or <code>null</code> if not overridden
*/
public static ClassLoader overrideThreadContextClassLoader(ClassLoader classLoaderToUse) {
Thread currentThread = Thread.currentThread();
ClassLoader threadContextClassLoader = currentThread.getContextClassLoader();
if (classLoaderToUse != null && !classLoaderToUse.equals(threadContextClassLoader)) {
currentThread.setContextClassLoader(classLoaderToUse);
return threadContextClassLoader;
}
else {
return null;
}
}
/**
* Replacement for <code>Class.forName()</code> that also returns Class instances
* for primitives (like "int") and array class names (like "String[]").
* <p>Always uses the default class loader: that is, preferably the thread context
* class loader, or the ClassLoader that loaded the ClassUtils class as fallback.
* @param name the name of the Class
* @return Class instance for the supplied name
* @throws ClassNotFoundException if the class was not found
* @throws LinkageError if the class file could not be loaded
* @see Class#forName(String, boolean, ClassLoader)
* @see #getDefaultClassLoader()
*/
public static Class forName(String name) throws ClassNotFoundException, LinkageError {
return forName(name, getDefaultClassLoader());
}
/**
* Replacement for <code>Class.forName()</code> that also returns Class instances
* for primitives (like "int") and array class names (like "String[]").
* @param name the name of the Class
* @param classLoader the class loader to use
* (may be <code>null</code>, which indicates the default class loader)
* @return Class instance for the supplied name
* @throws ClassNotFoundException if the class was not found
* @throws LinkageError if the class file could not be loaded
* @see Class#forName(String, boolean, ClassLoader)
*/
public static Class forName(String name, ClassLoader classLoader) throws ClassNotFoundException, LinkageError {
Assert.notNull(name, "Name must not be null");
Class clazz = resolvePrimitiveClassName(name);
if (clazz != null) {
return clazz;
}
// "java.lang.String[]" style arrays
if (name.endsWith(ARRAY_SUFFIX)) {
String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
Class elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
// "[Ljava.lang.String;" style arrays
int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX);
if (internalArrayMarker != -1 && name.endsWith(";")) {
String elementClassName = null;
if (internalArrayMarker == 0) {
elementClassName = name.substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1);
}
else if (name.startsWith("[")) {
elementClassName = name.substring(1);
}
Class elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
ClassLoader classLoaderToUse = classLoader;
if (classLoaderToUse == null) {
classLoaderToUse = getDefaultClassLoader();
}
return classLoaderToUse.loadClass(name);
}
/**
* Resolve the given class name into a Class instance. Supports
* primitives (like "int") and array class names (like "String[]").
* <p>This is effectively equivalent to the <code>forName</code>
* method with the same arguments, with the only difference being
* the exceptions thrown in case of class loading failure.
* @param className the name of the Class
* @param classLoader the class loader to use
* (may be <code>null</code>, which indicates the default class loader)
* @return Class instance for the supplied name
* @throws IllegalArgumentException if the class name was not resolvable
* (that is, the class could not be found or the class file could not be loaded)
* @see #forName(String, ClassLoader)
*/
public static Class resolveClassName(String className, ClassLoader classLoader) throws IllegalArgumentException {
try {
return forName(className, classLoader);
}
catch (ClassNotFoundException ex) {
IllegalArgumentException iae = new IllegalArgumentException("Cannot find class [" + className + "]");
iae.initCause(ex);
throw iae;
}
catch (LinkageError ex) {
IllegalArgumentException iae = new IllegalArgumentException(
"Error loading class [" + className + "]: problem with class file or dependent class.");
iae.initCause(ex);
throw iae;
}
}
/**
* Resolve the given class name as primitive class, if appropriate,
* according to the JVM's naming rules for primitive classes.
* <p>Also supports the JVM's internal class names for primitive arrays.
* Does <i>not</i> support the "[]" suffix notation for primitive arrays;
* this is only supported by {@link #forName}.
* @param name the name of the potentially primitive class
* @return the primitive class, or <code>null</code> if the name does not denote
* a primitive class or primitive array class
*/
public static Class resolvePrimitiveClassName(String name) {
Class result = null;
// Most class names will be quite long, considering that they
// SHOULD sit in a package, so a length check is worthwhile.
if (name != null && name.length() <= 8) {
// Could be a primitive - likely.
result = (Class) primitiveTypeNameMap.get(name);
}
return result;
}
/**
* Determine whether the {@link Class} identified by the supplied name is present
* and can be loaded. Will return <code>false</code> if either the class or
* one of its dependencies is not present or cannot be loaded.
* @param className the name of the class to check
* @return whether the specified class is present
* @deprecated as of Spring 2.5, in favor of {@link #isPresent(String, ClassLoader)}
*/
public static boolean isPresent(String className) {
return isPresent(className, getDefaultClassLoader());
}
/**
* Determine whether the {@link Class} identified by the supplied name is present
* and can be loaded. Will return <code>false</code> if either the class or
* one of its dependencies is not present or cannot be loaded.
* @param className the name of the class to check
* @param classLoader the class loader to use
* (may be <code>null</code>, which indicates the default class loader)
* @return whether the specified class is present
*/
public static boolean isPresent(String className, ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
// Class or one of its dependencies is not present...
return false;
}
}
/**
* Return the user-defined class for the given instance: usually simply
* the class of the given instance, but the original class in case of a
* CGLIB-generated subclass.
* @param instance the instance to check
* @return the user-defined class
*/
public static Class getUserClass(Object instance) {
Assert.notNull(instance, "Instance must not be null");
return getUserClass(instance.getClass());
}
/**
* Return the user-defined class for the given class: usually simply the given
* class, but the original class in case of a CGLIB-generated subclass.
* @param clazz the class to check
* @return the user-defined class
*/
public static Class getUserClass(Class clazz) {
return (clazz != null && clazz.getName().indexOf(CGLIB_CLASS_SEPARATOR) != -1 ?
clazz.getSuperclass() : clazz);
}
/**
* Check whether the given class is cache-safe in the given context,
* i.e. whether it is loaded by the given ClassLoader or a parent of it.
* @param clazz the class to analyze
* @param classLoader the ClassLoader to potentially cache metadata in
*/
public static boolean isCacheSafe(Class clazz, ClassLoader classLoader) {
Assert.notNull(clazz, "Class must not be null");
ClassLoader target = clazz.getClassLoader();
if (target == null) {
return false;
}
ClassLoader cur = classLoader;
if (cur == target) {
return true;
}
while (cur != null) {
cur = cur.getParent();
if (cur == target) {
return true;
}
}
return false;
}
/**
* Get the class name without the qualified package name.
* @param className the className to get the short name for
* @return the class name of the class without the package name
* @throws IllegalArgumentException if the className is empty
*/
public static String getShortName(String className) {
Assert.hasLength(className, "Class name must not be empty");
int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
int nameEndIndex = className.indexOf(CGLIB_CLASS_SEPARATOR);
if (nameEndIndex == -1) {
nameEndIndex = className.length();
}
String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
return shortName;
}
/**
* Get the class name without the qualified package name.
* @param clazz the class to get the short name for
* @return the class name of the class without the package name
*/
public static String getShortName(Class clazz) {
return getShortName(getQualifiedName(clazz));
}
/**
* Return the short string name of a Java class in decapitalized JavaBeans
* property format. Strips the outer class name in case of an inner class.
* @param clazz the class
* @return the short name rendered in a standard JavaBeans property format
* @see java.beans.Introspector#decapitalize(String)
*/
public static String getShortNameAsProperty(Class clazz) {
String shortName = ClassUtils.getShortName(clazz);
int dotIndex = shortName.lastIndexOf('.');
shortName = (dotIndex != -1 ? shortName.substring(dotIndex + 1) : shortName);
return Introspector.decapitalize(shortName);
}
/**
* Determine the name of the class file, relative to the containing
* package: e.g. "String.class"
* @param clazz the class
* @return the file name of the ".class" file
*/
public static String getClassFileName(Class clazz) {
Assert.notNull(clazz, "Class must not be null");
String className = clazz.getName();
int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
return className.substring(lastDotIndex + 1) + CLASS_FILE_SUFFIX;
}
/**
* Determine the name of the package of the given class:
* e.g. "java.lang" for the <code>java.lang.String</code> class.
* @param clazz the class
* @return the package name, or the empty String if the class
* is defined in the default package
*/
public static String getPackageName(Class clazz) {
Assert.notNull(clazz, "Class must not be null");
String className = clazz.getName();
int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR);
return (lastDotIndex != -1 ? className.substring(0, lastDotIndex) : "");
}
/**
* Return the qualified name of the given class: usually simply
* the class name, but component type class name + "[]" for arrays.
* @param clazz the class
* @return the qualified name of the class
*/
public static String getQualifiedName(Class clazz) {
Assert.notNull(clazz, "Class must not be null");
if (clazz.isArray()) {
return getQualifiedNameForArray(clazz);
}
else {
return clazz.getName();
}
}
/**
* Build a nice qualified name for an array:
* component type class name + "[]".
* @param clazz the array class
* @return a qualified name for the array class
*/
private static String getQualifiedNameForArray(Class clazz) {
StringBuffer buffer = new StringBuffer();
while (clazz.isArray()) {
clazz = clazz.getComponentType();
buffer.append(ClassUtils.ARRAY_SUFFIX);
}
buffer.insert(0, clazz.getName());
return buffer.toString();
}
/**
* Return the qualified name of the given method, consisting of
* fully qualified interface/class name + "." + method name.
* @param method the method
* @return the qualified name of the method
*/
public static String getQualifiedMethodName(Method method) {
Assert.notNull(method, "Method must not be null");
return method.getDeclaringClass().getName() + "." + method.getName();
}
/**
* Return a descriptive name for the given object's type: usually simply
* the class name, but component type class name + "[]" for arrays,
* and an appended list of implemented interfaces for JDK proxies.
* @param value the value to introspect
* @return the qualified name of the class
*/
public static String getDescriptiveType(Object value) {
if (value == null) {
return null;
}
Class clazz = value.getClass();
if (Proxy.isProxyClass(clazz)) {
StringBuffer buf = new StringBuffer(clazz.getName());
buf.append(" implementing ");
Class[] ifcs = clazz.getInterfaces();
for (int i = 0; i < ifcs.length; i++) {
buf.append(ifcs[i].getName());
if (i < ifcs.length - 1) {
buf.append(',');
}
}
return buf.toString();
}
else if (clazz.isArray()) {
return getQualifiedNameForArray(clazz);
}
else {
return clazz.getName();
}
}
/**
* Determine whether the given class has a constructor with the given signature.
* <p>Essentially translates <code>NoSuchMethodException</code> to "false".
* @param clazz the clazz to analyze
* @param paramTypes the parameter types of the method
* @return whether the class has a corresponding constructor
* @see java.lang.Class#getMethod
*/
public static boolean hasConstructor(Class clazz, Class[] paramTypes) {
return (getConstructorIfAvailable(clazz, paramTypes) != null);
}
/**
* Determine whether the given class has a constructor with the given signature,
* and return it if available (else return <code>null</code>).
* <p>Essentially translates <code>NoSuchMethodException</code> to <code>null</code>.
* @param clazz the clazz to analyze
* @param paramTypes the parameter types of the method
* @return the constructor, or <code>null</code> if not found
* @see java.lang.Class#getConstructor
*/
public static Constructor getConstructorIfAvailable(Class clazz, Class[] paramTypes) {
Assert.notNull(clazz, "Class must not be null");
try {
return clazz.getConstructor(paramTypes);
}
catch (NoSuchMethodException ex) {
return null;
}
}
/**
* Determine whether the given class has a method with the given signature.
* <p>Essentially translates <code>NoSuchMethodException</code> to "false".
* @param clazz the clazz to analyze
* @param methodName the name of the method
* @param paramTypes the parameter types of the method
* @return whether the class has a corresponding method
* @see java.lang.Class#getMethod
*/
public static boolean hasMethod(Class clazz, String methodName, Class[] paramTypes) {
return (getMethodIfAvailable(clazz, methodName, paramTypes) != null);
}
/**
* Determine whether the given class has a method with the given signature,
* and return it if available (else return <code>null</code>).
* <p>Essentially translates <code>NoSuchMethodException</code> to <code>null</code>.
* @param clazz the clazz to analyze
* @param methodName the name of the method
* @param paramTypes the parameter types of the method
* @return the method, or <code>null</code> if not found
* @see java.lang.Class#getMethod
*/
public static Method getMethodIfAvailable(Class clazz, String methodName, Class[] paramTypes) {
Assert.notNull(clazz, "Class must not be null");
Assert.notNull(methodName, "Method name must not be null");
try {
return clazz.getMethod(methodName, paramTypes);
}
catch (NoSuchMethodException ex) {
return null;
}
}
/**
* Return the number of methods with a given name (with any argument types),
* for the given class and/or its superclasses. Includes non-public methods.
* @param clazz the clazz to check
* @param methodName the name of the method
* @return the number of methods with the given name
*/
public static int getMethodCountForName(Class clazz, String methodName) {
Assert.notNull(clazz, "Class must not be null");
Assert.notNull(methodName, "Method name must not be null");
int count = 0;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++) {
Method method = declaredMethods[i];
if (methodName.equals(method.getName())) {
count++;
}
}
Class[] ifcs = clazz.getInterfaces();
for (int i = 0; i < ifcs.length; i++) {
count += getMethodCountForName(ifcs[i], methodName);
}
if (clazz.getSuperclass() != null) {
count += getMethodCountForName(clazz.getSuperclass(), methodName);
}
return count;
}
/**
* Does the given class and/or its superclasses at least have one or more
* methods (with any argument types)? Includes non-public methods.
* @param clazz the clazz to check
* @param methodName the name of the method
* @return whether there is at least one method with the given name
*/
public static boolean hasAtLeastOneMethodWithName(Class clazz, String methodName) {
Assert.notNull(clazz, "Class must not be null");
Assert.notNull(methodName, "Method name must not be null");
Method[] declaredMethods = clazz.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++) {
Method method = declaredMethods[i];
if (method.getName().equals(methodName)) {
return true;
}
}
Class[] ifcs = clazz.getInterfaces();
for (int i = 0; i < ifcs.length; i++) {
if (hasAtLeastOneMethodWithName(ifcs[i], methodName)) {
return true;
}
}
return (clazz.getSuperclass() != null && hasAtLeastOneMethodWithName(clazz.getSuperclass(), methodName));
}
/**
* Given a method, which may come from an interface, and a target class used
* in the current reflective invocation, find the corresponding target method
* if there is one. E.g. the method may be <code>IFoo.bar()</code> and the
* target class may be <code>DefaultFoo</code>. In this case, the method may be
* <code>DefaultFoo.bar()</code>. This enables attributes on that method to be found.
* <p><b>NOTE:</b> In contrast to {@link org.springframework.aop.support.AopUtils#getMostSpecificMethod},
* this method does <i>not</i> resolve Java 5 bridge methods automatically.
* Call {@link org.springframework.core.BridgeMethodResolver#findBridgedMethod}
* if bridge method resolution is desirable (e.g. for obtaining metadata from
* the original method definition).
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation.
* May be <code>null</code> or may not even implement the method.
* @return the specific target method, or the original method if the
* <code>targetClass</code> doesn't implement it or is <code>null</code>
* @see org.springframework.aop.support.AopUtils#getMostSpecificMethod
*/
public static Method getMostSpecificMethod(Method method, Class targetClass) {
if (method != null && targetClass != null && !targetClass.equals(method.getDeclaringClass())) {
try {
method = targetClass.getMethod(method.getName(), method.getParameterTypes());
}
catch (NoSuchMethodException ex) {
// Perhaps the target class doesn't implement this method:
// that's fine, just use the original method.
}
}
return method;
}
/**
* Return a static method of a class.
* @param methodName the static method name
* @param clazz the class which defines the method
* @param args the parameter types to the method
* @return the static method, or <code>null</code> if no static method was found
* @throws IllegalArgumentException if the method name is blank or the clazz is null
*/
public static Method getStaticMethod(Class clazz, String methodName, Class[] args) {
Assert.notNull(clazz, "Class must not be null");
Assert.notNull(methodName, "Method name must not be null");
try {
Method method = clazz.getDeclaredMethod(methodName, args);
if ((method.getModifiers() & Modifier.STATIC) != 0) {
return method;
}
}
catch (NoSuchMethodException ex) {
}
return null;
}
/**
* Check if the given class represents a primitive wrapper,
* i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double.
* @param clazz the class to check
* @return whether the given class is a primitive wrapper class
*/
public static boolean isPrimitiveWrapper(Class clazz) {
Assert.notNull(clazz, "Class must not be null");
return primitiveWrapperTypeMap.containsKey(clazz);
}
/**
* Check if the given class represents a primitive (i.e. boolean, byte,
* char, short, int, long, float, or double) or a primitive wrapper
* (i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double).
* @param clazz the class to check
* @return whether the given class is a primitive or primitive wrapper class
*/
public static boolean isPrimitiveOrWrapper(Class clazz) {
Assert.notNull(clazz, "Class must not be null");
return (clazz.isPrimitive() || isPrimitiveWrapper(clazz));
}
/**
* Check if the given class represents an array of primitives,
* i.e. boolean, byte, char, short, int, long, float, or double.
* @param clazz the class to check
* @return whether the given class is a primitive array class
*/
public static boolean isPrimitiveArray(Class clazz) {
Assert.notNull(clazz, "Class must not be null");
return (clazz.isArray() && clazz.getComponentType().isPrimitive());
}
/**
* Check if the given class represents an array of primitive wrappers,
* i.e. Boolean, Byte, Character, Short, Integer, Long, Float, or Double.
* @param clazz the class to check
* @return whether the given class is a primitive wrapper array class
*/
public static boolean isPrimitiveWrapperArray(Class clazz) {
Assert.notNull(clazz, "Class must not be null");
return (clazz.isArray() && isPrimitiveWrapper(clazz.getComponentType()));
}
/**
* Check if the right-hand side type may be assigned to the left-hand side
* type, assuming setting by reflection. Considers primitive wrapper
* classes as assignable to the corresponding primitive types.
* @param lhsType the target type
* @param rhsType the value type that should be assigned to the target type
* @return if the target type is assignable from the value type
* @see TypeUtils#isAssignable
*/
public static boolean isAssignable(Class lhsType, Class rhsType) {
Assert.notNull(lhsType, "Left-hand side type must not be null");
Assert.notNull(rhsType, "Right-hand side type must not be null");
return (lhsType.isAssignableFrom(rhsType) ||
lhsType.equals(primitiveWrapperTypeMap.get(rhsType)));
}
/**
* Determine if the given type is assignable from the given value,
* assuming setting by reflection. Considers primitive wrapper classes
* as assignable to the corresponding primitive types.
* @param type the target type
* @param value the value that should be assigned to the type
* @return if the type is assignable from the value
*/
public static boolean isAssignableValue(Class type, Object value) {
Assert.notNull(type, "Type must not be null");
return (value != null ? isAssignable(type, value.getClass()) : !type.isPrimitive());
}
/**
* Convert a "/"-based resource path to a "."-based fully qualified class name.
* @param resourcePath the resource path pointing to a class
* @return the corresponding fully qualified class name
*/
public static String convertResourcePathToClassName(String resourcePath) {
return resourcePath.replace('/', '.');
}
/**
* Convert a "."-based fully qualified class name to a "/"-based resource path.
* @param className the fully qualified class name
* @return the corresponding resource path, pointing to the class
*/
public static String convertClassNameToResourcePath(String className) {
return className.replace('.', '/');
}
/**
* Return a path suitable for use with <code>ClassLoader.getResource</code>
* (also suitable for use with <code>Class.getResource</code> by prepending a
* slash ('/') to the return value. Built by taking the package of the specified
* class file, converting all dots ('.') to slashes ('/'), adding a trailing slash
* if necesssary, and concatenating the specified resource name to this.
* <br/>As such, this function may be used to build a path suitable for
* loading a resource file that is in the same package as a class file,
* although {@link org.springframework.core.io.ClassPathResource} is usually
* even more convenient.
* @param clazz the Class whose package will be used as the base
* @param resourceName the resource name to append. A leading slash is optional.
* @return the built-up resource path
* @see java.lang.ClassLoader#getResource
* @see java.lang.Class#getResource
*/
public static String addResourcePathToPackagePath(Class clazz, String resourceName) {
Assert.notNull(resourceName, "Resource name must not be null");
if (!resourceName.startsWith("/")) {
return classPackageAsResourcePath(clazz) + "/" + resourceName;
}
return classPackageAsResourcePath(clazz) + resourceName;
}
/**
* Given an input class object, return a string which consists of the
* class's package name as a pathname, i.e., all dots ('.') are replaced by
* slashes ('/'). Neither a leading nor trailing slash is added. The result
* could be concatenated with a slash and the name of a resource, and fed
* directly to <code>ClassLoader.getResource()</code>. For it to be fed to
* <code>Class.getResource</code> instead, a leading slash would also have
* to be prepended to the returned value.
* @param clazz the input class. A <code>null</code> value or the default
* (empty) package will result in an empty string ("") being returned.
* @return a path which represents the package name
* @see ClassLoader#getResource
* @see Class#getResource
*/
public static String classPackageAsResourcePath(Class clazz) {
if (clazz == null) {
return "";
}
String className = clazz.getName();
int packageEndIndex = className.lastIndexOf('.');
if (packageEndIndex == -1) {
return "";
}
String packageName = className.substring(0, packageEndIndex);
return packageName.replace('.', '/');
}
/**
* Build a String that consists of the names of the classes/interfaces
* in the given array.
* <p>Basically like <code>AbstractCollection.toString()</code>, but stripping
* the "class "/"interface " prefix before every class name.
* @param classes a Collection of Class objects (may be <code>null</code>)
* @return a String of form "[com.foo.Bar, com.foo.Baz]"
* @see java.util.AbstractCollection#toString()
*/
public static String classNamesToString(Class[] classes) {
return classNamesToString(Arrays.asList(classes));
}
/**
* Build a String that consists of the names of the classes/interfaces
* in the given collection.
* <p>Basically like <code>AbstractCollection.toString()</code>, but stripping
* the "class "/"interface " prefix before every class name.
* @param classes a Collection of Class objects (may be <code>null</code>)
* @return a String of form "[com.foo.Bar, com.foo.Baz]"
* @see java.util.AbstractCollection#toString()
*/
public static String classNamesToString(Collection classes) {
if (CollectionUtils.isEmpty(classes)) {
return "[]";
}
StringBuffer sb = new StringBuffer("[");
for (Iterator it = classes.iterator(); it.hasNext(); ) {
Class clazz = (Class) it.next();
sb.append(clazz.getName());
if (it.hasNext()) {
sb.append(", ");
}
}
sb.append("]");
return sb.toString();
}
/**
* Return all interfaces that the given instance implements as array,
* including ones implemented by superclasses.
* @param instance the instance to analyse for interfaces
* @return all interfaces that the given instance implements as array
*/
public static Class[] getAllInterfaces(Object instance) {
Assert.notNull(instance, "Instance must not be null");
return getAllInterfacesForClass(instance.getClass());
}
/**
* Return all interfaces that the given class implements as array,
* including ones implemented by superclasses.
* <p>If the class itself is an interface, it gets returned as sole interface.
* @param clazz the class to analyse for interfaces
* @return all interfaces that the given object implements as array
*/
public static Class[] getAllInterfacesForClass(Class clazz) {
return getAllInterfacesForClass(clazz, null);
}
/**
* Return all interfaces that the given class implements as array,
* including ones implemented by superclasses.
* <p>If the class itself is an interface, it gets returned as sole interface.
* @param clazz the class to analyse for interfaces
* @param classLoader the ClassLoader that the interfaces need to be visible in
* (may be <code>null</code> when accepting all declared interfaces)
* @return all interfaces that the given object implements as array
*/
public static Class[] getAllInterfacesForClass(Class clazz, ClassLoader classLoader) {
Assert.notNull(clazz, "Class must not be null");
if (clazz.isInterface()) {
return new Class[] {clazz};
}
List interfaces = new ArrayList();
while (clazz != null) {
for (int i = 0; i < clazz.getInterfaces().length; i++) {
Class ifc = clazz.getInterfaces()[i];
if (!interfaces.contains(ifc) &&
(classLoader == null || isVisible(ifc, classLoader))) {
interfaces.add(ifc);
}
}
clazz = clazz.getSuperclass();
}
return (Class[]) interfaces.toArray(new Class[interfaces.size()]);
}
/**
* Return all interfaces that the given instance implements as Set,
* including ones implemented by superclasses.
* @param instance the instance to analyse for interfaces
* @return all interfaces that the given instance implements as Set
*/
public static Set getAllInterfacesAsSet(Object instance) {
Assert.notNull(instance, "Instance must not be null");
return getAllInterfacesForClassAsSet(instance.getClass());
}
/**
* Return all interfaces that the given class implements as Set,
* including ones implemented by superclasses.
* <p>If the class itself is an interface, it gets returned as sole interface.
* @param clazz the class to analyse for interfaces
* @return all interfaces that the given object implements as Set
*/
public static Set getAllInterfacesForClassAsSet(Class clazz) {
return getAllInterfacesForClassAsSet(clazz, null);
}
/**
* Return all interfaces that the given class implements as Set,
* including ones implemented by superclasses.
* <p>If the class itself is an interface, it gets returned as sole interface.
* @param clazz the class to analyse for interfaces
* @param classLoader the ClassLoader that the interfaces need to be visible in
* (may be <code>null</code> when accepting all declared interfaces)
* @return all interfaces that the given object implements as Set
*/
public static Set getAllInterfacesForClassAsSet(Class clazz, ClassLoader classLoader) {
Assert.notNull(clazz, "Class must not be null");
if (clazz.isInterface()) {
return Collections.singleton(clazz);
}
Set interfaces = new LinkedHashSet();
while (clazz != null) {
for (int i = 0; i < clazz.getInterfaces().length; i++) {
Class ifc = clazz.getInterfaces()[i];
if (classLoader == null || isVisible(ifc, classLoader)) {
interfaces.add(ifc);
}
}
clazz = clazz.getSuperclass();
}
return interfaces;
}
/**
* Create a composite interface Class for the given interfaces,
* implementing the given interfaces in one single Class.
* <p>This implementation builds a JDK proxy class for the given interfaces.
* @param interfaces the interfaces to merge
* @param classLoader the ClassLoader to create the composite Class in
* @return the merged interface as Class
* @see java.lang.reflect.Proxy#getProxyClass
*/
public static Class createCompositeInterface(Class[] interfaces, ClassLoader classLoader) {
Assert.notEmpty(interfaces, "Interfaces must not be empty");
Assert.notNull(classLoader, "ClassLoader must not be null");
return Proxy.getProxyClass(classLoader, interfaces);
}
/**
* Check whether the given class is visible in the given ClassLoader.
* @param clazz the class to check (typically an interface)
* @param classLoader the ClassLoader to check against (may be <code>null</code>,
* in which case this method will always return <code>true</code>)
*/
public static boolean isVisible(Class clazz, ClassLoader classLoader) {
if (classLoader == null) {
return true;
}
try {
Class actualClass = classLoader.loadClass(clazz.getName());
return (clazz == actualClass);
// Else: different interface class found...
}
catch (ClassNotFoundException ex) {
// No interface class found...
return false;
}
}
}

View File

@ -0,0 +1,275 @@
/*
* 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.util;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
/**
* Miscellaneous collection utility methods.
* Mainly for internal use within the framework.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 1.1.3
*/
public abstract class CollectionUtils {
/**
* Return <code>true</code> if the supplied Collection is <code>null</code>
* or empty. Otherwise, return <code>false</code>.
* @param collection the Collection to check
* @return whether the given Collection is empty
*/
public static boolean isEmpty(Collection collection) {
return (collection == null || collection.isEmpty());
}
/**
* Return <code>true</code> if the supplied Map is <code>null</code>
* or empty. Otherwise, return <code>false</code>.
* @param map the Map to check
* @return whether the given Map is empty
*/
public static boolean isEmpty(Map map) {
return (map == null || map.isEmpty());
}
/**
* Convert the supplied array into a List. A primitive array gets
* converted into a List of the appropriate wrapper type.
* <p>A <code>null</code> source value will be converted to an
* empty List.
* @param source the (potentially primitive) array
* @return the converted List result
* @see ObjectUtils#toObjectArray(Object)
*/
public static List arrayToList(Object source) {
return Arrays.asList(ObjectUtils.toObjectArray(source));
}
/**
* Merge the given array into the given Collection.
* @param array the array to merge (may be <code>null</code>)
* @param collection the target Collection to merge the array into
*/
public static void mergeArrayIntoCollection(Object array, Collection collection) {
if (collection == null) {
throw new IllegalArgumentException("Collection must not be null");
}
Object[] arr = ObjectUtils.toObjectArray(array);
for (int i = 0; i < arr.length; i++) {
collection.add(arr[i]);
}
}
/**
* Merge the given Properties instance into the given Map,
* copying all properties (key-value pairs) over.
* <p>Uses <code>Properties.propertyNames()</code> to even catch
* default properties linked into the original Properties instance.
* @param props the Properties instance to merge (may be <code>null</code>)
* @param map the target Map to merge the properties into
*/
public static void mergePropertiesIntoMap(Properties props, Map map) {
if (map == null) {
throw new IllegalArgumentException("Map must not be null");
}
if (props != null) {
for (Enumeration en = props.propertyNames(); en.hasMoreElements();) {
String key = (String) en.nextElement();
map.put(key, props.getProperty(key));
}
}
}
/**
* Check whether the given Iterator contains the given element.
* @param iterator the Iterator to check
* @param element the element to look for
* @return <code>true</code> if found, <code>false</code> else
*/
public static boolean contains(Iterator iterator, Object element) {
if (iterator != null) {
while (iterator.hasNext()) {
Object candidate = iterator.next();
if (ObjectUtils.nullSafeEquals(candidate, element)) {
return true;
}
}
}
return false;
}
/**
* Check whether the given Enumeration contains the given element.
* @param enumeration the Enumeration to check
* @param element the element to look for
* @return <code>true</code> if found, <code>false</code> else
*/
public static boolean contains(Enumeration enumeration, Object element) {
if (enumeration != null) {
while (enumeration.hasMoreElements()) {
Object candidate = enumeration.nextElement();
if (ObjectUtils.nullSafeEquals(candidate, element)) {
return true;
}
}
}
return false;
}
/**
* Check whether the given Collection contains the given element instance.
* <p>Enforces the given instance to be present, rather than returning
* <code>true</code> for an equal element as well.
* @param collection the Collection to check
* @param element the element to look for
* @return <code>true</code> if found, <code>false</code> else
*/
public static boolean containsInstance(Collection collection, Object element) {
if (collection != null) {
for (Iterator it = collection.iterator(); it.hasNext();) {
Object candidate = it.next();
if (candidate == element) {
return true;
}
}
}
return false;
}
/**
* Return <code>true</code> if any element in '<code>candidates</code>' is
* contained in '<code>source</code>'; otherwise returns <code>false</code>.
* @param source the source Collection
* @param candidates the candidates to search for
* @return whether any of the candidates has been found
*/
public static boolean containsAny(Collection source, Collection candidates) {
if (isEmpty(source) || isEmpty(candidates)) {
return false;
}
for (Iterator it = candidates.iterator(); it.hasNext();) {
if (source.contains(it.next())) {
return true;
}
}
return false;
}
/**
* Return the first element in '<code>candidates</code>' that is contained in
* '<code>source</code>'. If no element in '<code>candidates</code>' is present in
* '<code>source</code>' returns <code>null</code>. Iteration order is
* {@link Collection} implementation specific.
* @param source the source Collection
* @param candidates the candidates to search for
* @return the first present object, or <code>null</code> if not found
*/
public static Object findFirstMatch(Collection source, Collection candidates) {
if (isEmpty(source) || isEmpty(candidates)) {
return null;
}
for (Iterator it = candidates.iterator(); it.hasNext();) {
Object candidate = it.next();
if (source.contains(candidate)) {
return candidate;
}
}
return null;
}
/**
* Find a single value of the given type in the given Collection.
* @param collection the Collection to search
* @param type the type to look for
* @return a value of the given type found if there is a clear match,
* or <code>null</code> if none or more than one such value found
*/
public static Object findValueOfType(Collection collection, Class type) {
if (isEmpty(collection)) {
return null;
}
Object value = null;
for (Iterator it = collection.iterator(); it.hasNext();) {
Object obj = it.next();
if (type == null || type.isInstance(obj)) {
if (value != null) {
// More than one value found... no clear single value.
return null;
}
value = obj;
}
}
return value;
}
/**
* Find a single value of one of the given types in the given Collection:
* searching the Collection for a value of the first type, then
* searching for a value of the second type, etc.
* @param collection the collection to search
* @param types the types to look for, in prioritized order
* @return a value of one of the given types found if there is a clear match,
* or <code>null</code> if none or more than one such value found
*/
public static Object findValueOfType(Collection collection, Class[] types) {
if (isEmpty(collection) || ObjectUtils.isEmpty(types)) {
return null;
}
for (int i = 0; i < types.length; i++) {
Object value = findValueOfType(collection, types[i]);
if (value != null) {
return value;
}
}
return null;
}
/**
* Determine whether the given Collection only contains a single unique object.
* @param collection the Collection to check
* @return <code>true</code> if the collection contains a single reference or
* multiple references to the same instance, <code>false</code> else
*/
public static boolean hasUniqueObject(Collection collection) {
if (isEmpty(collection)) {
return false;
}
boolean hasCandidate = false;
Object candidate = null;
for (Iterator it = collection.iterator(); it.hasNext();) {
Object elem = it.next();
if (!hasCandidate) {
hasCandidate = true;
candidate = elem;
}
else if (candidate != elem) {
return false;
}
}
return true;
}
}

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