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

org.nuiton.util.version.SemVer Maven / Gradle / Ivy

There is a newer version: 3.1
Show newest version
package org.nuiton.util.version;

/*
 * #%L
 * Nuiton Utils
 * %%
 * Copyright (C) 2004 - 2013 CodeLutin
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */


import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

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

/**
 * Implantation de http://semver.org/.
 *
 * Cette objet est immutable, Il faut utiliser {@link #getCreator()}
 * pour modifier un de ses elements
 *
 * Quelques changement par rapport au site:
 * 
    *
  • le numero de version peut avoir entre 1 et N element, et non pas 3 obligatoirement *
  • on peut avoir un -SNAPSHOT ajoute en toute fin de la version *
* Un SemVer est en plusieurs elements dont chaque element peut avoir plusieurs * composant. Les composants sont utilise le meme separateur '.'. *
    *
  • version: 1 à N composant numerique, les 3 premiers sont nomme *
      *
    • major *
    • minor *
    • patch *
    *
  • prerelease: 0 à N composant alphanumerique, le prefix est '-' *
  • build: 0 à N composant alphanumerique, le prefix est '+' *
  • SNAPSHOT: 0 ou 1 composant dont le nom est fixe le prefix est '-' *
* Le mieux pour construire ou modifier un SemVer est d'utilise une methode creator: *
    *
  • SemVer.creator(1,2,3).done() → "1.2.3" *
  • SemVer.creator("1.2.3","rc2").done() → "1.2.3-rc2" *
  • SemVer.creator().setVersion(2.3.4).setBuild("r223").done() → "2.3.4+r223" *
  • SemVer.creator().setVersion(2.3.4).setSnapshot(true).done() → "2.3.4-SNAPSHOT" *
  • SemVer.creator(new SemVer("1.2.3")).incMajor().done() → "2.2.2" *
  • new SemVer("1.2.3").getCreator().incMinor().setPrerelease(beta).done() → "1.3.3-beta" *
* * @author Benjamin Poussin - [email protected] * @since 2.6.7 * @deprecated since 3.0, use now Nuiton version */ @Deprecated public class SemVer implements Comparable { /** Logger. */ private static final Log log = LogFactory.getLog(SemVer.class); /** Separateur utiliser entre chaque element d'une partie */ final static public String SERIES_SEPARATOR = "."; // le separateur /** Seperateur entre la version et l'element prerelease */ final static public String PRERELEASE_SEPARATOR = "-"; // le separateur de Prerelease /** Separateur utiliser devant l'element build */ final static public String BUILD_SEPARATOR = "+"; // le separateur de build /** pattern pour une partie de l'element version */ final static public String PATTERN_VERSION = "[0-9]+"; // le version ne sont que des nombres /** pattern pour une partie de l'element prerelease */ final static public String PATTERN_PRERELEASE = "[0-9A-Za-z-]+"; // les Prerelease sont des chiffre, lettre ou '-' /** pattern pour une partir de l'element build */ final static public String PATTERN_BUILD = "[0-9A-Za-z-]+";// les Build sont des chiffre, lettre ou '-' /** Separateur de SNAPSHOT */ final static public String SNAPSHOT_SEPARATOR = "-"; /** la chaine de caractere representant le SNAPSHOT */ final static public String SNAPSHOT = "SNAPSHOT"; // "(1.20.300)(-alpha-1.20)?(+r123.20130126)?" // version prerelease build /** pattern qui permet de separer les 3 constituante d'une version, ne * supporte pas le SNAPSHOT, il faut donc que le SNAPSHOT ait ete retirer * avant d'appliquer le pattern */ final static Pattern PATTERN_ALL = Pattern.compile( "("+PATTERN_VERSION+"(?:"+Pattern.quote(SERIES_SEPARATOR)+PATTERN_VERSION+")*)" + "("+Pattern.quote(PRERELEASE_SEPARATOR)+ PATTERN_PRERELEASE+"(?:"+Pattern.quote(SERIES_SEPARATOR)+PATTERN_PRERELEASE+")*)?" + "("+Pattern.quote(BUILD_SEPARATOR)+ PATTERN_BUILD+"(?:"+Pattern.quote(SERIES_SEPARATOR)+PATTERN_BUILD+")*)?"); protected String version; protected String prerelease; protected String build; protected String snapshot; /** * Create new Version object, strip is done on argument to remove extra space * @param versionString FIXME * @exception IllegalArgumentException if argument isn't valid version string */ public SemVer(String versionString) { String v = StringUtils.strip(versionString); // on conserve versionString intact pour le message d'erreur if (StringUtils.endsWithIgnoreCase(v, SNAPSHOT_SEPARATOR + SNAPSHOT)) { snapshot = SNAPSHOT; v = StringUtils.substringBeforeLast(v, SNAPSHOT_SEPARATOR); } Matcher matcher = PATTERN_ALL.matcher(v); boolean match = matcher.matches(); if (match) { version = matcher.group(1); prerelease = StringUtils.removeStart(matcher.group(2), PRERELEASE_SEPARATOR); build = StringUtils.removeStart(matcher.group(3), BUILD_SEPARATOR); } else { throw new IllegalArgumentException(String.format("Bad version string: '%s'", versionString)); } } /** * @param o the other version to test * @return {@code true} if current version is before the given one */ public boolean before(SemVer o) { int result = compareTo(o); return result < 0; } /** * @param o the other version to test * @return {@code true} if current version is after the given one */ public boolean after(SemVer o) { int result = compareTo(o); return result > 0; } public int compareTo(SemVer other) { int result = compare(this.getVersion(), other.getVersion(), false); if (result == 0) { result = compare(this.getPrerelease(), other.getPrerelease(), true); if (result == 0) { result = compare(this.getBuild(), other.getBuild(), false); if (result == 0) { result = compare(this.getSnapshot(), other.getSnapshot(), true); } } } return result; } /** * Compare deux elements de meme semantique (version, prerelease, build, snapshot) * sinon le comportement est non predictible * @param a FIXME * @param b FIXME * @param nullIsHigh indique si un des elements est null, s'il est plus * grand ou plus petit que l'autre * @return negatif si a inferieur b, 0 si a == b, positif si a superieur a b */ protected int compare(String a, String b, boolean nullIsHigh) { int result; if (nullIsHigh) { result = nullIsHigh(a, b); } else { result = nullIsLow(a, b); } if (result == 0 && a != null && b != null) { // on decoupe suivant le '.' String[] aSeries = StringUtils.split(a, SERIES_SEPARATOR); String[] bSeries = StringUtils.split(b, SERIES_SEPARATOR); int length = Math.max(aSeries.length, bSeries.length); for (int i=0; result==0 & i *
  • 1.2.3 retourne 3
  • *
  • 99.100 retourne 2
  • * * * @param element FIXME * @return FIXME */ protected int getComposantCount(String element) { int result = 0; if (StringUtils.isNotBlank(element)) { result = 1 + StringUtils.countMatches(element, SERIES_SEPARATOR); } return result; } /** * Retourne un des composants de l'element version * @param element FIXME * @param i FIXME * @return FIXME */ protected String getComposant(String element, int i) { String[] v = StringUtils.split(element, SERIES_SEPARATOR); Validate.validIndex(v, i, "Ask '%s', element '%s' don't have enought composant", i, element); String s = v[i]; return s; } public String getVersion() { return version; } /** * Donne le nombre de composante de la version *
      *
    • 1.2.3 retourne 3
    • *
    • 99.100 retourne 2
    • *
    * * @return FIXME */ public int getVersionCount() { int result = getComposantCount(getVersion()); return result; } /** * Retourne un des composants de l'element version * @param i FIXME * @return FIXME */ public String getVersion(int i) { String s = getComposant(getVersion(), i); return s; } public String getMajor() { String result = getVersion(0); return result; } public String getMinor() { String result = getVersion(1); return result; } public String getPatch() { String result = getVersion(2); return result; } public String getPrerelease() { return prerelease; } /** * Donne le nombre de composante de la Prerelease *
      *
    • 1.2.3 retourne 3
    • *
    • 99.100 retourne 2
    • *
    * * @return FIXME */ public int getPrereleaseCount() { int result = getComposantCount(getPrerelease()); return result; } /** * Retourne un des composants de l'element version * @param i FIXME * @return FIXME */ public String getPrerelease(int i) { String s = getComposant(getPrerelease(), i); return s; } public String getBuild() { return build; } /** * Donne le nombre de composante de la Prerelease *
      *
    • 1.2.3 retourne 3
    • *
    • 99.100 retourne 2
    • *
    * * @return FIXME */ public int getBuildCount() { int result = getComposantCount(getBuild()); return result; } /** * Retourne un des composants de l'element version * @param i FIXME * @return FIXME */ public String getBuild(int i) { String s = getComposant(getBuild(), i); return s; } public String getSnapshot() { return snapshot; } public boolean isSnapshot() { return StringUtils.isNotBlank(snapshot); } static protected String ifNotNull(String prefix, String s) { String result = ""; if (StringUtils.isNotBlank(s)) { result = prefix + s; } return result; } @Override public boolean equals(Object o) { boolean result = o instanceof SemVer && this.compareTo((SemVer)o) == 0; return result; } @Override public int hashCode() { return toString().hashCode(); } /** * Convertit la representation textuelle de la version en identifiant java valide : * - en java : "-" "." "+" interdit * * @return la valeur ou les carateres interdits sont remplaces par '_' */ public String toJavaIdentifier() { String result = toString(); // attention dans les crochets le '-' a une signification, il faut donc le mettre en 1er result = result.replaceAll("[-+.]", "_"); return result; } @Override public String toString() { return getVersion() + ifNotNull(PRERELEASE_SEPARATOR, getPrerelease()) + ifNotNull(BUILD_SEPARATOR, getBuild()) + ifNotNull(SNAPSHOT_SEPARATOR, getSnapshot()); } /** * Retourne un objet creator initialise avec les donnees de ce SemVer * ce qui permet de creer un nouveau SemVer en modifiant un des elements * @return FIXME */ public SemVerCreator getCreator() { return creator(this); } /** * Indique si la chaine represente bien une version au format SemVer * @param version FIXME * @return FIXME */ static public boolean isSemVer(String version) { if (StringUtils.endsWithIgnoreCase(version, SNAPSHOT)) { version = StringUtils.substringBeforeLast(version, SNAPSHOT); } Matcher matcher = PATTERN_ALL.matcher(version); boolean result = matcher.matches(); return result; } /** * Permet de creer un objet version. Si des arguments sont passer en parametre * ils sont pris dans l'ordre pour: la version, la prerelease, le build, le snapshot *
         * SemVer v = SemVer.creator().setVersion("1.2").setPrerelease("beta.1").setBuild("r123").done();
         * SemVer v = SemVer.creator("1.2", "beta.1","r123", SemVer.SNAPSHOT).done();
         * 
    * @param v FIXME * @return FIXME */ static public SemVerCreator creator(String ... v) { SemVerCreator result = new SemVerCreator(); if (v != null) { if (v.length > 0) { result.setVersion(v[0]); if (v.length > 1) { result.setPrerelease(v[1]); if (v.length > 2) { result.setBuild(v[2]); if (v.length > 3) { if (v[3] == null) { result.setSnapshot(false); } else if (StringUtils.equalsIgnoreCase(SNAPSHOT, v[3])) { result.setSnapshot(true); } else { throw new IllegalArgumentException(String.format( "Illegal SNAPSHOT string '%s'", v[3])); } if (v.length > 4) { throw new IllegalArgumentException(String.format( "too many string arguments '%s' maximum 4", v.length)); } } } } } } return result; } /** * Createur qui permet de passer en parametre les different composante de * l'element version * @param v FIXME * @return FIXME */ static public SemVerCreator creator(int ... v) { SemVerCreator result = creator(StringUtils.join(v, SERIES_SEPARATOR)); return result; } /** * Create creator initialized with SemVer value, you can change some Element * by puting null value. * * SemVer n = SemVer.creator(v).setBuild("r123").setSnapshot(false).done(); * * @param v FIXME * @return FIXME */ static public SemVerCreator creator(SemVer v) { SemVerCreator result = creator( v.getVersion(), v.getPrerelease(), v.getBuild(), v.getSnapshot()); return result; } /** * Retourne un Createur sans aucune information. Cette methode permet * aussi de desambiguise les deux autres methode creator qui si elle * n'ont pas de parametre sont semblable * * @return FIXME */ static public SemVerCreator creator() { SemVerCreator result = new SemVerCreator(); return result; } /** * Construit un objet version, la verification de la coherence est faite lors * de l'appel du create. */ static public class SemVerCreator { protected String version; protected String prerelease; protected String build; protected String snapshot; public SemVerCreator setVersion(Integer... v) { // l'argument est bien Integer, car join ne travail qu'avec des objets :( version = StringUtils.join(v, SERIES_SEPARATOR); return this; } public SemVerCreator setVersion(String v) { version = v; return this; } public SemVerCreator setPrerelease(String v) { prerelease = v; return this; } public SemVerCreator setBuild(String v) { build = v; return this; } public SemVerCreator setSnapshot(boolean b) { if (b) { snapshot = SNAPSHOT; } else { snapshot = null; } return this; } /** * Incremente la composante 'indice' de l'element version de 'inc', la composante * doit representer un entier sinon une exception est leve * * @param indice la composante de la version a incrementer * @param inc le nombre a lui ajouter * @return new instance, this SemVer is not modified */ public SemVerCreator incVersion(int indice, int inc) { String[] v = StringUtils.split(version, SERIES_SEPARATOR); Validate.validIndex(v, indice, "Version don't have enought composant"); String s = v[indice]; Validate.isTrue(StringUtils.isNumeric(s), "String '%s' must be numeric", s); int i = Integer.parseInt(s); i += inc; s = String.valueOf(i); v[indice] = s; version = StringUtils.join(v, SERIES_SEPARATOR); return this; } /** * Return new instance, this SemVer is not modified * @return new instance where major version number is incremented by 1 */ public SemVerCreator incMajor() { return incVersion(0, 1); } /** * Return new instance, this SemVer is not modified * @return new instance where major version number is incremented by 1 */ public SemVerCreator incMinor() { return incVersion(1, 1); } /** * Return new instance, this SemVer is not modified * @return new instance where major version number is incremented by 1 */ public SemVerCreator incPatch() { return incVersion(2, 1); } public SemVer done() { SemVer result = new SemVer( StringUtils.defaultString(version) + ifNotNull(PRERELEASE_SEPARATOR, prerelease) + ifNotNull(BUILD_SEPARATOR, build) + ifNotNull(SNAPSHOT_SEPARATOR, snapshot) ); return result; } } }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy