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

org.expath.pkg.repo.deps.Semver Maven / Gradle / Ivy

There is a newer version: 2.1.1
Show newest version
/****************************************************************************/
/*  File:       Semver.java                                                 */
/*  Author:     Adam Retter - Evolved Binary                                */
/*  Date:       2018-11-09                                                  */
/*  Tags:                                                                   */
/*      Copyright (c) 2018 Adam Retter (see end of file.)                   */
/* ------------------------------------------------------------------------ */


package org.expath.pkg.repo.deps;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.expath.pkg.repo.PackageException;

import javax.annotation.Nullable;

/**
 * Represents a SemVer template, or a SemVer version number.
 *
 * @author Adam Retter
 */
public class Semver implements Comparable {

    /**
     * https://semver.org/
     * v2.0
     */
    private static final Pattern PTN_SEM_VER = Pattern.compile("([0-9]+)(\\.[0-9]+)?(\\.[0-9]+)?(-[1-9A-Za-z][0-9A-Za-z-]*(?:\\.[1-9A-Za-z][0-9A-Za-z-]*)*)?(\\+[0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*)?");

    private final int majorVersion;
    private final Integer minorVersion;
    private final Integer patchVersion;
    private final String preReleaseVersion;
    private final String buildMetadata;

    public Semver(final int majorVersion, final Integer minorVersion,
            final Integer patchVersion, final String preReleaseVersion,
            final String buildMetadata) {
        this.majorVersion = majorVersion;
        this.minorVersion = minorVersion;
        this.patchVersion = patchVersion;
        this.preReleaseVersion = preReleaseVersion;
        this.buildMetadata = buildMetadata;
    }

    public static Semver parse(final String semver) throws PackageException {
        final Matcher matcher = PTN_SEM_VER.matcher(semver);
        if (!matcher.matches()) {
            parseError(semver, "Does not match EXPath SemVer Regex");
        }

        final int majorVersion = Integer.parseInt(matcher.group(1));
        Integer minorVersion = null;
        Integer patchVersion = null;
        String preReleaseVersion = null;
        String buildMetadata = null;

        for (int i = 2; i <= matcher.groupCount(); i++) {
            final String group = matcher.group(i);
            if (group != null) {
                switch (group.charAt(0)) {
                    case '.':
                        if (minorVersion == null) {
                            minorVersion = Integer.parseInt(group.substring(1));
                        } else {
                            patchVersion = Integer.parseInt(group.substring(1));
                        }
                        break;

                    case '-':
                        preReleaseVersion = group.substring(1);
                        break;

                    case '+':
                        buildMetadata = group.substring(1);
                        break;

                    default:
                        parseError(semver, "Illegal state");
                }
            }
        }

//        if (minorVersion == null) {
//            minorVersion = 0;
//        }
//
//        if (patchVersion == null) {
//            patchVersion = 0;
//        }

        return new Semver(
                majorVersion,
                minorVersion,
                patchVersion,
                preReleaseVersion,
                buildMetadata
        );
    }

    public int getMajorVersion() {
        return majorVersion;
    }

    public Integer getMinorVersion() {
        return minorVersion;
    }

    public Integer getPatchVersion() {
        return patchVersion;
    }

    public String getPreReleaseVersion() {
        return preReleaseVersion;
    }

    public String getBuildMetadata() {
        return buildMetadata;
    }

    /**
     * Does {@code rhs} (a SemVer version) match this SemVer template?
     *
     * @param rhs the other Semver
     *
     * @return true if this Semver matches the other Semver, false otherwise
     *
     * @throws PackageException if an error occurs
     */
    public boolean matches(final Semver rhs)
            throws PackageException {
        return matchesTemplate(rhs) == 0;
    }

