#54 - Added support for RFC5988 link headers.
Introduced new Links container element to group a list of links and allow parsing an RFC5988 compatible String representation of links into it. The parsing mechanism is currently regarding the rel attribute only. Added valueOf(…) factory methods to allow Spring MVC to pick the types up out of the box.
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,11 +16,17 @@
|
||||
package org.springframework.hateoas;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Value object for links.
|
||||
@@ -115,7 +121,8 @@ public class Link implements Serializable {
|
||||
return this.href.equals(that.href) && this.rel.equals(that.rel);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
@@ -133,6 +140,62 @@ public class Link implements Serializable {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("{ rel : %s, href : %s }", rel, href);
|
||||
return String.format("<%s>;rel=\"%s\"", href, rel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to easily create {@link Link} instances from RFC-5988 compatible {@link String} representations of a
|
||||
* link. Will return {@literal null} if an empty or {@literal null} {@link String} is given.
|
||||
*
|
||||
* @param element an RFC-5899 compatible representation of a link.
|
||||
* @throws IllegalArgumentException if a non-empty {@link String} was given that does not adhere to RFC-5899.
|
||||
* @throws IllegalArgumentException if no {@code rel} attribute could be found.
|
||||
* @return
|
||||
*/
|
||||
public static Link valueOf(String element) {
|
||||
|
||||
if (!StringUtils.hasText(element)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Pattern uriAndAttributes = Pattern.compile("<(.*)>;(.*)");
|
||||
Matcher matcher = uriAndAttributes.matcher(element);
|
||||
|
||||
if (matcher.find()) {
|
||||
|
||||
Map<String, String> attributes = getAttributeMap(matcher.group(2));
|
||||
|
||||
if (!attributes.containsKey("rel")) {
|
||||
throw new IllegalArgumentException("Link does not provide a rel attribute!");
|
||||
}
|
||||
|
||||
return new Link(matcher.group(1), attributes.get("rel"));
|
||||
|
||||
} else {
|
||||
throw new IllegalArgumentException(String.format("Given link header %s is not RFC5988 compliant!", element));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the links attributes from the given source {@link String}.
|
||||
*
|
||||
* @param source
|
||||
* @return
|
||||
*/
|
||||
private static Map<String, String> getAttributeMap(String source) {
|
||||
|
||||
if (!StringUtils.hasText(source)) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
Map<String, String> attributes = new HashMap<String, String>();
|
||||
Pattern keyAndValue = Pattern.compile("(\\w+)=\\\"(\\p{Alnum}*)\"");
|
||||
Matcher matcher = keyAndValue.matcher(source);
|
||||
|
||||
while (matcher.find()) {
|
||||
attributes.put(matcher.group(1), matcher.group(2));
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
}
|
||||
|
||||
163
src/main/java/org/springframework/hateoas/Links.java
Normal file
163
src/main/java/org/springframework/hateoas/Links.java
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Value object to represent a list of {@link Link}s.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class Links implements Iterable<Link> {
|
||||
|
||||
static final Links NO_LINKS = new Links(Collections.<Link> emptyList());
|
||||
|
||||
private final List<Link> links;
|
||||
|
||||
/**
|
||||
* Creates a new {@link Links} instance from the given {@link Link}s.
|
||||
*
|
||||
* @param links
|
||||
*/
|
||||
public Links(List<Link> links) {
|
||||
this.links = links == null ? Collections.<Link> emptyList() : Collections.unmodifiableList(links);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link Links} instance from the given {@link Link}s.
|
||||
*
|
||||
* @param links
|
||||
*/
|
||||
public Links(Link... links) {
|
||||
this(Arrays.asList(links));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link Link} with the given rel.
|
||||
*
|
||||
* @param rel the relation type to lookup a link for.
|
||||
* @return the {@link Link} with the given rel or {@literal null} if none found.
|
||||
*/
|
||||
public Link getLink(String rel) {
|
||||
|
||||
for (Link link : links) {
|
||||
if (link.getRel().equals(rel)) {
|
||||
return link;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all {@link Links} with the given relation type.
|
||||
*
|
||||
* @return the links
|
||||
*/
|
||||
public List<Link> getLinks(String rel) {
|
||||
|
||||
List<Link> result = new ArrayList<Link>();
|
||||
|
||||
for (Link link : links) {
|
||||
if (link.getRel().endsWith(rel)) {
|
||||
result.add(link);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Links} instance from the given RFC5988-compatible link format.
|
||||
*
|
||||
* @param source a comma separated list of {@link Link} representations.
|
||||
* @return the {@link Links} represented by the given {@link String}.
|
||||
*/
|
||||
public static Links valueOf(String source) {
|
||||
|
||||
if (!StringUtils.hasText(source)) {
|
||||
return NO_LINKS;
|
||||
}
|
||||
|
||||
String[] elements = source.split(",");
|
||||
List<Link> links = new ArrayList<Link>(elements.length);
|
||||
|
||||
for (String element : elements) {
|
||||
|
||||
Link link = Link.valueOf(element);
|
||||
|
||||
if (link != null) {
|
||||
links.add(link);
|
||||
}
|
||||
}
|
||||
|
||||
return new Links(links);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return StringUtils.collectionToCommaDelimitedString(links);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Iterable#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<Link> iterator() {
|
||||
return links.iterator();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object arg0) {
|
||||
|
||||
if (!(arg0 instanceof Links)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Links that = (Links) arg0;
|
||||
|
||||
return this.links.equals(that.links);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
|
||||
int result = 17;
|
||||
result += 31 * links.hashCode();
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user