Fix version formatting/make it depend on platform versions

This commit is contained in:
Nassim Jahnke 2023-02-14 20:06:53 +01:00
parent 4aaa87a096
commit 0d8341fe4f
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
7 changed files with 83 additions and 154 deletions

View File

@ -28,7 +28,6 @@ import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.info.GitProperties;
@ -95,7 +94,7 @@ public class BackendDataController {
final ArrayNode arrayNode = this.objectMapper.createArrayNode();
for (final Platform platform : Platform.getValues()) {
final ObjectNode objectNode = this.objectMapper.valueToTree(platform);
objectNode.set("possibleVersions", this.objectMapper.valueToTree(this.platformService.getVersionsForPlatform(platform)));
objectNode.set("possibleVersions", this.objectMapper.valueToTree(this.platformService.getDescendingVersionsForPlatform(platform)));
arrayNode.add(objectNode);
}
return ResponseEntity.ok(arrayNode);

View File

@ -39,7 +39,7 @@ public class PlatformService extends HangarComponent {
}
@Cacheable(CacheConfig.PLATFORMS)
public List<PlatformVersion> getVersionsForPlatform(final Platform platform) {
public List<PlatformVersion> getDescendingVersionsForPlatform(final Platform platform) {
final List<String> versions = this.platformVersionDAO.getVersionsForPlatform(platform);
final Map<String, List<String>> platformVersions = new LinkedHashMap<>();
for (final String version : versions) {

View File

@ -22,7 +22,6 @@ import io.papermc.hangar.model.internal.logs.contexts.VersionContext;
import io.papermc.hangar.model.internal.projects.HangarProject;
import io.papermc.hangar.service.internal.PlatformService;
import io.papermc.hangar.service.internal.projects.ProjectService;
import io.papermc.hangar.util.StringUtils;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap;
@ -31,6 +30,7 @@ import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.stream.Collectors;
import io.papermc.hangar.util.VersionFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
@ -65,7 +65,8 @@ public class VersionDependencyService extends HangarComponent {
final Map<Platform, SortedSet<String>> platformDependencies = this.versionsApiDAO.getPlatformDependencies(versionId);
final Map<Platform, String> platformDependenciesFormatted = new EnumMap<>(Platform.class);
for (final Map.Entry<Platform, SortedSet<String>> entry : platformDependencies.entrySet()) {
platformDependenciesFormatted.put(entry.getKey(), StringUtils.formatVersionNumbers(new ArrayList<>(entry.getValue())));
final String formattedVersionRange = VersionFormatter.formatVersionRange(new ArrayList<>(entry.getValue()), this.platformService.getFullVersionsForPlatform(entry.getKey()));
platformDependenciesFormatted.put(entry.getKey(), formattedVersionRange);
}
// TODO into one query

View File

@ -1,12 +1,5 @@
package io.papermc.hangar.util;
import jakarta.validation.constraints.NotNull;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public final class StringUtils {
private StringUtils() {
@ -31,109 +24,4 @@ public final class StringUtils {
public static String compact(final String str) {
return str.trim().replaceAll(" +", " ");
}
/**
* Returns a version number split into an ordered list of numbers
*
* @param str version string to check (e.g. 2.3.1) MUST BE ALL NUMBERS
* @return the list of integers in ltr order
*/
public static List<Integer> splitVersionNumber(final String str) {
return Arrays.stream(str.split("\\.")).map(Integer::parseInt).collect(Collectors.toList());
}
private static final Pattern LAST_WHOLE_VERSION = Pattern.compile("((?<=,\\s)|^)[0-9.]{2,}(?=-[0-9.]+$)");
private static final Pattern PREV_HAS_HYPHEN = Pattern.compile("(?<=\\d-)[0-9.]+$");
private static final Pattern PREV_HAS_COMMA_OR_FIRST = Pattern.compile("((?<=,\\s)|^)[0-9.]+$");
/**
* Format a list of version numbers (will do sorting)
*
* @param versionNumbers version numbers
* @return formatted string
*/
public static String formatVersionNumbers(final List<String> versionNumbers) {
versionNumbers.sort((version1, version2) -> {
int vnum1 = 0, vnum2 = 0;
for (int i = 0, j = 0; (i < version1.length() || j < version2.length()); ) {
while (i < version1.length() && version1.charAt(i) != '.') {
vnum1 = vnum1 * 10 + (version1.charAt(i) - '0');
i++;
}
while (j < version2.length() && version2.charAt(j) != '.') {
vnum2 = vnum2 * 10 + (version2.charAt(j) - '0');
j++;
}
if (vnum1 > vnum2) {
return 1;
}
if (vnum2 > vnum1) {
return -1;
}
vnum1 = vnum2 = 0;
i++;
j++;
}
return 0;
});
return versionNumbers.stream().reduce("", (verString, version) -> {
if (verString.isBlank()) {
return version;
}
final List<Integer> versionArr = splitVersionNumber(version);
final Matcher hyphen = PREV_HAS_HYPHEN.matcher(verString);
final Matcher comma = PREV_HAS_COMMA_OR_FIRST.matcher(verString);
if (hyphen.find()) {
final String[] group = hyphen.group().split("\\.");
final int prevVersion = Integer.parseInt(group[group.length - 1]);
final Matcher prevVersionMatcher = LAST_WHOLE_VERSION.matcher(verString);
if (!prevVersionMatcher.find()) {
throw new IllegalArgumentException("Bad version string");
}
final List<Integer> previousWholeVersion = splitVersionNumber(prevVersionMatcher.group());
if (previousWholeVersion.size() == versionArr.size()) {
if (versionArr.get(versionArr.size() - 1) - 1 == prevVersion) {
return verString.replaceFirst("-[0-9.]+$", "-" + version);
} else {
return verString + ", " + version;
}
} else {
return verString + ", " + version;
}
} else if (comma.find()) {
final List<Integer> prevVersion = splitVersionNumber(comma.group());
if (prevVersion.size() == versionArr.size()) {
if (versionArr.get(versionArr.size() - 1) - 1 == prevVersion.get(prevVersion.size() - 1)) {
return verString + "-" + version;
} else {
return verString + ", " + version;
}
} else {
return verString + ", " + version;
}
} else {
throw new IllegalArgumentException("bad formatting: " + version);
}
});
}
public static String parsePythonNullable(final String input) {
return input.equals("None") ? null : input;
}
public static boolean isAnyEqualIgnoreCase(@NotNull final String lhs, final String @NotNull ... rhs) {
for (final String string : rhs) {
if (lhs.equalsIgnoreCase(string)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,66 @@
package io.papermc.hangar.util;
import java.util.List;
public final class VersionFormatter {
private VersionFormatter() {
}
/**
* Formats a sorted version range string.
*
* @param versions list of versions to stringify
* @param allVersions sorted list of all valid versions
* @return formatted version range string
* @throws IllegalArgumentException if versions contains a string not included in allVersions
*/
public static String formatVersionRange(final List<String> versions, final List<String> allVersions) {
if (versions.isEmpty()) {
return "";
} else if (versions.size() == 1) {
return versions.get(0);
}
versions.sort((version1, version2) -> {
final int index1 = allVersions.indexOf(version1);
final int index2 = allVersions.indexOf(version2);
if (index1 == -1) {
throw new IllegalArgumentException("Version " + version1 + " not included in allVersions");
} else if (index2 == -1) {
throw new IllegalArgumentException("Version " + version2 + " not included in allVersions");
}
return index1 - index2;
});
final StringBuilder builder = new StringBuilder();
String fromVersion = versions.get(0);
String lastVersion = fromVersion;
int lastVersionIndex = allVersions.indexOf(fromVersion);
for (int i = 1; i < versions.size(); i++) {
final String version = versions.get(i);
final int versionIndex = allVersions.indexOf(version);
if (versionIndex != lastVersionIndex + 1) {
// Append last version/range if a new range starts
if (!lastVersion.equals(fromVersion)) {
builder.append(fromVersion).append('-').append(lastVersion);
} else {
builder.append(fromVersion);
}
builder.append(", ");
fromVersion = version;
}
lastVersion = version;
lastVersionIndex = versionIndex;
}
// Append last version or range
builder.append(fromVersion);
if (!fromVersion.equals(lastVersion)) {
builder.append('-').append(lastVersion);
}
return builder.toString();
}
}

View File

@ -1,25 +1,24 @@
package io.papermc.hangar.util;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
class FormattedVersionsTest {
private final List<String> versions = List.of("1.11", "1.11.1", "1.11.2", "1.11.3", "1.12", "1.13", "1.14", "1.14.1", "1.15", "1.15.1", "1.18");
@Test
void testFormattedVersions() {
final List<String> list1 = List.of("1.1", "1.2", "1.3", "1.5", "1.7", "1.8");
this.testFormatVersionNumbers("1.11-1.11.2, 1.15-1.18", "1.11", "1.11.1", "1.11.2", "1.15", "1.15.1", "1.18");
this.testFormatVersionNumbers("1.11-1.11.2, 1.15, 1.18", "1.11", "1.11.1", "1.11.2", "1.15", "1.18");
this.testFormatVersionNumbers("1.11.1-1.11.2", "1.11.1", "1.11.2");
this.testFormatVersionNumbers("1.11, 1.11.2, 1.15.1", "1.11", "1.11.2", "1.15.1");
this.testFormatVersionNumbers("1.11, 1.12-1.13", "1.11", "1.12", "1.13");
}
Assertions.assertEquals("1.1-1.3, 1.5, 1.7-1.8", StringUtils.formatVersionNumbers(new ArrayList<>(list1)));
final List<String> list2 = List.of("1.20", "1.23", "1.25", "1.30", "1.31");
Assertions.assertEquals("1.20, 1.23, 1.25, 1.30-1.31", StringUtils.formatVersionNumbers(new ArrayList<>(list2)));
final List<String> list3 = List.of("1.1.0", "1.1.1", "1.2.0", "1.2.2", "1.3", "1.4");
Assertions.assertEquals("1.1.0-1.1.1, 1.2.0, 1.2.2, 1.3-1.4", StringUtils.formatVersionNumbers(new ArrayList<>(list3)));
final List<String> list4 = List.of("1.1", "1.2", "1.3", "1.4", "1.5", "1.7", "1.8", "1.9", "1.10", "1.12");
Assertions.assertEquals("1.1-1.5, 1.7-1.10, 1.12", StringUtils.formatVersionNumbers(new ArrayList<>(list4)));
private void testFormatVersionNumbers(final String expected, final String... versions) {
Assertions.assertEquals(expected, VersionFormatter.formatVersionRange(new ArrayList<>(List.of(versions)), this.versions));
}
}

View File

@ -1,24 +0,0 @@
package io.papermc.hangar.util;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
class StringUtilsTest {
@Test
void testFormatVersionNumbers() {
this.testFormatVersionNumbers("1.15-1.17", "1.15", "1.17", "1.16");
this.testFormatVersionNumbers("1.15-1.17", "1.15", "1.17", "1.16");
this.testFormatVersionNumbers("1.13, 1.15-1.17", "1.15", "1.17", "1.16", "1.13");
this.testFormatVersionNumbers("1.13", "1.13");
// this is a bit meh, but we have no reason to overthink this
this.testFormatVersionNumbers("1.13, 1.15.2, 1.16, 1.17.0-1.17.1", "1.15.2", "1.17.1", "1.17.0", "1.16", "1.13");
}
private void testFormatVersionNumbers(final String expected, final String... versions) {
assertEquals(expected, StringUtils.formatVersionNumbers(new ArrayList<>(List.of(versions))));
}
}