#1460 - Parsing Link instances now supports unquoted attribute values.
Switched from using regular expressions to a simple manual parsing algorithm for the attributes attached to a link. Adapt a couple of broken tests that previously erroneously worked.
This commit is contained in:
@@ -47,11 +47,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
|
|||||||
public class Link implements Serializable {
|
public class Link implements Serializable {
|
||||||
|
|
||||||
private static final long serialVersionUID = -9037755944661782121L;
|
private static final long serialVersionUID = -9037755944661782121L;
|
||||||
private static final String URI_PATTERN = "(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";
|
|
||||||
|
|
||||||
private static final Pattern URI_AND_ATTRIBUTES_PATTERN = Pattern.compile("<(.*)>;(.*)");
|
private static final Pattern URI_AND_ATTRIBUTES_PATTERN = Pattern.compile("<(.*)>;(.*)");
|
||||||
private static final Pattern KEY_AND_VALUE_PATTERN = Pattern
|
|
||||||
.compile("(\\w+)=\"(\\p{Lower}[\\p{Lower}\\p{Digit}.\\-\\s]*|" + URI_PATTERN + ")\"");
|
|
||||||
|
|
||||||
public static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
|
public static final String ATOM_NAMESPACE = "http://www.w3.org/2005/Atom";
|
||||||
|
|
||||||
@@ -515,11 +511,20 @@ public class Link implements Serializable {
|
|||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String[] parts = source.split(";");
|
||||||
Map<String, String> attributes = new HashMap<>();
|
Map<String, String> attributes = new HashMap<>();
|
||||||
Matcher matcher = KEY_AND_VALUE_PATTERN.matcher(source);
|
|
||||||
|
|
||||||
while (matcher.find()) {
|
for (String part : parts) {
|
||||||
attributes.put(matcher.group(1), matcher.group(2));
|
|
||||||
|
int delimiter = part.indexOf('=');
|
||||||
|
|
||||||
|
String key = part.substring(0, delimiter).trim();
|
||||||
|
String value = part.substring(delimiter + 1).trim();
|
||||||
|
|
||||||
|
// Potentially unquote value
|
||||||
|
value = value.startsWith("\"") ? value.substring(1, value.length() - 1) : value;
|
||||||
|
|
||||||
|
attributes.put(key, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
return attributes;
|
return attributes;
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ import com.fasterxml.jackson.annotation.JsonValue;
|
|||||||
public class Links implements Iterable<Link> {
|
public class Links implements Iterable<Link> {
|
||||||
|
|
||||||
public static final Links NONE = new Links(Collections.emptyList());
|
public static final Links NONE = new Links(Collections.emptyList());
|
||||||
private static final Pattern LINK_HEADER_PATTERN = Pattern.compile("(<[^>]*>(;\\s*\\w+=\"[^\"]*\")+)");
|
private static final Pattern LINK_HEADER_PATTERN = Pattern.compile("(<[^>]*>(;\\s*\\w+=\"?[^\"]*\"?)+)");
|
||||||
|
|
||||||
private final List<Link> links;
|
private final List<Link> links;
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ class LinkUnitTest {
|
|||||||
|
|
||||||
softly.assertThat(Link.valueOf("</something>;rel=\"foo\"")).isEqualTo(Link.of("/something", "foo"));
|
softly.assertThat(Link.valueOf("</something>;rel=\"foo\"")).isEqualTo(Link.of("/something", "foo"));
|
||||||
softly.assertThat(Link.valueOf("</something>;rel=\"foo\";title=\"Some title\""))
|
softly.assertThat(Link.valueOf("</something>;rel=\"foo\";title=\"Some title\""))
|
||||||
.isEqualTo(Link.of("/something", "foo"));
|
.isEqualTo(Link.of("/something", "foo").withTitle("Some title"));
|
||||||
softly.assertThat(Link.valueOf("</customer/1>;" //
|
softly.assertThat(Link.valueOf("</customer/1>;" //
|
||||||
+ "rel=\"self\";" //
|
+ "rel=\"self\";" //
|
||||||
+ "hreflang=\"en\";" //
|
+ "hreflang=\"en\";" //
|
||||||
|
|||||||
@@ -135,4 +135,11 @@ class LinksUnitTest {
|
|||||||
assertThat(Links.of(first, second).containsSameLinksAs(Links.of(first))).isFalse();
|
assertThat(Links.of(first, second).containsSameLinksAs(Links.of(first))).isFalse();
|
||||||
assertThat(Links.of(first, second).containsSameLinksAs(Links.of(first, second))).isTrue();
|
assertThat(Links.of(first, second).containsSameLinksAs(Links.of(first, second))).isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // #1460
|
||||||
|
void supportsUnquotedAttributes() {
|
||||||
|
|
||||||
|
assertThat(Links.parse("<https://url.com?page=1>; rel=first").getRequiredLink("first").getHref())
|
||||||
|
.isEqualTo("https://url.com?page=1");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class HalLinkDiscovererUnitTest extends LinkDiscovererUnitTest {
|
|||||||
+ "title=\"pdf customer copy\";" //
|
+ "title=\"pdf customer copy\";" //
|
||||||
+ "type=\"portable document\";" //
|
+ "type=\"portable document\";" //
|
||||||
+ "deprecation=\"https://example.com/customers/deprecated\";" //
|
+ "deprecation=\"https://example.com/customers/deprecated\";" //
|
||||||
+ "profile=\"my-profile\"" //
|
+ "profile=\"my-profile\";" //
|
||||||
+ "name=\"my-name\"");
|
+ "name=\"my-name\"");
|
||||||
|
|
||||||
assertThat(getDiscoverer().findLinkWithRel(IanaLinkRelations.SELF.value(), linkText)) //
|
assertThat(getDiscoverer().findLinkWithRel(IanaLinkRelations.SELF.value(), linkText)) //
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class HalFormsLinkDiscovererUnitTest extends LinkDiscovererUnitTest {
|
|||||||
+ "title=\"pdf customer copy\";" //
|
+ "title=\"pdf customer copy\";" //
|
||||||
+ "type=\"portable document\";" //
|
+ "type=\"portable document\";" //
|
||||||
+ "deprecation=\"https://example.com/customers/deprecated\";" //
|
+ "deprecation=\"https://example.com/customers/deprecated\";" //
|
||||||
+ "profile=\"my-profile\"" //
|
+ "profile=\"my-profile\";" //
|
||||||
+ "name=\"my-name\"");
|
+ "name=\"my-name\"");
|
||||||
|
|
||||||
assertThat(getDiscoverer().findLinkWithRel(IanaLinkRelations.SELF, linkText)) //
|
assertThat(getDiscoverer().findLinkWithRel(IanaLinkRelations.SELF, linkText)) //
|
||||||
|
|||||||
Reference in New Issue
Block a user