Skip to content

Commit

Permalink
Embed internal VersionNumber in GradleRuntimeCompatibility
Browse files Browse the repository at this point in the history
Signed-off-by: Daniel Lacasse <[email protected]>
  • Loading branch information
lacasseio committed Jul 17, 2024
1 parent e38b99a commit c75360f
Show file tree
Hide file tree
Showing 2 changed files with 271 additions and 289 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
import org.gradle.api.logging.Logger;
import org.gradle.api.logging.Logging;

import javax.annotation.Nullable;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;

/**
Expand Down Expand Up @@ -396,4 +399,272 @@ private static String lastMinorReleaseOf(VersionNumber gradleVersion) {
throw new IllegalArgumentException(String.format("Unknown Gradle version, please open an issue on https://github.com/gradle-plugins/toolbox.", gradleVersion));
}
}

/**
* Represents, parses, and compares version numbers. Supports a couple of different schemes: <ul> <li>MAJOR.MINOR.MICRO-QUALIFIER (the default).</li> <li>MAJOR.MINOR.MICRO.PATCH-QUALIFIER.</li> </ul>
*
* <p>The {@link #parse} method handles missing parts and allows "." to be used instead of "-", and "_" to be used instead of "." for the patch number.
*
* <p>This class considers missing parts to be 0, so that "1.0" == "1.0.0" == "1.0.0_0".</p>
*
* <p>Note that this class considers "1.2.3-something" less than "1.2.3". Qualifiers are compared lexicographically ("1.2.3-alpha" < "1.2.3-beta") and case-insensitive ("1.2.3-alpha" <
* "1.2.3.RELEASE").
*
* <p>To check if a version number is at least "1.2.3", disregarding a potential qualifier like "beta", use {@code version.getBaseVersion().compareTo(VersionNumber.parse("1.2.3")) >= 0}.
*/
static final class VersionNumber implements Comparable<VersionNumber> {
private static final DefaultScheme DEFAULT_SCHEME = new DefaultScheme();
private static final SchemeWithPatchVersion PATCH_SCHEME = new SchemeWithPatchVersion();
public static final VersionNumber UNKNOWN = version(0);

private final int major;
private final int minor;
private final int micro;
private final int patch;
private final String qualifier;
private final AbstractScheme scheme;

public VersionNumber(int major, int minor, int micro, @Nullable String qualifier) {
this(major, minor, micro, 0, qualifier, DEFAULT_SCHEME);
}

public VersionNumber(int major, int minor, int micro, int patch, @Nullable String qualifier) {
this(major, minor, micro, patch, qualifier, PATCH_SCHEME);
}

private VersionNumber(int major, int minor, int micro, int patch, @Nullable String qualifier, AbstractScheme scheme) {
this.major = major;
this.minor = minor;
this.micro = micro;
this.patch = patch;
this.qualifier = qualifier;
this.scheme = scheme;
}

public int getMajor() {
return major;
}

public int getMinor() {
return minor;
}

public int getMicro() {
return micro;
}

public int getPatch() {
return patch;
}

public String getQualifier() {
return qualifier;
}

public VersionNumber getBaseVersion() {
return new VersionNumber(major, minor, micro, patch, null, scheme);
}

@Override
public int compareTo(VersionNumber other) {
if (major != other.major) {
return major - other.major;
}
if (minor != other.minor) {
return minor - other.minor;
}
if (micro != other.micro) {
return micro - other.micro;
}
if (patch != other.patch) {
return patch - other.patch;
}
return Comparator.nullsLast(Comparator.<String>naturalOrder()).compare(toLowerCase(qualifier), toLowerCase(other.qualifier));
}

public boolean equals(Object other) {
return other instanceof VersionNumber && compareTo((VersionNumber) other) == 0;
}

public int hashCode() {
int result = major;
result = 31 * result + minor;
result = 31 * result + micro;
result = 31 * result + patch;
result = 31 * result + Objects.hashCode(qualifier);
return result;
}

public String toString() {
return scheme.format(this);
}

public static VersionNumber version(int major) {
return new VersionNumber(major, 0, 0, 0, null, DEFAULT_SCHEME);
}

/**
* Returns the default MAJOR.MINOR.MICRO-QUALIFIER scheme.
*/
public static Scheme scheme() {
return DEFAULT_SCHEME;
}

/**
* Returns the MAJOR.MINOR.MICRO.PATCH-QUALIFIER scheme.
*/
public static Scheme withPatchNumber() {
return PATCH_SCHEME;
}

public static VersionNumber parse(String versionString) {
return DEFAULT_SCHEME.parse(versionString);
}

private String toLowerCase(@Nullable String string) {
return string == null ? null : string.toLowerCase();
}

public interface Scheme {
public VersionNumber parse(String value);

public String format(VersionNumber versionNumber);
}

private abstract static class AbstractScheme implements Scheme {
final int depth;

protected AbstractScheme(int depth) {
this.depth = depth;
}

@Override
public VersionNumber parse(String versionString) {
if (versionString == null || versionString.length() == 0) {
return UNKNOWN;
}
Scanner scanner = new Scanner(versionString);

int major = 0;
int minor = 0;
int micro = 0;
int patch = 0;

if (!scanner.hasDigit()) {
return UNKNOWN;
}
major = scanner.scanDigit();
if (scanner.isSeparatorAndDigit('.')) {
scanner.skipSeparator();
minor = scanner.scanDigit();
if (scanner.isSeparatorAndDigit('.')) {
scanner.skipSeparator();
micro = scanner.scanDigit();
if (depth > 3 && scanner.isSeparatorAndDigit('.', '_')) {
scanner.skipSeparator();
patch = scanner.scanDigit();
}
}
}

if (scanner.isEnd()) {
return new VersionNumber(major, minor, micro, patch, null, this);
}

if (scanner.isQualifier()) {
scanner.skipSeparator();
return new VersionNumber(major, minor, micro, patch, scanner.remainder(), this);
}

return UNKNOWN;
}

private static class Scanner {
int pos;
final String str;

private Scanner(String string) {
this.str = string;
}

boolean hasDigit() {
return pos < str.length() && Character.isDigit(str.charAt(pos));
}

boolean isSeparatorAndDigit(char... separators) {
return pos < str.length() - 1 && oneOf(separators) && Character.isDigit(str.charAt(pos + 1));
}

private boolean oneOf(char... separators) {
char current = str.charAt(pos);
for (int i = 0; i < separators.length; i++) {
char separator = separators[i];
if (current == separator) {
return true;
}
}
return false;
}

boolean isQualifier() {
return pos < str.length() - 1 && oneOf('.', '-');
}

int scanDigit() {
int start = pos;
while (hasDigit()) {
pos++;
}
return Integer.parseInt(str.substring(start, pos));
}

public boolean isEnd() {
return pos == str.length();
}

private boolean skip(char ch) {
if (pos < str.length() && str.charAt(pos) == ch) {
pos++;
return true;
}
return false;
}

public void skipSeparator() {
pos++;
}

public String remainder() {
return pos == str.length() ? null : str.substring(pos);
}
}
}

private static class DefaultScheme extends AbstractScheme {
private static final String VERSION_TEMPLATE = "%d.%d.%d%s";

public DefaultScheme() {
super(3);
}

@Override
public String format(VersionNumber versionNumber) {
return String.format(VERSION_TEMPLATE, versionNumber.major, versionNumber.minor, versionNumber.micro, versionNumber.qualifier == null ? "" : "-" + versionNumber.qualifier);
}
}

private static class SchemeWithPatchVersion extends AbstractScheme {
private static final String VERSION_TEMPLATE = "%d.%d.%d.%d%s";

private SchemeWithPatchVersion() {
super(4);
}

@Override
public String format(VersionNumber versionNumber) {
return String.format(VERSION_TEMPLATE, versionNumber.major, versionNumber.minor, versionNumber.micro, versionNumber.patch, versionNumber.qualifier == null ? "" : "-" + versionNumber.qualifier);
}
}

}
}
Loading

0 comments on commit c75360f

Please sign in to comment.