From b8e457db9b8404e1388ec53db18a993917a21b64 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Tue, 30 Jul 2013 15:02:51 +0200 Subject: [PATCH] DATACMNS-350 - Improved extensibility of Parameters. Parameters is now a class explicitly designed for extension using generics. We provide a new DefaultParameters which is essentially a drop in replacement for former Parameters as it exposes individual Parameter instances. Modules that previously extended Parameters will now have to implement the abstract factory methods exposed to create concrete - and potentially customized - Parameter instances. --- .../repository/query/DefaultParameters.java | 60 ++++++++++++++ .../data/repository/query/Parameter.java | 11 +-- .../data/repository/query/Parameters.java | 82 ++++++++++--------- .../query/ParametersParameterAccessor.java | 8 +- .../data/repository/query/QueryMethod.java | 10 +-- .../ParametersParameterAccessorUnitTests.java | 8 +- .../repository/query/ParametersUnitTests.java | 28 +++---- .../SimpleParameterAccessorUnitTests.java | 32 ++++---- 8 files changed, 148 insertions(+), 91 deletions(-) create mode 100644 src/main/java/org/springframework/data/repository/query/DefaultParameters.java diff --git a/src/main/java/org/springframework/data/repository/query/DefaultParameters.java b/src/main/java/org/springframework/data/repository/query/DefaultParameters.java new file mode 100644 index 000000000..edda098a8 --- /dev/null +++ b/src/main/java/org/springframework/data/repository/query/DefaultParameters.java @@ -0,0 +1,60 @@ +/* + * Copyright 2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.repository.query; + +import java.lang.reflect.Method; +import java.util.List; + +import org.springframework.core.MethodParameter; + +/** + * Default implementation of {@link Parameters}. + * + * @author Oliver Gierke + */ +public final class DefaultParameters extends Parameters { + + /** + * Creates a new {@link DefaultParameters} instance from the given {@link Method}. + * + * @param method must not be {@literal null}. + */ + public DefaultParameters(Method method) { + super(method); + } + + private DefaultParameters(List parameters) { + super(parameters); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.Parameters#createParameter(org.springframework.core.MethodParameter) + */ + @Override + protected Parameter createParameter(MethodParameter parameter) { + return new Parameter(parameter); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.query.Parameters#createFrom(java.util.List) + */ + @Override + protected DefaultParameters createFrom(List parameters) { + return new DefaultParameters(parameters); + } +} diff --git a/src/main/java/org/springframework/data/repository/query/Parameter.java b/src/main/java/org/springframework/data/repository/query/Parameter.java index 7e7ab9006..124214b2a 100644 --- a/src/main/java/org/springframework/data/repository/query/Parameter.java +++ b/src/main/java/org/springframework/data/repository/query/Parameter.java @@ -32,11 +32,7 @@ import org.springframework.util.Assert; */ public class Parameter { - @SuppressWarnings("unchecked") - static final List> TYPES = Arrays.asList(Pageable.class, Sort.class); - - private static final String PARAM_ON_SPECIAL = format("You must not user @%s on a parameter typed %s or %s", - Param.class.getSimpleName(), Pageable.class.getSimpleName(), Sort.class.getSimpleName()); + @SuppressWarnings("unchecked") static final List> TYPES = Arrays.asList(Pageable.class, Sort.class); private static final String NAMED_PARAMETER_TEMPLATE = ":%s"; private static final String POSITION_PARAMETER_TEMPLATE = "?%s"; @@ -51,12 +47,7 @@ public class Parameter { protected Parameter(MethodParameter parameter) { Assert.notNull(parameter); - this.parameter = parameter; - - if (isSpecialParameter() && isNamedParameter()) { - throw new IllegalArgumentException(PARAM_ON_SPECIAL); - } } /** diff --git a/src/main/java/org/springframework/data/repository/query/Parameters.java b/src/main/java/org/springframework/data/repository/query/Parameters.java index c2085c84e..a889c45a0 100644 --- a/src/main/java/org/springframework/data/repository/query/Parameters.java +++ b/src/main/java/org/springframework/data/repository/query/Parameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2012 the original author or authors. + * Copyright 2008-2013 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,6 +15,8 @@ */ package org.springframework.data.repository.query; +import static java.lang.String.*; + import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -33,11 +35,12 @@ import org.springframework.util.Assert; * * @author Oliver Gierke */ -public class Parameters implements Iterable { +public abstract class Parameters, T extends Parameter> implements Iterable { - @SuppressWarnings("unchecked") - public static final List> TYPES = Arrays.asList(Pageable.class, Sort.class); + @SuppressWarnings("unchecked") public static final List> TYPES = Arrays.asList(Pageable.class, Sort.class); + private static final String PARAM_ON_SPECIAL = format("You must not user @%s on a parameter typed %s or %s", + Param.class.getSimpleName(), Pageable.class.getSimpleName(), Sort.class.getSimpleName()); private static final String ALL_OR_NOTHING = String.format("Either use @%s " + "on all parameters except %s and %s typed once, or none at all!", Param.class.getSimpleName(), Pageable.class.getSimpleName(), Sort.class.getSimpleName()); @@ -45,7 +48,7 @@ public class Parameters implements Iterable { private final int pageableIndex; private final int sortIndex; - private final List parameters; + private final List parameters; private final ParameterNameDiscoverer discoverer = new LocalVariableTableParameterNameDiscoverer(); /** @@ -57,14 +60,22 @@ public class Parameters implements Iterable { Assert.notNull(method); - this.parameters = new ArrayList(); + this.parameters = new ArrayList(); List> types = Arrays.asList(method.getParameterTypes()); for (int i = 0; i < types.size(); i++) { - MethodParameter parameter = new MethodParameter(method, i); - parameter.initParameterNameDiscovery(discoverer); - parameters.add(createParameter(parameter)); + + MethodParameter methodParameter = new MethodParameter(method, i); + methodParameter.initParameterNameDiscovery(discoverer); + + T parameter = createParameter(methodParameter); + + if (parameter.isSpecialParameter() && parameter.isNamedParameter()) { + throw new IllegalArgumentException(PARAM_ON_SPECIAL); + } + + parameters.add(parameter); } this.pageableIndex = types.indexOf(Pageable.class); @@ -78,16 +89,16 @@ public class Parameters implements Iterable { * * @param originals */ - private Parameters(List originals) { + protected Parameters(List originals) { - this.parameters = new ArrayList(); + this.parameters = new ArrayList(); int pageableIndexTemp = -1; int sortIndexTemp = -1; for (int i = 0; i < originals.size(); i++) { - Parameter original = originals.get(i); + T original = originals.get(i); this.parameters.add(original); pageableIndexTemp = original.isPageable() ? i : -1; @@ -98,9 +109,13 @@ public class Parameters implements Iterable { this.sortIndex = sortIndexTemp; } - protected Parameter createParameter(MethodParameter parameter) { - return new Parameter(parameter); - } + /** + * Creates a {@link Parameter} instance for the given {@link MethodParameter}. + * + * @param parameter will never be {@literal null}. + * @return + */ + protected abstract T createParameter(MethodParameter parameter); /** * Returns whether the method the {@link Parameters} was created for contains a {@link Pageable} argument. @@ -108,7 +123,6 @@ public class Parameters implements Iterable { * @return */ public boolean hasPageableParameter() { - return pageableIndex != -1; } @@ -119,7 +133,6 @@ public class Parameters implements Iterable { * @return the pageableIndex */ public int getPageableIndex() { - return pageableIndex; } @@ -130,7 +143,6 @@ public class Parameters implements Iterable { * @return */ public int getSortIndex() { - return sortIndex; } @@ -140,7 +152,6 @@ public class Parameters implements Iterable { * @return */ public boolean hasSortParameter() { - return sortIndex != -1; } @@ -150,7 +161,6 @@ public class Parameters implements Iterable { * @return */ public boolean potentiallySortsDynamically() { - return hasSortParameter() || hasPageableParameter(); } @@ -160,7 +170,7 @@ public class Parameters implements Iterable { * @param index * @return */ - public Parameter getParameter(int index) { + public T getParameter(int index) { try { return parameters.get(index); @@ -191,7 +201,6 @@ public class Parameters implements Iterable { * @return */ public boolean hasSpecialParameter() { - return hasSortParameter() || hasPageableParameter(); } @@ -201,7 +210,6 @@ public class Parameters implements Iterable { * @return */ public int getNumberOfParameters() { - return parameters.size(); } @@ -212,20 +220,22 @@ public class Parameters implements Iterable { * @see Parameter#TYPES * @see Parameter#isSpecialParameter() */ - public Parameters getBindableParameters() { + public S getBindableParameters() { - List bindables = new ArrayList(); + List bindables = new ArrayList(); - for (Parameter candidate : this) { + for (T candidate : this) { if (candidate.isBindable()) { bindables.add(candidate); } } - return new Parameters(bindables); + return createFrom(bindables); } + protected abstract S createFrom(List parameters); + /** * Returns a bindable parameter with the given index. So for a method with a signature of * {@code (Pageable pageable, String name)} a call to {@code #getBindableParameter(0)} will return the {@link String} @@ -234,8 +244,7 @@ public class Parameters implements Iterable { * @param bindableIndex * @return */ - public Parameter getBindableParameter(int bindableIndex) { - + public T getBindableParameter(int bindableIndex) { return getBindableParameters().getParameter(bindableIndex); } @@ -249,7 +258,7 @@ public class Parameters implements Iterable { boolean nameFound = false; - for (Parameter parameter : this.getBindableParameters()) { + for (T parameter : this.getBindableParameters()) { if (parameter.isNamedParameter()) { Assert.isTrue(nameFound || parameter.isFirst(), ALL_OR_NOTHING); @@ -267,17 +276,14 @@ public class Parameters implements Iterable { * @return */ public static boolean isBindable(Class type) { - return !TYPES.contains(type); } /* - * (non-Javadoc) - * - * @see java.lang.Iterable#iterator() - */ - public Iterator iterator() { - + * (non-Javadoc) + * @see java.lang.Iterable#iterator() + */ + public Iterator iterator() { return parameters.iterator(); } -} \ No newline at end of file +} 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 58ab19d33..963bc52dc 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-2011 the original author or authors. + * Copyright 2008-2013 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,7 @@ import org.springframework.util.Assert; */ public class ParametersParameterAccessor implements ParameterAccessor { - private final Parameters parameters; + private final Parameters parameters; private final Object[] values; /** @@ -37,7 +37,7 @@ public class ParametersParameterAccessor implements ParameterAccessor { * @param parameters * @param values */ - public ParametersParameterAccessor(Parameters parameters, Object[] values) { + public ParametersParameterAccessor(Parameters parameters, Object[] values) { Assert.notNull(parameters); Assert.notNull(values); @@ -53,7 +53,7 @@ public class ParametersParameterAccessor implements ParameterAccessor { * * @return the parameters will never be {@literal null}. */ - public Parameters getParameters() { + public Parameters getParameters() { return parameters; } diff --git a/src/main/java/org/springframework/data/repository/query/QueryMethod.java b/src/main/java/org/springframework/data/repository/query/QueryMethod.java index 3b200926e..69d536eb2 100644 --- a/src/main/java/org/springframework/data/repository/query/QueryMethod.java +++ b/src/main/java/org/springframework/data/repository/query/QueryMethod.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2012 the original author or authors. + * Copyright 2008-2013 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. @@ -37,7 +37,7 @@ public class QueryMethod { private final RepositoryMetadata metadata; private final Method method; - private final Parameters parameters; + private final Parameters parameters; /** * Creates a new {@link QueryMethod} from the given parameters. Looks up the correct query to use for following @@ -84,8 +84,8 @@ public class QueryMethod { * @param method * @return must not return {@literal null}. */ - protected Parameters createParameters(Method method) { - return new Parameters(method); + protected Parameters createParameters(Method method) { + return new DefaultParameters(method); } /** @@ -189,7 +189,7 @@ public class QueryMethod { * * @return */ - public Parameters getParameters() { + public Parameters getParameters() { return parameters; } diff --git a/src/test/java/org/springframework/data/repository/query/ParametersParameterAccessorUnitTests.java b/src/test/java/org/springframework/data/repository/query/ParametersParameterAccessorUnitTests.java index a874c5879..39aecd10e 100644 --- a/src/test/java/org/springframework/data/repository/query/ParametersParameterAccessorUnitTests.java +++ b/src/test/java/org/springframework/data/repository/query/ParametersParameterAccessorUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011 the original author or authors. + * Copyright 2011-2013 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. @@ -32,11 +32,11 @@ import org.springframework.data.domain.Pageable; */ public class ParametersParameterAccessorUnitTests { - Parameters parameters; + Parameters parameters; @Before public void setUp() throws Exception { - parameters = new Parameters(Sample.class.getMethod("method", String.class, int.class)); + parameters = new DefaultParameters(Sample.class.getMethod("method", String.class, int.class)); } @Test @@ -59,7 +59,7 @@ public class ParametersParameterAccessorUnitTests { assertThat(accessor.hasBindableNullValue(), is(true)); Method method = Sample.class.getMethod("method", Pageable.class, String.class); - Parameters parameters = new Parameters(method); + DefaultParameters parameters = new DefaultParameters(method); accessor = new ParametersParameterAccessor(parameters, new Object[] { null, "Foo" }); assertThat(accessor.hasBindableNullValue(), is(false)); diff --git a/src/test/java/org/springframework/data/repository/query/ParametersUnitTests.java b/src/test/java/org/springframework/data/repository/query/ParametersUnitTests.java index e205466bf..11d16cb6d 100644 --- a/src/test/java/org/springframework/data/repository/query/ParametersUnitTests.java +++ b/src/test/java/org/springframework/data/repository/query/ParametersUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2010 the original author or authors. + * Copyright 2008-2013 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of @@ -46,28 +46,28 @@ public class ParametersUnitTests { Method validWithPageable = SampleDao.class.getMethod("validWithPageable", String.class, Pageable.class); Method validWithSort = SampleDao.class.getMethod("validWithSort", String.class, Sort.class); - new Parameters(valid); - new Parameters(validWithPageable); - new Parameters(validWithSort); + new DefaultParameters(valid); + new DefaultParameters(validWithPageable); + new DefaultParameters(validWithSort); } @Test(expected = IllegalArgumentException.class) public void rejectsInvalidMethodWithParamMissing() throws Exception { Method method = SampleDao.class.getMethod("invalidParamMissing", String.class, String.class); - new Parameters(method); + new DefaultParameters(method); } @Test(expected = IllegalArgumentException.class) public void rejectsNullMethod() throws Exception { - new Parameters(null); + new DefaultParameters(null); } @Test public void detectsNamedParameterCorrectly() throws Exception { - Parameters parameters = getParametersFor("validWithSort", String.class, Sort.class); + Parameters parameters = getParametersFor("validWithSort", String.class, Sort.class); Parameter parameter = parameters.getParameter(0); @@ -85,12 +85,12 @@ public class ParametersUnitTests { Method method = SampleDao.class.getMethod("validWithSortFirst", Sort.class, String.class); - Parameters parameters = new Parameters(method); + Parameters parameters = new DefaultParameters(method); assertThat(parameters.getBindableParameter(0).getIndex(), is(1)); method = SampleDao.class.getMethod("validWithSortInBetween", String.class, Sort.class, String.class); - parameters = new Parameters(method); + parameters = new DefaultParameters(method); assertThat(parameters.getBindableParameter(0).getIndex(), is(0)); assertThat(parameters.getBindableParameter(1).getIndex(), is(2)); @@ -99,28 +99,28 @@ public class ParametersUnitTests { @Test public void detectsEmptyParameterListCorrectly() throws Exception { - Parameters parameters = getParametersFor("emptyParameters"); + Parameters parameters = getParametersFor("emptyParameters"); assertThat(parameters.hasParameterAt(0), is(false)); } @Test public void detectsPageableParameter() throws Exception { - Parameters parameters = getParametersFor("validWithPageable", String.class, Pageable.class); + Parameters parameters = getParametersFor("validWithPageable", String.class, Pageable.class); assertThat(parameters.getPageableIndex(), is(1)); } @Test public void detectsSortParameter() throws Exception { - Parameters parameters = getParametersFor("validWithSort", String.class, Sort.class); + Parameters parameters = getParametersFor("validWithSort", String.class, Sort.class); assertThat(parameters.getSortIndex(), is(1)); } - private Parameters getParametersFor(String methodName, Class... parameterTypes) throws SecurityException, + private Parameters getParametersFor(String methodName, Class... parameterTypes) throws SecurityException, NoSuchMethodException { Method method = SampleDao.class.getMethod(methodName, parameterTypes); - return new Parameters(method); + return new DefaultParameters(method); } static class User { diff --git a/src/test/java/org/springframework/data/repository/query/SimpleParameterAccessorUnitTests.java b/src/test/java/org/springframework/data/repository/query/SimpleParameterAccessorUnitTests.java index 52c577e99..f374f1584 100644 --- a/src/test/java/org/springframework/data/repository/query/SimpleParameterAccessorUnitTests.java +++ b/src/test/java/org/springframework/data/repository/query/SimpleParameterAccessorUnitTests.java @@ -1,17 +1,17 @@ /* - * Copyright 2008-2010 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * + * Copyright 2008-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.data.repository.query; @@ -31,14 +31,14 @@ import org.springframework.data.domain.Sort; */ public class SimpleParameterAccessorUnitTests { - Parameters parameters, sortParameters, pageableParameters; + Parameters parameters, sortParameters, pageableParameters; @Before public void setUp() throws SecurityException, NoSuchMethodException { - parameters = new Parameters(Sample.class.getMethod("sample", String.class)); - sortParameters = new Parameters(Sample.class.getMethod("sample1", String.class, Sort.class)); - pageableParameters = new Parameters(Sample.class.getMethod("sample2", String.class, Pageable.class)); + parameters = new DefaultParameters(Sample.class.getMethod("sample", String.class)); + sortParameters = new DefaultParameters(Sample.class.getMethod("sample1", String.class, Sort.class)); + pageableParameters = new DefaultParameters(Sample.class.getMethod("sample2", String.class, Pageable.class)); } @Test