Avoid defensive checks against Java 8 API (java.util.Optional etc)

This commit also fixes broken javadoc links and code references.

Issue: SPR-13188
This commit is contained in:
Juergen Hoeller
2016-07-05 02:08:59 +02:00
parent adb935db79
commit 51252ebbca
98 changed files with 290 additions and 1232 deletions

View File

@@ -20,9 +20,7 @@ import java.util.Collections;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.springframework.lang.UsesJava7;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Base class for decorating ClassLoaders such as {@link OverridingClassLoader}
@@ -33,20 +31,10 @@ import org.springframework.util.ClassUtils;
* @author Rod Johnson
* @since 2.5.2
*/
@UsesJava7
public abstract class DecoratingClassLoader extends ClassLoader {
/**
* Java 7+ {@code ClassLoader.registerAsParallelCapable()} available?
* @since 4.1.2
*/
protected static final boolean parallelCapableClassLoaderAvailable =
ClassUtils.hasMethod(ClassLoader.class, "registerAsParallelCapable");
static {
if (parallelCapableClassLoaderAvailable) {
ClassLoader.registerAsParallelCapable();
}
ClassLoader.registerAsParallelCapable();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@@ -16,8 +16,6 @@
package org.springframework.core;
import org.springframework.util.ClassUtils;
/**
* Default implementation of the {@link ParameterNameDiscoverer} strategy interface,
* using the Java 8 standard reflection mechanism (if available), and falling back
@@ -33,14 +31,8 @@ import org.springframework.util.ClassUtils;
*/
public class DefaultParameterNameDiscoverer extends PrioritizedParameterNameDiscoverer {
private static final boolean standardReflectionAvailable = ClassUtils.isPresent(
"java.lang.reflect.Executable", DefaultParameterNameDiscoverer.class.getClassLoader());
public DefaultParameterNameDiscoverer() {
if (standardReflectionAvailable) {
addDiscoverer(new StandardReflectionParameterNameDiscoverer());
}
addDiscoverer(new StandardReflectionParameterNameDiscoverer());
addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
}

View File

@@ -85,10 +85,9 @@ public abstract class MethodIntrospector {
/**
* Select methods on the given target type based on a filter.
* <p>Callers define methods of interest through the
* {@link ReflectionUtils.MethodFilter} parameter.
* <p>Callers define methods of interest through the {@code MethodFilter} parameter.
* @param targetType the target type to search methods on
* @param methodFilter a {@link ReflectionUtils.MethodFilter} to help
* @param methodFilter a {@code MethodFilter} to help
* recognize handler methods of interest
* @return the selected methods, or an empty set in case of no match
*/

View File

@@ -25,9 +25,9 @@ import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Helper class that encapsulates the specification of a method parameter, i.e. a {@link Method}
@@ -48,21 +48,6 @@ import org.springframework.util.ClassUtils;
*/
public class MethodParameter {
private static final Class<?> javaUtilOptionalClass;
static {
Class<?> clazz;
try {
clazz = ClassUtils.forName("java.util.Optional", MethodParameter.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Java 8 not available - Optional references simply not supported then.
clazz = null;
}
javaUtilOptionalClass = clazz;
}
private final Method method;
private final Constructor<?> constructor;
@@ -320,7 +305,7 @@ public class MethodParameter {
* @since 4.3
*/
public boolean isOptional() {
return (getParameterType() == javaUtilOptionalClass);
return (getParameterType() == Optional.class);
}
/**

View File

@@ -19,7 +19,6 @@ package org.springframework.core;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.lang.UsesJava7;
import org.springframework.util.FileCopyUtils;
/**
@@ -34,7 +33,6 @@ import org.springframework.util.FileCopyUtils;
* @author Juergen Hoeller
* @since 2.0.1
*/
@UsesJava7
public class OverridingClassLoader extends DecoratingClassLoader {
/** Packages that are excluded by default */
@@ -44,9 +42,7 @@ public class OverridingClassLoader extends DecoratingClassLoader {
private static final String CLASS_FILE_SUFFIX = ".class";
static {
if (parallelCapableClassLoaderAvailable) {
ClassLoader.registerAsParallelCapable();
}
ClassLoader.registerAsParallelCapable();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 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.
@@ -20,8 +20,6 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import org.springframework.lang.UsesJava8;
/**
* {@link ParameterNameDiscoverer} implementation which uses JDK 8's reflection facilities
* for introspecting parameter names (based on the "-parameters" compiler flag).
@@ -30,7 +28,6 @@ import org.springframework.lang.UsesJava8;
* @since 4.0
* @see java.lang.reflect.Parameter#getName()
*/
@UsesJava8
public class StandardReflectionParameterNameDiscoverer implements ParameterNameDiscoverer {
@Override

View File

@@ -28,7 +28,6 @@ import java.util.stream.Stream;
import org.springframework.core.MethodParameter;
import org.springframework.core.ResolvableType;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.lang.UsesJava8;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
@@ -49,9 +48,6 @@ public class TypeDescriptor implements Serializable {
static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
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 Class<?>[] CACHED_COMMON_TYPES = {
@@ -340,10 +336,10 @@ public class TypeDescriptor implements Serializable {
if (this.resolvableType.isArray()) {
return new TypeDescriptor(this.resolvableType.getComponentType(), null, this.annotations);
}
if (streamAvailable && StreamDelegate.isStream(this.type)) {
return StreamDelegate.getStreamElementType(this);
if (Stream.class.isAssignableFrom(this.type)) {
return getRelatedIfResolvable(this, this.resolvableType.as(Stream.class).getGeneric(0));
}
return getRelatedIfResolvable(this, this.resolvableType.asCollection().getGeneric());
return getRelatedIfResolvable(this, this.resolvableType.asCollection().getGeneric(0));
}
/**
@@ -694,20 +690,4 @@ public class TypeDescriptor implements Serializable {
return new TypeDescriptor(type, null, source.annotations);
}
/**
* Inner class to avoid a hard dependency on Java 8.
*/
@UsesJava8
private static class StreamDelegate {
public static boolean isStream(Class<?> type) {
return Stream.class.isAssignableFrom(type);
}
public static TypeDescriptor getStreamElementType(TypeDescriptor source) {
return getRelatedIfResolvable(source, source.resolvableType.as(Stream.class).getGeneric());
}
}
}

View File

@@ -40,19 +40,10 @@ import org.springframework.util.ClassUtils;
*/
public class DefaultConversionService extends GenericConversionService {
/** Java 8's java.util.Optional class available? */
private static final boolean javaUtilOptionalClassAvailable =
ClassUtils.isPresent("java.util.Optional", DefaultConversionService.class.getClassLoader());
/** Java 8's java.time package available? */
private static final boolean jsr310Available =
ClassUtils.isPresent("java.time.ZoneId", DefaultConversionService.class.getClassLoader());
/** Java 8's java.util.stream.Stream class available? */
private static final boolean streamAvailable = ClassUtils.isPresent(
"java.util.stream.Stream", DefaultConversionService.class.getClassLoader());
/**
* Create a new {@code DefaultConversionService} with the set of
@@ -83,9 +74,7 @@ public class DefaultConversionService extends GenericConversionService {
converterRegistry.addConverter(new ObjectToObjectConverter());
converterRegistry.addConverter(new IdToEntityConverter((ConversionService) converterRegistry));
converterRegistry.addConverter(new FallbackObjectToStringConverter());
if (javaUtilOptionalClassAvailable) {
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
converterRegistry.addConverter(new ObjectToOptionalConverter((ConversionService) converterRegistry));
}
/**
@@ -117,9 +106,7 @@ public class DefaultConversionService extends GenericConversionService {
converterRegistry.addConverter(new CollectionToObjectConverter(conversionService));
converterRegistry.addConverter(new ObjectToCollectionConverter(conversionService));
if (streamAvailable) {
converterRegistry.addConverter(new StreamConverter(conversionService));
}
converterRegistry.addConverter(new StreamConverter(conversionService));
}

View File

@@ -25,6 +25,7 @@ import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.springframework.core.ResolvableType;
@@ -72,20 +73,6 @@ public class GenericConversionService implements ConfigurableConversionService {
private static final GenericConverter NO_MATCH = new NoOpConverter("NO_MATCH");
/** Java 8's java.util.Optional.empty() */
private static Object javaUtilOptionalEmpty = null;
static {
try {
Class<?> clazz = ClassUtils.forName("java.util.Optional", GenericConversionService.class.getClassLoader());
javaUtilOptionalEmpty = ClassUtils.getMethod(clazz, "empty").invoke(null);
}
catch (Exception ex) {
// Java 8 not available - conversion to Optional not supported then.
}
}
private final Converters converters = new Converters();
private final Map<ConverterCacheKey, GenericConverter> converterCache =
@@ -231,8 +218,8 @@ public class GenericConversionService implements ConfigurableConversionService {
* @return the converted null object
*/
protected Object convertNullSource(TypeDescriptor sourceType, TypeDescriptor targetType) {
if (javaUtilOptionalEmpty != null && targetType.getObjectType() == javaUtilOptionalEmpty.getClass()) {
return javaUtilOptionalEmpty;
if (targetType.getObjectType() == Optional.class) {
return Optional.empty();
}
return null;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.Set;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.lang.UsesJava8;
/**
* Convert an Object to {@code java.util.Optional<T>} if necessary using the
@@ -34,7 +33,6 @@ import org.springframework.lang.UsesJava8;
* @author Juergen Hoeller
* @since 4.1
*/
@UsesJava8
final class ObjectToOptionalConverter implements ConditionalGenericConverter {
private final ConversionService conversionService;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@@ -26,7 +26,6 @@ import java.util.stream.Stream;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.converter.ConditionalGenericConverter;
import org.springframework.lang.UsesJava8;
/**
* Converts a {@link Stream} to and from a collection or array, converting the
@@ -35,7 +34,6 @@ import org.springframework.lang.UsesJava8;
* @author Stephane Nicoll
* @since 4.2
*/
@UsesJava8
class StreamConverter implements ConditionalGenericConverter {
private static final TypeDescriptor STREAM_TYPE = TypeDescriptor.valueOf(Stream.class);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@@ -19,7 +19,6 @@ package org.springframework.core.convert.support;
import java.util.TimeZone;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.UsesJava8;
import org.springframework.util.StringUtils;
/**
@@ -28,7 +27,6 @@ import org.springframework.util.StringUtils;
* @author Stephane Nicoll
* @since 4.2
*/
@UsesJava8
class StringToTimeZoneConverter implements Converter<String, TimeZone> {
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 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.
@@ -20,7 +20,6 @@ import java.time.ZoneId;
import java.util.TimeZone;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.UsesJava8;
/**
* Simple converter from Java 8's {@link java.time.ZoneId} to {@link java.util.TimeZone}.
@@ -35,7 +34,6 @@ import org.springframework.lang.UsesJava8;
* @since 4.0
* @see TimeZone#getTimeZone(java.time.ZoneId)
*/
@UsesJava8
final class ZoneIdToTimeZoneConverter implements Converter<ZoneId, TimeZone> {
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 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,7 +21,6 @@ import java.util.Calendar;
import java.util.GregorianCalendar;
import org.springframework.core.convert.converter.Converter;
import org.springframework.lang.UsesJava8;
/**
* Simple converter from Java 8's {@link java.time.ZonedDateTime} to {@link java.util.Calendar}.
@@ -36,7 +35,6 @@ import org.springframework.lang.UsesJava8;
* @since 4.0.1
* @see java.util.GregorianCalendar#from(java.time.ZonedDateTime)
*/
@UsesJava8
final class ZonedDateTimeToCalendarConverter implements Converter<ZonedDateTime, Calendar> {
@Override

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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.
@@ -28,7 +28,6 @@ import java.nio.file.OpenOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.springframework.lang.UsesJava7;
import org.springframework.util.Assert;
/**
@@ -41,7 +40,6 @@ import org.springframework.util.Assert;
* @since 4.0
* @see java.nio.file.Path
*/
@UsesJava7
public class PathResource extends AbstractResource implements WritableResource {
private final Path path;

View File

@@ -32,5 +32,6 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE})
@Documented
@Deprecated
public @interface UsesJava7 {
}

View File

@@ -32,5 +32,6 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS)
@Target({ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE})
@Documented
@Deprecated
public @interface UsesJava8 {
}

View File

@@ -18,62 +18,22 @@ package org.springframework.util;
import java.nio.charset.Charset;
import java.util.Base64;
import javax.xml.bind.DatatypeConverter;
import org.springframework.lang.UsesJava8;
/**
* A simple utility class for Base64 encoding and decoding.
*
* <p>Adapts to either Java 8's {@link java.util.Base64} class or Apache Commons Codec's
* {@link org.apache.commons.codec.binary.Base64} class. With neither Java 8 nor Commons
* Codec present, {@link #encode}/{@link #decode} calls will throw an IllegalStateException.
* However, as of Spring 4.2, {@link #encodeToString} and {@link #decodeFromString} will
* nevertheless work since they can delegate to the JAXB DatatypeConverter as a fallback.
* However, this does not apply when using the ...UrlSafe... methods for RFC 4648 "URL and
* Filename Safe Alphabet"; a delegate is required.
* <p>
* <em>Note:</em> Apache Commons Codec does not add padding ({@code =}) when encoding with
* the URL and Filename Safe Alphabet.
* <p>Adapts to Java 8's {@link java.util.Base64} in a convenience fashion.
*
* @author Juergen Hoeller
* @author Gary Russell
* @since 4.1
* @see java.util.Base64
* @see org.apache.commons.codec.binary.Base64
* @see javax.xml.bind.DatatypeConverter#printBase64Binary
* @see javax.xml.bind.DatatypeConverter#parseBase64Binary
*/
public abstract class Base64Utils {
private static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
private static final Base64Delegate delegate;
static {
Base64Delegate delegateToUse = null;
// JDK 8's java.util.Base64 class present?
if (ClassUtils.isPresent("java.util.Base64", Base64Utils.class.getClassLoader())) {
delegateToUse = new JdkBase64Delegate();
}
// Apache Commons Codec present on the classpath?
else if (ClassUtils.isPresent("org.apache.commons.codec.binary.Base64", Base64Utils.class.getClassLoader())) {
delegateToUse = new CommonsCodecBase64Delegate();
}
delegate = delegateToUse;
}
/**
* Assert that Byte64 encoding between byte arrays is actually supported.
* @throws IllegalStateException if neither Java 8 nor Apache Commons Codec is present
*/
private static void assertDelegateAvailable() {
Assert.state(delegate != null,
"Neither Java 8 nor Apache Commons Codec found - Base64 encoding between byte arrays not supported");
}
/**
* Base64-encode the given byte array.
* @param src the original byte array (may be {@code null})
@@ -82,8 +42,10 @@ public abstract class Base64Utils {
* supported, i.e. neither Java 8 nor Apache Commons Codec is present at runtime
*/
public static byte[] encode(byte[] src) {
assertDelegateAvailable();
return delegate.encode(src);
if (src == null || src.length == 0) {
return src;
}
return Base64.getEncoder().encode(src);
}
/**
@@ -94,8 +56,10 @@ public abstract class Base64Utils {
* supported, i.e. neither Java 8 nor Apache Commons Codec is present at runtime
*/
public static byte[] decode(byte[] src) {
assertDelegateAvailable();
return delegate.decode(src);
if (src == null || src.length == 0) {
return src;
}
return Base64.getDecoder().decode(src);
}
/**
@@ -108,8 +72,10 @@ public abstract class Base64Utils {
* @since 4.2.4
*/
public static byte[] encodeUrlSafe(byte[] src) {
assertDelegateAvailable();
return delegate.encodeUrlSafe(src);
if (src == null || src.length == 0) {
return src;
}
return Base64.getUrlEncoder().encode(src);
}
/**
@@ -122,8 +88,10 @@ public abstract class Base64Utils {
* @since 4.2.4
*/
public static byte[] decodeUrlSafe(byte[] src) {
assertDelegateAvailable();
return delegate.decodeUrlSafe(src);
if (src == null || src.length == 0) {
return src;
}
return Base64.getUrlDecoder().decode(src);
}
/**
@@ -139,15 +107,7 @@ public abstract class Base64Utils {
if (src.length == 0) {
return "";
}
if (delegate != null) {
// Full encoder available
return new String(delegate.encode(src), DEFAULT_CHARSET);
}
else {
// JAXB fallback for String case
return DatatypeConverter.printBase64Binary(src);
}
return new String(encode(src), DEFAULT_CHARSET);
}
/**
@@ -162,15 +122,7 @@ public abstract class Base64Utils {
if (src.length() == 0) {
return new byte[0];
}
if (delegate != null) {
// Full encoder available
return delegate.decode(src.getBytes(DEFAULT_CHARSET));
}
else {
// JAXB fallback for String case
return DatatypeConverter.parseBase64Binary(src);
}
return decode(src.getBytes(DEFAULT_CHARSET));
}
/**
@@ -183,8 +135,7 @@ public abstract class Base64Utils {
* supported, i.e. neither Java 8 nor Apache Commons Codec is present at runtime
*/
public static String encodeToUrlSafeString(byte[] src) {
assertDelegateAvailable();
return new String(delegate.encodeUrlSafe(src), DEFAULT_CHARSET);
return new String(encodeUrlSafe(src), DEFAULT_CHARSET);
}
/**
@@ -196,89 +147,7 @@ public abstract class Base64Utils {
* supported, i.e. neither Java 8 nor Apache Commons Codec is present at runtime
*/
public static byte[] decodeFromUrlSafeString(String src) {
assertDelegateAvailable();
return delegate.decodeUrlSafe(src.getBytes(DEFAULT_CHARSET));
}
interface Base64Delegate {
byte[] encode(byte[] src);
byte[] decode(byte[] src);
byte[] encodeUrlSafe(byte[] src);
byte[] decodeUrlSafe(byte[] src);
}
@UsesJava8
static class JdkBase64Delegate implements Base64Delegate {
@Override
public byte[] encode(byte[] src) {
if (src == null || src.length == 0) {
return src;
}
return Base64.getEncoder().encode(src);
}
@Override
public byte[] decode(byte[] src) {
if (src == null || src.length == 0) {
return src;
}
return Base64.getDecoder().decode(src);
}
@Override
public byte[] encodeUrlSafe(byte[] src) {
if (src == null || src.length == 0) {
return src;
}
return Base64.getUrlEncoder().encode(src);
}
@Override
public byte[] decodeUrlSafe(byte[] src) {
if (src == null || src.length == 0) {
return src;
}
return Base64.getUrlDecoder().decode(src);
}
}
static class CommonsCodecBase64Delegate implements Base64Delegate {
private final org.apache.commons.codec.binary.Base64 base64 =
new org.apache.commons.codec.binary.Base64();
private final org.apache.commons.codec.binary.Base64 base64UrlSafe =
new org.apache.commons.codec.binary.Base64(0, null, true);
@Override
public byte[] encode(byte[] src) {
return this.base64.encode(src);
}
@Override
public byte[] decode(byte[] src) {
return this.base64.decode(src);
}
@Override
public byte[] encodeUrlSafe(byte[] src) {
return this.base64UrlSafe.encode(src);
}
@Override
public byte[] decodeUrlSafe(byte[] src) {
return this.base64UrlSafe.decode(src);
}
return decodeUrlSafe(src.getBytes(DEFAULT_CHARSET));
}
}

View File

@@ -24,13 +24,12 @@ import java.security.NoSuchAlgorithmException;
/**
* Miscellaneous methods for calculating digests.
* <p>Mainly for internal use within the framework; consider
* <a href="http://commons.apache.org/codec/">Apache Commons Codec</a> for a
* more comprehensive suite of digest utilities.
* <a href="http://commons.apache.org/codec/">Apache Commons Codec</a>
* for a more comprehensive suite of digest utilities.
*
* @author Arjen Poutsma
* @author Craig Andrews
* @since 3.0
* @see org.apache.commons.codec.digest.DigestUtils
*/
public abstract class DigestUtils {

View File

@@ -20,6 +20,7 @@ import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Optional;
/**
* Miscellaneous object utility methods.
@@ -109,6 +110,7 @@ public abstract class ObjectUtils {
* Determine whether the given object is empty.
* <p>This method supports the following object types.
* <ul>
* <li>{@code Optional}: considered empty if {@link Optional#empty()}</li>
* <li>{@code Array}: considered empty if its length is zero</li>
* <li>{@link CharSequence}: considered empty if its length is zero</li>
* <li>{@link Collection}: delegates to {@link Collection#isEmpty()}</li>
@@ -119,6 +121,7 @@ public abstract class ObjectUtils {
* @param obj the object to check
* @return {@code true} if the object is {@code null} or <em>empty</em>
* @since 4.2
* @see Optional#isPresent()
* @see ObjectUtils#isEmpty(Object[])
* @see StringUtils#hasLength(CharSequence)
* @see StringUtils#isEmpty(Object)
@@ -131,6 +134,9 @@ public abstract class ObjectUtils {
return true;
}
if (obj instanceof Optional) {
return !((Optional) obj).isPresent();
}
if (obj.getClass().isArray()) {
return Array.getLength(obj) == 0;
}
@@ -148,6 +154,26 @@ public abstract class ObjectUtils {
return false;
}
/**
* Unwrap the given object which is potentially a {@link java.util.Optional}.
* @param obj the candidate object
* @return either the value held within the {@code Optional}, {@code null}
* if the {@code Optional} is empty, or simply the given object as-is
* @since 5.0
*/
public static Object unwrapOptional(Object obj) {
if (obj instanceof Optional) {
Optional<?> optional = (Optional<?>) obj;
if (!optional.isPresent()) {
return null;
}
Object result = optional.get();
Assert.isTrue(!(result instanceof Optional), "Multi-level Optional usage not supported");
return result;
}
return obj;
}
/**
* Check whether the given array contains the given element.
* @param array the array to check (may be {@code null},

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 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,22 +22,19 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.BiFunction;
import org.springframework.lang.UsesJava8;
/**
* Adapts a {@link CompletableFuture} into a {@link ListenableFuture}.
*
* @author Sebastien Deleuze
* @since 4.2
*/
@UsesJava8
public class CompletableToListenableFutureAdapter<T> implements ListenableFuture<T> {
private final CompletableFuture<T> completableFuture;
private final ListenableFutureCallbackRegistry<T> callbacks = new ListenableFutureCallbackRegistry<T>();
public CompletableToListenableFutureAdapter(CompletableFuture<T> completableFuture) {
this.completableFuture = completableFuture;
this.completableFuture.handle(new BiFunction<T, Throwable, Object>() {
@@ -54,6 +51,7 @@ public class CompletableToListenableFutureAdapter<T> implements ListenableFuture
});
}
@Override
public void addCallback(ListenableFutureCallback<? super T> callback) {
this.callbacks.addCallback(callback);