diff --git a/src/main/java/org/springframework/hateoas/core/AnnotationAttribute.java b/src/main/java/org/springframework/hateoas/core/AnnotationAttribute.java index a790349f..7c9dde0b 100644 --- a/src/main/java/org/springframework/hateoas/core/AnnotationAttribute.java +++ b/src/main/java/org/springframework/hateoas/core/AnnotationAttribute.java @@ -18,10 +18,13 @@ package org.springframework.hateoas.core; import java.lang.annotation.Annotation; import java.lang.reflect.AnnotatedElement; +import org.springframework.core.MethodParameter; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; /** + * Simply helper to reference a dedicated attribute of an {@link Annotation}. + * * @author Oliver Gierke */ public class AnnotationAttribute { @@ -29,13 +32,20 @@ public class AnnotationAttribute { private final Class annotationType; private final String attributeName; + /** + * Creates a new {@link AnnotationAttribute} to the {@code value} attribute of the given {@link Annotation} type. + * + * @param annotationType must not be {@literal null}. + */ public AnnotationAttribute(Class annotationType) { this(annotationType, null); } /** - * @param annotationType - * @param attributeName + * Creates a new {@link AnnotationAttribute} for the given {@link Annotation} type and annotation attribute name. + * + * @param annotationType must not be {@literal null}. + * @param attributeName can be {@literal null}, defaults to {@code value}. */ public AnnotationAttribute(Class annotationType, String attributeName) { @@ -46,21 +56,49 @@ public class AnnotationAttribute { } /** + * Returns the annotation type. + * * @return the annotationType */ public Class getAnnotationType() { return annotationType; } - public String getValueFrom(AnnotatedElement annotatedElement) { + /** + * Reads the {@link Annotation} attribute's value from the given {@link MethodParameter}. + * + * @param parameter must not be {@literal null}. + * @return + */ + public String getValueFrom(MethodParameter parameter) { - Annotation annotation = annotatedElement.getAnnotation(annotationType); - return (String) (attributeName == null ? AnnotationUtils.getValue(annotation) : AnnotationUtils.getValue( - annotation, attributeName)); + Assert.notNull(parameter, "MethodParameter must not be null!"); + Annotation annotation = parameter.getParameterAnnotation(annotationType); + return annotation == null ? null : getValueFrom(annotation); } + /** + * Reads the {@link Annotation} attribute's value from the given {@link AnnotatedElement}. + * + * @param annotatedElement must not be {@literal null}. + * @return + */ + public String getValueFrom(AnnotatedElement annotatedElement) { + + Assert.notNull(annotatedElement, "Annotated element must not be null!"); + Annotation annotation = annotatedElement.getAnnotation(annotationType); + return annotation == null ? null : getValueFrom(annotation); + } + + /** + * Returns the {@link Annotation} attribute's value from the given {@link Annotation}. + * + * @param annotation must not be {@literal null}. + * @return + */ public String getValueFrom(Annotation annotation) { + Assert.notNull(annotation, "Annotation must not be null!"); return (String) (attributeName == null ? AnnotationUtils.getValue(annotation) : AnnotationUtils.getValue( annotation, attributeName)); } diff --git a/src/main/java/org/springframework/hateoas/core/MethodParameters.java b/src/main/java/org/springframework/hateoas/core/MethodParameters.java index 60e96782..adfbb546 100644 --- a/src/main/java/org/springframework/hateoas/core/MethodParameters.java +++ b/src/main/java/org/springframework/hateoas/core/MethodParameters.java @@ -37,18 +37,29 @@ public class MethodParameters { private final List parameters; /** - * Creates a new {@link MethodParameter} from the given {@link Method}. + * Creates a new {@link MethodParameters} from the given {@link Method}. * * @param method must not be {@literal null}. */ public MethodParameters(Method method) { + this(method, null); + } + + /** + * Creates a new {@link MethodParameters} for the given {@link Method} and {@link AnnotationAttribute}. If the latter + * is given, method parameter names will be looked up from the annotation attribute if present. + * + * @param method must not be {@literal null}. + * @param namingAnnotation can be {@literal null}. + */ + public MethodParameters(Method method, AnnotationAttribute namingAnnotation) { Assert.notNull(method); this.parameters = new ArrayList(); for (int i = 0; i < method.getParameterTypes().length; i++) { - MethodParameter parameter = new MethodParameter(method, i); + MethodParameter parameter = new AnnotationNamingMethodParameter(method, i, namingAnnotation); parameter.initParameterNameDiscovery(DISCOVERER); parameters.add(parameter); } @@ -63,6 +74,25 @@ public class MethodParameters { return parameters; } + /** + * Returns the {@link MethodParameter} with the given name or {@literal null} if none found. + * + * @param name must not be {@literal null} or empty. + * @return + */ + public MethodParameter getParameter(String name) { + + Assert.hasText(name, "Parameter name must not be null!"); + + for (MethodParameter parameter : parameters) { + if (name.equals(parameter.getParameterName())) { + return parameter; + } + } + + return null; + } + /** * Returns all {@link MethodParameter}s annotated with the given annotation type. * @@ -82,4 +112,54 @@ public class MethodParameters { return result; } + + /** + * Custom {@link MethodParameter} extension that will favor the name configured in the {@link AnnotationAttribute} if + * set over discovering it. + * + * @author Oliver Gierke + */ + private static class AnnotationNamingMethodParameter extends MethodParameter { + + private final AnnotationAttribute attribute; + private String name; + + /** + * Creates a new {@link AnnotationNamingMethodParameter} for the given {@link Method}'s parameter with the given + * index. + * + * @param method must not be {@literal null}. + * @param parameterIndex + * @param attribute can be {@literal null} + */ + public AnnotationNamingMethodParameter(Method method, int parameterIndex, AnnotationAttribute attribute) { + + super(method, parameterIndex); + this.attribute = attribute; + + } + + /* + * (non-Javadoc) + * @see org.springframework.core.MethodParameter#getParameterName() + */ + @Override + public String getParameterName() { + + if (name != null) { + return name; + } + + if (attribute != null) { + String foundName = attribute.getValueFrom(this); + if (foundName != null) { + name = foundName; + return name; + } + } + + name = super.getParameterName(); + return name; + } + } } diff --git a/src/test/java/org/springframework/hateoas/core/MethodParametersUnitTests.java b/src/test/java/org/springframework/hateoas/core/MethodParametersUnitTests.java new file mode 100644 index 00000000..e528b319 --- /dev/null +++ b/src/test/java/org/springframework/hateoas/core/MethodParametersUnitTests.java @@ -0,0 +1,49 @@ +/* + * 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.hateoas.core; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; + +import java.lang.reflect.Method; + +import org.junit.Test; +import org.springframework.beans.factory.annotation.Qualifier; + +/** + * Unit tests for {@link MethodParameters}. + * + * @author Oliver Gierke + */ +public class MethodParametersUnitTests { + + @Test + public void prefersAnnotatedParameterOverDiscovered() throws Exception { + + Method method = Sample.class.getMethod("method", String.class, String.class); + MethodParameters parameters = new MethodParameters(method, new AnnotationAttribute(Qualifier.class)); + + assertThat(parameters.getParameter("param"), is(notNullValue())); + assertThat(parameters.getParameter("foo"), is(notNullValue())); + assertThat(parameters.getParameter("another"), is(nullValue())); + } + + static class Sample { + + public void method(String param, @Qualifier("foo") String another) {} + } + +}