#57 - Method reference link building now considers formatting annotations.
The String value handed into the UriTemplate created through mock method invocations now uses a DefaultFormattingConversionService to consider formatting annotations on the controller method parameters.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
* Copyright 2012-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.
|
||||
@@ -16,44 +16,131 @@
|
||||
package org.springframework.hateoas.mvc;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.convert.ConversionService;
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.format.support.DefaultFormattingConversionService;
|
||||
import org.springframework.hateoas.core.AnnotationAttribute;
|
||||
import org.springframework.hateoas.core.MethodParameters;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.util.UriTemplate;
|
||||
|
||||
/**
|
||||
* Value object to allow accessing {@link MethodInvocation} parameters with the configured {@link AnnotationAttribute}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class AnnotatedParametersParameterAccessor {
|
||||
class AnnotatedParametersParameterAccessor {
|
||||
|
||||
private final AnnotationAttribute attribute;
|
||||
|
||||
/**
|
||||
* Creates a new {@link AnnotatedParametersParameterAccessor} using the given {@link AnnotationAttribute}.
|
||||
*
|
||||
* @param attribute must not be {@literal null}.
|
||||
*/
|
||||
public AnnotatedParametersParameterAccessor(AnnotationAttribute attribute) {
|
||||
|
||||
Assert.notNull(attribute);
|
||||
this.attribute = attribute;
|
||||
}
|
||||
|
||||
public Map<String, Object> getBoundParameters(MethodInvocation invocation) {
|
||||
/**
|
||||
* Returns {@link BoundMethodParameter}s contained in the given {@link MethodInvocation}.
|
||||
*
|
||||
* @param invocation must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public List<BoundMethodParameter> getBoundParameters(MethodInvocation invocation) {
|
||||
|
||||
Assert.notNull(invocation, "MethodInvocation must not be null!");
|
||||
|
||||
MethodParameters parameters = new MethodParameters(invocation.getMethod());
|
||||
Object[] arguments = invocation.getArguments();
|
||||
Map<String, Object> result = new HashMap<String, Object>();
|
||||
List<BoundMethodParameter> result = new ArrayList<BoundMethodParameter>();
|
||||
|
||||
for (MethodParameter parameter : parameters.getParametersWith(attribute.getAnnotationType())) {
|
||||
|
||||
Annotation annotation = parameter.getParameterAnnotation(attribute.getAnnotationType());
|
||||
String annotationAttributeValue = attribute.getValueFrom(annotation);
|
||||
String key = StringUtils.hasText(annotationAttributeValue) ? annotationAttributeValue : parameter
|
||||
.getParameterName();
|
||||
result.put(key, arguments[parameter.getParameterIndex()]);
|
||||
result.add(new BoundMethodParameter(parameter, arguments[parameter.getParameterIndex()], attribute));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a {@link MethodParameter} alongside the value it has been bound to.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static class BoundMethodParameter {
|
||||
|
||||
private static final ConversionService CONVERSION_SERVICE = new DefaultFormattingConversionService();
|
||||
private static final TypeDescriptor STRING_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
|
||||
|
||||
private final MethodParameter parameter;
|
||||
private final Object value;
|
||||
private final AnnotationAttribute attribute;
|
||||
private final TypeDescriptor parameterTypeDecsriptor;
|
||||
|
||||
/**
|
||||
* Creates a new {@link BoundMethodParameter}
|
||||
*
|
||||
* @param parameter
|
||||
* @param value
|
||||
* @param attribute
|
||||
*/
|
||||
public BoundMethodParameter(MethodParameter parameter, Object value, AnnotationAttribute attribute) {
|
||||
|
||||
Assert.notNull(parameter, "MethodParameter must not be null!");
|
||||
|
||||
this.parameter = parameter;
|
||||
this.value = value;
|
||||
this.attribute = attribute;
|
||||
this.parameterTypeDecsriptor = TypeDescriptor.nested(parameter, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the {@link UriTemplate} variable to be bound. The name will be derived from the configured
|
||||
* {@link AnnotationAttribute} or the {@link MethodParameter} name as fallback.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getVariableName() {
|
||||
|
||||
if (attribute == null) {
|
||||
return parameter.getParameterName();
|
||||
}
|
||||
|
||||
Annotation annotation = parameter.getParameterAnnotation(attribute.getAnnotationType());
|
||||
String annotationAttributeValue = attribute.getValueFrom(annotation);
|
||||
return StringUtils.hasText(annotationAttributeValue) ? annotationAttributeValue : parameter.getParameterName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the raw value bound to the {@link MethodParameter}.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the bound value converted into a {@link String} based on default conversion service setup.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String asString() {
|
||||
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (String) CONVERSION_SERVICE.convert(value, parameterTypeDecsriptor, STRING_DESCRIPTOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.springframework.core.MethodParameter;
|
||||
@@ -36,6 +35,7 @@ import org.springframework.hateoas.core.DummyInvocationUtils.LastInvocationAware
|
||||
import org.springframework.hateoas.core.LinkBuilderSupport;
|
||||
import org.springframework.hateoas.core.MappingDiscoverer;
|
||||
import org.springframework.hateoas.core.MethodParameters;
|
||||
import org.springframework.hateoas.mvc.AnnotatedParametersParameterAccessor.BoundMethodParameter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -115,19 +115,21 @@ public class ControllerLinkBuilderFactory implements MethodLinkBuilderFactory<Co
|
||||
}
|
||||
}
|
||||
|
||||
values.putAll(PATH_VARIABLE_ACCESSOR.getBoundParameters(invocation));
|
||||
for (BoundMethodParameter parameter : PATH_VARIABLE_ACCESSOR.getBoundParameters(invocation)) {
|
||||
values.put(parameter.getVariableName(), parameter.asString());
|
||||
}
|
||||
|
||||
for (Entry<String, Object> param : REQUEST_PARAM_ACCESSOR.getBoundParameters(invocation).entrySet()) {
|
||||
for (BoundMethodParameter parameter : REQUEST_PARAM_ACCESSOR.getBoundParameters(invocation)) {
|
||||
|
||||
Object value = param.getValue();
|
||||
String key = param.getKey();
|
||||
Object value = parameter.getValue();
|
||||
String key = parameter.getVariableName();
|
||||
|
||||
if (value instanceof Collection) {
|
||||
for (Object element : (Collection<?>) value) {
|
||||
builder.queryParam(key, element);
|
||||
}
|
||||
} else {
|
||||
builder.queryParam(key, value);
|
||||
builder.queryParam(key, parameter.asString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user