From 3b9b46b48002a7c921fb5c1e2c3ecdc8ceb3975b Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Wed, 22 Oct 2008 13:55:20 +0000 Subject: [PATCH] Moved over initial version of core bundle git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@108 50f2f4bb-b051-0410-bef5-90022cba6387 --- org.springframework.core/ivy.xml | 10 +- .../springframework/core/AliasRegistry.java | 59 + .../core/AttributeAccessor.java | 67 + .../core/AttributeAccessorSupport.java | 102 ++ .../core/BridgeMethodResolver.java | 206 +++ .../core/CollectionFactory.java | 370 ++++++ .../springframework/core/ConcurrentMap.java | 43 + .../core/ConfigurableObjectInputStream.java | 126 ++ .../core/ConstantException.java | 49 + .../org/springframework/core/Constants.java | 346 +++++ .../org/springframework/core/ControlFlow.java | 50 + .../core/ControlFlowFactory.java | 118 ++ .../org/springframework/core/Conventions.java | 307 +++++ .../core/DecoratingClassLoader.java | 108 ++ .../org/springframework/core/ErrorCoded.java | 39 + .../core/GenericCollectionTypeResolver.java | 426 +++++++ .../core/GenericTypeResolver.java | 262 ++++ .../core/InfrastructureProxy.java | 43 + .../org/springframework/core/JdkVersion.java | 143 +++ ...lVariableTableParameterNameDiscoverer.java | 278 ++++ .../springframework/core/MethodParameter.java | 328 +++++ .../core/NamedInheritableThreadLocal.java | 47 + .../core/NamedThreadLocal.java | 47 + .../core/NestedCheckedException.java | 133 ++ .../core/NestedExceptionUtils.java | 55 + .../core/NestedIOException.java | 66 + .../core/NestedRuntimeException.java | 134 ++ .../springframework/core/OrderComparator.java | 64 + .../org/springframework/core/Ordered.java | 69 + .../core/OverridingClassLoader.java | 154 +++ .../core/ParameterNameDiscoverer.java | 54 + .../PrioritizedParameterNameDiscoverer.java | 73 ++ .../springframework/core/PriorityOrdered.java | 43 + .../core/ReflectiveVisitorHelper.java | 225 ++++ .../core/SimpleAliasRegistry.java | 147 +++ .../core/SmartClassLoader.java | 43 + .../springframework/core/SpringVersion.java | 44 + .../AbstractCachingLabeledEnumResolver.java | 129 ++ .../enums/AbstractGenericLabeledEnum.java | 52 + .../core/enums/AbstractLabeledEnum.java | 71 ++ .../core/enums/LabeledEnum.java | 94 ++ .../core/enums/LabeledEnumResolver.java | 69 + .../core/enums/LetterCodedLabeledEnum.java | 63 + .../core/enums/ShortCodedLabeledEnum.java | 59 + .../core/enums/StaticLabeledEnum.java | 100 ++ .../core/enums/StaticLabeledEnumResolver.java | 74 ++ .../core/enums/StringCodedLabeledEnum.java | 64 + .../springframework/core/enums/package.html | 8 + .../core/io/AbstractResource.java | 177 +++ .../core/io/ByteArrayResource.java | 116 ++ .../core/io/ClassPathResource.java | 244 ++++ .../core/io/ContextResource.java | 40 + .../core/io/DefaultResourceLoader.java | 143 +++ .../core/io/DescriptiveResource.java | 80 ++ .../core/io/FileSystemResource.java | 177 +++ .../core/io/FileSystemResourceLoader.java | 72 ++ .../core/io/InputStreamResource.java | 121 ++ .../core/io/InputStreamSource.java | 55 + .../org/springframework/core/io/Resource.java | 124 ++ .../core/io/ResourceEditor.java | 106 ++ .../core/io/ResourceLoader.java | 77 ++ .../springframework/core/io/UrlResource.java | 218 ++++ .../org/springframework/core/io/package.html | 7 + .../core/io/support/EncodedResource.java | 117 ++ .../io/support/LocalizedResourceHelper.java | 129 ++ .../PathMatchingResourcePatternResolver.java | 605 +++++++++ .../io/support/PropertiesLoaderSupport.java | 214 ++++ .../io/support/PropertiesLoaderUtils.java | 122 ++ .../support/ResourceArrayPropertyEditor.java | 148 +++ .../io/support/ResourcePatternResolver.java | 76 ++ .../core/io/support/ResourcePatternUtils.java | 71 ++ .../core/io/support/package.html | 8 + .../org/springframework/core/package.html | 8 + .../core/style/DefaultToStringStyler.java | 98 ++ .../core/style/DefaultValueStyler.java | 148 +++ .../core/style/StylerUtils.java | 50 + .../core/style/ToStringCreator.java | 188 +++ .../core/style/ToStringStyler.java | 64 + .../core/style/ValueStyler.java | 35 + .../springframework/core/style/package.html | 7 + .../core/task/AsyncTaskExecutor.java | 55 + .../core/task/SimpleAsyncTaskExecutor.java | 200 +++ .../core/task/SyncTaskExecutor.java | 52 + .../core/task/TaskExecutor.java | 47 + .../core/task/TaskRejectedException.java | 53 + .../core/task/TaskTimeoutException.java | 51 + .../springframework/core/task/package.html | 8 + .../support/ConcurrentExecutorAdapter.java | 60 + .../core/task/support/package.html | 8 + .../springframework/metadata/Attributes.java | 98 ++ .../metadata/commons/CommonsAttributes.java | 87 ++ .../metadata/commons/package.html | 8 + .../org/springframework/metadata/package.html | 8 + .../springframework/util/AntPathMatcher.java | 411 ++++++ .../java/org/springframework/util/Assert.java | 401 ++++++ .../util/AutoPopulatingList.java | 274 ++++ .../util/CachingMapDecorator.java | 276 ++++ .../util/ClassLoaderUtils.java | 100 ++ .../org/springframework/util/ClassUtils.java | 982 +++++++++++++++ .../springframework/util/CollectionUtils.java | 275 ++++ .../util/CommonsLogWriter.java | 75 ++ .../util/ConcurrencyThrottleSupport.java | 169 +++ .../util/CustomizableThreadCreator.java | 177 +++ .../util/DefaultPropertiesPersister.java | 257 ++++ .../springframework/util/FileCopyUtils.java | 244 ++++ .../springframework/util/FileSystemUtils.java | 102 ++ .../springframework/util/Log4jConfigurer.java | 132 ++ .../springframework/util/MethodInvoker.java | 327 +++++ .../org/springframework/util/NumberUtils.java | 273 ++++ .../org/springframework/util/ObjectUtils.java | 833 ++++++++++++ .../org/springframework/util/PathMatcher.java | 91 ++ .../util/PatternMatchUtils.java | 86 ++ .../util/PropertiesPersister.java | 121 ++ .../springframework/util/ReflectionUtils.java | 624 +++++++++ .../springframework/util/ResourceUtils.java | 318 +++++ .../util/ResponseTimeMonitor.java | 55 + .../util/ResponseTimeMonitorImpl.java | 121 ++ .../org/springframework/util/StopWatch.java | 288 +++++ .../org/springframework/util/StringUtils.java | 1113 +++++++++++++++++ .../util/StringValueResolver.java | 38 + .../util/SystemPropertyUtils.java | 86 ++ .../org/springframework/util/TypeUtils.java | 94 ++ .../util/WeakReferenceMonitor.java | 169 +++ .../util/comparator/BooleanComparator.java | 85 ++ .../util/comparator/ComparableComparator.java | 40 + .../util/comparator/CompoundComparator.java | 206 +++ .../util/comparator/InvertibleComparator.java | 118 ++ .../util/comparator/NullSafeComparator.java | 119 ++ .../util/comparator/package.html | 8 + .../org/springframework/util/package.html | 8 + .../springframework/util/xml/DomUtils.java | 167 +++ .../util/xml/SimpleSaxErrorHandler.java | 58 + .../xml/SimpleTransformErrorListener.java | 58 + .../util/xml/TransformerUtils.java | 86 ++ .../util/xml/XmlValidationModeDetector.java | 194 +++ .../org/springframework/util/xml/package.html | 8 + org.springframework.core/template.mf | 11 +- 137 files changed, 19915 insertions(+), 4 deletions(-) create mode 100644 org.springframework.core/src/main/java/org/springframework/core/AliasRegistry.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/AttributeAccessor.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/AttributeAccessorSupport.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/BridgeMethodResolver.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/CollectionFactory.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/ConcurrentMap.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/ConstantException.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/Constants.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/ControlFlow.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/ControlFlowFactory.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/Conventions.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/DecoratingClassLoader.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/ErrorCoded.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/GenericTypeResolver.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/InfrastructureProxy.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/JdkVersion.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/LocalVariableTableParameterNameDiscoverer.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/MethodParameter.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/NamedInheritableThreadLocal.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/NamedThreadLocal.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/NestedCheckedException.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/NestedExceptionUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/NestedIOException.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/NestedRuntimeException.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/OrderComparator.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/Ordered.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/OverridingClassLoader.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/ParameterNameDiscoverer.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/PrioritizedParameterNameDiscoverer.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/PriorityOrdered.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/ReflectiveVisitorHelper.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/SimpleAliasRegistry.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/SmartClassLoader.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/SpringVersion.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/AbstractCachingLabeledEnumResolver.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/AbstractGenericLabeledEnum.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/AbstractLabeledEnum.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/LabeledEnum.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/LabeledEnumResolver.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/LetterCodedLabeledEnum.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/ShortCodedLabeledEnum.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/StaticLabeledEnum.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/StaticLabeledEnumResolver.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/StringCodedLabeledEnum.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/enums/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/AbstractResource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/ByteArrayResource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/ClassPathResource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/ContextResource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/DefaultResourceLoader.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/DescriptiveResource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/FileSystemResource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/FileSystemResourceLoader.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/InputStreamResource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/InputStreamSource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/Resource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/ResourceEditor.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/ResourceLoader.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/UrlResource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/support/EncodedResource.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/support/LocalizedResourceHelper.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/support/PropertiesLoaderSupport.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/support/PropertiesLoaderUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/support/ResourceArrayPropertyEditor.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/support/ResourcePatternResolver.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/support/ResourcePatternUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/io/support/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/core/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/core/style/DefaultToStringStyler.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/style/DefaultValueStyler.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/style/StylerUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/style/ToStringCreator.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/style/ToStringStyler.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/style/ValueStyler.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/style/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/core/task/AsyncTaskExecutor.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/task/SyncTaskExecutor.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/task/TaskExecutor.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/task/TaskRejectedException.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/task/TaskTimeoutException.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/task/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/core/task/support/ConcurrentExecutorAdapter.java create mode 100644 org.springframework.core/src/main/java/org/springframework/core/task/support/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/metadata/Attributes.java create mode 100644 org.springframework.core/src/main/java/org/springframework/metadata/commons/CommonsAttributes.java create mode 100644 org.springframework.core/src/main/java/org/springframework/metadata/commons/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/metadata/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/util/AntPathMatcher.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/Assert.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/AutoPopulatingList.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/CachingMapDecorator.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/ClassLoaderUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/CollectionUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/CommonsLogWriter.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/ConcurrencyThrottleSupport.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/CustomizableThreadCreator.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/DefaultPropertiesPersister.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/FileCopyUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/FileSystemUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/Log4jConfigurer.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/MethodInvoker.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/NumberUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/ObjectUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/PathMatcher.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/PatternMatchUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/PropertiesPersister.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/ReflectionUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/ResourceUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/ResponseTimeMonitor.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/ResponseTimeMonitorImpl.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/StopWatch.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/StringUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/StringValueResolver.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/SystemPropertyUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/TypeUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/WeakReferenceMonitor.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/comparator/BooleanComparator.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/comparator/ComparableComparator.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/comparator/CompoundComparator.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/comparator/InvertibleComparator.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/comparator/NullSafeComparator.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/comparator/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/util/package.html create mode 100644 org.springframework.core/src/main/java/org/springframework/util/xml/DomUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/xml/SimpleSaxErrorHandler.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/xml/SimpleTransformErrorListener.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/xml/TransformerUtils.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/xml/XmlValidationModeDetector.java create mode 100644 org.springframework.core/src/main/java/org/springframework/util/xml/package.html diff --git a/org.springframework.core/ivy.xml b/org.springframework.core/ivy.xml index d6418d085a9..92e2dba0eec 100644 --- a/org.springframework.core/ivy.xml +++ b/org.springframework.core/ivy.xml @@ -21,11 +21,15 @@ - + + + + + + - - \ No newline at end of file + diff --git a/org.springframework.core/src/main/java/org/springframework/core/AliasRegistry.java b/org.springframework.core/src/main/java/org/springframework/core/AliasRegistry.java new file mode 100644 index 00000000000..0e1e18bdaf5 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/AliasRegistry.java @@ -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); + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/AttributeAccessor.java b/org.springframework.core/src/main/java/org/springframework/core/AttributeAccessor.java new file mode 100644 index 00000000000..f0a11b506cd --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/AttributeAccessor.java @@ -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 name to the supplied value. + * If value is null, the attribute is {@link #removeAttribute removed}. + *

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 name. + * Return null 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 name and return its value. + * Return null if no attribute under name is found. + * @param name the unique attribute key + * @return the last value of the attribute, if any + */ + Object removeAttribute(String name); + + /** + * Return true if the attribute identified by name exists. + * Otherwise return false. + * @param name the unique attribute key + */ + boolean hasAttribute(String name); + + /** + * Return the names of all attributes. + */ + String[] attributeNames(); + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/AttributeAccessorSupport.java b/org.springframework.core/src/main/java/org/springframework/core/AttributeAccessorSupport.java new file mode 100644 index 00000000000..ece4a05862b --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/AttributeAccessorSupport.java @@ -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. + * + *

{@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(); + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/BridgeMethodResolver.java b/org.springframework.core/src/main/java/org/springframework/core/BridgeMethodResolver.java new file mode 100644 index 00000000000..a29a2f0d2c8 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/BridgeMethodResolver.java @@ -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. + * + *

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}. + * + *

See + * The Java Language Specification for more details on the use of bridge methods. + * + *

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}. + *

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 not 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 null 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 true if the supplied 'candidateMethod' 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 true 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 false. + */ + 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 null is returned. + */ + private static Method searchForMatch(Class type, Method bridgeMethod) { + return ReflectionUtils.findMethod(type, bridgeMethod.getName(), bridgeMethod.getParameterTypes()); + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/CollectionFactory.java b/org.springframework.core/src/main/java/org/springframework/core/CollectionFactory.java new file mode 100644 index 00000000000..f06cc2d8306 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/CollectionFactory.java @@ -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. + * + *

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 true if the type is approximable, + * false 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. + *

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 true if the type is approximable, + * false 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. + *

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); + } + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/ConcurrentMap.java b/org.springframework.core/src/main/java/org/springframework/core/ConcurrentMap.java new file mode 100644 index 00000000000..9cdb50c702b --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/ConcurrentMap.java @@ -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. + * + *

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); + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java b/org.springframework.core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java new file mode 100644 index 00000000000..30b5f0cc29c --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/ConfigurableObjectInputStream.java @@ -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. + *

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 null) + */ + 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. + *

