Created
August 16, 2022 16:17
-
-
Save Geolykt/eb7ad7245cdc289501c0a3542df40ba0 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.geolykt.mavenresolver.version; | |
@Deprecated // This class was created by mistake | |
public class SemverVersion { | |
public final long major; | |
public final int minor; | |
public final int patch; | |
public final String[] preReleaseIdentifiers; | |
public final String[] metadata; | |
public SemverVersion(long major, int minor, int patch) { | |
this.major = major; | |
this.minor = minor; | |
this.patch = patch; | |
this.preReleaseIdentifiers = new String[0]; | |
this.metadata = new String[0]; | |
} | |
public SemverVersion(long major, int minor, int patch, String[] prereleaseIdentifiers, String[] metadataIdentifiers) { | |
this.major = major; | |
this.minor = minor; | |
this.patch = patch; | |
this.preReleaseIdentifiers = prereleaseIdentifiers; | |
this.metadata = metadataIdentifiers; | |
} | |
/** | |
* Parses a version string as a {@link SemverVersion} object. Will not necessarily fail even if the string | |
* does not represent a semver release. Will however not be able to parse outlandish version strings. | |
* Calling {@link SemverVersion#toString()} on the output may as such not return the inserted string. | |
* | |
* @param string The version string | |
* @return The parsed object | |
*/ | |
public static SemverVersion parseLeniently(String string) { | |
try { | |
return parse200(string); | |
} catch (NotASemverVersionException ignore) { | |
} | |
if (string.charAt(0) == 'v') { | |
// It is common that people prefix releases with "v". While it isn't really a valid semver string we will consider it as one | |
try { | |
return parse200(string.substring(1)); | |
} catch (NotASemverVersionException ignore) { | |
} | |
} | |
int hyphenIndex = string.indexOf('-'); | |
if (hyphenIndex == -1) { | |
// I have seen some artifacts excluding the hyphen - we can reconstruct it rather easily however | |
String[] dotParts = string.split("\\."); | |
if (dotParts.length == 3) { | |
try { | |
int major = Integer.parseInt(dotParts[0]); | |
int minor = Integer.parseInt(dotParts[1]); | |
int lastDigitChar = -1; | |
for (int i = 0; i < dotParts[2].length(); i++) { | |
if (Character.isDigit(dotParts[2].codePointAt(i))) { | |
lastDigitChar = i; | |
} else { | |
break; | |
} | |
} | |
int patch = Integer.parseInt(dotParts[2], 0, lastDigitChar + 1, 10); | |
String prerelease = dotParts[2].substring(lastDigitChar + 1); | |
int plusCharIndex = prerelease.indexOf('+'); | |
if (plusCharIndex != -1) { | |
prerelease = prerelease.substring(0, plusCharIndex); | |
} | |
return new SemverVersion(major, minor, patch, prerelease.split("\\."), new String[0]); | |
} catch (NumberFormatException ignored) { | |
} | |
} else if (dotParts.length == 2) { | |
// Maven versions could technically just consist of "MAJOR.MINOR" | |
try { | |
int major = Integer.parseInt(dotParts[0]); | |
int minor = Integer.parseInt(dotParts[1]); | |
return new SemverVersion(major, minor, 0); | |
} catch (NumberFormatException ignored) { | |
} | |
} else if (dotParts.length == 4) { | |
// Sometimes MAJOR.MINOR.PATCH.MICROPATCH is used | |
try { | |
int major = Integer.parseInt(dotParts[0]); | |
int minor = Integer.parseInt(dotParts[1]); | |
int patch = Integer.parseInt(dotParts[2]); | |
int plusIndex = dotParts[3].indexOf('+'); | |
if (plusIndex == -1) { | |
return new SemverVersion(major, minor, patch, new String[] {dotParts[3]}, new String[0]); | |
} else { | |
return new SemverVersion(major, minor, patch, new String[] {dotParts[3].substring(0, plusIndex)}, dotParts[3].substring(plusIndex + 1).split("\\.")); | |
} | |
} catch (NumberFormatException ignored) { | |
} | |
} else if (dotParts.length == 1) { | |
// DATE is used quite often too. | |
try { | |
// For the sake of simplicity, we assume that DATE is a long and corresponds to the "major" field of SemVer, even though the two standards are very incompatible with each other | |
return new SemverVersion(Long.parseLong(dotParts[0]), 0, 0); | |
} catch (NumberFormatException ignored) { | |
} | |
} | |
} else { | |
String core = string.substring(0, hyphenIndex); | |
String[] dotParts = core.split("\\."); | |
if (dotParts.length == 2) { | |
// Maven versions could consist of "MAJOR.MINOR-PRERELEASE" | |
try { | |
int major = Integer.parseInt(dotParts[0]); | |
int minor = Integer.parseInt(dotParts[1]); | |
return new SemverVersion(major, minor, 0, string.substring(hyphenIndex + 1).split("\\."), new String[0]); | |
} catch (NumberFormatException ignored) { | |
} | |
} | |
} | |
throw new IllegalArgumentException("Cannot parse version string \"" + string + "\"."); | |
} | |
/** | |
* Parses a SemVer 2.0.0 (https://semver.org/spec/v2.0.0.html) version string as a {@link SemverVersion} object. | |
* | |
* @param string The semver string | |
* @return The parsed object | |
*/ | |
public static SemverVersion parse200(String string) throws NotASemverVersionException { | |
int prereleaseHyphenIndex = string.indexOf('-'); | |
int metadataSeperator = string.indexOf('+'); | |
if (metadataSeperator != -1 && prereleaseHyphenIndex != -1 && metadataSeperator < prereleaseHyphenIndex) { | |
throw new NotASemverVersionException("Does not follow the MAJOR.MINOR.PATCH-PRERELEASE+METADATA scheme"); | |
} | |
int majorSeperator = string.indexOf('.'); | |
if (majorSeperator == -1) { | |
throw new NotASemverVersionException("Does not follow the X.Y.Z scheme (\"X.\" not found)"); | |
} | |
int minorSeperator = string.indexOf('.', majorSeperator + 1); | |
if (minorSeperator == -1) { | |
throw new NotASemverVersionException("Does not follow the X.Y.Z scheme (\"Y.\" not found)"); | |
} | |
int patchSeperator; | |
if (prereleaseHyphenIndex != -1) { | |
if (minorSeperator > prereleaseHyphenIndex) { | |
throw new NotASemverVersionException("Does not follow the MAJOR.MINOR.PATCH-PRERELEASE scheme (hypen is far too early)"); | |
} else { | |
patchSeperator = prereleaseHyphenIndex; | |
} | |
} else if (metadataSeperator != -1) { | |
patchSeperator = metadataSeperator; | |
} else { | |
patchSeperator = string.length(); | |
} | |
int major; | |
int minor; | |
int patch; | |
try { | |
major = Integer.parseInt(string, 0, majorSeperator, 10); | |
minor = Integer.parseInt(string, majorSeperator + 1, minorSeperator, 10); | |
patch = Integer.parseInt(string, minorSeperator + 1, patchSeperator, 10); | |
} catch (NumberFormatException nfe) { | |
throw new NotASemverVersionException("Does not follow the X.Y.Z scheme (Either X, Y or Z not an integer)", nfe); | |
} | |
if (patchSeperator == string.length()) { | |
return new SemverVersion(major, minor, patch); | |
} | |
String metadata; | |
String prerelease; | |
if (prereleaseHyphenIndex == -1) { | |
metadata = string.substring(metadataSeperator + 1); | |
prerelease = null; | |
} else if (metadataSeperator == -1) { | |
metadata = null; | |
prerelease = string.substring(prereleaseHyphenIndex + 1); | |
} else { | |
metadata = string.substring(metadataSeperator + 1); | |
prerelease = string.substring(prereleaseHyphenIndex + 1, metadataSeperator); | |
} | |
String[] metadataParts; | |
String[] prereleaseParts; | |
if (metadata == null) { | |
metadataParts = new String[0]; | |
} else { | |
metadataParts = metadata.split("\\."); | |
} | |
if (prerelease == null) { | |
prereleaseParts = new String[0]; | |
} else { | |
prereleaseParts = prerelease.split("\\."); | |
} | |
return new SemverVersion(major, minor, patch, prereleaseParts, metadataParts); | |
} | |
@Override | |
public String toString() { | |
StringBuilder builder = new StringBuilder(); | |
builder.append(major); | |
builder.append('.'); | |
builder.append(minor); | |
builder.append('.'); | |
builder.append(patch); | |
if (preReleaseIdentifiers.length != 0) { | |
builder.append('-'); | |
for (String id : preReleaseIdentifiers) { | |
builder.append(id); | |
builder.append('.'); | |
} | |
builder.setLength(builder.length() - 1); // Remove extraneous dot | |
} | |
if (metadata.length != 0) { | |
builder.append('+'); | |
for (String id : metadata) { | |
builder.append(id); | |
builder.append('.'); | |
} | |
builder.setLength(builder.length() - 1); // Remove extraneous dot | |
} | |
return builder.toString(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment