8379334: jpackage: fix bug in DottedVersion.greedy() function

Reviewed-by: almatvee
This commit is contained in:
Alexey Semenyuk 2026-03-06 01:08:49 +00:00
parent f266079d26
commit c2f6bd87c6
2 changed files with 172 additions and 22 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -37,12 +37,11 @@ import java.util.stream.Stream;
public final class DottedVersion {
private DottedVersion(String version, boolean greedy) {
this.value = version;
if (version.isEmpty()) {
if (greedy) {
throw new IllegalArgumentException(I18N.getString("error.version-string-empty"));
} else {
this.components = new BigInteger[0];
this.components = new Component[0];
this.suffix = "";
}
} else {
@ -53,12 +52,12 @@ public final class DottedVersion {
if (!greedy) {
return null;
} else {
ds.throwException();
throw ds.createException();
}
}
try {
return new BigInteger(digits);
return new Component(digits);
} catch (NumberFormatException ex) {
if (!greedy) {
return null;
@ -68,17 +67,25 @@ public final class DottedVersion {
digits));
}
}
}).takeWhile(Objects::nonNull).toArray(BigInteger[]::new);
}).takeWhile(Objects::nonNull).toArray(Component[]::new);
suffix = ds.getUnprocessedString();
if (!suffix.isEmpty() && greedy) {
ds.throwException();
throw ds.createException();
}
}
}
private DottedVersion(Component[] components, String suffix) {
this.components = components;
this.suffix = suffix;
}
private static class DigitsSupplier {
DigitsSupplier(String input) {
if (input.isEmpty()) {
throw new IllegalArgumentException();
}
this.input = input;
}
@ -95,7 +102,7 @@ public final class DottedVersion {
} else {
var curStopAtDot = (chr == '.');
if (!curStopAtDot) {
if (lastDotPos >= 0) {
if (sb.isEmpty() && lastDotPos >= 0) {
cursor = lastDotPos;
} else {
cursor--;
@ -112,11 +119,7 @@ public final class DottedVersion {
}
if (sb.isEmpty()) {
if (lastDotPos >= 0) {
cursor = lastDotPos;
} else {
cursor--;
}
cursor = lastDotPos;
}
stoped = true;
@ -127,7 +130,7 @@ public final class DottedVersion {
return input.substring(cursor);
}
void throwException() {
IllegalArgumentException createException() {
final String tail;
if (lastDotPos >= 0) {
tail = input.substring(lastDotPos + 1);
@ -143,7 +146,7 @@ public final class DottedVersion {
errMessage = MessageFormat.format(I18N.getString(
"error.version-string-invalid-component"), input, tail);
}
throw new IllegalArgumentException(errMessage);
return new IllegalArgumentException(errMessage);
}
private int cursor;
@ -211,9 +214,31 @@ public final class DottedVersion {
return Arrays.deepEquals(this.components, other.components);
}
public DottedVersion trim(int limit) {
if (limit < 0) {
throw new IllegalArgumentException();
} else if (limit >= components.length) {
return this;
} else {
return new DottedVersion(Arrays.copyOf(components, limit), suffix);
}
}
public DottedVersion pad(int limit) {
if (limit < 0) {
throw new IllegalArgumentException();
} else if (limit <= components.length) {
return this;
} else {
var newComponents = Arrays.copyOf(components, limit);
Arrays.fill(newComponents, components.length, newComponents.length, Component.ZERO);
return new DottedVersion(newComponents, suffix);
}
}
@Override
public String toString() {
return value;
return Stream.of(components).map(Component::toString).collect(Collectors.joining(".")) + suffix;
}
public String getUnprocessedSuffix() {
@ -221,14 +246,35 @@ public final class DottedVersion {
}
public String toComponentsString() {
return Stream.of(components).map(BigInteger::toString).collect(Collectors.joining("."));
return Stream.of(components).map(Component::parsedValue).map(BigInteger::toString).collect(Collectors.joining("."));
}
public int getComponentsCount() {
return components.length;
}
public BigInteger[] getComponents() {
return components;
return Stream.of(components).map(Component::parsedValue).toArray(BigInteger[]::new);
}
private final BigInteger[] components;
private final String value;
private record Component(BigInteger parsedValue, String strValue) {
Component {
Objects.requireNonNull(parsedValue);
Objects.requireNonNull(strValue);
}
Component(String strValue) {
this(new BigInteger(strValue), strValue);
}
@Override
public String toString() {
return strValue;
}
static final Component ZERO = new Component(BigInteger.ZERO, "0");
}
private final Component[] components;
private final String suffix;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -32,8 +32,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertNotSame;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.EnumSource;
import org.junit.jupiter.params.provider.MethodSource;
@ -108,12 +111,113 @@ public class DottedVersionTest {
TestConfig.lazy("+1", "+1", 0, ""),
TestConfig.lazy("-1", "-1", 0, ""),
TestConfig.lazy("-0", "-0", 0, ""),
TestConfig.lazy("+0", "+0", 0, "")
TestConfig.lazy("+0", "+0", 0, ""),
TestConfig.lazy("+0", "+0", 0, ""),
TestConfig.lazy("1.2.3+ea", "+ea", 3, "1.2.3"),
TestConfig.lazy(".7", ".7", 0, ""),
TestConfig.lazy(".+7", ".+7", 0, "")
));
return data;
}
@ParameterizedTest
@MethodSource
public void testTrim(DottedVersion ver, String expectedStr, int limit) {
var expected = DottedVersion.lazy(expectedStr);
var actual = ver.trim(limit);
assertEquals(expected, actual);
if (limit >= ver.getComponents().length) {
assertSame(ver, actual);
} else {
assertNotSame(ver, actual);
}
assertEquals(expectedStr, actual.toString());
}
@ParameterizedTest
@MethodSource
public void testTrimNegative(DottedVersion ver, int limit) {
assertThrowsExactly(IllegalArgumentException.class, () -> {
ver.trim(limit);
});
}
private static Stream<Arguments> testTrim() {
var testCases = new ArrayList<Arguments>();
for (var suffix : List.of("", ".foo", "-ea", "+345")) {
testCases.addAll(List.of(
Arguments.of("1.02.3" + suffix, "" + suffix, 0),
Arguments.of("1.02.3" + suffix, "1" + suffix, 1),
Arguments.of("1.02.3" + suffix, "1.02" + suffix, 2),
Arguments.of("1.02.3" + suffix, "1.02.3" + suffix, 3),
Arguments.of("1.02.3" + suffix, "1.02.3" + suffix, 4)
));
}
return testCases.stream().map(DottedVersionTest::mapFirstStringToDottedVersion);
}
private static Stream<Arguments> testTrimNegative() {
return Stream.of(
Arguments.of("10.5.foo", -1)
).map(DottedVersionTest::mapFirstStringToDottedVersion);
}
@ParameterizedTest
@MethodSource
public void testPad(DottedVersion ver, String expectedStr, int limit) {
var expected = DottedVersion.lazy(expectedStr);
var actual = ver.pad(limit);
assertEquals(expected, actual);
if (limit <= ver.getComponents().length) {
assertSame(ver, actual);
} else {
assertNotSame(ver, actual);
}
assertEquals(expectedStr, actual.toString());
}
@ParameterizedTest
@MethodSource
public void testPadNegative(DottedVersion ver, int limit) {
assertThrowsExactly(IllegalArgumentException.class, () -> {
ver.pad(limit);
});
}
private static Stream<Arguments> testPad() {
var testCases = new ArrayList<Arguments>();
for (var suffix : List.of("", ".foo", "-ea", "+345")) {
testCases.addAll(List.of(
Arguments.of("" + suffix, "" + suffix, 0),
Arguments.of("1.02.3" + suffix, "1.02.3" + suffix, 0),
Arguments.of("" + suffix, "0" + suffix, 1),
Arguments.of("1" + suffix, "1" + suffix, 1),
Arguments.of("1" + suffix, "1.0" + suffix, 2),
Arguments.of("1.02.3" + suffix, "1.02.3.0.0" + suffix, 5)
));
}
return testCases.stream().map(DottedVersionTest::mapFirstStringToDottedVersion);
}
private static Stream<Arguments> testPadNegative() {
return Stream.of(
Arguments.of("10.5.foo", -1)
).map(DottedVersionTest::mapFirstStringToDottedVersion);
}
private static Arguments mapFirstStringToDottedVersion(Arguments v) {
var objs = v.get();
objs[0] = DottedVersion.lazy((String)objs[0]);
return Arguments.of(objs);
}
record InvalidVersionTestSpec(String version, String invalidComponent) {
public InvalidVersionTestSpec {
Objects.requireNonNull(version);