The default implementation simply returns null. + */ + protected ClassLoader getFallbackClassLoader() throws IOException { + return null; + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/ConstantException.java b/org.springframework.core/src/main/java/org/springframework/core/ConstantException.java new file mode 100644 index 00000000000..938667454bb --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/ConstantException.java @@ -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 + "]"); + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/Constants.java b/org.springframework.core/src/main/java/org/springframework/core/Constants.java new file mode 100644 index 00000000000..e0fb6d1310c --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/Constants.java @@ -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 asXXXX methods of this class + * allow these constant values to be accessed via their string names. + * + *

Consider class Foo containing public final static int CONSTANT1 = 66; + * An instance of this class wrapping Foo.class will return the constant value + * of 66 from its asNumber method given the argument "CONSTANT1". + * + *

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. + *

All public static final variables will be exposed, whatever their type. + * @param clazz the class to analyze + * @throws IllegalArgumentException if the supplied clazz is null + */ + 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 null) + * @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 null) + * @return the String value + * Works even if it's not a string (invokes toString()). + * @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 null) + * @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. + *

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 namePrefix + * 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 null) + * @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. + *

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 nameSuffix + * 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 null) + * @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. + *

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 namePrefix + * 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 null) + * @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. + *

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 nameSuffix + * 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 null) + * @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. + *

