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:
parent
a0e7cf11a8
commit
3b9b46b480
|
|
@ -21,11 +21,15 @@
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<!-- compile 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.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 -->
|
<!-- 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" />
|
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.4.0" conf="test->runtime" />
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</ivy-module>
|
</ivy-module>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Common interface for managing aliases. Serves as super-interface for
|
||||||
|
* {@link org.springframework.beans.factory.support.BeanDefinitionRegistry}.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.5.2
|
||||||
|
*/
|
||||||
|
public interface AliasRegistry {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a name, register an alias for it.
|
||||||
|
* @param name the canonical name
|
||||||
|
* @param alias the alias to be registered
|
||||||
|
* @throws IllegalStateException if the alias is already in use
|
||||||
|
* and may not be overridden
|
||||||
|
*/
|
||||||
|
void registerAlias(String name, String alias);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the specified alias from this registry.
|
||||||
|
* @param alias the alias to remove
|
||||||
|
* @throws IllegalStateException if no such alias was found
|
||||||
|
*/
|
||||||
|
void removeAlias(String alias);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether this given name is defines as an alias
|
||||||
|
* (as opposed to the name of an actually registered component).
|
||||||
|
* @param beanName the bean name to check
|
||||||
|
* @return whether the given name is an alias
|
||||||
|
*/
|
||||||
|
boolean isAlias(String beanName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the aliases for the given name, if defined.
|
||||||
|
* @param name the name to check for aliases
|
||||||
|
* @return the aliases, or an empty array if none
|
||||||
|
*/
|
||||||
|
String[] getAliases(String name);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface defining a generic contract for attaching and accessing metadata
|
||||||
|
* to/from arbitrary objects.
|
||||||
|
*
|
||||||
|
* @author Rob Harrop
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public interface AttributeAccessor {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the attribute defined by <code>name</code> to the supplied <code>value</code>.
|
||||||
|
* If <code>value</code> is <code>null</code>, the attribute is {@link #removeAttribute removed}.
|
||||||
|
* <p>In general, users should take care to prevent overlaps with other
|
||||||
|
* metadata attributes by using fully-qualified names, perhaps using
|
||||||
|
* class or package names as prefix.
|
||||||
|
* @param name the unique attribute key
|
||||||
|
* @param value the attribute value to be attached
|
||||||
|
*/
|
||||||
|
void setAttribute(String name, Object value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the value of the attribute identified by <code>name</code>.
|
||||||
|
* Return <code>null</code> if the attribute doesn't exist.
|
||||||
|
* @param name the unique attribute key
|
||||||
|
* @return the current value of the attribute, if any
|
||||||
|
*/
|
||||||
|
Object getAttribute(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove the attribute identified by <code>name</code> and return its value.
|
||||||
|
* Return <code>null</code> if no attribute under <code>name</code> is found.
|
||||||
|
* @param name the unique attribute key
|
||||||
|
* @return the last value of the attribute, if any
|
||||||
|
*/
|
||||||
|
Object removeAttribute(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return <code>true</code> if the attribute identified by <code>name</code> exists.
|
||||||
|
* Otherwise return <code>false</code>.
|
||||||
|
* @param name the unique attribute key
|
||||||
|
*/
|
||||||
|
boolean hasAttribute(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the names of all attributes.
|
||||||
|
*/
|
||||||
|
String[] attributeNames();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown when the {@link Constants} class is asked for
|
||||||
|
* an invalid constant name.
|
||||||
|
*
|
||||||
|
* @author Rod Johnson
|
||||||
|
* @since 28.04.2003
|
||||||
|
* @see org.springframework.core.Constants
|
||||||
|
*/
|
||||||
|
public class ConstantException extends IllegalArgumentException {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when an invalid constant name is requested.
|
||||||
|
* @param className name of the class containing the constant definitions
|
||||||
|
* @param field invalid constant name
|
||||||
|
* @param message description of the problem
|
||||||
|
*/
|
||||||
|
public ConstantException(String className, String field, String message) {
|
||||||
|
super("Field '" + field + "' " + message + " in class [" + className + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when an invalid constant value is looked up.
|
||||||
|
* @param className name of the class containing the constant definitions
|
||||||
|
* @param namePrefix prefix of the searched constant names
|
||||||
|
* @param value the looked up constant value
|
||||||
|
*/
|
||||||
|
public ConstantException(String className, String namePrefix, Object value) {
|
||||||
|
super("No '" + namePrefix + "' field with value '" + value + "' found in class [" + className + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2005 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by objects that can return information about
|
||||||
|
* the current call stack. Useful in AOP (as in AspectJ cflow concept)
|
||||||
|
* but not AOP-specific.
|
||||||
|
*
|
||||||
|
* @author Rod Johnson
|
||||||
|
* @since 02.02.2004
|
||||||
|
*/
|
||||||
|
public interface ControlFlow {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect whether we're under the given class,
|
||||||
|
* according to the current stack trace.
|
||||||
|
* @param clazz the clazz to look for
|
||||||
|
*/
|
||||||
|
boolean under(Class clazz);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect whether we're under the given class and method,
|
||||||
|
* according to the current stack trace.
|
||||||
|
* @param clazz the clazz to look for
|
||||||
|
* @param methodName the name of the method to look for
|
||||||
|
*/
|
||||||
|
boolean under(Class clazz, String methodName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detect whether the current stack trace contains the given token.
|
||||||
|
* @param token the token to look for
|
||||||
|
*/
|
||||||
|
boolean underToken(String token);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2005 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that can be implemented by exceptions etc that are error coded.
|
||||||
|
* The error code is a String, rather than a number, so it can be given
|
||||||
|
* user-readable values, such as "object.failureDescription".
|
||||||
|
*
|
||||||
|
* <p>An error code can be resolved by a MessageSource, for example.
|
||||||
|
*
|
||||||
|
* @author Rod Johnson
|
||||||
|
* @see org.springframework.context.MessageSource
|
||||||
|
*/
|
||||||
|
public interface ErrorCoded {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the error code associated with this failure.
|
||||||
|
* The GUI can render this any way it pleases, allowing for localization etc.
|
||||||
|
* @return a String error code associated with this failure,
|
||||||
|
* or <code>null</code> if not error-coded
|
||||||
|
*/
|
||||||
|
String getErrorCode();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2008 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by transparent resource proxies that need to be
|
||||||
|
* considered as equal to the underlying resource, for example for consistent
|
||||||
|
* lookup key comparisons. Note that this interface does imply such special
|
||||||
|
* semantics and does not constitute a general-purpose mixin!
|
||||||
|
*
|
||||||
|
* <p>Such wrappers will automatically be unwrapped for key comparisons in
|
||||||
|
* {@link org.springframework.transaction.support.TransactionSynchronizationManager}.
|
||||||
|
*
|
||||||
|
* <p>Only fully transparent proxies, e.g. for redirection or service lookups,
|
||||||
|
* are supposed to implement this interface. Proxies that decorate the target
|
||||||
|
* object with new behavior, such as AOP proxies, do <i>not</i> qualify here!
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.5.4
|
||||||
|
* @see org.springframework.transaction.support.TransactionSynchronizationManager
|
||||||
|
*/
|
||||||
|
public interface InfrastructureProxy {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the underlying resource (never <code>null</code>).
|
||||||
|
*/
|
||||||
|
Object getWrappedObject();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to discover parameter names for methods and constructors.
|
||||||
|
*
|
||||||
|
* <p>Parameter name discovery is not always possible, but various strategies are
|
||||||
|
* available to try, such as looking for debug information that may have been
|
||||||
|
* emitted at compile time, and looking for argname annotation values optionally
|
||||||
|
* accompanying AspectJ annotated methods.
|
||||||
|
*
|
||||||
|
* @author Rod Johnson
|
||||||
|
* @author Adrian Colyer
|
||||||
|
* @since 2.0
|
||||||
|
*/
|
||||||
|
public interface ParameterNameDiscoverer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return parameter names for this method,
|
||||||
|
* or <code>null</code> if they cannot be determined.
|
||||||
|
* @param method method to find parameter names for
|
||||||
|
* @return an array of parameter names if the names can be resolved,
|
||||||
|
* or <code>null</code> if they cannot
|
||||||
|
*/
|
||||||
|
String[] getParameterNames(Method method);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return parameter names for this constructor,
|
||||||
|
* or <code>null</code> if they cannot be determined.
|
||||||
|
* @param ctor constructor to find parameter names for
|
||||||
|
* @return an array of parameter names if the names can be resolved,
|
||||||
|
* or <code>null</code> if they cannot
|
||||||
|
*/
|
||||||
|
String[] getParameterNames(Constructor ctor);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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(<valueType> arg) {
|
||||||
|
* // process argument of type <valueType>
|
||||||
|
* }
|
||||||
|
* </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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2007 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface to be implemented by a reloading-aware ClassLoader
|
||||||
|
* (e.g. a Groovy-based ClassLoader). Detected for example by
|
||||||
|
* Spring's CGLIB proxy factory for making a caching decision.
|
||||||
|
*
|
||||||
|
* <p>If a ClassLoader does <i>not</i> implement this interface,
|
||||||
|
* then all of the classes obtained from it should be considered
|
||||||
|
* as not reloadable (i.e. cacheable).
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 2.5.1
|
||||||
|
*/
|
||||||
|
public interface SmartClassLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the given class is reloadable (in this ClassLoader).
|
||||||
|
* <p>Typically used to check whether the result may be cached (for this
|
||||||
|
* ClassLoader) or whether it should be reobtained every time.
|
||||||
|
* @param clazz the class to check (usually loaded from this ClassLoader)
|
||||||
|
* @return whether the class should be expected to appear in a reloaded
|
||||||
|
* version (with a different <code>Class</code> object) later on
|
||||||
|
*/
|
||||||
|
boolean isClassReloadable(Class clazz);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2006 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.core;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class that exposes the Spring version. Fetches the
|
||||||
|
* "Implementation-Version" manifest attribute from the jar file.
|
||||||
|
*
|
||||||
|
* <p>Note that some ClassLoaders do not expose the package metadata,
|
||||||
|
* hence this class might not be able to determine the Spring version
|
||||||
|
* in all environments. Consider using a reflection-based check instead:
|
||||||
|
* For example, checking for the presence of a specific Spring 2.0
|
||||||
|
* method that you intend to call.
|
||||||
|
*
|
||||||
|
* @author Juergen Hoeller
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
public class SpringVersion {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the full version string of the present Spring codebase,
|
||||||
|
* or <code>null</code> if it cannot be determined.
|
||||||
|
* @see java.lang.Package#getImplementationVersion()
|
||||||
|
*/
|
||||||
|
public static String getVersion() {
|
||||||
|
Package pkg = SpringVersion.class.getPackage();
|
||||||
|
return (pkg != null ? pkg.getImplementationVersion() : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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: "CSV". Different classes of codes are possible
|
||||||
|
* (e.g., Character, Integer, String).</li>
|
||||||
|
*
|
||||||
|
* <li>A descriptive label. For example: "the CSV File Format".</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @author Keith Donald
|
||||||
|
* @since 1.2.2
|
||||||
|
*/
|
||||||
|
public interface LabeledEnum extends Comparable, Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return this enumeration's type.
|
||||||
|
*/
|
||||||
|
Class getType();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return this enumeration's code.
|
||||||
|
* <p>Each code should be unique within enumerations of the same type.
|
||||||
|
*/
|
||||||
|
Comparable getCode();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a descriptive, optional label.
|
||||||
|
*/
|
||||||
|
String getLabel();
|
||||||
|
|
||||||
|
|
||||||
|
// Constants for standard enum ordering (Comparator implementations)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared Comparator instance that sorts enumerations by <code>CODE_ORDER</code>.
|
||||||
|
*/
|
||||||
|
Comparator CODE_ORDER = new Comparator() {
|
||||||
|
public int compare(Object o1, Object o2) {
|
||||||
|
Comparable c1 = ((LabeledEnum) o1).getCode();
|
||||||
|
Comparable c2 = ((LabeledEnum) o2).getCode();
|
||||||
|
return c1.compareTo(c2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared Comparator instance that sorts enumerations by <code>LABEL_ORDER</code>.
|
||||||
|
*/
|
||||||
|
Comparator LABEL_ORDER = new Comparator() {
|
||||||
|
public int compare(Object o1, Object o2) {
|
||||||
|
LabeledEnum e1 = (LabeledEnum) o1;
|
||||||
|
LabeledEnum e2 = (LabeledEnum) o2;
|
||||||
|
Comparator comp = new NullSafeComparator(String.CASE_INSENSITIVE_ORDER, true);
|
||||||
|
return comp.compare(e1.getLabel(), e2.getLabel());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shared Comparator instance that sorts enumerations by <code>LABEL_ORDER</code>,
|
||||||
|
* then <code>CODE_ORDER</code>.
|
||||||
|
*/
|
||||||
|
Comparator DEFAULT_ORDER =
|
||||||
|
new CompoundComparator(new Comparator[] { LABEL_ORDER, CODE_ORDER });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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, "Created");
|
||||||
|
* public static FlowSessionStatus ACTIVE = new FlowSessionStatus(1, "Active");
|
||||||
|
* public static FlowSessionStatus PAUSED = new FlowSessionStatus(2, "Paused");
|
||||||
|
* public static FlowSessionStatus SUSPENDED = new FlowSessionStatus(3, "Suspended");
|
||||||
|
* public static FlowSessionStatus ENDED = new FlowSessionStatus(4, "Ended");
|
||||||
|
*
|
||||||
|
* // 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Generic abstraction for (file-based) resources, used throughout the framework.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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/**/applicationContext.xml
|
||||||
|
* file:C:/some/path/*-context.xml
|
||||||
|
* classpath:com/mycompany/**/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/**/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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Support classes for Spring's resource abstraction.
|
||||||
|
Includes a ResourcePatternResolver mechanism.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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(',');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Support for styling values as Strings, with ToStringCreator as central class.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
This package defines Spring's core TaskExecutor abstraction,
|
||||||
|
and provides SyncTaskExecutor and SimpleAsyncTaskExecutor implementations.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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>
|
||||||
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Attributes wrapper for
|
||||||
|
<a href="http://jakarta.apache.org/commons/sandbox/attributes">Commons Attributes</a>.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
Package defining a facade for accessing source-level
|
||||||
|
metadata attributes at runtime.
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -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/**/test.jsp</code> - matches all <code>test.jsp</code>
|
||||||
|
* files underneath the <code>com</code> path</li>
|
||||||
|
* <li><code>org/springframework/**/*.jsp</code> - matches all <code>.jsp</code>
|
||||||
|
* files underneath the <code>org/springframework</code> path</li>
|
||||||
|
* <li><code>org/**/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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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 > 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 > 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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
Loading…
Reference in New Issue