diff --git a/src/main/java/org/springframework/data/repository/query/ParametersParameterAccessor.java b/src/main/java/org/springframework/data/repository/query/ParametersParameterAccessor.java index 963bc52dc..493fbf7c1 100644 --- a/src/main/java/org/springframework/data/repository/query/ParametersParameterAccessor.java +++ b/src/main/java/org/springframework/data/repository/query/ParametersParameterAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2013 the original author or authors. + * Copyright 2008-2015 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. @@ -15,10 +15,13 @@ */ package org.springframework.data.repository.query; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.repository.util.QueryExecutionConverters; import org.springframework.util.Assert; /** @@ -29,13 +32,13 @@ import org.springframework.util.Assert; public class ParametersParameterAccessor implements ParameterAccessor { private final Parameters parameters; - private final Object[] values; + private final List values; /** * Creates a new {@link ParametersParameterAccessor}. * - * @param parameters - * @param values + * @param parameters must not be {@literal null}. + * @param values must not be {@literal null}. */ public ParametersParameterAccessor(Parameters parameters, Object[] values) { @@ -45,7 +48,14 @@ public class ParametersParameterAccessor implements ParameterAccessor { Assert.isTrue(parameters.getNumberOfParameters() == values.length, "Invalid number of parameters given!"); this.parameters = parameters; - this.values = values.clone(); + + List unwrapped = new ArrayList(values.length); + + for (Object element : values.clone()) { + unwrapped.add(QueryExecutionConverters.unwrap(element)); + } + + this.values = unwrapped; } /** @@ -67,7 +77,7 @@ public class ParametersParameterAccessor implements ParameterAccessor { return null; } - return (Pageable) values[parameters.getPageableIndex()]; + return (Pageable) values.get(parameters.getPageableIndex()); } /* @@ -77,7 +87,7 @@ public class ParametersParameterAccessor implements ParameterAccessor { public Sort getSort() { if (parameters.hasSortParameter()) { - return (Sort) values[parameters.getSortIndex()]; + return (Sort) values.get(parameters.getSortIndex()); } if (parameters.hasPageableParameter() && getPageable() != null) { @@ -95,7 +105,7 @@ public class ParametersParameterAccessor implements ParameterAccessor { */ @SuppressWarnings("unchecked") protected T getValue(int index) { - return (T) values[index]; + return (T) values.get(index); } /* @@ -103,8 +113,7 @@ public class ParametersParameterAccessor implements ParameterAccessor { * @see org.springframework.data.repository.query.ParameterAccessor#getBindableValue(int) */ public Object getBindableValue(int index) { - - return values[parameters.getBindableParameter(index).getIndex()]; + return values.get(parameters.getBindableParameter(index).getIndex()); } /* @@ -114,7 +123,7 @@ public class ParametersParameterAccessor implements ParameterAccessor { public boolean hasBindableNullValue() { for (Parameter parameter : parameters.getBindableParameters()) { - if (values[parameter.getIndex()] == null) { + if (values.get(parameter.getIndex()) == null) { return true; } } @@ -127,7 +136,6 @@ public class ParametersParameterAccessor implements ParameterAccessor { * @see org.springframework.data.repository.query.ParameterAccessor#iterator() */ public BindableParameterIterator iterator() { - return new BindableParameterIterator(); } @@ -146,7 +154,6 @@ public class ParametersParameterAccessor implements ParameterAccessor { * @return */ public Object next() { - return getBindableValue(currentIndex++); } @@ -155,8 +162,7 @@ public class ParametersParameterAccessor implements ParameterAccessor { * @see java.util.Iterator#hasNext() */ public boolean hasNext() { - - return values.length > currentIndex; + return values.size() > currentIndex; } /* @@ -164,7 +170,6 @@ public class ParametersParameterAccessor implements ParameterAccessor { * @see java.util.Iterator#remove() */ public void remove() { - throw new UnsupportedOperationException(); } } diff --git a/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java b/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java index 664429995..6d554d901 100644 --- a/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java +++ b/src/main/java/org/springframework/data/repository/util/QueryExecutionConverters.java @@ -52,6 +52,7 @@ public abstract class QueryExecutionConverters { QueryExecutionConverters.class.getClassLoader()); private static final Set> WRAPPER_TYPES = new HashSet>(); + private static final Set> UNWRAPPERS = new HashSet>(); static { @@ -60,10 +61,12 @@ public abstract class QueryExecutionConverters { if (GUAVA_PRESENT) { WRAPPER_TYPES.add(NullableWrapperToGuavaOptionalConverter.getWrapperType()); + UNWRAPPERS.add(GuavaOptionalUnwrapper.INSTANCE); } if (JDK_8_PRESENT) { WRAPPER_TYPES.add(NullableWrapperToJdk8OptionalConverter.getWrapperType()); + UNWRAPPERS.add(Jdk8OptionalUnwrapper.INSTANCE); } if (JDK_8_PRESENT && SPRING_4_2_PRESENT) { @@ -82,7 +85,14 @@ public abstract class QueryExecutionConverters { public static boolean supports(Class type) { Assert.notNull(type, "Type must not be null!"); - return WRAPPER_TYPES.contains(type); + + for (Class candidate : WRAPPER_TYPES) { + if (candidate.isAssignableFrom(type)) { + return true; + } + } + + return false; } /** @@ -106,6 +116,30 @@ public abstract class QueryExecutionConverters { conversionService.addConverter(new NullableWrapperToFutureConverter(conversionService)); } + /** + * Unwraps the given source value in case it's one of the currently supported wrapper types detected at runtime. + * + * @param source can be {@literal null}. + * @return + */ + public static Object unwrap(Object source) { + + if (source == null || !supports(source.getClass())) { + return source; + } + + for (Converter converter : UNWRAPPERS) { + + Object result = converter.convert(source); + + if (result != source) { + return result; + } + } + + return source; + } + /** * Base class for converters that create instances of wrapper types such as Google Guava's and JDK 8's * {@code Optional} types. @@ -334,4 +368,44 @@ public abstract class QueryExecutionConverters { return CompletableFuture.class; } } + + /** + * A {@link Converter} to unwrap Guava {@link Optional} instances. + * + * @author Oliver Gierke + * @since 1.12 + */ + private static enum GuavaOptionalUnwrapper implements Converter { + + INSTANCE; + + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) + */ + @Override + public Object convert(Object source) { + return source instanceof Optional ? ((Optional) source).orNull() : source; + } + } + + /** + * A {@link Converter} to unwrap JDK 8 {@link java.util.Optional} instances. + * + * @author Oliver Gierke + * @since 1.12 + */ + private static enum Jdk8OptionalUnwrapper implements Converter { + + INSTANCE; + + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) + */ + @Override + public Object convert(Object source) { + return source instanceof java.util.Optional ? ((java.util.Optional) source).orElse(null) : source; + } + } } diff --git a/src/test/java/org/springframework/data/repository/util/QueryExecutionConvertersUnitTests.java b/src/test/java/org/springframework/data/repository/util/QueryExecutionConvertersUnitTests.java index 6c804eaaa..88305324a 100644 --- a/src/test/java/org/springframework/data/repository/util/QueryExecutionConvertersUnitTests.java +++ b/src/test/java/org/springframework/data/repository/util/QueryExecutionConvertersUnitTests.java @@ -109,4 +109,36 @@ public class QueryExecutionConvertersUnitTests { assertThat(result.isDone(), is(true)); assertThat(result.get(), is(nullValue())); } + + /** + * @see DATACMNS-768 + */ + @Test + public void unwrapsJdk8Optional() { + assertThat(QueryExecutionConverters.unwrap(java.util.Optional.of("Foo")), is((Object) "Foo")); + } + + /** + * @see DATACMNS-768 + */ + @Test + public void unwrapsGuava8Optional() { + assertThat(QueryExecutionConverters.unwrap(Optional.of("Foo")), is((Object) "Foo")); + } + + /** + * @see DATACMNS-768 + */ + @Test + public void unwrapsNullToNull() { + assertThat(QueryExecutionConverters.unwrap(null), is(nullValue())); + } + + /** + * @see DATACMNS-768 + */ + @Test + public void unwrapsNonWrapperTypeToItself() { + assertThat(QueryExecutionConverters.unwrap("Foo"), is((Object) "Foo")); + } }