Will return the first match. + * @param value constant value to look up + * @param namePrefix prefix of the constant names to search (may be null) + * @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. + *

Will return the first match. + * @param value constant value to look up + * @param nameSuffix suffix of the constant names to search (may be null) + * @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. + *

Uses a common naming idiom: turning all lower case characters to + * upper case, and prepending upper case characters with an underscore. + *

Example: "imageSize" -> "IMAGE_SIZE"
+ * Example: "imagesize" -> "IMAGESIZE".
+ * Example: "ImageSize" -> "_IMAGE_SIZE".
+ * 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(); + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/ControlFlow.java b/org.springframework.core/src/main/java/org/springframework/core/ControlFlow.java new file mode 100644 index 00000000000..4875f1c191f --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/ControlFlow.java @@ -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); + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/ControlFlowFactory.java b/org.springframework.core/src/main/java/org/springframework/core/ControlFlowFactory.java new file mode 100644 index 00000000000..ef10946ed6c --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/ControlFlowFactory.java @@ -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. + * + *

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. + *

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(); + } + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/Conventions.java b/org.springframework.core/src/main/java/org/springframework/core/Conventions.java new file mode 100644 index 00000000000..dde279eaf67 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/Conventions.java @@ -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 + * Object based on its concrete type. The convention + * used is to return the uncapitalized short name of the Class, + * according to JavaBeans property naming rules: So, + * com.myapp.Product becomes product; + * com.myapp.MyProduct becomes myProduct; + * com.myapp.UKProduct becomes UKProduct. + *

