Skip to content

Instantly share code, notes, and snippets.

@alexanderankin
Last active November 19, 2023 10:36
Show Gist options
  • Select an option

  • Save alexanderankin/1d4c664a101ae43480cad514b35665e1 to your computer and use it in GitHub Desktop.

Select an option

Save alexanderankin/1d4c664a101ae43480cad514b35665e1 to your computer and use it in GitHub Desktop.
package org.npm4j.utils;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.npm4j.utils.SemVerUtils.Internal.Constants.ReleaseType;
import org.springframework.util.StringUtils;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class SemVerUtils {
public static SemVer parse(String version) {
return parse(version, Map.of());
}
public static SemVer parse(SemVer version) {
return parse(version, Map.of());
}
public static SemVer parse(Object version, Map<String, Object> options) {
return parse(version, options, false);
}
public static SemVer parse(Object version, Map<String, Object> options, boolean throwErrors) {
if (version instanceof SemVer semVer) return semVer;
try {
return SemVer.constructor(version, options);
} catch (Exception e) {
if (!throwErrors) return null;
throw e;
}
}
public static boolean valid(Object version) {
return valid(version, Map.of());
}
public static boolean valid(Object version, Map<String, Object> options) {
return null != parse(version, options);
}
public static String clean(String version, Map<String, Object> options) {
return Optional.ofNullable(parse(version.trim().replaceAll("^[=v]+", ""), options)).map(SemVer::getVersion).orElse(null);
}
public static String inc(
Object version,
ReleaseType release,
Map<String, Object> options,
String identifier,
String identifierBase
) {
return parse(version, options).inc(release, identifier, identifierBase).getVersion();
}
public static void diff() {
}
public static void major() {
}
public static void minor() {
}
public static void patch() {
}
public static void prerelease() {
}
public static void compare() {
}
public static void rcompare() {
}
public static void compareLoose() {
}
public static void compareBuild() {
}
public static void sort() {
}
public static void rsort() {
}
public static void gt() {
}
public static void lt() {
}
public static void eq() {
}
public static void neq() {
}
public static void gte() {
}
public static void lte() {
}
public static void cmp() {
}
public static void coerce() {
}
public static void Comparator() {
}
public static void Range() {
}
public static void satisfies() {
}
public static void toComparators() {
}
public static void maxSatisfying() {
}
public static void minSatisfying() {
}
public static void minVersion() {
}
public static void validRange() {
}
public static void outside() {
}
public static void gtr() {
}
public static void ltr() {
}
public static void intersects() {
}
public static void simplifyRange() {
}
public static void subset() {
}
@Slf4j
@Data
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Builder(toBuilder = true, access = AccessLevel.PRIVATE)
public static class SemVer implements Comparable<SemVer> {
private static final int MAX_LENGTH = 256;
final String version;
final String raw;
final private Map<String, Object> options;
final boolean loose;
final boolean includePrerelease;
final private long major, minor, patch;
final private List<String> prerelease;
final private List<String> build;
public SemVer(String version, Map<String, Object> options) {
// consider custom builder to avoid double build here
this(constructor(version, options).toBuilder());
}
private SemVer(SemVerBuilder builder) {
builder.version = format(builder);
this.version = builder.version;
this.raw = builder.raw;
this.options = builder.options;
this.loose = builder.loose;
this.includePrerelease = builder.includePrerelease;
this.major = builder.major;
this.minor = builder.minor;
this.patch = builder.patch;
this.prerelease = builder.prerelease;
this.build = builder.build;
}
// js constructor not like java one
public static SemVer constructor(Object version, Map<String, Object> options) {
if (version instanceof SemVer semVer) {
if (semVer.loose == getBoolean(options, "loose") &&
semVer.includePrerelease == getBoolean(options, "includePrerelease")) {
return semVer;
} else {
version = semVer.version;
}
}
if (!(version instanceof String stringVersion)) {
throw new IllegalArgumentException("Invalid version. Must be a string. Got type \"" + version.getClass().getName() + "\".");
}
if (stringVersion.length() > MAX_LENGTH)
throw new IllegalArgumentException("version is longer than " + MAX_LENGTH + " characters");
log.debug("{}, {}", stringVersion, options);
// SemVer semVer = new SemVer();
// var options = options;
var loose = getBoolean(options, "loose");
// this isn't actually relevant for versions, but keep it so that we
// don't run into trouble passing this.options around.
var includePrerelease = getBoolean(options, "includePrerelease");
Pattern toMatch = loose ? Internal.Regexes.LOOSE.regex : Internal.Regexes.FULL.regex;
Matcher matcher = toMatch.matcher(stringVersion.trim());
if (!matcher.matches()) {
if (log.isDebugEnabled()) {
Matcher m = Internal.Regexes.FULLPLAIN.regex.matcher(stringVersion.trim());
if (m.find()) {
System.out.println("found: " + m.group());
} else {
System.out.println("not even found");
}
}
throw new IllegalArgumentException("Invalid Version: " + stringVersion);
}
var major = Long.parseLong(matcher.group(1));
var minor = Long.parseLong(matcher.group(2));
var patch = Long.parseLong(matcher.group(3));
if (major > Internal.Constants.MAX_SAFE_INTEGER || major < 0) {
throw new IllegalArgumentException("Invalid major version");
}
if (minor > Internal.Constants.MAX_SAFE_INTEGER || minor < 0) {
throw new IllegalArgumentException("Invalid minor version");
}
if (patch > Internal.Constants.MAX_SAFE_INTEGER || patch < 0) {
throw new IllegalArgumentException("Invalid patch version");
}
// numberify any prerelease numeric ids
List<String> prerelease;
if (matcher.groupCount() < 4 || null == matcher.group(4)) {
prerelease = List.of();
} else {
prerelease = Arrays.asList(matcher.group(4).split("\\."));
// .map((id) => {
// if (/^[0-9]+$/.test(id)) {
// const num = +id
// if (num >= 0 && num < MAX_SAFE_INTEGER) {
// return num
// }
// }
// return id
// })
}
List<String> build = (matcher.groupCount() < 5 || null == matcher.group(5))
? List.of()
: Arrays.asList(matcher.group(5).split("\\."));
var builder = SemVer.builder()
.raw(stringVersion)
.options(options)
.loose(loose)
.includePrerelease(includePrerelease)
.major(major)
.minor(minor)
.patch(patch)
.prerelease(prerelease)
.build(build);
builder.version(format(builder));
return builder.build();
}
private static boolean getBoolean(Map<String, Object> options, String optionName) {
Object aFalse = options.getOrDefault(optionName, false);
if (aFalse instanceof Boolean booleanValue) return booleanValue;
if (aFalse instanceof String stringValue) return Boolean.parseBoolean(stringValue);
return false;
}
private static String format(SemVerBuilder version) {
StringBuilder stringBuilder = new StringBuilder()
.append(version.major).append(".")
.append(version.minor).append(".")
.append(version.patch);
if (version.prerelease != null && !version.prerelease.isEmpty()) {
stringBuilder.append("-");
stringBuilder.append(String.join(".", version.prerelease));
}
return stringBuilder.toString();
}
@Override
public String toString() {
return version;
}
// backwards compatibility
public int compare(Object other) {
if (other instanceof SemVer otherSemver) {
return compareTo(otherSemver);
}
return compareTo(constructor(other, this.options));
}
/**
* @see SemVerUtils#compareIdentifiers
*/
@Override
public int compareTo(SemVer o) {
if (o.version.equals(this.version))
return 0;
int result;
if (0 != (result = compareMain(o)))
return result;
if (0 != (result = comparePre(o)))
return result;
return 0;
}
public int compareMain(String o) {
return compareMain(constructor(o, options));
}
public int compareMain(SemVer o) {
int result;
if (0 != (result = Long.compare(this.major, o.major)))
return result;
if (0 != (result = Long.compare(this.minor, o.minor)))
return result;
if (0 != (result = Long.compare(this.patch, o.patch)))
return result;
return 0;
}
public int comparePre(String comparedTo) {
return comparePre(constructor(comparedTo, options));
}
public int comparePre(SemVer o) {
// NOT having a prerelease is > having one
if (!this.prerelease.isEmpty() && o.prerelease.isEmpty()) {
return -1;
} else if (this.prerelease.isEmpty() && !o.prerelease.isEmpty()) {
return 1;
} else if (this.prerelease.isEmpty() /* && o.prerelease.isEmpty() */) {
return 0;
}
return compareIdentifierList(prerelease, o.prerelease);
}
private int compareIdentifierList(List<String> aList, List<String> bList) {
for (int i = 0; true; i++) {
var a = (aList.size() == i) ? null : aList.get(i);
var b = (bList.size() == i) ? null : bList.get(i);
// var a = prerelease.get(i);
// var b = o.prerelease.get(i);
// log.debug("prerelease compare", i, a, b);
if (a == null && b == null) {
return 0;
} else if (b == null) {
return 1;
} else if (a == null) {
return -1;
} else if (!a.equals(b)) {
return compareIdentifiers(a, b);
}
}
}
public int compareBuild(String o) {
return compareBuild(constructor(o, options));
}
public int compareBuild(SemVer o) {
return compareIdentifierList(build, o.build);
}
/**
* {@link ReleaseType#preminor} will bump the version up to the next minor release, and immediately
* down to {@link ReleaseType#prerelease}.
* {@link ReleaseType#premajor} and {@link ReleaseType#prepatch} work the same way.
*/
public SemVer inc(ReleaseType release, String identifier, String identifierBase) {
// js compat silliness
if (release == null)
throw new IllegalArgumentException("invalid increment argument: null");
var builder = toBuilder();
builder = switch (release) {
case premajor -> {
builder.prerelease = List.of();
builder.patch = 0;
builder.minor = 0;
builder.major++;
yield builder.build().pre(identifier, identifierBase);
}
case preminor -> {
builder.prerelease = List.of();
builder.patch = 0;
builder.minor++;
yield builder.build().pre(identifier, identifierBase);
}
case prepatch -> {
// If this is already a prerelease, it will bump to the next version
// drop any pre releases that might already exist, since they are not
// relevant at this point.
builder.prerelease = List.of();
var tmp = builder.build();
var patchBump = tmp.inc(ReleaseType.patch, identifier, identifierBase);
yield patchBump.pre(identifier, identifierBase);
}
// If the input is a non-prerelease version, this acts the same as
// pre patch.
case prerelease -> {
SemVer patched;
if (prerelease.isEmpty()) {
patched = inc(ReleaseType.patch, identifier, identifierBase);
} else {
patched = this;
}
yield patched.pre(identifier, identifierBase);
}
case major -> {
// If this is a pre-major version, bump up to the same major version.
// Otherwise, increment major.
// 1.0.0-5 bumps to 1.0.0
// 1.1.0 bumps to 2.0.0
if (
this.minor != 0 ||
this.patch != 0 ||
this.prerelease.isEmpty()
) {
builder.major++;
}
builder.minor = 0;
builder.patch = 0;
builder.prerelease = List.of();
yield builder;
}
case minor -> {
// If this is a pre-minor version, bump up to the same minor version.
// Otherwise, increment minor.
// 1.2.0-5 bumps to 1.2.0
// 1.2.1 bumps to 1.3.0
if (this.patch != 0 || this.prerelease.isEmpty()) {
builder.minor++;
}
builder.patch = 0;
builder.prerelease = List.of();
yield builder;
}
case patch -> {
// If this is not a pre-release version, it will increment the patch.
// If it is a pre-release it will bump up to the same patch version.
// 1.2.0-5 patches to 1.2.0
// 1.2.0 patches to 1.2.1
if (this.prerelease.isEmpty()) {
builder.patch++;
}
builder.prerelease = List.of();
yield builder;
}
};
/*
this.raw = this.format()
if (this.build.length) {
this.raw += `+${this.build.join('.')}`
}
return this
*/
builder.raw = format(builder);
if (builder.build != null && !builder.build.isEmpty())
builder.raw += "+" + String.join(".", builder.build);
return new SemVer(builder);
}
private SemVerBuilder pre(String identifier, String identifierBase) {
// Number(identifierBase)
boolean isIdentifierBaseATruthyNumber =
// null check
identifierBase != null &&
// numeric check
Internal.Regexes.IS_NUMERIC_STRING.test(identifierBase) &&
// truthy means not 0
Integer.parseInt(identifierBase) != 0;
// const base = Number(identifierBase) ? 1 : 0
var base = isIdentifierBaseATruthyNumber ? 1 : 0;
// if (!identifier && identifierBase === false)
if (!StringUtils.hasText(identifier) && "false".equals(identifierBase)) {
throw new IllegalArgumentException("invalid increment argument: identifier is empty");
}
// make it look like it is not immutable
List<String> nextPrerelease;
if (this.prerelease.isEmpty()) {
nextPrerelease = List.of(String.valueOf(base));
} else {
nextPrerelease = new ArrayList<>(prerelease);
var i = this.prerelease.size();
while (--i >= 0) {
String s = this.prerelease.get(i);
// if (typeof this.prerelease[i] === 'number') {
if (Internal.Regexes.IS_NUMERIC_STRING.test(s)) {
// this.prerelease[i]++
nextPrerelease.set(i, String.valueOf(Long.parseLong(s) + 1));
i = -2; // found
}
}
if (i == -1) {
// didn't increment anything
// if (identifier === this.prerelease.join('.') && identifierBase === false) {
if (String.join(".", this.prerelease).equals(identifier) &&
"false".equals(identifierBase)) {
throw new IllegalArgumentException("invalid increment argument: identifier already exists");
}
// this.prerelease.push(base)
nextPrerelease.add(String.valueOf(base));
}
}
// if (identifier) {
if (StringUtils.hasText(identifier)) {
/*
// 1.2.0-beta.1 bumps to 1.2.0-beta.2,
// 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
let prerelease = [identifier, base]
if (identifierBase === false) {
prerelease = [identifier]
}
*/
var prerelease = List.of(identifier, String.valueOf(base));
if ("false".equals(identifierBase)) {
prerelease = List.of(identifier);
}
String first = nextPrerelease.isEmpty() ? null : nextPrerelease.get(0);
Objects.requireNonNull(first, "first was null: need to implement handling");
/*
if (compareIdentifiers(this.prerelease[0], identifier) === 0) {
if (isNaN(this.prerelease[1])) {
this.prerelease = prerelease
}
} else {
this.prerelease = prerelease
}
*/
if (compareIdentifiers(first, identifier) == 0) {
// if we don't have a second element, use empty string (not numeric)
String second = nextPrerelease.size() > 1 ? nextPrerelease.get(1) : "";
if (!Internal.Regexes.IS_NUMERIC_STRING.test(second)) {
nextPrerelease = prerelease;
}
} else {
nextPrerelease = prerelease;
}
}
var builder = toBuilder();
builder.prerelease(Collections.unmodifiableList(nextPrerelease));
return builder;
}
}
// public static void re: internalRe.re() {}
// public static void src: internalRe.src() {}
// public static void tokens: internalRe.t() {}
/**
* Note: this is the semver.org version of the spec that it implements
* Not necessarily the package version of this code.
*/
public static final String SEMVER_SPEC_VERSION = "2.0.0";
public static final List<String> RELEASE_TYPES =
Arrays.stream(ReleaseType.values()).map(Enum::name).toList();
public static int compareIdentifiers(String a, String b) {
var numeric = Pattern.compile("^[0-9]{1," + SemVer.MAX_LENGTH + "}$");
var aNum = numeric.matcher(a).matches();
var bNum = numeric.matcher(b).matches();
if (aNum && bNum) {
var aN = Long.parseLong(a);
var bN = Integer.parseInt(b);
return Long.compare(aN, bN);
}
return a.equals(b) ? 0
: aNum ? -1
: bNum ? 1
: a.compareTo(b);
}
public static int rcompareIdentifiers(String a, String b) {
return compareIdentifiers(b, a);
}
public static class Internal {
/**
* Max safe length for a build identifier. The max length minus 6 characters for
* the shortest version with a build 0.0.0+BUILD.
*/
private static final int MAX_SAFE_BUILD_LENGTH = SemVer.MAX_LENGTH - 6;
interface RegexesSupport {
@SuppressWarnings("SpellCheckingInspection")
String LETTERDASHNUMBER = "[a-zA-Z0-9-]";
Map<String, String> SAFE_REGEX_REPLACEMENTS = Map.of(
"\\s", "1",
"\\d", String.valueOf(SemVer.MAX_LENGTH),
RegexesSupport.LETTERDASHNUMBER, String.valueOf(MAX_SAFE_BUILD_LENGTH)
);
static String makeSafeRegex(String regex) {
// https://github.com/npm/node-semver/blob/14d263faa156e408a033b9b12a2f87735c2df42c/internal/re.js#L29
for (var rep : RegexesSupport.SAFE_REGEX_REPLACEMENTS.entrySet()) {
regex = regex
.replaceAll(Pattern.quote(rep.getKey() + "*"),
rep.getKey() + "{0," + rep.getValue() + "}")
.replaceAll(Pattern.quote(rep.getKey() + "+"),
rep.getKey() + "{1," + rep.getValue() + "}");
}
return regex;
}
}
interface Constants {
// Note: this is the semver.org version of the spec that it implements
// Not necessarily the package version of this code.
String SEMVER_SPEC_VERSION = "2.0.0";
int MAX_LENGTH = 256;
long MAX_SAFE_INTEGER = 9007199254740991L;
// Max safe segment length for coercion.
int MAX_SAFE_COMPONENT_LENGTH = 16;
// Max safe length for a build identifier. The max length minus 6 characters for
// the shortest version with a build 0.0.0+BUILD.
int MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6;
List<String> RELEASE_TYPES = Arrays.stream(ReleaseType.values()).map(Enum::name).toList();
@SuppressWarnings("SpellCheckingInspection")
enum ReleaseType {
major,
premajor,
minor,
preminor,
patch,
prepatch,
prerelease,
}
}
/**
* The following Regular Expressions can be used for tokenizing,
* validating, and parsing SemVer version strings.
*/
@SuppressWarnings("SpellCheckingInspection")
@AllArgsConstructor
enum Regexes {
/**
* ## Numeric Identifier
* <p>
* A single {@code 0}, or a non-zero digit followed by zero or more digits.
*/
NUMERICIDENTIFIER("0|[1-9]\\d*"),
NUMERICIDENTIFIERLOOSE("\\d+"),
NONNUMERICIDENTIFIER("\\d*[a-zA-Z-]" + RegexesSupport.LETTERDASHNUMBER + "*"),
/**
* ## Main Version
* <p>
* Three dot-separated numeric identifiers.
*/
MAINVERSION("(" + NUMERICIDENTIFIER.src + ")\\." +
"(" + NUMERICIDENTIFIER.src + ")\\." +
"(" + NUMERICIDENTIFIER.src + ")"),
MAINVERSIONLOOSE("(" + NUMERICIDENTIFIERLOOSE.src + ")\\." +
"(" + NUMERICIDENTIFIERLOOSE.src + ")\\." +
"(" + NUMERICIDENTIFIERLOOSE.src + ")"),
/**
* ## Pre-release Version Identifier
* <p>
* A numeric identifier, or a non-numeric identifier.
*/
PRERELEASEIDENTIFIER("(?:" + NUMERICIDENTIFIER.src + "|" + NONNUMERICIDENTIFIER.src + ")"),
PRERELEASEIDENTIFIERLOOSE("(?:" + NUMERICIDENTIFIERLOOSE.src + "|" + NONNUMERICIDENTIFIER.src + ")"),
/**
* ## Pre-release Version
* <p>
* Hyphen, followed by one or more dot-separated pre-release version identifiers.
*/
PRERELEASE("(?:-(" + PRERELEASEIDENTIFIER.src +
"(?:\\." + PRERELEASEIDENTIFIER.src + ")*))"),
PRERELEASELOOSE("(?:-?(" + PRERELEASEIDENTIFIERLOOSE.src +
"(?:\\." + PRERELEASEIDENTIFIERLOOSE.src + ")*))"),
/**
* ## Build Metadata Identifier
* <p>
* Any combination of digits, letters, or hyphens.
*/
BUILDIDENTIFIER(RegexesSupport.LETTERDASHNUMBER + "+"),
/**
* ## Build Metadata
* <p>
* Plus sign, followed by one or more period-separated build metadata
* identifiers.
*/
BUILD("(?:\\+(" + BUILDIDENTIFIER.src + "(?:\\." + BUILDIDENTIFIER.src + ")*))"),
/**
* ## Full Version String
* <p>
* A main version, followed optionally by a pre-release version and
* build metadata.
* <p>
* Note that the only major, minor, patch, and pre-release sections of
* the version string are capturing groups. The build metadata is not a
* capturing group, because it should not ever be used in version
* comparison.
*/
FULLPLAIN("v?" + MAINVERSION.src + PRERELEASE.src + "?" + BUILD.src + "?"),
FULL("^" + FULLPLAIN.src + "$"),
/**
* like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
* also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
* common in the npm registry.
*/
LOOSEPLAIN("[v=\\s]*" + MAINVERSIONLOOSE.src + PRERELEASELOOSE.src + "?" + BUILD.src /* added: */ + "?"),
LOOSE("^" + LOOSEPLAIN.src + "$"),
GTLT("((?:<|>)?=?)"),
/**
* Something like "2.*" or "1.2.x".
* Note that "x.x" is a valid xRange identifer, meaning "any version"
* Only the first item is strictly required.
*/
XRANGEIDENTIFIERLOOSE(NUMERICIDENTIFIERLOOSE.src + "|x|X|\\*"),
XRANGEIDENTIFIER(NUMERICIDENTIFIER.src + "|x|X|\\*"),
XRANGEPLAIN(
"[v=\\s]*(" + XRANGEIDENTIFIER.src + ")" +
"(?:\\.(" + XRANGEIDENTIFIER.src + ")" +
"(?:\\.(" + XRANGEIDENTIFIER.src + ")" +
"(?:" + PRERELEASE.src + ")?" +
BUILD.src + "?" +
")?)?"
),
XRANGEPLAINLOOSE(
"[v=\\s]*(" + XRANGEIDENTIFIERLOOSE.src + ")" +
"(?:\\.(" + XRANGEIDENTIFIERLOOSE.src + ")" +
"(?:\\.(" + XRANGEIDENTIFIERLOOSE.src + ")" +
"(?:" + PRERELEASELOOSE.src + ")?" +
BUILD.src + "?" +
")?)?"
),
XRANGE("^" + GTLT.src + "\\s*" + XRANGEPLAIN.src + "$"),
XRANGELOOSE("^" + GTLT.src + "\\s*" + XRANGEPLAINLOOSE.src + "$"),
// Coercion.
// Extract anything that could conceivably be a part of a valid semver
COERCE("(^|[^\\d])" +
"(\\d{1," + Constants.MAX_SAFE_COMPONENT_LENGTH + "})" +
"(?:\\.(\\d{1," + Constants.MAX_SAFE_COMPONENT_LENGTH + "}))?" +
"(?:\\.(\\d{1," + Constants.MAX_SAFE_COMPONENT_LENGTH + "}))?" +
"(?:$|[^\\d])"),
COERCERTL(COERCE.src, true),
// Tilde ranges.
// Meaning is "reasonably at or greater than"
LONETILDE("(?:~>?)"),
TILDETRIM("(\\s*)" + LONETILDE.src + "\\s+", true),
TILDE("^" + LONETILDE.src + "}" + XRANGEPLAIN.src + "$"),
TILDELOOSE("^" + LONETILDE.src + "}" + XRANGEPLAINLOOSE.src + "$"),
// Caret ranges.
// Meaning is "at least and backwards compatible with"
LONECARET("(?:\\^)"),
CARETTRIM("(\\s*)" + LONECARET.src + "\\s+", true),
CARET("^" + LONECARET.src + XRANGEPLAINLOOSE.src + "$"),
/**
* A simple gt/lt/eq thing, or just "" to indicate "any version"
*/
COMPARATORLOOSE("^" + GTLT.src + "\\s*(" + LOOSEPLAIN.src + ")$|^$"),
/**
* A simple gt/lt/eq thing, or just "" to indicate "any version"
*/
COMPARATOR("^" + GTLT.src + "\\s*(" + FULLPLAIN.src + ")$|^$"),
/**
* An expression to strip any whitespace between the gtlt and the thing
* it modifies, so that {@code > 1.2.3} ==> {@code >1.2.3}
*/
COMPARATORTRIM("(\\s*)" + GTLT.src + "\\s*" +
"(" + LOOSEPLAIN.src + "|" + XRANGEPLAIN.src + ")", true),
/**
* Something like {@code 1.2.3 - 1.2.4}
* Note that these all use the loose form, because they'll be
* checked against either the strict or loose comparator form
* later.
*/
HYPHENRANGE("^\\s*(" + XRANGEPLAIN.src + ")" +
"\\s+-\\s+" +
"(" + XRANGEPLAIN.src + ")" +
"\\s*$"),
HYPHENRANGELOOSE("^\\s*(" + XRANGEPLAINLOOSE.src + ")" +
"\\s+-\\s+" +
"(" + XRANGEPLAINLOOSE.src + ")" +
"\\s*$"),
/**
* Star ranges basically just allow anything at all.
*/
STAR("(<|>)?=?\\s*\\*"),
/**
* >=0.0.0 is like a star
*/
GTE0("^\\s*>=\\s*0\\.0\\.0\\s*$"),
GTE0PRE("^\\s*>=\\s*0\\.0\\.0-0\\s*$"),
;
static final Predicate<String> IS_NUMERIC_STRING = Pattern.compile("^\\d+$").asMatchPredicate();
final String src;
final Pattern regex;
final String safeSrc;
final Pattern safeRegex;
final boolean global;
Regexes(String regex) {
this(regex, false);
}
Regexes(String regex, boolean global) {
this(regex, RegexesSupport.makeSafeRegex(regex), global);
}
Regexes(String regex, String safe, boolean global) {
this(regex, Pattern.compile(regex), safe, Pattern.compile(safe), global);
}
}
}
public static void main(String[] args) {
System.out.println(Internal.Constants.RELEASE_TYPES);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment