Fix bug in common path prefix calculation (#20310)

Signed-off-by: Tim Quinn <tim.quinn@oracle.com>
This commit is contained in:
Tim Quinn 2024-12-14 00:12:26 -06:00 committed by GitHub
parent 1a3d7d4ae6
commit d55a41e985
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 97 additions and 4 deletions

View File

@ -37,6 +37,7 @@ import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Scanner; import java.util.Scanner;
import java.util.Set; import java.util.Set;
import java.util.StringJoiner;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.prefs.BackingStoreException; import java.util.prefs.BackingStoreException;
@ -352,7 +353,7 @@ public abstract class JavaHelidonCommonCodegen extends AbstractJavaCodegen
Scan the paths of all the operations, computing the longest common prefix. Then compute and set the path suffix Scan the paths of all the operations, computing the longest common prefix. Then compute and set the path suffix
for each operation. for each operation.
*/ */
String commonPathPrefixForApi = StringUtils.getCommonPrefix(objs.getOperations().getOperation() String commonPathPrefixForApi = commonPathPrefix(objs.getOperations().getOperation()
.stream() .stream()
.map(op -> op.path) .map(op -> op.path)
.map(path -> path.charAt(0) != '/' ? "/" + path : path ) .map(path -> path.charAt(0) != '/' ? "/" + path : path )
@ -450,6 +451,46 @@ public abstract class JavaHelidonCommonCodegen extends AbstractJavaCodegen
return rootJavaEEPackage; return rootJavaEEPackage;
} }
static String commonPathPrefix(String[] paths) {
if (paths.length == 0) {
return "/";
}
// Start out with the first path as the longest common prefix. The eventual longest common
// prefix can be no longer than the first path, so as we check other paths we simply
// revise the number of matching segments we have.
String[] commonSegments = stripAnyLeadingSlash(paths[0]).split("/");
int commonSegmentsCount = commonSegments.length;
// Examine the remaining paths.
for (int i = 1; i < paths.length; i++) {
String[] segments = stripAnyLeadingSlash(paths[i]).split("/");
// Check each segment of this next path against the common segments we have so far.
int segmentIndex = 0;
while (segmentIndex < Math.min(commonSegmentsCount, segments.length)
&& commonSegments[segmentIndex].equals(segments[segmentIndex])) {
segmentIndex++;
}
commonSegmentsCount = segmentIndex;
if (commonSegmentsCount == 0) {
break;
}
}
StringJoiner commonPath = new StringJoiner("/", "/", "");
commonPath.setEmptyValue("/");
for (int i = 0; i < commonSegmentsCount; i++) {
commonPath.add(commonSegments[i]);
}
return commonPath.toString();
}
private static String stripAnyLeadingSlash(String path) {
return path.startsWith("/") ? path.substring(1) : path;
}
/** /**
* Prepares a map of predefined HTTP status code constants. * Prepares a map of predefined HTTP status code constants.
* <p> * <p>
@ -716,9 +757,9 @@ public abstract class JavaHelidonCommonCodegen extends AbstractJavaCodegen
private static final String DEFAULT_VERSIONS = "<data>\n" + private static final String DEFAULT_VERSIONS = "<data>\n" +
" <archetypes>\n" + " <archetypes>\n" +
" <version>2.6.5</version>\n" + " <version>2.6.10</version>\n" +
" <version>3.2.7</version>\n" + " <version>3.2.11</version>\n" +
" <version>4.0.9</version>\n" + " <version>4.1.4</version>\n" +
" </archetypes>\n" + " </archetypes>\n" +
"</data>"; "</data>";

View File

@ -54,4 +54,56 @@ public class HelidonCommonCodegenTest {
List.of("4.0.10", "3.2.1", "3.2.0", "2.0.4", "1.2.3", "1.2.2", "1.1.0"))) List.of("4.0.10", "3.2.1", "3.2.0", "2.0.4", "1.2.3", "1.2.2", "1.1.0")))
.isEqualTo("4.0.11-SNAPSHOT"); .isEqualTo("4.0.11-SNAPSHOT");
} }
@Test
void checkCommonPathWithPathParams() {
String[] paths = List.of("/users/{userId}/profile",
"/users/{userId}/problems",
"/users/{userEmail}",
"/users/{username}")
.toArray(new String[0]);
String commonPrefix = JavaHelidonCommonCodegen.commonPathPrefix(paths);
assertThat(commonPrefix).isEqualTo("/users");
}
@Test
void checkCommonPathWithMultipleCommonLevels() {
String[] paths = List.of("/users/a/x",
"/users/a/y",
"/users/a/z")
.toArray(new String[0]);
String commonPrefix = JavaHelidonCommonCodegen.commonPathPrefix(paths);
assertThat(commonPrefix).isEqualTo("/users/a");
}
@Test
void checkNoCommonSegments() {
String[] paths = List.of("/a/x",
"/b/y",
"/c")
.toArray(new String[0]);
String commonPrefix = JavaHelidonCommonCodegen.commonPathPrefix(paths);
assertThat(commonPrefix).isEqualTo("/");
}
@Test
void checkSinglePathCommonSegments() {
String commonPrefix = JavaHelidonCommonCodegen.commonPathPrefix(new String[0]);
assertThat(commonPrefix).isEqualTo("/");
}
@Test
void checkMixedWithPathParam() {
String[] paths = List.of("/store/order/{order_id}",
"/store/inventory",
"/store/order/{order_id}",
"/store/order")
.toArray(new String[0]);
String commonPrefix = JavaHelidonCommonCodegen.commonPathPrefix(paths);
assertThat(commonPrefix).isEqualTo("/store");
}
} }