#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:
Oliver Drotbohm
2021-02-15 15:41:22 +01:00
parent 1192eba607
commit 5f2d8a623d
6 changed files with 23 additions and 11 deletions

View File

@@ -47,11 +47,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
public class Link implements Serializable {
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 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";
@@ -515,11 +511,20 @@ public class Link implements Serializable {
return Collections.emptyMap();
}
String[] parts = source.split(";");
Map<String, String> attributes = new HashMap<>();
Matcher matcher = KEY_AND_VALUE_PATTERN.matcher(source);
while (matcher.find()) {
attributes.put(matcher.group(1), matcher.group(2));
for (String part : parts) {
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;

View File

@@ -44,7 +44,7 @@ import com.fasterxml.jackson.annotation.JsonValue;
public class Links implements Iterable<Link> {
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;

View File

@@ -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\";title=\"Some title\""))
.isEqualTo(Link.of("/something", "foo"));
.isEqualTo(Link.of("/something", "foo").withTitle("Some title"));
softly.assertThat(Link.valueOf("</customer/1>;" //
+ "rel=\"self\";" //
+ "hreflang=\"en\";" //

View File

@@ -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, 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");
}
}

View File

@@ -63,7 +63,7 @@ class HalLinkDiscovererUnitTest extends LinkDiscovererUnitTest {
+ "title=\"pdf customer copy\";" //
+ "type=\"portable document\";" //
+ "deprecation=\"https://example.com/customers/deprecated\";" //
+ "profile=\"my-profile\"" //
+ "profile=\"my-profile\";" //
+ "name=\"my-name\"");
assertThat(getDiscoverer().findLinkWithRel(IanaLinkRelations.SELF.value(), linkText)) //

View File

@@ -59,7 +59,7 @@ class HalFormsLinkDiscovererUnitTest extends LinkDiscovererUnitTest {
+ "title=\"pdf customer copy\";" //
+ "type=\"portable document\";" //
+ "deprecation=\"https://example.com/customers/deprecated\";" //
+ "profile=\"my-profile\"" //
+ "profile=\"my-profile\";" //
+ "name=\"my-name\"");
assertThat(getDiscoverer().findLinkWithRel(IanaLinkRelations.SELF, linkText)) //