diff --git a/pom.xml b/pom.xml
index 9f4021fe..b639ed89 100644
--- a/pom.xml
+++ b/pom.xml
@@ -206,6 +206,13 @@
1.9.0
test
+
+
+ joda-time
+ joda-time
+ 2.1
+ test
+
diff --git a/src/main/java/org/springframework/hateoas/mvc/AnnotatedParametersParameterAccessor.java b/src/main/java/org/springframework/hateoas/mvc/AnnotatedParametersParameterAccessor.java
index dfdbc532..764a7810 100644
--- a/src/main/java/org/springframework/hateoas/mvc/AnnotatedParametersParameterAccessor.java
+++ b/src/main/java/org/springframework/hateoas/mvc/AnnotatedParametersParameterAccessor.java
@@ -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 getBoundParameters(MethodInvocation invocation) {
+ /**
+ * Returns {@link BoundMethodParameter}s contained in the given {@link MethodInvocation}.
+ *
+ * @param invocation must not be {@literal null}.
+ * @return
+ */
+ public List getBoundParameters(MethodInvocation invocation) {
+
+ Assert.notNull(invocation, "MethodInvocation must not be null!");
MethodParameters parameters = new MethodParameters(invocation.getMethod());
Object[] arguments = invocation.getArguments();
- Map result = new HashMap();
+ List result = new ArrayList();
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);
+ }
+ }
}
diff --git a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java
index f4fc0d14..10870ca2 100644
--- a/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java
+++ b/src/main/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactory.java
@@ -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 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());
}
}
diff --git a/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactoryUnitTest.java b/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactoryUnitTest.java
index a3cd1f20..abfc9ec9 100644
--- a/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactoryUnitTest.java
+++ b/src/test/java/org/springframework/hateoas/mvc/ControllerLinkBuilderFactoryUnitTest.java
@@ -21,8 +21,12 @@ import static org.springframework.hateoas.core.DummyInvocationUtils.*;
import java.util.Arrays;
+import org.joda.time.DateTime;
+import org.joda.time.format.ISODateTimeFormat;
import org.junit.Test;
import org.springframework.core.MethodParameter;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.format.annotation.DateTimeFormat.ISO;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.TestUtils;
import org.springframework.hateoas.mvc.ControllerLinkBuilderUnitTest.PersonControllerImpl;
@@ -76,10 +80,26 @@ public class ControllerLinkBuilderFactoryUnitTest extends TestUtils {
assertThat(link.getHref(), endsWith("/sample/1?foo=value"));
}
+ /**
+ * @see #57
+ */
+ @Test
+ public void usesDateTimeFormatForUriBinding() {
+
+ DateTime now = DateTime.now();
+
+ ControllerLinkBuilderFactory factory = new ControllerLinkBuilderFactory();
+ Link link = factory.linkTo(methodOn(SampleController.class).sampleMethod(now)).withSelfRel();
+ assertThat(link.getHref(), endsWith("/sample/" + ISODateTimeFormat.date().print(now)));
+ }
+
static interface SampleController {
@RequestMapping("/sample/{id}")
HttpEntity> sampleMethod(@PathVariable("id") Long id, SpecialType parameter);
+
+ @RequestMapping("/sample/{time}")
+ HttpEntity> sampleMethod(@PathVariable("time") @DateTimeFormat(iso = ISO.DATE) DateTime time);
}
static class SampleUriComponentsContributor implements UriComponentsContributor {