diff options
author | Emmanuel Bourg <ebourg@apache.org> | 2018-04-08 23:27:38 +0200 |
---|---|---|
committer | Emmanuel Bourg <ebourg@apache.org> | 2018-04-08 23:27:38 +0200 |
commit | e9dafb5ce16aa2faa4fee1019417cc0a7456af57 (patch) | |
tree | c38471d22f5c367a45673724d3a6571d5daf591b /spring-core/src/main/java/org | |
parent | 6dc3d6835b664af0d21e774a5342b90d4417f628 (diff) |
New upstream version 4.3.15
Diffstat (limited to 'spring-core/src/main/java/org')
30 files changed, 434 insertions, 315 deletions
diff --git a/spring-core/src/main/java/org/springframework/asm/ClassWriter.java b/spring-core/src/main/java/org/springframework/asm/ClassWriter.java index a024937d..513b2e5d 100644 --- a/spring-core/src/main/java/org/springframework/asm/ClassWriter.java +++ b/spring-core/src/main/java/org/springframework/asm/ClassWriter.java @@ -1699,7 +1699,7 @@ public class ClassWriter extends ClassVisitor { */ private Item addType(final Item item) { ++typeCount; - Item result = new Item(typeCount, key); + Item result = new Item(typeCount, item); put(result); if (typeTable == null) { typeTable = new Item[16]; diff --git a/spring-core/src/main/java/org/springframework/core/AttributeAccessorSupport.java b/spring-core/src/main/java/org/springframework/core/AttributeAccessorSupport.java index dbbe1aff..ba321bf7 100644 --- a/spring-core/src/main/java/org/springframework/core/AttributeAccessorSupport.java +++ b/spring-core/src/main/java/org/springframework/core/AttributeAccessorSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.util.LinkedHashMap; import java.util.Map; import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Support class for {@link AttributeAccessor AttributeAccessors}, providing @@ -70,7 +71,7 @@ public abstract class AttributeAccessorSupport implements AttributeAccessor, Ser @Override public String[] attributeNames() { - return this.attributes.keySet().toArray(new String[this.attributes.size()]); + return StringUtils.toStringArray(this.attributes.keySet()); } diff --git a/spring-core/src/main/java/org/springframework/core/MethodParameter.java b/spring-core/src/main/java/org/springframework/core/MethodParameter.java index 5bc50d97..7267704e 100644 --- a/spring-core/src/main/java/org/springframework/core/MethodParameter.java +++ b/spring-core/src/main/java/org/springframework/core/MethodParameter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import java.lang.reflect.AnnotatedElement; import java.lang.reflect.Constructor; import java.lang.reflect.Member; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.HashMap; @@ -47,6 +48,8 @@ import org.springframework.util.ClassUtils; */ public class MethodParameter { + private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; + private static final Class<?> javaUtilOptionalClass; static { @@ -482,17 +485,27 @@ public class MethodParameter { * Return the annotations associated with the specific method/constructor parameter. */ public Annotation[] getParameterAnnotations() { - if (this.parameterAnnotations == null) { + Annotation[] paramAnns = this.parameterAnnotations; + if (paramAnns == null) { Annotation[][] annotationArray = (this.method != null ? this.method.getParameterAnnotations() : this.constructor.getParameterAnnotations()); - if (this.parameterIndex >= 0 && this.parameterIndex < annotationArray.length) { - this.parameterAnnotations = adaptAnnotationArray(annotationArray[this.parameterIndex]); + int index = this.parameterIndex; + if (this.constructor != null && this.constructor.getDeclaringClass().isMemberClass() && + !Modifier.isStatic(this.constructor.getDeclaringClass().getModifiers()) && + annotationArray.length == this.constructor.getParameterTypes().length - 1) { + // Bug in javac in JDK <9: annotation array excludes enclosing instance parameter + // for inner classes, so access it with the actual parameter index lowered by 1 + index = this.parameterIndex - 1; + } + if (index >= 0 && index < annotationArray.length) { + paramAnns = adaptAnnotationArray(annotationArray[index]); } else { - this.parameterAnnotations = new Annotation[0]; + paramAnns = EMPTY_ANNOTATION_ARRAY; } + this.parameterAnnotations = paramAnns; } - return this.parameterAnnotations; + return paramAnns; } /** diff --git a/spring-core/src/main/java/org/springframework/core/ParameterizedTypeReference.java b/spring-core/src/main/java/org/springframework/core/ParameterizedTypeReference.java index 7f6d807f..5ed2e1ad 100644 --- a/spring-core/src/main/java/org/springframework/core/ParameterizedTypeReference.java +++ b/spring-core/src/main/java/org/springframework/core/ParameterizedTypeReference.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,14 +24,14 @@ import org.springframework.util.Assert; /** * The purpose of this class is to enable capturing and passing a generic * {@link Type}. In order to capture the generic type and retain it at runtime, - * you need to create a subclass as follows: + * you need to create a subclass (ideally as anonymous inline class) as follows: * * <pre class="code"> * ParameterizedTypeReference<List<String>> typeRef = new ParameterizedTypeReference<List<String>>() {}; * </pre> * - * <p>The resulting {@code typeReference} instance can then be used to obtain a - * {@link Type} instance that carries parameterized type information. + * <p>The resulting {@code typeRef} instance can then be used to obtain a {@link Type} + * instance that carries the captured parameterized type information at runtime. * For more information on "super type tokens" see the link to Neal Gafter's blog post. * * @author Arjen Poutsma @@ -49,8 +49,9 @@ public abstract class ParameterizedTypeReference<T> { Type type = parameterizedTypeReferenceSubclass.getGenericSuperclass(); Assert.isInstanceOf(ParameterizedType.class, type, "Type must be a parameterized type"); ParameterizedType parameterizedType = (ParameterizedType) type; - Assert.isTrue(parameterizedType.getActualTypeArguments().length == 1, "Number of type arguments must be 1"); - this.type = parameterizedType.getActualTypeArguments()[0]; + Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); + Assert.isTrue(actualTypeArguments.length == 1, "Number of type arguments must be 1"); + this.type = actualTypeArguments[0]; } private ParameterizedTypeReference(Type type) { diff --git a/spring-core/src/main/java/org/springframework/core/ResolvableType.java b/spring-core/src/main/java/org/springframework/core/ResolvableType.java index 35e6da4d..155236d5 100644 --- a/spring-core/src/main/java/org/springframework/core/ResolvableType.java +++ b/spring-core/src/main/java/org/springframework/core/ResolvableType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import java.util.Map; import org.springframework.core.SerializableTypeWrapper.FieldTypeProvider; import org.springframework.core.SerializableTypeWrapper.MethodParameterTypeProvider; import org.springframework.core.SerializableTypeWrapper.TypeProvider; +import org.springframework.lang.UsesJava8; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ConcurrentReferenceHashMap; @@ -355,7 +356,7 @@ public class ResolvableType implements Serializable { if (this == NONE) { return false; } - return (((this.type instanceof Class && ((Class<?>) this.type).isArray())) || + return ((this.type instanceof Class && ((Class<?>) this.type).isArray()) || this.type instanceof GenericArrayType || resolveType().isArray()); } @@ -1427,8 +1428,9 @@ public class ResolvableType implements Serializable { @Override public ResolvableType resolveVariable(TypeVariable<?> variable) { for (int i = 0; i < this.variables.length; i++) { - if (SerializableTypeWrapper.unwrap(this.variables[i]).equals( - SerializableTypeWrapper.unwrap(variable))) { + TypeVariable<?> v1 = SerializableTypeWrapper.unwrap(this.variables[i]); + TypeVariable<?> v2 = SerializableTypeWrapper.unwrap(variable); + if (ObjectUtils.nullSafeEquals(v1, v2)) { return this.generics[i]; } } @@ -1453,6 +1455,23 @@ public class ResolvableType implements Serializable { this.typeArguments = typeArguments; } + @Override // on Java 8 + @UsesJava8 + public String getTypeName() { + StringBuilder result = new StringBuilder(this.rawType.getTypeName()); + if (this.typeArguments.length > 0) { + result.append('<'); + for (int i = 0; i < this.typeArguments.length; i++) { + if (i > 0) { + result.append(", "); + } + result.append(this.typeArguments[i].getTypeName()); + } + result.append('>'); + } + return result.toString(); + } + @Override public Type getOwnerType() { return null; diff --git a/spring-core/src/main/java/org/springframework/core/SimpleAliasRegistry.java b/spring-core/src/main/java/org/springframework/core/SimpleAliasRegistry.java index 16c73b5f..fcfbd0f6 100644 --- a/spring-core/src/main/java/org/springframework/core/SimpleAliasRegistry.java +++ b/spring-core/src/main/java/org/springframework/core/SimpleAliasRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -45,23 +45,25 @@ public class SimpleAliasRegistry implements AliasRegistry { public void registerAlias(String name, String alias) { Assert.hasText(name, "'name' must not be empty"); Assert.hasText(alias, "'alias' must not be empty"); - if (alias.equals(name)) { - this.aliasMap.remove(alias); - } - else { - String registeredName = this.aliasMap.get(alias); - if (registeredName != null) { - if (registeredName.equals(name)) { - // An existing alias - no need to re-register - return; - } - if (!allowAliasOverriding()) { - throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + - name + "': It is already registered for name '" + registeredName + "'."); + synchronized (this.aliasMap) { + if (alias.equals(name)) { + this.aliasMap.remove(alias); + } + else { + String registeredName = this.aliasMap.get(alias); + if (registeredName != null) { + if (registeredName.equals(name)) { + // An existing alias - no need to re-register + return; + } + if (!allowAliasOverriding()) { + throw new IllegalStateException("Cannot register alias '" + alias + "' for name '" + + name + "': It is already registered for name '" + registeredName + "'."); + } } + checkForAliasCircle(name, alias); + this.aliasMap.put(alias, name); } - checkForAliasCircle(name, alias); - this.aliasMap.put(alias, name); } } @@ -92,9 +94,11 @@ public class SimpleAliasRegistry implements AliasRegistry { @Override public void removeAlias(String alias) { - String name = this.aliasMap.remove(alias); - if (name == null) { - throw new IllegalStateException("No alias '" + alias + "' registered"); + synchronized (this.aliasMap) { + String name = this.aliasMap.remove(alias); + if (name == null) { + throw new IllegalStateException("No alias '" + alias + "' registered"); + } } } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java index d83747fe..75f42f02 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotatedElementUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import java.util.Set; import org.springframework.core.BridgeMethodResolver; import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -59,10 +60,9 @@ import org.springframework.util.MultiValueMap; * individual method for details on which search algorithm is used. * * <p><strong>Get semantics</strong> are limited to searching for annotations - * that are either <em>present</em> on an {@code AnnotatedElement} (i.e., - * declared locally or {@linkplain java.lang.annotation.Inherited inherited}) - * or declared within the annotation hierarchy <em>above</em> the - * {@code AnnotatedElement}. + * that are either <em>present</em> on an {@code AnnotatedElement} (i.e. declared + * locally or {@linkplain java.lang.annotation.Inherited inherited}) or declared + * within the annotation hierarchy <em>above</em> the {@code AnnotatedElement}. * * <p><strong>Find semantics</strong> are much more exhaustive, providing * <em>get semantics</em> plus support for the following: @@ -76,14 +76,13 @@ import org.springframework.util.MultiValueMap; * </ul> * * <h3>Support for {@code @Inherited}</h3> - * <p>Methods following <em>get semantics</em> will honor the contract of - * Java's {@link java.lang.annotation.Inherited @Inherited} annotation except - * that locally declared annotations (including custom composed annotations) - * will be favored over inherited annotations. In contrast, methods following - * <em>find semantics</em> will completely ignore the presence of - * {@code @Inherited} since the <em>find</em> search algorithm manually - * traverses type and method hierarchies and thereby implicitly supports - * annotation inheritance without the need for {@code @Inherited}. + * <p>Methods following <em>get semantics</em> will honor the contract of Java's + * {@link java.lang.annotation.Inherited @Inherited} annotation except that locally + * declared annotations (including custom composed annotations) will be favored over + * inherited annotations. In contrast, methods following <em>find semantics</em> + * will completely ignore the presence of {@code @Inherited} since the <em>find</em> + * search algorithm manually traverses type and method hierarchies and thereby + * implicitly supports annotation inheritance without a need for {@code @Inherited}. * * @author Phillip Webb * @author Juergen Hoeller @@ -242,7 +241,6 @@ public class AnnotatedElementUtils { return Boolean.TRUE.equals( searchWithGetSemantics(element, annotationType, annotationName, new SimpleAnnotationProcessor<Boolean>() { - @Override public Boolean process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) { return (metaDepth > 0 ? Boolean.TRUE : CONTINUE); @@ -272,7 +270,6 @@ public class AnnotatedElementUtils { if (element.isAnnotationPresent(annotationType)) { return true; } - return Boolean.TRUE.equals(searchWithGetSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor)); } @@ -617,7 +614,6 @@ public class AnnotatedElementUtils { if (element.isAnnotationPresent(annotationType)) { return true; } - return Boolean.TRUE.equals(searchWithFindSemantics(element, annotationType, null, alwaysTrueAnnotationProcessor)); } @@ -873,10 +869,10 @@ public class AnnotatedElementUtils { * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param processor the processor to delegate to - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) */ - private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType, - String annotationName, Processor<T> processor) { + private static <T> T searchWithGetSemantics(AnnotatedElement element, + Class<? extends Annotation> annotationType, String annotationName, Processor<T> processor) { return searchWithGetSemantics(element, annotationType, annotationName, null, processor); } @@ -892,15 +888,16 @@ public class AnnotatedElementUtils { * @param containerType the type of the container that holds repeatable * annotations, or {@code null} if the annotation is not repeatable * @param processor the processor to delegate to - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) * @since 4.3 */ - private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType, - String annotationName, Class<? extends Annotation> containerType, Processor<T> processor) { + private static <T> T searchWithGetSemantics(AnnotatedElement element, + Class<? extends Annotation> annotationType, String annotationName, + Class<? extends Annotation> containerType, Processor<T> processor) { try { - return searchWithGetSemantics(element, annotationType, annotationName, containerType, processor, - new HashSet<AnnotatedElement>(), 0); + return searchWithGetSemantics(element, annotationType, annotationName, + containerType, processor, new HashSet<AnnotatedElement>(), 0); } catch (Throwable ex) { AnnotationUtils.rethrowAnnotationConfigurationException(ex); @@ -923,10 +920,11 @@ public class AnnotatedElementUtils { * @param processor the processor to delegate to * @param visited the set of annotated elements that have already been visited * @param metaDepth the meta-depth of the annotation - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) */ - private static <T> T searchWithGetSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType, - String annotationName, Class<? extends Annotation> containerType, Processor<T> processor, + private static <T> T searchWithGetSemantics(AnnotatedElement element, + Class<? extends Annotation> annotationType, String annotationName, + Class<? extends Annotation> containerType, Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) { Assert.notNull(element, "AnnotatedElement must not be null"); @@ -941,7 +939,7 @@ public class AnnotatedElementUtils { return result; } - if (element instanceof Class) { // otherwise getAnnotations doesn't return anything new + if (element instanceof Class) { // otherwise getAnnotations does not return anything new List<Annotation> inheritedAnnotations = new ArrayList<Annotation>(); for (Annotation annotation : element.getAnnotations()) { if (!declaredAnnotations.contains(annotation)) { @@ -984,13 +982,13 @@ public class AnnotatedElementUtils { * @param processor the processor to delegate to * @param visited the set of annotated elements that have already been visited * @param metaDepth the meta-depth of the annotation - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) * @since 4.2 */ private static <T> T searchWithGetSemanticsInAnnotations(AnnotatedElement element, - List<Annotation> annotations, Class<? extends Annotation> annotationType, String annotationName, - Class<? extends Annotation> containerType, Processor<T> processor, Set<AnnotatedElement> visited, - int metaDepth) { + List<Annotation> annotations, Class<? extends Annotation> annotationType, + String annotationName, Class<? extends Annotation> containerType, + Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth) { // Search in annotations for (Annotation annotation : annotations) { @@ -1053,10 +1051,11 @@ public class AnnotatedElementUtils { * @param annotationName the fully qualified class name of the annotation * type to find (as an alternative to {@code annotationType}) * @param processor the processor to delegate to - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) * @since 4.2 */ - private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType, + private static <T> T searchWithFindSemantics(AnnotatedElement element, + Class<? extends Annotation> annotationType, String annotationName, Processor<T> processor) { return searchWithFindSemantics(element, annotationType, annotationName, null, processor); @@ -1073,11 +1072,12 @@ public class AnnotatedElementUtils { * @param containerType the type of the container that holds repeatable * annotations, or {@code null} if the annotation is not repeatable * @param processor the processor to delegate to - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) * @since 4.3 */ - private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType, - String annotationName, Class<? extends Annotation> containerType, Processor<T> processor) { + private static <T> T searchWithFindSemantics(AnnotatedElement element, + Class<? extends Annotation> annotationType, String annotationName, + Class<? extends Annotation> containerType, Processor<T> processor) { if (containerType != null && !processor.aggregates()) { throw new IllegalArgumentException( @@ -1085,8 +1085,8 @@ public class AnnotatedElementUtils { } try { - return searchWithFindSemantics( - element, annotationType, annotationName, containerType, processor, new HashSet<AnnotatedElement>(), 0); + return searchWithFindSemantics(element, annotationType, annotationName, + containerType, processor, new HashSet<AnnotatedElement>(), 0); } catch (Throwable ex) { AnnotationUtils.rethrowAnnotationConfigurationException(ex); @@ -1109,7 +1109,7 @@ public class AnnotatedElementUtils { * @param processor the processor to delegate to * @param visited the set of annotated elements that have already been visited * @param metaDepth the meta-depth of the annotation - * @return the result of the processor, potentially {@code null} + * @return the result of the processor (potentially {@code null}) * @since 4.2 */ private static <T> T searchWithFindSemantics(AnnotatedElement element, Class<? extends Annotation> annotationType, @@ -1122,79 +1122,86 @@ public class AnnotatedElementUtils { try { // Locally declared annotations (ignoring @Inherited) Annotation[] annotations = element.getDeclaredAnnotations(); - List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<T>() : null); - - // Search in local annotations - for (Annotation annotation : annotations) { - Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); - if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { - if (currentAnnotationType == annotationType || - currentAnnotationType.getName().equals(annotationName) || - processor.alwaysProcesses()) { - T result = processor.process(element, annotation, metaDepth); - if (result != null) { - if (processor.aggregates() && metaDepth == 0) { - aggregatedResults.add(result); - } - else { - return result; + if (annotations.length > 0) { + List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<T>() : null); + + // Search in local annotations + for (Annotation annotation : annotations) { + Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); + if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { + if (currentAnnotationType == annotationType || + currentAnnotationType.getName().equals(annotationName) || + processor.alwaysProcesses()) { + T result = processor.process(element, annotation, metaDepth); + if (result != null) { + if (aggregatedResults != null && metaDepth == 0) { + aggregatedResults.add(result); + } + else { + return result; + } } } - } - // Repeatable annotations in container? - else if (currentAnnotationType == containerType) { - for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) { - T result = processor.process(element, contained, metaDepth); - if (result != null) { - // No need to post-process since repeatable annotations within a - // container cannot be composed annotations. - aggregatedResults.add(result); + // Repeatable annotations in container? + else if (currentAnnotationType == containerType) { + for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) { + T result = processor.process(element, contained, metaDepth); + if (aggregatedResults != null && result != null) { + // No need to post-process since repeatable annotations within a + // container cannot be composed annotations. + aggregatedResults.add(result); + } } } } } - } - // Search in meta annotations on local annotations - for (Annotation annotation : annotations) { - Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); - if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { - T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName, - containerType, processor, visited, metaDepth + 1); - if (result != null) { - processor.postProcess(currentAnnotationType, annotation, result); - if (processor.aggregates() && metaDepth == 0) { - aggregatedResults.add(result); - } - else { - return result; + // Recursively search in meta-annotations + for (Annotation annotation : annotations) { + Class<? extends Annotation> currentAnnotationType = annotation.annotationType(); + if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) { + T result = searchWithFindSemantics(currentAnnotationType, annotationType, annotationName, + containerType, processor, visited, metaDepth + 1); + if (result != null) { + processor.postProcess(currentAnnotationType, annotation, result); + if (aggregatedResults != null && metaDepth == 0) { + aggregatedResults.add(result); + } + else { + return result; + } } } } - } - if (processor.aggregates()) { - // Prepend to support top-down ordering within class hierarchies - processor.getAggregatedResults().addAll(0, aggregatedResults); + if (!CollectionUtils.isEmpty(aggregatedResults)) { + // Prepend to support top-down ordering within class hierarchies + processor.getAggregatedResults().addAll(0, aggregatedResults); + } } if (element instanceof Method) { Method method = (Method) element; + T result; // Search on possibly bridged method Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); - T result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName, containerType, - processor, visited, metaDepth); - if (result != null) { - return result; + if (resolvedMethod != method) { + result = searchWithFindSemantics(resolvedMethod, annotationType, annotationName, + containerType, processor, visited, metaDepth); + if (result != null) { + return result; + } } // Search on methods in interfaces declared locally Class<?>[] ifcs = method.getDeclaringClass().getInterfaces(); - result = searchOnInterfaces(method, annotationType, annotationName, containerType, processor, - visited, metaDepth, ifcs); - if (result != null) { - return result; + if (ifcs.length > 0) { + result = searchOnInterfaces(method, annotationType, annotationName, + containerType, processor, visited, metaDepth, ifcs); + if (result != null) { + return result; + } } // Search on methods in class hierarchy and interface hierarchy @@ -1204,7 +1211,6 @@ public class AnnotatedElementUtils { if (clazz == null || Object.class == clazz) { break; } - try { Method equivalentMethod = clazz.getDeclaredMethod(method.getName(), method.getParameterTypes()); Method resolvedEquivalentMethod = BridgeMethodResolver.findBridgedMethod(equivalentMethod); @@ -1217,10 +1223,9 @@ public class AnnotatedElementUtils { catch (NoSuchMethodException ex) { // No equivalent method found } - // Search on interfaces declared on superclass - result = searchOnInterfaces(method, annotationType, annotationName, containerType, processor, - visited, metaDepth, clazz.getInterfaces()); + result = searchOnInterfaces(method, annotationType, annotationName, + containerType, processor, visited, metaDepth, clazz.getInterfaces()); if (result != null) { return result; } @@ -1231,8 +1236,8 @@ public class AnnotatedElementUtils { // Search on interfaces for (Class<?> ifc : clazz.getInterfaces()) { - T result = searchWithFindSemantics(ifc, annotationType, annotationName, containerType, - processor, visited, metaDepth); + T result = searchWithFindSemantics(ifc, annotationType, annotationName, + containerType, processor, visited, metaDepth); if (result != null) { return result; } @@ -1241,8 +1246,8 @@ public class AnnotatedElementUtils { // Search on superclass Class<?> superclass = clazz.getSuperclass(); if (superclass != null && Object.class != superclass) { - T result = searchWithFindSemantics(superclass, annotationType, annotationName, containerType, - processor, visited, metaDepth); + T result = searchWithFindSemantics(superclass, annotationType, annotationName, + containerType, processor, visited, metaDepth); if (result != null) { return result; } diff --git a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java index 5e19db46..971568da 100644 --- a/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java +++ b/spring-core/src/main/java/org/springframework/core/annotation/AnnotationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -145,17 +145,17 @@ public abstract class AnnotationUtils { * <p>Note that this method supports only a single level of meta-annotations. * For support for arbitrary levels of meta-annotations, use one of the * {@code find*()} methods instead. - * @param ann the Annotation to check + * @param annotation the Annotation to check * @param annotationType the annotation type to look for, both locally and as a meta-annotation * @return the first matching annotation, or {@code null} if not found * @since 4.0 */ @SuppressWarnings("unchecked") - public static <A extends Annotation> A getAnnotation(Annotation ann, Class<A> annotationType) { - if (annotationType.isInstance(ann)) { - return synthesizeAnnotation((A) ann); + public static <A extends Annotation> A getAnnotation(Annotation annotation, Class<A> annotationType) { + if (annotationType.isInstance(annotation)) { + return synthesizeAnnotation((A) annotation); } - Class<? extends Annotation> annotatedElement = ann.annotationType(); + Class<? extends Annotation> annotatedElement = annotation.annotationType(); try { return synthesizeAnnotation(annotatedElement.getAnnotation(annotationType), annotatedElement); } @@ -568,7 +568,6 @@ public abstract class AnnotationUtils { if (result == null) { Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method); result = findAnnotation((AnnotatedElement) resolvedMethod, annotationType); - if (result == null) { result = searchOnInterfaces(method, annotationType, method.getDeclaringClass().getInterfaces()); } @@ -603,10 +602,10 @@ public abstract class AnnotationUtils { private static <A extends Annotation> A searchOnInterfaces(Method method, Class<A> annotationType, Class<?>... ifcs) { A annotation = null; - for (Class<?> iface : ifcs) { - if (isInterfaceWithAnnotatedMethods(iface)) { + for (Class<?> ifc : ifcs) { + if (isInterfaceWithAnnotatedMethods(ifc)) { try { - Method equivalentMethod = iface.getMethod(method.getName(), method.getParameterTypes()); + Method equivalentMethod = ifc.getMethod(method.getName(), method.getParameterTypes()); annotation = getAnnotation(equivalentMethod, annotationType); } catch (NoSuchMethodException ex) { @@ -620,13 +619,13 @@ public abstract class AnnotationUtils { return annotation; } - static boolean isInterfaceWithAnnotatedMethods(Class<?> iface) { - Boolean found = annotatedInterfaceCache.get(iface); + static boolean isInterfaceWithAnnotatedMethods(Class<?> ifc) { + Boolean found = annotatedInterfaceCache.get(ifc); if (found != null) { return found; } found = Boolean.FALSE; - for (Method ifcMethod : iface.getMethods()) { + for (Method ifcMethod : ifc.getMethods()) { try { if (ifcMethod.getAnnotations().length > 0) { found = Boolean.TRUE; @@ -637,7 +636,7 @@ public abstract class AnnotationUtils { handleIntrospectionFailure(ifcMethod, ex); } } - annotatedInterfaceCache.put(iface, found); + annotatedInterfaceCache.put(ifc, found); return found; } @@ -933,6 +932,32 @@ public abstract class AnnotationUtils { } /** + * Check the declared attributes of the given annotation, in particular covering + * Google App Engine's late arrival of {@code TypeNotPresentExceptionProxy} for + * {@code Class} values (instead of early {@code Class.getAnnotations() failure}. + * <p>This method not failing indicates that {@link #getAnnotationAttributes(Annotation)} + * won't failure either (when attempted later on). + * @param annotation the annotation to validate + * @throws IllegalStateException if a declared {@code Class} attribute could not be read + * @since 4.3.15 + * @see Class#getAnnotations() + * @see #getAnnotationAttributes(Annotation) + */ + public static void validateAnnotation(Annotation annotation) { + for (Method method : getAttributeMethods(annotation.annotationType())) { + Class<?> returnType = method.getReturnType(); + if (returnType == Class.class || returnType == Class[].class) { + try { + method.invoke(annotation); + } + catch (Throwable ex) { + throw new IllegalStateException("Could not obtain annotation attribute value for " + method, ex); + } + } + } + } + + /** * Retrieve the given annotation's attributes as a {@link Map}, preserving all * attribute types. * <p>Equivalent to calling {@link #getAnnotationAttributes(Annotation, boolean, boolean)} @@ -1260,15 +1285,12 @@ public abstract class AnnotationUtils { } Object value = attributes.get(attributeName); boolean valuePresent = (value != null && !(value instanceof DefaultValueHolder)); - for (String aliasedAttributeName : aliasMap.get(attributeName)) { if (valuesAlreadyReplaced.contains(aliasedAttributeName)) { continue; } - Object aliasedValue = attributes.get(aliasedAttributeName); boolean aliasPresent = (aliasedValue != null && !(aliasedValue instanceof DefaultValueHolder)); - // Something to validate or replace with an alias? if (valuePresent || aliasPresent) { if (valuePresent && aliasPresent) { @@ -1882,17 +1904,31 @@ public abstract class AnnotationUtils { if (element instanceof Class && Annotation.class.isAssignableFrom((Class<?>) element)) { // Meta-annotation or (default) value lookup on an annotation type if (loggerToUse.isDebugEnabled()) { - loggerToUse.debug("Failed to meta-introspect annotation [" + element + "]: " + ex); + loggerToUse.debug("Failed to meta-introspect annotation " + element + ": " + ex); } } else { // Direct annotation lookup on regular Class, Method, Field if (loggerToUse.isInfoEnabled()) { - loggerToUse.info("Failed to introspect annotations on [" + element + "]: " + ex); + loggerToUse.info("Failed to introspect annotations on " + element + ": " + ex); } } } + /** + * Clear the internal annotation metadata cache. + * @since 4.3.15 + */ + public static void clearCache() { + findAnnotationCache.clear(); + metaPresentCache.clear(); + annotatedInterfaceCache.clear(); + synthesizableCache.clear(); + attributeAliasesCache.clear(); + attributeMethodsCache.clear(); + aliasDescriptorCache.clear(); + } + /** * Cache key for the AnnotatedElement cache. diff --git a/spring-core/src/main/java/org/springframework/core/convert/Property.java b/spring-core/src/main/java/org/springframework/core/convert/Property.java index 9c6db43c..c7c1bddc 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/Property.java +++ b/spring-core/src/main/java/org/springframework/core/convert/Property.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,8 +36,8 @@ import org.springframework.util.StringUtils; * is not available in a number of environments (e.g. Android, Java ME), so this is * desirable for portability of Spring's core conversion facility. * - * <p>Used to build a TypeDescriptor from a property location. - * The built TypeDescriptor can then be used to convert from/to the property type. + * <p>Used to build a {@link TypeDescriptor} from a property location. The built + * {@code TypeDescriptor} can then be used to convert from/to the property type. * * @author Keith Donald * @author Phillip Webb diff --git a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java index 593687ef..03374f75 100644 --- a/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java +++ b/spring-core/src/main/java/org/springframework/core/convert/TypeDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -54,12 +54,12 @@ public class TypeDescriptor implements Serializable { private static final boolean streamAvailable = ClassUtils.isPresent( "java.util.stream.Stream", TypeDescriptor.class.getClassLoader()); - private static final Map<Class<?>, TypeDescriptor> commonTypesCache = new HashMap<Class<?>, TypeDescriptor>(18); + private static final Map<Class<?>, TypeDescriptor> commonTypesCache = new HashMap<Class<?>, TypeDescriptor>(32); private static final Class<?>[] CACHED_COMMON_TYPES = { boolean.class, Boolean.class, byte.class, Byte.class, char.class, Character.class, - double.class, Double.class, int.class, Integer.class, long.class, Long.class, - float.class, Float.class, short.class, Short.class, String.class, Object.class}; + double.class, Double.class, float.class, Float.class, int.class, Integer.class, + long.class, Long.class, short.class, Short.class, String.class, Object.class}; static { for (Class<?> preCachedClass : CACHED_COMMON_TYPES) { diff --git a/spring-core/src/main/java/org/springframework/core/env/SimpleCommandLineArgsParser.java b/spring-core/src/main/java/org/springframework/core/env/SimpleCommandLineArgsParser.java index b7ad3c42..d9e0afb6 100644 --- a/spring-core/src/main/java/org/springframework/core/env/SimpleCommandLineArgsParser.java +++ b/spring-core/src/main/java/org/springframework/core/env/SimpleCommandLineArgsParser.java @@ -65,8 +65,8 @@ class SimpleCommandLineArgsParser { String optionName; String optionValue = null; if (optionText.contains("=")) { - optionName = optionText.substring(0, optionText.indexOf("=")); - optionValue = optionText.substring(optionText.indexOf("=")+1, optionText.length()); + optionName = optionText.substring(0, optionText.indexOf('=')); + optionValue = optionText.substring(optionText.indexOf('=')+1, optionText.length()); } else { optionName = optionText; diff --git a/spring-core/src/main/java/org/springframework/core/env/SimpleCommandLinePropertySource.java b/spring-core/src/main/java/org/springframework/core/env/SimpleCommandLinePropertySource.java index f1b1f8d7..36631a5a 100644 --- a/spring-core/src/main/java/org/springframework/core/env/SimpleCommandLinePropertySource.java +++ b/spring-core/src/main/java/org/springframework/core/env/SimpleCommandLinePropertySource.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,8 @@ package org.springframework.core.env; import java.util.List; +import org.springframework.util.StringUtils; + /** * {@link CommandLinePropertySource} implementation backed by a simple String array. * @@ -100,7 +102,7 @@ public class SimpleCommandLinePropertySource extends CommandLinePropertySource<C */ @Override public String[] getPropertyNames() { - return source.getOptionNames().toArray(new String[source.getOptionNames().size()]); + return StringUtils.toStringArray(this.source.getOptionNames()); } @Override diff --git a/spring-core/src/main/java/org/springframework/core/io/VfsUtils.java b/spring-core/src/main/java/org/springframework/core/io/VfsUtils.java index d66b9a63..36c2019b 100644 --- a/spring-core/src/main/java/org/springframework/core/io/VfsUtils.java +++ b/spring-core/src/main/java/org/springframework/core/io/VfsUtils.java @@ -46,24 +46,24 @@ public abstract class VfsUtils { private static final String VFS3_PKG = "org.jboss.vfs."; private static final String VFS_NAME = "VFS"; - private static Method VFS_METHOD_GET_ROOT_URL; - private static Method VFS_METHOD_GET_ROOT_URI; - - private static Method VIRTUAL_FILE_METHOD_EXISTS; - private static Method VIRTUAL_FILE_METHOD_GET_INPUT_STREAM; - private static Method VIRTUAL_FILE_METHOD_GET_SIZE; - private static Method VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED; - private static Method VIRTUAL_FILE_METHOD_TO_URL; - private static Method VIRTUAL_FILE_METHOD_TO_URI; - private static Method VIRTUAL_FILE_METHOD_GET_NAME; - private static Method VIRTUAL_FILE_METHOD_GET_PATH_NAME; - private static Method VIRTUAL_FILE_METHOD_GET_CHILD; - - protected static Class<?> VIRTUAL_FILE_VISITOR_INTERFACE; - protected static Method VIRTUAL_FILE_METHOD_VISIT; - - private static Field VISITOR_ATTRIBUTES_FIELD_RECURSE; - private static Method GET_PHYSICAL_FILE; + private static final Method VFS_METHOD_GET_ROOT_URL; + private static final Method VFS_METHOD_GET_ROOT_URI; + + private static final Method VIRTUAL_FILE_METHOD_EXISTS; + private static final Method VIRTUAL_FILE_METHOD_GET_INPUT_STREAM; + private static final Method VIRTUAL_FILE_METHOD_GET_SIZE; + private static final Method VIRTUAL_FILE_METHOD_GET_LAST_MODIFIED; + private static final Method VIRTUAL_FILE_METHOD_TO_URL; + private static final Method VIRTUAL_FILE_METHOD_TO_URI; + private static final Method VIRTUAL_FILE_METHOD_GET_NAME; + private static final Method VIRTUAL_FILE_METHOD_GET_PATH_NAME; + private static final Method VIRTUAL_FILE_METHOD_GET_CHILD; + + protected static final Class<?> VIRTUAL_FILE_VISITOR_INTERFACE; + protected static final Method VIRTUAL_FILE_METHOD_VISIT; + + private static final Field VISITOR_ATTRIBUTES_FIELD_RECURSE; + private static final Method GET_PHYSICAL_FILE; static { ClassLoader loader = VfsUtils.class.getClassLoader(); diff --git a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java index f9ae32cf..131bb0a1 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/PathMatchingResourcePatternResolver.java @@ -287,7 +287,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol // Generally only look for a pattern after a prefix here, // and on Tomcat only after the "*/" separator for its "war:" protocol. int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : - locationPattern.indexOf(":") + 1); + locationPattern.indexOf(':') + 1); if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) { // a file pattern return findPathMatchingResources(locationPattern); @@ -525,7 +525,7 @@ public class PathMatchingResourcePatternResolver implements ResourcePatternResol * @see #retrieveMatchingFiles */ protected String determineRootDir(String location) { - int prefixEnd = location.indexOf(":") + 1; + int prefixEnd = location.indexOf(':') + 1; int rootDirEnd = location.length(); while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) { rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1; diff --git a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java index 908b54d3..fa378ed0 100644 --- a/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java +++ b/spring-core/src/main/java/org/springframework/core/io/support/SpringFactoriesLoader.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -69,7 +69,7 @@ public abstract class SpringFactoriesLoader { /** * Load and instantiate the factory implementations of the given type from * {@value #FACTORIES_RESOURCE_LOCATION}, using the given class loader. - * <p>The returned factories are sorted in accordance with the {@link AnnotationAwareOrderComparator}. + * <p>The returned factories are sorted through {@link AnnotationAwareOrderComparator}. * <p>If a custom instantiation strategy is required, use {@link #loadFactoryNames} * to obtain all registered factory names. * @param factoryClass the interface or abstract class representing the factory diff --git a/spring-core/src/main/java/org/springframework/core/task/support/ExecutorServiceAdapter.java b/spring-core/src/main/java/org/springframework/core/task/support/ExecutorServiceAdapter.java index 393ecee6..d666e7f3 100644 --- a/spring-core/src/main/java/org/springframework/core/task/support/ExecutorServiceAdapter.java +++ b/spring-core/src/main/java/org/springframework/core/task/support/ExecutorServiceAdapter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,18 +24,18 @@ import org.springframework.core.task.TaskExecutor; import org.springframework.util.Assert; /** - * Adapter that takes a Spring {@link org.springframework.core.task.TaskExecutor}) + * Adapter that takes a Spring {@link org.springframework.core.task.TaskExecutor} * and exposes a full {@code java.util.concurrent.ExecutorService} for it. * * <p>This is primarily for adapting to client components that communicate via the * {@code java.util.concurrent.ExecutorService} API. It can also be used as * common ground between a local Spring {@code TaskExecutor} backend and a - * JNDI-located {@code ManagedExecutorService} in a Java EE 6 environment. + * JNDI-located {@code ManagedExecutorService} in a Java EE 7 environment. * * <p><b>NOTE:</b> This ExecutorService adapter does <em>not</em> support the * lifecycle methods in the {@code java.util.concurrent.ExecutorService} API * ("shutdown()" etc), similar to a server-wide {@code ManagedExecutorService} - * in a Java EE 6 environment. The lifecycle is always up to the backend pool, + * in a Java EE 7 environment. The lifecycle is always up to the backend pool, * with this adapter acting as an access-only proxy for that target pool. * * @author Juergen Hoeller diff --git a/spring-core/src/main/java/org/springframework/core/type/filter/AnnotationTypeFilter.java b/spring-core/src/main/java/org/springframework/core/type/filter/AnnotationTypeFilter.java index 2b2faa4d..9aaea290 100644 --- a/spring-core/src/main/java/org/springframework/core/type/filter/AnnotationTypeFilter.java +++ b/spring-core/src/main/java/org/springframework/core/type/filter/AnnotationTypeFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -70,7 +70,9 @@ public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter * @param considerMetaAnnotations whether to also match on meta-annotations * @param considerInterfaces whether to also match interfaces */ - public AnnotationTypeFilter(Class<? extends Annotation> annotationType, boolean considerMetaAnnotations, boolean considerInterfaces) { + public AnnotationTypeFilter( + Class<? extends Annotation> annotationType, boolean considerMetaAnnotations, boolean considerInterfaces) { + super(annotationType.isAnnotationPresent(Inherited.class), considerInterfaces); this.annotationType = annotationType; this.considerMetaAnnotations = considerMetaAnnotations; @@ -99,6 +101,11 @@ public class AnnotationTypeFilter extends AbstractTypeHierarchyTraversingFilter return false; } else if (typeName.startsWith("java")) { + if (!this.annotationType.getName().startsWith("java")) { + // Standard Java types do not have non-standard annotations on them -> + // skip any load attempt, in particular for Java language interfaces. + return false; + } try { Class<?> clazz = ClassUtils.forName(typeName, getClass().getClassLoader()); return ((this.considerMetaAnnotations ? AnnotationUtils.getAnnotation(clazz, this.annotationType) : diff --git a/spring-core/src/main/java/org/springframework/util/ClassUtils.java b/spring-core/src/main/java/org/springframework/util/ClassUtils.java index 5a99de2e..89418ced 100644 --- a/spring-core/src/main/java/org/springframework/util/ClassUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ClassUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -112,7 +112,7 @@ public abstract class ClassUtils { registerCommonClasses(entry.getKey()); } - Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(32); + Set<Class<?>> primitiveTypes = new HashSet<Class<?>>(64); primitiveTypes.addAll(primitiveWrapperTypeMap.values()); primitiveTypes.addAll(Arrays.asList(new Class<?>[] { boolean[].class, byte[].class, char[].class, double[].class, @@ -125,9 +125,10 @@ public abstract class ClassUtils { registerCommonClasses(Boolean[].class, Byte[].class, Character[].class, Double[].class, Float[].class, Integer[].class, Long[].class, Short[].class); registerCommonClasses(Number.class, Number[].class, String.class, String[].class, - Object.class, Object[].class, Class.class, Class[].class); + Class.class, Class[].class, Object.class, Object[].class); registerCommonClasses(Throwable.class, Exception.class, RuntimeException.class, Error.class, StackTraceElement.class, StackTraceElement[].class); + registerCommonClasses(Enum.class, Iterable.class, Cloneable.class, Comparable.class); } @@ -1064,11 +1065,12 @@ public abstract class ClassUtils { } /** - * Copy the given Collection into a Class array. - * The Collection must contain Class elements only. - * @param collection the Collection to copy - * @return the Class array ({@code null} if the passed-in - * Collection was {@code null}) + * Copy the given {@code Collection} into a {@code Class} array. + * <p>The {@code Collection} must contain {@code Class} elements only. + * @param collection the {@code Collection} to copy + * @return the {@code Class} array + * @since 3.1 + * @see StringUtils#toStringArray */ public static Class<?>[] toClassArray(Collection<Class<?>> collection) { if (collection == null) { @@ -1109,8 +1111,7 @@ public abstract class ClassUtils { * @return all interfaces that the given object implements as an array */ public static Class<?>[] getAllInterfacesForClass(Class<?> clazz, ClassLoader classLoader) { - Set<Class<?>> ifcs = getAllInterfacesForClassAsSet(clazz, classLoader); - return ifcs.toArray(new Class<?>[ifcs.size()]); + return toClassArray(getAllInterfacesForClassAsSet(clazz, classLoader)); } /** @@ -1150,12 +1151,13 @@ public abstract class ClassUtils { return Collections.<Class<?>>singleton(clazz); } Set<Class<?>> interfaces = new LinkedHashSet<Class<?>>(); - while (clazz != null) { - Class<?>[] ifcs = clazz.getInterfaces(); + Class<?> current = clazz; + while (current != null) { + Class<?>[] ifcs = current.getInterfaces(); for (Class<?> ifc : ifcs) { interfaces.addAll(getAllInterfacesForClassAsSet(ifc, classLoader)); } - clazz = clazz.getSuperclass(); + current = current.getSuperclass(); } return interfaces; } diff --git a/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java b/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java index 17127f2e..cee33acd 100644 --- a/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java +++ b/spring-core/src/main/java/org/springframework/util/ConcurrentReferenceHashMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -223,18 +223,27 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen @Override public V get(Object key) { - Reference<K, V> reference = getReference(key, Restructure.WHEN_NECESSARY); - Entry<K, V> entry = (reference != null ? reference.get() : null); + Entry<K, V> entry = getEntryIfAvailable(key); return (entry != null ? entry.getValue() : null); } @Override + public V getOrDefault(Object key, V defaultValue) { + Entry<K, V> entry = getEntryIfAvailable(key); + return (entry != null ? entry.getValue() : defaultValue); + } + + @Override public boolean containsKey(Object key) { - Reference<K, V> reference = getReference(key, Restructure.WHEN_NECESSARY); - Entry<K, V> entry = (reference != null ? reference.get() : null); + Entry<K, V> entry = getEntryIfAvailable(key); return (entry != null && ObjectUtils.nullSafeEquals(entry.getKey(), key)); } + private Entry<K, V> getEntryIfAvailable(Object key) { + Reference<K, V> reference = getReference(key, Restructure.WHEN_NECESSARY); + return (reference != null ? reference.get() : null); + } + /** * Return a {@link Reference} to the {@link Entry} for the specified {@code key}, * or {@code null} if not found. @@ -582,17 +591,18 @@ public class ConcurrentReferenceHashMap<K, V> extends AbstractMap<K, V> implemen } private Reference<K, V> findInChain(Reference<K, V> reference, Object key, int hash) { - while (reference != null) { - if (reference.getHash() == hash) { - Entry<K, V> entry = reference.get(); + Reference<K, V> currRef = reference; + while (currRef != null) { + if (currRef.getHash() == hash) { + Entry<K, V> entry = currRef.get(); if (entry != null) { K entryKey = entry.getKey(); - if (entryKey == key || entryKey.equals(key)) { - return reference; + if (ObjectUtils.nullSafeEquals(entryKey, key)) { + return currRef; } } } - reference = reference.getNext(); + currRef = currRef.getNext(); } return null; } diff --git a/spring-core/src/main/java/org/springframework/util/DigestUtils.java b/spring-core/src/main/java/org/springframework/util/DigestUtils.java index 36b1a583..cc782565 100644 --- a/spring-core/src/main/java/org/springframework/util/DigestUtils.java +++ b/spring-core/src/main/java/org/springframework/util/DigestUtils.java @@ -169,7 +169,7 @@ public abstract class DigestUtils { } private static char[] encodeHex(byte[] bytes) { - char chars[] = new char[32]; + char[] chars = new char[32]; for (int i = 0; i < chars.length; i = i + 2) { byte b = bytes[i / 2]; chars[i] = HEX_CHARS[(b >>> 0x4) & 0xf]; diff --git a/spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java b/spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java index 5e327797..886f92c0 100644 --- a/spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java +++ b/spring-core/src/main/java/org/springframework/util/FastByteArrayOutputStream.java @@ -489,6 +489,7 @@ public class FastByteArrayOutputStream extends OutputStream { * Update the message digest with the remaining bytes in this stream. * @param messageDigest The message digest to update */ + @Override public void updateMessageDigest(MessageDigest messageDigest) { updateMessageDigest(messageDigest, available()); } @@ -499,6 +500,7 @@ public class FastByteArrayOutputStream extends OutputStream { * @param messageDigest The message digest to update * @param len how many bytes to read from this stream and use to update the message digest */ + @Override public void updateMessageDigest(MessageDigest messageDigest, int len) { if (this.currentBuffer == null) { // This stream doesn't have any data in it... diff --git a/spring-core/src/main/java/org/springframework/util/MimeType.java b/spring-core/src/main/java/org/springframework/util/MimeType.java index 23ceb3e3..26afb8ef 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeType.java +++ b/spring-core/src/main/java/org/springframework/util/MimeType.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,15 +29,15 @@ import java.util.Map; import java.util.TreeSet; /** - * Represents a MIME Type, as originally defined in RFC 2046 and subsequently used in - * other Internet protocols including HTTP. + * Represents a MIME Type, as originally defined in RFC 2046 and subsequently + * used in other Internet protocols including HTTP. * * <p>This class, however, does not contain support for the q-parameters used - * in HTTP content negotiation. Those can be found in the sub-class + * in HTTP content negotiation. Those can be found in the subclass * {@code org.springframework.http.MediaType} in the {@code spring-web} module. * * <p>Consists of a {@linkplain #getType() type} and a {@linkplain #getSubtype() subtype}. - * Also has functionality to parse media types from a string using + * Also has functionality to parse MIME Type values from a {@code String} using * {@link #valueOf(String)}. For more parsing options see {@link MimeTypeUtils}. * * @author Arjen Poutsma @@ -137,7 +137,7 @@ public class MimeType implements Comparable<MimeType>, Serializable { /** * Copy-constructor that copies the type, subtype, parameters of the given {@code MimeType}, * and allows to set the specified character set. - * @param other the other media type + * @param other the other MimeType * @param charset the character set * @throws IllegalArgumentException if any of the parameters contains illegal characters * @since 4.3 @@ -149,8 +149,8 @@ public class MimeType implements Comparable<MimeType>, Serializable { /** * Copy-constructor that copies the type and subtype of the given {@code MimeType}, * and allows for different parameter. - * @param other the other media type - * @param parameters the parameters, may be {@code null} + * @param other the other MimeType + * @param parameters the parameters (may be {@code null}) * @throws IllegalArgumentException if any of the parameters contains illegal characters */ public MimeType(MimeType other, Map<String, String> parameters) { @@ -161,7 +161,7 @@ public class MimeType implements Comparable<MimeType>, Serializable { * Create a new {@code MimeType} for the given type, subtype, and parameters. * @param type the primary type * @param subtype the subtype - * @param parameters the parameters, may be {@code null} + * @param parameters the parameters (may be {@code null}) * @throws IllegalArgumentException if any of the parameters contains illegal characters */ public MimeType(String type, String subtype, Map<String, String> parameters) { @@ -227,7 +227,7 @@ public class MimeType implements Comparable<MimeType>, Serializable { if (s == null) { return null; } - return isQuotedString(s) ? s.substring(1, s.length() - 1) : s; + return (isQuotedString(s) ? s.substring(1, s.length() - 1) : s); } /** @@ -249,9 +249,9 @@ public class MimeType implements Comparable<MimeType>, Serializable { } /** - * Indicates whether this media type is concrete, i.e. whether neither the type + * Indicates whether this MIME Type is concrete, i.e. whether neither the type * nor the subtype is a wildcard character <code>*</code>. - * @return whether this media type is concrete + * @return whether this MIME Type is concrete */ public boolean isConcrete() { return !isWildcardType() && !isWildcardSubtype(); @@ -310,19 +310,19 @@ public class MimeType implements Comparable<MimeType>, Serializable { } /** - * Indicate whether this {@code MediaType} includes the given media type. + * Indicate whether this MIME Type includes the given MIME Type. * <p>For instance, {@code text/*} includes {@code text/plain} and {@code text/html}, - * and {@code application/*+xml} includes {@code application/soap+xml}, etc. This - * method is <b>not</b> symmetric. - * @param other the reference media type with which to compare - * @return {@code true} if this media type includes the given media type; + * and {@code application/*+xml} includes {@code application/soap+xml}, etc. + * This method is <b>not</b> symmetric. + * @param other the reference MIME Type with which to compare + * @return {@code true} if this MIME Type includes the given MIME Type; * {@code false} otherwise */ public boolean includes(MimeType other) { if (other == null) { return false; } - if (this.isWildcardType()) { + if (isWildcardType()) { // */* includes anything return true; } @@ -330,9 +330,9 @@ public class MimeType implements Comparable<MimeType>, Serializable { if (getSubtype().equals(other.getSubtype())) { return true; } - if (this.isWildcardSubtype()) { - // wildcard with suffix, e.g. application/*+xml - int thisPlusIdx = getSubtype().indexOf('+'); + if (isWildcardSubtype()) { + // Wildcard with suffix, e.g. application/*+xml + int thisPlusIdx = getSubtype().lastIndexOf('+'); if (thisPlusIdx == -1) { return true; } @@ -354,12 +354,12 @@ public class MimeType implements Comparable<MimeType>, Serializable { } /** - * Indicate whether this {@code MediaType} is compatible with the given media type. + * Indicate whether this MIME Type is compatible with the given MIME Type. * <p>For instance, {@code text/*} is compatible with {@code text/plain}, * {@code text/html}, and vice versa. In effect, this method is similar to * {@link #includes}, except that it <b>is</b> symmetric. - * @param other the reference media type with which to compare - * @return {@code true} if this media type is compatible with the given media type; + * @param other the reference MIME Type with which to compare + * @return {@code true} if this MIME Type is compatible with the given MIME Type; * {@code false} otherwise */ public boolean isCompatibleWith(MimeType other) { @@ -373,22 +373,18 @@ public class MimeType implements Comparable<MimeType>, Serializable { if (getSubtype().equals(other.getSubtype())) { return true; } - // wildcard with suffix? e.g. application/*+xml - if (this.isWildcardSubtype() || other.isWildcardSubtype()) { - + // Wildcard with suffix? e.g. application/*+xml + if (isWildcardSubtype() || other.isWildcardSubtype()) { int thisPlusIdx = getSubtype().indexOf('+'); int otherPlusIdx = other.getSubtype().indexOf('+'); - if (thisPlusIdx == -1 && otherPlusIdx == -1) { return true; } else if (thisPlusIdx != -1 && otherPlusIdx != -1) { String thisSubtypeNoSuffix = getSubtype().substring(0, thisPlusIdx); String otherSubtypeNoSuffix = other.getSubtype().substring(0, otherPlusIdx); - String thisSubtypeSuffix = getSubtype().substring(thisPlusIdx + 1); String otherSubtypeSuffix = other.getSubtype().substring(otherPlusIdx + 1); - if (thisSubtypeSuffix.equals(otherSubtypeSuffix) && (WILDCARD_TYPE.equals(thisSubtypeNoSuffix) || WILDCARD_TYPE.equals(otherSubtypeNoSuffix))) { return true; @@ -429,7 +425,6 @@ public class MimeType implements Comparable<MimeType>, Serializable { if (!other.parameters.containsKey(key)) { return false; } - if (PARAM_CHARSET.equals(key)) { if (!ObjectUtils.nullSafeEquals(getCharset(), other.getCharset())) { return false; @@ -475,8 +470,8 @@ public class MimeType implements Comparable<MimeType>, Serializable { } /** - * Compares this {@code MediaType} to another alphabetically. - * @param other media type to compare to + * Compares this MIME Type to another alphabetically. + * @param other the MIME Type to compare to * @see MimeTypeUtils#sortBySpecificity(List) */ @Override @@ -493,12 +488,14 @@ public class MimeType implements Comparable<MimeType>, Serializable { if (comp != 0) { return comp; } + TreeSet<String> thisAttributes = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER); thisAttributes.addAll(getParameters().keySet()); TreeSet<String> otherAttributes = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER); otherAttributes.addAll(other.getParameters().keySet()); Iterator<String> thisAttributesIterator = thisAttributes.iterator(); Iterator<String> otherAttributesIterator = otherAttributes.iterator(); + while (thisAttributesIterator.hasNext()) { String thisAttribute = thisAttributesIterator.next(); String otherAttribute = otherAttributesIterator.next(); @@ -506,16 +503,35 @@ public class MimeType implements Comparable<MimeType>, Serializable { if (comp != 0) { return comp; } - String thisValue = getParameters().get(thisAttribute); - String otherValue = other.getParameters().get(otherAttribute); - if (otherValue == null) { - otherValue = ""; + if (PARAM_CHARSET.equals(thisAttribute)) { + Charset thisCharset = getCharset(); + Charset otherCharset = other.getCharset(); + if (thisCharset != otherCharset) { + if (thisCharset == null) { + return -1; + } + if (otherCharset == null) { + return 1; + } + comp = thisCharset.compareTo(otherCharset); + if (comp != 0) { + return comp; + } + } } - comp = thisValue.compareTo(otherValue); - if (comp != 0) { - return comp; + else { + String thisValue = getParameters().get(thisAttribute); + String otherValue = other.getParameters().get(otherAttribute); + if (otherValue == null) { + otherValue = ""; + } + comp = thisValue.compareTo(otherValue); + if (comp != 0) { + return comp; + } } } + return 0; } @@ -541,26 +557,26 @@ public class MimeType implements Comparable<MimeType>, Serializable { @Override public int compare(T mimeType1, T mimeType2) { - if (mimeType1.isWildcardType() && !mimeType2.isWildcardType()) { // */* < audio/* + if (mimeType1.isWildcardType() && !mimeType2.isWildcardType()) { // */* < audio/* return 1; } - else if (mimeType2.isWildcardType() && !mimeType1.isWildcardType()) { // audio/* > */* + else if (mimeType2.isWildcardType() && !mimeType1.isWildcardType()) { // audio/* > */* return -1; } - else if (!mimeType1.getType().equals(mimeType2.getType())) { // audio/basic == text/html + else if (!mimeType1.getType().equals(mimeType2.getType())) { // audio/basic == text/html return 0; } - else { // mediaType1.getType().equals(mediaType2.getType()) - if (mimeType1.isWildcardSubtype() && !mimeType2.isWildcardSubtype()) { // audio/* < audio/basic + else { // mediaType1.getType().equals(mediaType2.getType()) + if (mimeType1.isWildcardSubtype() && !mimeType2.isWildcardSubtype()) { // audio/* < audio/basic return 1; } - else if (mimeType2.isWildcardSubtype() && !mimeType1.isWildcardSubtype()) { // audio/basic > audio/* + else if (mimeType2.isWildcardSubtype() && !mimeType1.isWildcardSubtype()) { // audio/basic > audio/* return -1; } - else if (!mimeType1.getSubtype().equals(mimeType2.getSubtype())) { // audio/basic == audio/wave + else if (!mimeType1.getSubtype().equals(mimeType2.getSubtype())) { // audio/basic == audio/wave return 0; } - else { // mediaType2.getSubtype().equals(mediaType2.getSubtype()) + else { // mediaType2.getSubtype().equals(mediaType2.getSubtype()) return compareParameters(mimeType1, mimeType2); } } @@ -569,7 +585,7 @@ public class MimeType implements Comparable<MimeType>, Serializable { protected int compareParameters(T mimeType1, T mimeType2) { int paramsSize1 = mimeType1.getParameters().size(); int paramsSize2 = mimeType2.getParameters().size(); - return (paramsSize2 < paramsSize1 ? -1 : (paramsSize2 == paramsSize1 ? 0 : 1)); // audio/basic;level=1 < audio/basic + return (paramsSize2 < paramsSize1 ? -1 : (paramsSize2 == paramsSize1 ? 0 : 1)); // audio/basic;level=1 < audio/basic } } diff --git a/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java b/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java index 5b3a7a0f..decf3918 100644 --- a/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java +++ b/spring-core/src/main/java/org/springframework/util/MimeTypeUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.util; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -46,7 +47,7 @@ public abstract class MimeTypeUtils { 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'}; - private static final Random RND = new Random(); + private static final Random RND = new SecureRandom(); private static Charset US_ASCII = Charset.forName("US-ASCII"); @@ -287,8 +288,8 @@ public abstract class MimeTypeUtils { } int eqIndex = parameter.indexOf('='); if (eqIndex >= 0) { - String attribute = parameter.substring(0, eqIndex); - String value = parameter.substring(eqIndex + 1, parameter.length()); + String attribute = parameter.substring(0, eqIndex).trim(); + String value = parameter.substring(eqIndex + 1, parameter.length()).trim(); parameters.put(attribute, value); } } diff --git a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java index 04f3e020..a43d016b 100644 --- a/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java +++ b/spring-core/src/main/java/org/springframework/util/ReflectionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -645,8 +645,7 @@ public abstract class ReflectionUtils { } /** - * Invoke the given callback on all fields in the target class, going up the - * class hierarchy to get all declared fields. + * Invoke the given callback on all locally declared fields in the given class. * @param clazz the target class to analyze * @param fc the callback to invoke for each field * @since 4.2 diff --git a/spring-core/src/main/java/org/springframework/util/StreamUtils.java b/spring-core/src/main/java/org/springframework/util/StreamUtils.java index d3374bc3..8feb0f4e 100644 --- a/spring-core/src/main/java/org/springframework/util/StreamUtils.java +++ b/spring-core/src/main/java/org/springframework/util/StreamUtils.java @@ -165,7 +165,7 @@ public abstract class StreamUtils { } long bytesToCopy = end - start + 1; - byte buffer[] = new byte[StreamUtils.BUFFER_SIZE]; + byte[] buffer = new byte[StreamUtils.BUFFER_SIZE]; while (bytesToCopy > 0) { int bytesRead = in.read(buffer); if (bytesRead == -1) { diff --git a/spring-core/src/main/java/org/springframework/util/StringUtils.java b/spring-core/src/main/java/org/springframework/util/StringUtils.java index 5e2fbfec..8dcef81e 100644 --- a/spring-core/src/main/java/org/springframework/util/StringUtils.java +++ b/spring-core/src/main/java/org/springframework/util/StringUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -634,7 +634,7 @@ public abstract class StringUtils { // first path element. This is necessary to correctly parse paths like // "file:core/../core/io/Resource.class", where the ".." should just // strip the first "core" directory while keeping the "file:" prefix. - int prefixIndex = pathToUse.indexOf(":"); + int prefixIndex = pathToUse.indexOf(':'); String prefix = ""; if (prefixIndex != -1) { prefix = pathToUse.substring(0, prefixIndex + 1); @@ -694,11 +694,11 @@ public abstract class StringUtils { } /** - * Parse the given {@code localeString} value into a {@link Locale}. + * Parse the given {@code String} representation into a {@link Locale}. * <p>This is the inverse operation of {@link Locale#toString Locale's toString}. - * @param localeString the locale {@code String}, following {@code Locale's} - * {@code toString()} format ("en", "en_UK", etc); - * also accepts spaces as separators, as an alternative to underscores + * @param localeString the locale {@code String}: following {@code Locale's} + * {@code toString()} format ("en", "en_UK", etc), also accepting spaces as + * separators (as an alternative to underscores) * @return a corresponding {@code Locale} instance, or {@code null} if none * @throws IllegalArgumentException in case of an invalid locale specification */ @@ -815,7 +815,10 @@ public abstract class StringUtils { * @param array1 the first array (can be {@code null}) * @param array2 the second array (can be {@code null}) * @return the new array ({@code null} if both given arrays were {@code null}) + * @deprecated as of 4.3.15, in favor of manual merging via {@link LinkedHashSet} + * (with every entry included at most once, even entries within the first array) */ + @Deprecated public static String[] mergeStringArrays(String[] array1, String[] array2) { if (ObjectUtils.isEmpty(array1)) { return array2; @@ -858,7 +861,6 @@ public abstract class StringUtils { if (collection == null) { return null; } - return collection.toArray(new String[collection.size()]); } @@ -872,9 +874,7 @@ public abstract class StringUtils { if (enumeration == null) { return null; } - - List<String> list = Collections.list(enumeration); - return list.toArray(new String[list.size()]); + return toStringArray(Collections.list(enumeration)); } /** @@ -939,10 +939,9 @@ public abstract class StringUtils { /** * Take an array of strings and split each element based on the given delimiter. - * A {@code Properties} instance is then generated, with the left of the - * delimiter providing the key, and the right of the delimiter providing the value. - * <p>Will trim both the key and value before adding them to the - * {@code Properties} instance. + * A {@code Properties} instance is then generated, with the left of the delimiter + * providing the key, and the right of the delimiter providing the value. + * <p>Will trim both the key and value before adding them to the {@code Properties}. * @param array the array to process * @param delimiter to split each element using (typically the equals symbol) * @return a {@code Properties} instance representing the array contents, diff --git a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallbackRegistry.java b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallbackRegistry.java index 46eb746b..eca80014 100644 --- a/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallbackRegistry.java +++ b/spring-core/src/main/java/org/springframework/util/concurrent/ListenableFutureCallbackRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -134,8 +134,9 @@ public class ListenableFutureCallbackRegistry<T> { synchronized (this.mutex) { this.state = State.SUCCESS; this.result = result; - while (!this.successCallbacks.isEmpty()) { - notifySuccess(this.successCallbacks.poll()); + SuccessCallback<? super T> callback; + while ((callback = this.successCallbacks.poll()) != null) { + notifySuccess(callback); } } } @@ -149,8 +150,9 @@ public class ListenableFutureCallbackRegistry<T> { synchronized (this.mutex) { this.state = State.FAILURE; this.result = ex; - while (!this.failureCallbacks.isEmpty()) { - notifyFailure(this.failureCallbacks.poll()); + FailureCallback callback; + while ((callback = this.failureCallbacks.poll()) != null) { + notifyFailure(callback); } } } diff --git a/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxHandler.java b/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxHandler.java index e4294843..dbc0b700 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxHandler.java +++ b/spring-core/src/main/java/org/springframework/util/xml/AbstractStaxHandler.java @@ -100,7 +100,7 @@ abstract class AbstractStaxHandler implements ContentHandler, LexicalHandler { } @Override - public final void characters(char ch[], int start, int length) throws SAXException { + public final void characters(char[] ch, int start, int length) throws SAXException { try { String data = new String(ch, start, length); if (!this.inCData) { diff --git a/spring-core/src/main/java/org/springframework/util/xml/DomContentHandler.java b/spring-core/src/main/java/org/springframework/util/xml/DomContentHandler.java index 5a8d92aa..e7987ce0 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/DomContentHandler.java +++ b/spring-core/src/main/java/org/springframework/util/xml/DomContentHandler.java @@ -94,7 +94,7 @@ class DomContentHandler implements ContentHandler { } @Override - public void characters(char ch[], int start, int length) throws SAXException { + public void characters(char[] ch, int start, int length) throws SAXException { String data = new String(ch, start, length); Node parent = getParent(); Node lastChild = parent.getLastChild(); @@ -139,7 +139,7 @@ class DomContentHandler implements ContentHandler { } @Override - public void ignorableWhitespace(char ch[], int start, int length) throws SAXException { + public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { } @Override diff --git a/spring-core/src/main/java/org/springframework/util/xml/TransformerUtils.java b/spring-core/src/main/java/org/springframework/util/xml/TransformerUtils.java index 4b48abd4..42d88149 100644 --- a/spring-core/src/main/java/org/springframework/util/xml/TransformerUtils.java +++ b/spring-core/src/main/java/org/springframework/util/xml/TransformerUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,8 +22,8 @@ import javax.xml.transform.Transformer; import org.springframework.util.Assert; /** - * Contains common behavior relating to {@link javax.xml.transform.Transformer Transformers}, and the - * {@code javax.xml.transform} package in general. + * Contains common behavior relating to {@link javax.xml.transform.Transformer Transformers} + * and the {@code javax.xml.transform} package in general. * * @author Rick Evans * @author Juergen Hoeller @@ -32,16 +32,16 @@ import org.springframework.util.Assert; public abstract class TransformerUtils { /** - * The indent amount of characters if {@link #enableIndenting(javax.xml.transform.Transformer) indenting is enabled}. + * The indent amount of characters if {@link #enableIndenting indenting is enabled}. * <p>Defaults to "2". */ public static final int DEFAULT_INDENT_AMOUNT = 2; + /** - * Enable indenting for the supplied {@link javax.xml.transform.Transformer}. <p>If the underlying XSLT engine is - * Xalan, then the special output key {@code indent-amount} will be also be set to a value of {@link - * #DEFAULT_INDENT_AMOUNT} characters. - * + * Enable indenting for the supplied {@link javax.xml.transform.Transformer}. + * <p>If the underlying XSLT engine is Xalan, then the special output key {@code indent-amount} + * will be also be set to a value of {@link #DEFAULT_INDENT_AMOUNT} characters. * @param transformer the target transformer * @see javax.xml.transform.Transformer#setOutputProperty(String, String) * @see javax.xml.transform.OutputKeys#INDENT @@ -51,18 +51,19 @@ public abstract class TransformerUtils { } /** - * Enable indenting for the supplied {@link javax.xml.transform.Transformer}. <p>If the underlying XSLT engine is - * Xalan, then the special output key {@code indent-amount} will be also be set to a value of {@link - * #DEFAULT_INDENT_AMOUNT} characters. - * - * @param transformer the target transformer - * @param indentAmount the size of the indent (2 characters, 3 characters, etc.) + * Enable indenting for the supplied {@link javax.xml.transform.Transformer}. + * <p>If the underlying XSLT engine is Xalan, then the special output key {@code indent-amount} + * will be also be set to a value of {@link #DEFAULT_INDENT_AMOUNT} characters. + * @param transformer the target transformer + * @param indentAmount the size of the indent (2 characters, 3 characters, etc) * @see javax.xml.transform.Transformer#setOutputProperty(String, String) * @see javax.xml.transform.OutputKeys#INDENT */ public static void enableIndenting(Transformer transformer, int indentAmount) { Assert.notNull(transformer, "Transformer must not be null"); - Assert.isTrue(indentAmount > -1, "The indent amount cannot be less than zero : got " + indentAmount); + if (indentAmount < 0) { + throw new IllegalArgumentException("Invalid indent amount (must not be less than zero): " + indentAmount); + } transformer.setOutputProperty(OutputKeys.INDENT, "yes"); try { // Xalan-specific, but this is the most common XSLT engine in any case @@ -74,7 +75,6 @@ public abstract class TransformerUtils { /** * Disable indenting for the supplied {@link javax.xml.transform.Transformer}. - * * @param transformer the target transformer * @see javax.xml.transform.OutputKeys#INDENT */ |