#483 - Add composite field support to UriTemplate.

* Added Tests to demonstrate required functionality.
* Expanding variables correctly to meet RFC spec.  Composite values can be expanded either as a list or as an associative array

Also see https://tools.ietf.org/html/rfc6570#section-2.4.2
This commit is contained in:
JamesE Richardson
2016-08-22 15:27:21 +01:00
committed by Greg Turnquist
parent 2882e82ba4
commit 8ced6c5154
3 changed files with 107 additions and 4 deletions

View File

@@ -31,6 +31,7 @@ import org.springframework.util.StringUtils;
* A single template variable.
*
* @author Oliver Gierke
* @author JamesE Richardson
*/
@Value
@EqualsAndHashCode
@@ -150,14 +151,15 @@ public final class TemplateVariable implements Serializable {
REQUEST_PARAM("?", true), //
REQUEST_PARAM_CONTINUED("&", true), //
SEGMENT("/", true), //
FRAGMENT("#", true);
FRAGMENT("#", true), //
COMPOSITE_PARAM("*", true);
private static final List<VariableType> COMBINABLE_TYPES = Arrays.asList(REQUEST_PARAM, REQUEST_PARAM_CONTINUED);
private final String key;
private final boolean optional;
private VariableType(String key, boolean optional) {
VariableType(String key, boolean optional) {
this.key = key;
this.optional = optional;

View File

@@ -22,6 +22,8 @@ import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@@ -36,12 +38,13 @@ import org.springframework.web.util.UriComponentsBuilder;
* Custom URI template to support qualified URI template variables.
*
* @author Oliver Gierke
* @author JamesE Richardson
* @see http://tools.ietf.org/html/rfc6570
* @since 0.9
*/
public class UriTemplate implements Iterable<TemplateVariable>, Serializable {
private static final Pattern VARIABLE_REGEX = Pattern.compile("\\{([\\?\\&#/]?)([\\w\\,]+)\\}");
private static final Pattern VARIABLE_REGEX = Pattern.compile("\\{([\\?\\&#/]?)([\\w\\,*]+)\\}");
private static final long serialVersionUID = -1007874653930162262L;
private final TemplateVariables variables;
@@ -68,7 +71,14 @@ public class UriTemplate implements Iterable<TemplateVariable>, Serializable {
String[] names = matcher.group(2).split(",");
for (String name : names) {
TemplateVariable variable = new TemplateVariable(name, type);
TemplateVariable variable;
if (name.endsWith(VariableType.COMPOSITE_PARAM.toString())) {
variable = new TemplateVariable(name.substring(0, name.length() - 1), VariableType.COMPOSITE_PARAM);
} else {
variable = new TemplateVariable(name, type);
}
if (!variable.isRequired() && start < baseUriEndIndex) {
baseUriEndIndex = start;
@@ -276,6 +286,9 @@ public class UriTemplate implements Iterable<TemplateVariable>, Serializable {
}
switch (variable.getType()) {
case COMPOSITE_PARAM:
appendComposite(builder, variable.getName(), value);
break;
case REQUEST_PARAM:
case REQUEST_PARAM_CONTINUED:
builder.queryParam(variable.getName(), value);
@@ -289,4 +302,27 @@ public class UriTemplate implements Iterable<TemplateVariable>, Serializable {
break;
}
}
/**
* Expand what could be a single value, a {@link List}, or a {@link Map}.
*
* @param builder
* @param name
* @param value
* @see https://tools.ietf.org/html/rfc6570#section-2.4.2
*/
private static void appendComposite(UriComponentsBuilder builder, String name, Object value) {
if (value instanceof Iterable) {
for (Object valuePart : (Iterable<?>) value) {
builder.queryParam(name, valuePart);
}
} else if (value instanceof Map) {
for (Entry<String, Object> queryParam : (Set<Entry<String, Object>>) ((Map) value).entrySet()) {
builder.queryParam(queryParam.getKey(), queryParam.getValue());
}
} else {
builder.queryParam(name, value);
}
}
}