    /**
     * Does {@code rhs} (a SemVer version) match this SemVer template as a minimum?
     * 

* Return true if {@code rhs} is equal or above this template. * * @param rhs the other Semver * * @return true if this Semver minimally matches the other Semver, false otherwise * * @throws PackageException if an error occurs */ public boolean matchesMin(final Semver rhs) throws PackageException { return matchesTemplate(rhs) <= 0; } /** * Does {@code rhs} (a SemVer version) match this SemVer template as a maximum? *

* Return true if {@code rhs} is equal or below this template. * * @param rhs the other Semver * * @return true if this Semver maximally matches the other Semver, false otherwise * * @throws PackageException if an error occurs */ public boolean matchesMax(final Semver rhs) throws PackageException { return matchesTemplate(rhs) >= 0; } @Override public int compareTo(final Semver other) { final int majorDiff = this.majorVersion - other.majorVersion; if (majorDiff != 0) { return majorDiff * 1000; } final int minorDiff = toInt(this.minorVersion) - toInt(other.minorVersion); if (minorDiff != 0) { return minorDiff * 100; } final int patchDiff = toInt(this.patchVersion) - toInt(other.patchVersion); if (patchDiff != 0) { return patchDiff * 10; } if (this.preReleaseVersion == null && other.preReleaseVersion == null) { return 0; } else if (this.preReleaseVersion == null) { return 1; } else if (other.preReleaseVersion == null) { return -1; } // both have preReleaseVersion, need to compare them final String[] preReleaseIdentfiers = this.preReleaseVersion.split("\\."); final String[] otherPreReleaseIdentfiers = other.preReleaseVersion.split("\\."); for(int i = 0; i < Math.min(preReleaseIdentfiers.length, otherPreReleaseIdentfiers.length); i++) { final String preReleaseIdentfier = preReleaseIdentfiers[i]; final String otherPreReleaseIdentfier = otherPreReleaseIdentfiers[i]; final boolean preReleaseIdentfierIsNum = isNumeric(preReleaseIdentfier); final boolean otherPreReleaseIdentfierIsNum = isNumeric(otherPreReleaseIdentfier); if (preReleaseIdentfierIsNum && otherPreReleaseIdentfierIsNum) { // both are numeric final int preReleaseIdentifierDiff = Integer.parseInt(preReleaseIdentfier) - Integer.parseInt(otherPreReleaseIdentfier); if (preReleaseIdentifierDiff != 0) { return preReleaseIdentifierDiff; } } else if (preReleaseIdentfierIsNum) { return -1; } else if (otherPreReleaseIdentfierIsNum) { return 1; } else { // both are alphanumeric final int preReleaseIdentifierDiff = preReleaseIdentfier.compareTo(otherPreReleaseIdentfier); if (preReleaseIdentifierDiff != 0) { return preReleaseIdentifierDiff; } } } return preReleaseIdentfiers.length - otherPreReleaseIdentfiers.length; } private int matchesTemplate(final Semver other) { final int majorDiff = this.majorVersion - other.majorVersion; if (majorDiff != 0) { return majorDiff * 1000; } if (this.minorVersion != null) { final int minorDiff = this.minorVersion - other.minorVersion; if (minorDiff != 0) { return minorDiff * 100; } } else { return 0; } if (this.patchVersion != null) { final int patchDiff = this.patchVersion - other.patchVersion; if (patchDiff != 0) { return patchDiff * 10; } } else { return 0; } if (this.preReleaseVersion == null && other.preReleaseVersion == null) { return 0; } else if (this.preReleaseVersion == null) { return 1; } else if (other.preReleaseVersion == null) { return -1; } // both have preReleaseVersion, need to compare them final String[] preReleaseIdentfiers = this.preReleaseVersion.split("\\."); final String[] otherPreReleaseIdentfiers = other.preReleaseVersion.split("\\."); for(int i = 0; i < Math.min(preReleaseIdentfiers.length, otherPreReleaseIdentfiers.length); i++) { final String preReleaseIdentfier = preReleaseIdentfiers[i]; final String otherPreReleaseIdentfier = otherPreReleaseIdentfiers[i]; final boolean preReleaseIdentfierIsNum = isNumeric(preReleaseIdentfier); final boolean otherPreReleaseIdentfierIsNum = isNumeric(otherPreReleaseIdentfier); if (preReleaseIdentfierIsNum && otherPreReleaseIdentfierIsNum) { // both are numeric final int preReleaseIdentifierDiff = Integer.parseInt(preReleaseIdentfier) - Integer.parseInt(otherPreReleaseIdentfier); if (preReleaseIdentifierDiff != 0) { return preReleaseIdentifierDiff; } } else if (preReleaseIdentfierIsNum) { return -1; } else if (otherPreReleaseIdentfierIsNum) { return 1; } else { // both are alphanumeric final int preReleaseIdentifierDiff = preReleaseIdentfier.compareTo(otherPreReleaseIdentfier); if (preReleaseIdentifierDiff != 0) { return preReleaseIdentifierDiff; } } } return preReleaseIdentfiers.length - otherPreReleaseIdentfiers.length; } /** * Safely un-boxes an Integer which might be null. * * @param integer the Integer object or null * @return 0 if the Integer is null, otherwise the int value of the Integer */ private static int toInt(@Nullable final Integer integer) { if (integer == null) { return 0; } else { return integer; } } private static boolean isNumeric(final String string) { for (int i = 0; i < string.length(); i++) { final char c = string.charAt(i); if ('0' < c || c > '9') { return false; } } return true; } private static void parseError(final String semver, final String msg) throws PackageException { throw new PackageException("Invalid SemVer '" + semver + "': " + msg); } } /* ------------------------------------------------------------------------ */ /* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS COMMENT. */ /* */ /* The contents of this file are subject to the Mozilla Public License */ /* Version 1.0 (the "License"); you may not use this file except in */ /* compliance with the License. You may obtain a copy of the License at */ /* http://www.mozilla.org/MPL/. */ /* */ /* Software distributed under the License is distributed on an "AS IS" */ /* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See */ /* the License for the specific language governing rights and limitations */ /* under the License. */ /* */ /* The Original Code is: all this file. */ /* */ /* The Initial Developer of the Original Code is Adam Retter. */ /* */ /* Contributor(s): none. */ /* ------------------------------------------------------------------------ */





© 2015 - 2024 Weber Informatics LLC | Privacy Policy