Nullability refinements (based on IntelliJ IDEA 2018.1 introspection)

Issue: SPR-15756
This commit is contained in:
Juergen Hoeller 2018-03-29 23:50:17 +02:00
parent 1cc513d7db
commit d553ddc5b3
17 changed files with 84 additions and 107 deletions

View File

@ -367,23 +367,23 @@ public abstract class BeanUtils {
public static Method resolveSignature(String signature, Class<?> clazz) {
Assert.hasText(signature, "'signature' must not be empty");
Assert.notNull(clazz, "Class must not be null");
int firstParen = signature.indexOf('(');
int lastParen = signature.indexOf(')');
if (firstParen > -1 && lastParen == -1) {
int startParen = signature.indexOf('(');
int endParen = signature.indexOf(')');
if (startParen > -1 && endParen == -1) {
throw new IllegalArgumentException("Invalid method signature '" + signature +
"': expected closing ')' for args list");
}
else if (lastParen > -1 && firstParen == -1) {
else if (startParen == -1 && endParen > -1) {
throw new IllegalArgumentException("Invalid method signature '" + signature +
"': expected opening '(' for args list");
}
else if (firstParen == -1 && lastParen == -1) {
else if (startParen == -1) {
return findMethodWithMinimalParameters(clazz, signature);
}
else {
String methodName = signature.substring(0, firstParen);
String methodName = signature.substring(0, startParen);
String[] parameterTypeNames =
StringUtils.commaDelimitedListToStringArray(signature.substring(firstParen + 1, lastParen));
StringUtils.commaDelimitedListToStringArray(signature.substring(startParen + 1, endParen));
Class<?>[] parameterTypes = new Class<?>[parameterTypeNames.length];
for (int i = 0; i < parameterTypeNames.length; i++) {
String parameterTypeName = parameterTypeNames[i].trim();

View File

@ -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.
@ -144,15 +144,16 @@ public class BeanConfigurerSupport implements BeanFactoryAware, InitializingBean
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(beanFactory != null, "No BeanFactory available");
try {
if (bwi.indicatesAutowiring() || (bwi.isDefaultBeanName() && bwi.getBeanName() != null &&
!beanFactory.containsBean(bwi.getBeanName()))) {
String beanName = bwi.getBeanName();
if (bwi.indicatesAutowiring() || (bwi.isDefaultBeanName() && beanName != null &&
!beanFactory.containsBean(beanName))) {
// Perform autowiring (also applying standard factory / post-processor callbacks).
beanFactory.autowireBeanProperties(beanInstance, bwi.getAutowireMode(), bwi.getDependencyCheck());
beanFactory.initializeBean(beanInstance, bwi.getBeanName());
beanFactory.initializeBean(beanInstance, (beanName != null ? beanName : ""));
}
else {
// Perform explicit wiring based on the specified bean definition.
beanFactory.configureBean(beanInstance, bwi.getBeanName());
beanFactory.configureBean(beanInstance, (beanName != null ? beanName : ""));
}
}
catch (BeanCreationException ex) {

View File

@ -588,7 +588,7 @@ class ConfigurationClassParser {
}
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularImports) throws IOException {
Collection<SourceClass> importCandidates, boolean checkForCircularImports) {
if (importCandidates.isEmpty()) {
return;
@ -1033,9 +1033,9 @@ class ConfigurationClassParser {
public CircularImportProblem(ConfigurationClass attemptedImport, Deque<ConfigurationClass> importStack) {
super(String.format("A circular @Import has been detected: " +
"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is " +
"already present in the current import stack %s", importStack.peek().getSimpleName(),
"already present in the current import stack %s", importStack.element().getSimpleName(),
attemptedImport.getSimpleName(), attemptedImport.getSimpleName(), importStack),
new Location(importStack.peek().getResource(), attemptedImport.getMetadata()));
new Location(importStack.element().getResource(), attemptedImport.getMetadata()));
}
}

View File

@ -366,7 +366,7 @@ public class ApplicationListenerMethodAdapter implements GenericApplicationListe
payloadType != null && declaredEventType.isAssignableFrom(payloadType)) {
return declaredEventType;
}
if (declaredEventType.getRawClass().isInstance(event)) {
if (eventClass != null && eventClass.isInstance(event)) {
return declaredEventType;
}
}

View File

@ -209,14 +209,6 @@ public abstract class ClassUtils {
}
}
/**
* Determine if the supplied class is an <em>inner class</em>.
* @return {@code true} if the supplied class is an inner class
*/
public static boolean isInnerClass(Class<?> clazz) {
return clazz != null && clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers());
}
/**
* Replacement for {@code Class.forName()} that also returns Class instances
* for primitives (e.g. "int") and array class names (e.g. "String[]").
@ -383,6 +375,17 @@ public abstract class ClassUtils {
return clazz;
}
/**
* Determine if the supplied class is an <em>inner class</em>,
* i.e. a non-static member of an enclosing class.
* @return {@code true} if the supplied class is an inner class
* @since 5.0.5
* @see Class#isMemberClass()
*/
public static boolean isInnerClass(Class<?> clazz) {
return (clazz.isMemberClass() && !Modifier.isStatic(clazz.getModifiers()));
}
/**
* Check whether the given class is cache-safe in the given context,
* i.e. whether it is loaded by the given ClassLoader or a parent of it.

View File

@ -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.
@ -136,8 +136,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);
}
}
}
@ -151,8 +152,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);
}
}
}

View File

@ -58,7 +58,7 @@ public class CodeFlow implements Opcodes {
* sub-expressions like the expressions for the argument values in a method invocation
* expression.
*/
private final Deque<ArrayList<String>> compilationScopes;
private final Deque<List<String>> compilationScopes;
/**
* As SpEL ast nodes are called to generate code for the main evaluation method
@ -128,7 +128,7 @@ public class CodeFlow implements Opcodes {
*/
public void pushDescriptor(@Nullable String descriptor) {
if (descriptor != null) {
this.compilationScopes.peek().add(descriptor);
this.compilationScopes.element().add(descriptor);
}
}

View File

@ -106,7 +106,7 @@ public class ExpressionState {
if (CollectionUtils.isEmpty(this.contextObjects)) {
return this.rootObject;
}
return this.contextObjects.peek();
return this.contextObjects.element();
}
public void pushActiveContextObject(TypedValue obj) {
@ -136,7 +136,7 @@ public class ExpressionState {
if (CollectionUtils.isEmpty(this.scopeRootObjects)) {
return this.rootObject;
}
return this.scopeRootObjects.peek();
return this.scopeRootObjects.element();
}
public void setVariable(String name, @Nullable Object value) {
@ -157,8 +157,8 @@ public class ExpressionState {
}
public Object convertValue(Object value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
Object result = this.relatedContext.getTypeConverter().convertValue(value,
TypeDescriptor.forObject(value), targetTypeDescriptor);
Object result = this.relatedContext.getTypeConverter().convertValue(
value, TypeDescriptor.forObject(value), targetTypeDescriptor);
if (result == null) {
throw new IllegalStateException("Null conversion result for value [" + value + "]");
}
@ -172,7 +172,8 @@ public class ExpressionState {
@Nullable
public Object convertValue(TypedValue value, TypeDescriptor targetTypeDescriptor) throws EvaluationException {
Object val = value.getValue();
return this.relatedContext.getTypeConverter().convertValue(val, TypeDescriptor.forObject(val), targetTypeDescriptor);
return this.relatedContext.getTypeConverter().convertValue(
val, TypeDescriptor.forObject(val), targetTypeDescriptor);
}
/*
@ -199,7 +200,7 @@ public class ExpressionState {
}
public void setLocalVariable(String name, Object value) {
initVariableScopes().peek().setVariable(name, value);
initVariableScopes().element().setVariable(name, value);
}
@Nullable
@ -212,16 +213,16 @@ public class ExpressionState {
return null;
}
private LinkedList<VariableScope> initVariableScopes() {
private Deque<VariableScope> initVariableScopes() {
if (this.variableScopes == null) {
this.variableScopes = new LinkedList<>();
// top level empty variable scope
// top-level empty variable scope
this.variableScopes.add(new VariableScope());
}
return this.variableScopes;
}
private ArrayDeque<TypedValue> initScopeRootObjects() {
private Deque<TypedValue> initScopeRootObjects() {
if (this.scopeRootObjects == null) {
this.scopeRootObjects = new ArrayDeque<>();
}

View File

@ -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.
@ -58,7 +58,7 @@ public interface ValueRef {
/**
* A ValueRef for the null value.
*/
static class NullValueRef implements ValueRef {
class NullValueRef implements ValueRef {
static final NullValueRef INSTANCE = new NullValueRef();
@ -85,13 +85,13 @@ public interface ValueRef {
/**
* A ValueRef holder for a single value, which cannot be set.
*/
static class TypedValueHolderValueRef implements ValueRef {
class TypedValueHolderValueRef implements ValueRef {
private final TypedValue typedValue;
private final SpelNodeImpl node; // used only for error reporting
public TypedValueHolderValueRef(TypedValue typedValue,SpelNodeImpl node) {
public TypedValueHolderValueRef(TypedValue typedValue, SpelNodeImpl node) {
this.typedValue = typedValue;
this.node = node;
}

View File

@ -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.
@ -268,16 +268,13 @@ abstract class AbstractListenerContainerParser implements BeanDefinitionParser {
boolean replyPubSubDomain = false;
String replyDestinationType = containerEle.getAttribute(RESPONSE_DESTINATION_TYPE_ATTRIBUTE);
if (DESTINATION_TYPE_TOPIC.equals(replyDestinationType)) {
if (!StringUtils.hasText(replyDestinationType)) {
replyPubSubDomain = pubSubDomain; // the default: same value as pubSubDomain
}
else if (DESTINATION_TYPE_TOPIC.equals(replyDestinationType)) {
replyPubSubDomain = true;
}
else if (DESTINATION_TYPE_QUEUE.equals(replyDestinationType)) {
replyPubSubDomain = false;
}
else if (!StringUtils.hasText(replyDestinationType)) {
replyPubSubDomain = pubSubDomain; // the default: same value as pubSubDomain
}
else if (StringUtils.hasText(replyDestinationType)) {
else if (!DESTINATION_TYPE_QUEUE.equals(replyDestinationType)) {
parserContext.getReaderContext().error("Invalid listener container 'response-destination-type': only " +
"\"queue\", \"topic\" supported.", containerEle);
}

View File

@ -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.
@ -209,6 +209,7 @@ public class SimpleNamingContext implements Context {
}
@Override
@Nullable
public Object addToEnvironment(String propName, Object propVal) {
return this.environment.put(propName, propVal);
}

View File

@ -21,7 +21,6 @@ import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.lang.reflect.Parameter;
import java.util.Optional;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
@ -41,23 +40,21 @@ import org.springframework.util.ClassUtils;
*
* @author Sam Brannen
* @since 5.0
* @see #isAutowirable(Parameter)
* @see #resolveDependency(Parameter, Class, ApplicationContext)
* @see #isAutowirable
* @see #resolveDependency
*/
abstract class ParameterAutowireUtils {
private static final AnnotatedElement EMPTY_ANNOTATED_ELEMENT = new AnnotatedElement() {
@Override
@Nullable
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
return null;
}
@Override
public Annotation[] getAnnotations() {
return new Annotation[0];
}
@Override
public Annotation[] getDeclaredAnnotations() {
return new Annotation[0];
@ -65,11 +62,6 @@ abstract class ParameterAutowireUtils {
};
private ParameterAutowireUtils() {
/* no-op */
}
/**
* Determine if the supplied {@link Parameter} can potentially be
* autowired from an {@link ApplicationContext}.
@ -79,16 +71,16 @@ abstract class ParameterAutowireUtils {
* {@link Qualifier @Qualifier}, or {@link Value @Value}.
* @param parameter the parameter whose dependency should be autowired
* @param parameterIndex the index of the parameter
* @see #resolveDependency(Parameter, Class, ApplicationContext)
* @see #resolveDependency
*/
static boolean isAutowirable(Parameter parameter, int parameterIndex) {
if (ApplicationContext.class.isAssignableFrom(parameter.getType())) {
return true;
}
AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex);
return AnnotatedElementUtils.hasAnnotation(annotatedParameter, Autowired.class)
|| AnnotatedElementUtils.hasAnnotation(annotatedParameter, Qualifier.class)
|| AnnotatedElementUtils.hasAnnotation(annotatedParameter, Value.class);
return (AnnotatedElementUtils.hasAnnotation(annotatedParameter, Autowired.class) ||
AnnotatedElementUtils.hasAnnotation(annotatedParameter, Qualifier.class) ||
AnnotatedElementUtils.hasAnnotation(annotatedParameter, Value.class));
}
/**
@ -113,62 +105,54 @@ abstract class ParameterAutowireUtils {
* dependency
* @return the resolved object, or {@code null} if none found
* @throws BeansException if dependency resolution failed
* @see #isAutowirable(Parameter)
* @see #isAutowirable
* @see Autowired#required
* @see SynthesizingMethodParameter#forParameter(Parameter)
* @see AutowireCapableBeanFactory#resolveDependency(DependencyDescriptor, String)
*/
@Nullable
static Object resolveDependency(Parameter parameter, int parameterIndex, Class<?> containingClass, ApplicationContext applicationContext) {
static Object resolveDependency(
Parameter parameter, int parameterIndex, Class<?> containingClass, ApplicationContext applicationContext) {
AnnotatedElement annotatedParameter = getEffectiveAnnotatedParameter(parameter, parameterIndex);
boolean required = findMergedAnnotation(annotatedParameter, Autowired.class).map(Autowired::required).orElse(true);
Autowired autowired = AnnotatedElementUtils.findMergedAnnotation(annotatedParameter, Autowired.class);
boolean required = (autowired == null || autowired.required());
MethodParameter methodParameter = SynthesizingMethodParameter.forParameter(parameter);
DependencyDescriptor descriptor = new DependencyDescriptor(methodParameter, required);
descriptor.setContainingClass(containingClass);
return applicationContext.getAutowireCapableBeanFactory().resolveDependency(descriptor, null);
}
private static <A extends Annotation> Optional<A> findMergedAnnotation(AnnotatedElement element, Class<A> annotationType) {
return Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(element, annotationType));
}
/**
* Due to a bug in {@code javac} on JDK versions prior to JDK 9, looking up
* annotations directly on a {@link Parameter} will fail for inner class
* constructors.
*
* <h4>Bug in javac in JDK &lt; 9</h4>
* <p>The parameter annotations array in the compiled byte code excludes an entry
* for the implicit <em>enclosing instance</em> parameter for an inner class
* constructor.
*
* <h4>Workaround</h4>
* <p>This method provides a workaround for this off-by-one error by allowing the
* caller to access annotations on the preceding {@link Parameter} object (i.e.,
* {@code index - 1}). If the supplied {@code index} is zero, this method returns
* an empty {@code AnnotatedElement}.
*
* <h4>WARNING</h4>
* <p>The {@code AnnotatedElement} returned by this method should never be cast and
* treated as a {@code Parameter} since the metadata (e.g., {@link Parameter#getName()},
* {@link Parameter#getType()}, etc.) will not match those for the declared parameter
* at the given index in an inner class constructor.
*
* @return the supplied {@code parameter} or the <em>effective</em> {@code Parameter}
* if the aforementioned bug is in effect
*/
private static AnnotatedElement getEffectiveAnnotatedParameter(Parameter parameter, int index) {
Executable executable = parameter.getDeclaringExecutable();
if (executable instanceof Constructor &&
ClassUtils.isInnerClass(executable.getDeclaringClass()) &&
if (executable instanceof Constructor && ClassUtils.isInnerClass(executable.getDeclaringClass()) &&
executable.getParameterAnnotations().length == executable.getParameterCount() - 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
return (index == 0) ? EMPTY_ANNOTATED_ELEMENT : executable.getParameters()[index - 1];
return (index == 0 ? EMPTY_ANNOTATED_ELEMENT : executable.getParameters()[index - 1]);
}
return parameter;
}

View File

@ -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.
@ -222,12 +222,14 @@ public class Jaxb2XmlDecoder extends AbstractDecoder<Object> {
this.elementDepth++;
}
if (this.elementDepth > this.barrier) {
Assert.state(this.events != null, "No XMLEvent List");
this.events.add(event);
}
if (event.isEndElement()) {
this.elementDepth--;
if (this.elementDepth == this.barrier) {
this.barrier = Integer.MAX_VALUE;
Assert.state(this.events != null, "No XMLEvent List");
return Mono.just(this.events);
}
}

View File

@ -180,9 +180,9 @@ class DefaultPathContainer implements PathContainer {
return EMPTY_PATH;
}
Assert.isTrue(fromIndex < toIndex, () -> "fromIndex: " + fromIndex + " should be < toIndex " + toIndex);
Assert.isTrue(fromIndex >= 0 && fromIndex < elements.size(), () -> "Invalid fromIndex: " + fromIndex);
Assert.isTrue(toIndex >= 0 && toIndex <= elements.size(), () -> "Invalid toIndex: " + toIndex);
Assert.isTrue(fromIndex < toIndex, () -> "fromIndex: " + fromIndex + " should be < toIndex " + toIndex);
List<Element> subList = elements.subList(fromIndex, toIndex);
String path = subList.stream().map(Element::value).collect(Collectors.joining(""));

View File

@ -109,9 +109,7 @@ class UndertowServerHttpResponse extends AbstractListenerServerHttpResponse impl
public Mono<Void> writeWith(File file, long position, long count) {
return doCommit(() ->
Mono.defer(() -> {
FileChannel source = null;
try {
source = FileChannel.open(file.toPath(), StandardOpenOption.READ);
try (FileChannel source = FileChannel.open(file.toPath(), StandardOpenOption.READ)) {
StreamSinkChannel destination = this.exchange.getResponseChannel();
Channels.transferBlocking(destination, source, position, count);
return Mono.empty();
@ -119,17 +117,6 @@ class UndertowServerHttpResponse extends AbstractListenerServerHttpResponse impl
catch (IOException ex) {
return Mono.error(ex);
}
finally {
if (source != null) {
try {
source.close();
}
catch (IOException ex) {
// ignore
}
}
}
}));
}

View File

@ -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.
@ -83,7 +83,7 @@ public abstract class AbstractView implements View, ApplicationContextAware {
* Set the supported media types for this view.
* Default is "text/html;charset=UTF-8".
*/
public void setSupportedMediaTypes(@Nullable List<MediaType> supportedMediaTypes) {
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
Assert.notEmpty(supportedMediaTypes, "MediaType List must not be empty");
this.mediaTypes.clear();
this.mediaTypes.addAll(supportedMediaTypes);

View File

@ -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.
@ -23,7 +23,6 @@ import java.util.List;
import org.springframework.core.Ordered;
import org.springframework.http.MediaType;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
@ -54,7 +53,7 @@ public abstract class ViewResolverSupport implements Ordered {
* Set the supported media types for this view.
* Default is "text/html;charset=UTF-8".
*/
public void setSupportedMediaTypes(@Nullable List<MediaType> supportedMediaTypes) {
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
Assert.notEmpty(supportedMediaTypes, "MediaType List must not be empty");
this.mediaTypes.clear();
this.mediaTypes.addAll(supportedMediaTypes);