From 8c4420bcbca7ed5422f2b0a48d79cf8a56c63991 Mon Sep 17 00:00:00 2001 From: Oliver Drotbohm Date: Sat, 12 Dec 2020 01:16:56 +0100 Subject: [PATCH] #1414 - Discovered mappings now get regex matchers removed. We now remove potentially used regular expression matchers from template variables to avoid the to show up in links generated for the templates. Backport of #1412. --- .../core/AnnotationMappingDiscoverer.java | 49 ++++++++++++++++++- .../AnnotationMappingDiscovererUnitTest.java | 12 ++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/springframework/hateoas/server/core/AnnotationMappingDiscoverer.java b/src/main/java/org/springframework/hateoas/server/core/AnnotationMappingDiscoverer.java index 01d30a7c..e6dfc9b3 100644 --- a/src/main/java/org/springframework/hateoas/server/core/AnnotationMappingDiscoverer.java +++ b/src/main/java/org/springframework/hateoas/server/core/AnnotationMappingDiscoverer.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.regex.Matcher; import java.util.regex.Pattern; import org.springframework.http.HttpMethod; @@ -41,6 +42,7 @@ import org.springframework.web.bind.annotation.RequestMethod; public class AnnotationMappingDiscoverer implements MappingDiscoverer { private static final Pattern MULTIPLE_SLASHES = Pattern.compile("/{2,}"); + private static final Pattern TEMPLATE_VARIABLE_NAME = Pattern.compile("\\{(.*)\\}"); private final Class annotationType; private final String mappingAttributeName; @@ -114,7 +116,7 @@ public class AnnotationMappingDiscoverer implements MappingDiscoverer { return typeMapping; } - return typeMapping == null || "/".equals(typeMapping) ? mapping[0] : join(typeMapping, mapping[0]); + return cleanup(typeMapping == null || "/".equals(typeMapping) ? mapping[0] : join(typeMapping, mapping[0])); } /** @@ -177,6 +179,49 @@ public class AnnotationMappingDiscoverer implements MappingDiscoverer { * @return */ private static String join(String typeMapping, String mapping) { - return MULTIPLE_SLASHES.matcher(typeMapping.concat("/").concat(mapping)).replaceAll("/"); + return typeMapping.concat("/").concat(mapping); + } + + /** + * @param mapping + * @return + */ + private static String cleanup(String mapping) { + + String[] parts = mapping.split("/"); + StringBuilder result = new StringBuilder(); + + for (int i = 0; i < parts.length; i++) { + + String part = parts[i]; + + if (i != 0) { + result.append("/"); + } + + result.append(part.contains(":") ? cleanupPart(part) : part); + } + + return MULTIPLE_SLASHES.matcher(result.toString()).replaceAll("/"); + } + + private static String cleanupPart(String variable) { + + if (!variable.contains("{")) { + return variable; + } + + Matcher matcher = TEMPLATE_VARIABLE_NAME.matcher(variable); + + if (!matcher.find()) { + return variable; + } + + String rawName = matcher.group(1); + int colonIndex = rawName.indexOf(':'); + + return colonIndex < 0 + ? variable + : variable.replace(matcher.group(0), "{" + rawName.substring(0, colonIndex) + "}"); } } diff --git a/src/test/java/org/springframework/hateoas/server/core/AnnotationMappingDiscovererUnitTest.java b/src/test/java/org/springframework/hateoas/server/core/AnnotationMappingDiscovererUnitTest.java index 0834a74f..ce4fc59c 100755 --- a/src/test/java/org/springframework/hateoas/server/core/AnnotationMappingDiscovererUnitTest.java +++ b/src/test/java/org/springframework/hateoas/server/core/AnnotationMappingDiscovererUnitTest.java @@ -25,7 +25,7 @@ import org.springframework.web.bind.annotation.RequestMapping; /** * Unit tests for {@link AnnotationMappingDiscoverer}. - * + * * @author Oliver Gierke * @author Kevin Conaway * @author Mark Paluch @@ -153,6 +153,13 @@ class AnnotationMappingDiscovererUnitTest { assertThat(discoverer.getMapping(method)).isEqualTo("/type/otherMethod"); } + @Test // #1412 + void removesMatchingExpressionFromTemplateVariable() throws Exception { + + Method method = MyController.class.getMethod("mappingWithMatchingExpression"); + assertThat(discoverer.getMapping(method)).isEqualTo("/type/foo/{bar}"); + } + @RequestMapping("/type") interface MyController { @@ -164,6 +171,9 @@ class AnnotationMappingDiscovererUnitTest { @RequestMapping void noMethodMapping(); + + @RequestMapping("/foo/{bar:[ABC]{1}}") + void mappingWithMatchingExpression(); } interface ControllerWithoutTypeLevelMapping {