For arrays, we use the pluralized version of the array component type. + * For Collections we attempt to 'peek ahead' in the + * Collection 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 Object or as untyped collection). + * @param method the method to generate a variable name for + * @param value the return value (may be null 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 Object 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 null 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 Strings in attribute name format (lowercase, hyphens separating words) + * into property name format (camel-cased). For example, transaction-manager is + * converted into transactionManager. + */ + 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 'foo' qualified by {@link Class} 'com.myapp.SomeClass' + * would be 'com.myapp.SomeClass.foo' + */ + 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. + *

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 Class of an element in the Collection. + * The exact element for which the Class is retreived will depend + * on the concrete Collection 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; + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/DecoratingClassLoader.java b/org.springframework.core/src/main/java/org/springframework/core/DecoratingClassLoader.java new file mode 100644 index 00000000000..7ae436d4ba3 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/DecoratingClassLoader.java @@ -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). + *

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). + *

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. + *

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; + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/ErrorCoded.java b/org.springframework.core/src/main/java/org/springframework/core/ErrorCoded.java new file mode 100644 index 00000000000..6b75babe921 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/ErrorCoded.java @@ -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". + * + *

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 null if not error-coded + */ + String getErrorCode(); + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java b/org.springframework.core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java new file mode 100644 index 00000000000..51fcafe7d01 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/GenericCollectionTypeResolver.java @@ -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. + * + *

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). + * + *

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 null 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 null 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 null 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 null 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 null 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 null 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 null 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 null 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 null 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 null 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 null 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 null 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 null 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. + *

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 null 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 null 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 null 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 null 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 null 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 null 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 null 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 null 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 null 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 null) + * @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 null 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 null) + * @param typeIndex the index of the actual type argument + * @return the generic type as Class, or null 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 null) + * @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 null 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)); + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/GenericTypeResolver.java b/org.springframework.core/src/main/java/org/springframework/core/GenericTypeResolver.java new file mode 100644 index 00000000000..6826571ad8d --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/GenericTypeResolver.java @@ -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. + * + *

Mainly intended for usage within the framework, resolving method + * parameter types even when they are declared generically. + * + *

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 Object.class 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 Type 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}. + *

Consider this case: + *