All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pkl.executor.Version Maven / Gradle / Ivy

There is a newer version: 0.26.3
Show newest version
/**
 * Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.pkl.executor;

import java.util.*;
import java.util.regex.Pattern;

/**
 * A semantic version.
 *
 * 

This class guarantees that valid semantic version numbers are handled correctly, but does * not guarantee that invalid semantic version numbers are rejected. */ // copied from `org.pkl.core.Version` to avoid dependency on pkl-core @SuppressWarnings("Duplicates") final class Version implements Comparable { // https://semver.org/#backusnaur-form-grammar-for-valid-semver-versions private static final Pattern VERSION = Pattern.compile("(\\d+)\\.(\\d+)\\.(\\d+)(?:-([^+]+))?(?:\\+(.+))?"); private static final Pattern NUMERIC_IDENTIFIER = Pattern.compile("(0|[1-9]\\d*)"); private static final Comparator COMPARATOR = Comparator.comparingInt(Version::getMajor) .thenComparingInt(Version::getMinor) .thenComparingInt(Version::getPatch) .thenComparing( (v1, v2) -> { if (v1.preRelease == null) return v2.preRelease == null ? 0 : 1; if (v2.preRelease == null) return -1; var ids1 = v1.getPreReleaseIdentifiers(); var ids2 = v2.getPreReleaseIdentifiers(); var minSize = Math.min(ids1.length, ids2.length); for (var i = 0; i < minSize; i++) { var result = ids1[i].compareTo(ids2[i]); if (result != 0) return result; } return Integer.compare(ids1.length, ids2.length); }); private final int major; private final int minor; private final int patch; private final /*@Nullable*/ String preRelease; private final /*@Nullable*/ String build; // always access through getter private volatile Identifier[] __preReleaseIdentifiers; /** Constructs a semantic version. */ public Version( int major, int minor, int patch, /*@Nullable*/ String preRelease, /*@Nullable*/ String build) { this.major = major; this.minor = minor; this.patch = patch; this.preRelease = preRelease; this.build = build; } /** * Parses the given string as a semantic version number. * *

Throws {@link IllegalArgumentException} if the given string could not be parsed as a * semantic version number or is too large to fit into a {@link Version}. */ public static Version parse(String version) { var result = parseOrNull(version); if (result != null) return result; if (VERSION.matcher(version).matches()) { throw new IllegalArgumentException( String.format("`%s` is too large to fit into a Version.", version)); } throw new IllegalArgumentException( String.format("`%s` could not be parsed as a semantic version number.", version)); } /** * Parses the given string as a semantic version number. * *

Returns {@code null} if the given string could not be parsed as a semantic version number or * is too large to fit into a {@link Version}. */ public static /*@Nullable*/ Version parseOrNull(String version) { var matcher = VERSION.matcher(version); if (!matcher.matches()) return null; try { return new Version( Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)), Integer.parseInt(matcher.group(3)), matcher.group(4), matcher.group(5)); } catch (NumberFormatException e) { return null; } } /** Returns a comparator for semantic versions. */ public static Comparator comparator() { return COMPARATOR; } /** Returns the major version. */ public int getMajor() { return major; } /** Returns a copy of this version with the given major version. */ public Version withMajor(int major) { return new Version(major, minor, patch, preRelease, build); } /** Returns the minor version. */ public int getMinor() { return minor; } /** Returns a copy of this version with the given minor version. */ public Version withMinor(int minor) { return new Version(major, minor, patch, preRelease, build); } /** Returns the patch version. */ public int getPatch() { return patch; } /** Returns a copy of this version with the given patch version. */ public Version withPatch(int patch) { return new Version(major, minor, patch, preRelease, build); } /** Returns the pre-release version (if any). */ public /*@Nullable*/ String getPreRelease() { return preRelease; } /** Returns a copy of this version with the given pre-release version. */ public Version withPreRelease(/*@Nullable*/ String preRelease) { return new Version(major, minor, patch, preRelease, build); } /** Returns the build metadata (if any). */ public /*@Nullable*/ String getBuild() { return build; } /** Returns a copy of this version with the given build metadata. */ public Version withBuild(/*@Nullable*/ String build) { return new Version(major, minor, patch, preRelease, build); } /** Tells if this version has no pre-release version or build metadata. */ @SuppressWarnings("unused") public boolean isNormal() { return preRelease == null && build == null; } /** Tells if this version has a non-zero major version and no pre-release version. */ public boolean isStable() { return major != 0 && preRelease == null; } /** Strips any pre-release version and build metadata from this version. */ public Version toNormal() { return preRelease == null && build == null ? this : new Version(major, minor, patch, null, null); } /** Compares this version to the given version according to semantic versioning rules. */ @Override public int compareTo(@SuppressWarnings("NullableProblems") /*@Nonnull*/ Version other) { return COMPARATOR.compare(this, other); } /** Tells if this version is equal to {@code obj} according to semantic versioning rules. */ @Override public boolean equals(/* @Nullable */ Object obj) { if (this == obj) return true; if (!(obj instanceof Version other)) return false; return major == other.major && minor == other.minor && patch == other.patch && Objects.equals(preRelease, other.preRelease); } @Override public int hashCode() { return Objects.hash(major, minor, patch, preRelease); } @Override public String toString() { return major + "." + minor + "." + patch + (preRelease != null ? "-" + preRelease : "") + (build != null ? "+" + build : ""); } private Identifier[] getPreReleaseIdentifiers() { if (__preReleaseIdentifiers == null) { __preReleaseIdentifiers = preRelease == null ? new Identifier[0] : Arrays.stream(preRelease.split("\\.")) .map( str -> NUMERIC_IDENTIFIER.matcher(str).matches() ? new Identifier(Long.parseLong(str), null) : new Identifier(-1, str)) .toArray(Identifier[]::new); } return __preReleaseIdentifiers; } private static final class Identifier implements Comparable { private final long numericId; private final /*@Nullable*/ String alphanumericId; Identifier(long numericId, /*@Nullable*/ String alphanumericId) { this.numericId = numericId; this.alphanumericId = alphanumericId; } @Override public int compareTo(/*@Nonnull*/ @SuppressWarnings("NullableProblems") Identifier other) { return alphanumericId != null ? other.alphanumericId != null ? alphanumericId.compareTo(other.alphanumericId) : 1 : other.alphanumericId != null ? -1 : Long.compare(numericId, other.numericId); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy