blob: 1e896041aa51a10f55652d690871d3a97676547c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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.apache.maven.shared.release.versions;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.maven.artifact.ArtifactUtils;
import org.codehaus.plexus.util.StringUtils;
/**
* <p>Version class.</p>
*/
public class Version implements Comparable<Version>, Cloneable {
private final AetherVersion aetherVersion;
private final MavenArtifactVersion mavenArtifactVersion;
private final String strVersion;
private final List<String> digits;
private String annotation;
private String annotationRevision;
private final String buildSpecifier;
private String annotationSeparator;
private String annotationRevSeparator;
private String buildSeparator;
private static final int DIGITS_INDEX = 1;
private static final int ANNOTATION_SEPARATOR_INDEX = 2;
private static final int ANNOTATION_INDEX = 3;
private static final int ANNOTATION_REV_SEPARATOR_INDEX = 4;
private static final int ANNOTATION_REVISION_INDEX = 5;
private static final int BUILD_SEPARATOR_INDEX = 6;
private static final int BUILD_SPECIFIER_INDEX = 7;
private static final String SNAPSHOT_IDENTIFIER = "SNAPSHOT";
private static final String DIGIT_SEPARATOR_STRING = ".";
private static final String DEFAULT_ANNOTATION_REV_SEPARATOR = "-";
private static final String DEFAULT_BUILD_SEPARATOR = "-";
/** Constant <code>STANDARD_PATTERN</code> */
public static final Pattern STANDARD_PATTERN = Pattern.compile(
"^((?:\\d+\\.)*\\d+)" // digit(s) and '.' repeated -
// followed by digit (version
// digits 1.22.0, etc)
+ "([-_])?" // optional - or _ (annotation separator)
+ "([a-zA-Z]*)" // alpha characters (looking for annotation - alpha, beta, RC, etc.)
+ "([-_])?" // optional - or _ (annotation revision separator)
+ "(\\d*)" // digits (any digits after rc or beta is an annotation revision)
+ "(?:([-_])?(.*?))?$"); // - or _ followed everything else (build specifier)
/* *
* cmaki 02242009 FIX for non-digit release numbers, e.g. trunk-SNAPSHOT or just SNAPSHOT This alternate pattern
* supports version numbers like: trunk-SNAPSHOT branchName-SNAPSHOT SNAPSHOT
*/
// for SNAPSHOT releases only (possible versions include: trunk-SNAPSHOT or SNAPSHOT)
/** Constant <code>ALTERNATE_PATTERN</code> */
public static final Pattern ALTERNATE_PATTERN = Pattern.compile("^(SNAPSHOT|[a-zA-Z]+[_-]SNAPSHOT)");
private Version(
List<String> digits,
String annotation,
String annotationRevision,
String buildSpecifier,
String annotationSeparator,
String annotationRevSeparator,
String buildSeparator) {
this.digits = digits;
this.annotation = annotation;
this.annotationRevision = annotationRevision;
this.buildSpecifier = buildSpecifier;
this.annotationSeparator = annotationSeparator;
this.annotationRevSeparator = annotationRevSeparator;
this.buildSeparator = buildSeparator;
this.strVersion = getVersionString(this, buildSpecifier, buildSeparator);
// for now no need to reparse, original version was valid
this.aetherVersion = null;
this.mavenArtifactVersion = null;
}
/**
* <p>Constructor for Version.</p>
*
* @param version a {@link java.lang.String} object
* @throws org.apache.maven.shared.release.versions.VersionParseException if any.
*/
public Version(String version) throws VersionParseException {
this.strVersion = version;
this.aetherVersion = new AetherVersion(version);
this.mavenArtifactVersion = new MavenArtifactVersion(version);
// FIX for non-digit release numbers, e.g. trunk-SNAPSHOT or just SNAPSHOT
Matcher matcher = ALTERNATE_PATTERN.matcher(strVersion);
// TODO: hack because it didn't support "SNAPSHOT"
if (matcher.matches()) {
annotation = null;
digits = null;
buildSpecifier = version;
buildSeparator = null;
return;
}
Matcher m = STANDARD_PATTERN.matcher(strVersion);
if (m.matches()) {
digits = parseDigits(m.group(DIGITS_INDEX));
if (!SNAPSHOT_IDENTIFIER.equals(m.group(ANNOTATION_INDEX))) {
annotationSeparator = m.group(ANNOTATION_SEPARATOR_INDEX);
annotation = nullIfEmpty(m.group(ANNOTATION_INDEX));
if (StringUtils.isNotEmpty(m.group(ANNOTATION_REV_SEPARATOR_INDEX))
&& StringUtils.isEmpty(m.group(ANNOTATION_REVISION_INDEX))) {
// The build separator was picked up as the annotation revision separator
buildSeparator = m.group(ANNOTATION_REV_SEPARATOR_INDEX);
buildSpecifier = nullIfEmpty(m.group(BUILD_SPECIFIER_INDEX));
} else {
annotationRevSeparator = m.group(ANNOTATION_REV_SEPARATOR_INDEX);
annotationRevision = nullIfEmpty(m.group(ANNOTATION_REVISION_INDEX));
buildSeparator = m.group(BUILD_SEPARATOR_INDEX);
buildSpecifier = nullIfEmpty(m.group(BUILD_SPECIFIER_INDEX));
}
} else {
// Annotation was "SNAPSHOT" so populate the build specifier with that data
buildSeparator = m.group(ANNOTATION_SEPARATOR_INDEX);
buildSpecifier = nullIfEmpty(m.group(ANNOTATION_INDEX));
}
} else {
throw new VersionParseException("Unable to parse the version string: \"" + version + "\"");
}
}
/**
* <p>isSnapshot.</p>
*
* @return a boolean
*/
public boolean isSnapshot() {
return ArtifactUtils.isSnapshot(strVersion);
}
/**
* <p>toString.</p>
*
* @return a {@link java.lang.String} object
*/
public String toString() {
return strVersion;
}
/**
* <p>getVersionString.</p>
*
* @param info a {@link org.apache.maven.shared.release.versions.Version} object
* @param buildSpecifier a {@link java.lang.String} object
* @param buildSeparator a {@link java.lang.String} object
* @return a {@link java.lang.String} object
*/
protected static String getVersionString(Version info, String buildSpecifier, String buildSeparator) {
StringBuilder sb = new StringBuilder();
if (info.digits != null) {
sb.append(joinDigitString(info.digits));
}
if (info.annotation != null && !info.annotation.isEmpty()) {
sb.append(StringUtils.defaultString(info.annotationSeparator));
sb.append(info.annotation);
}
if (info.annotationRevision != null && !info.annotationRevision.isEmpty()) {
if (info.annotation == null || info.annotation.isEmpty()) {
sb.append(StringUtils.defaultString(info.annotationSeparator));
} else {
sb.append(StringUtils.defaultString(info.annotationRevSeparator));
}
sb.append(info.annotationRevision);
}
if (buildSpecifier != null && !buildSpecifier.isEmpty()) {
sb.append(StringUtils.defaultString(buildSeparator));
sb.append(buildSpecifier);
}
return sb.toString();
}
/**
* Simply joins the items in the list with "." period
*
* @return a {@code String} containing the items in the list joined by "." period
* @param digits the list of digits {@code List<String>}
*/
protected static String joinDigitString(List<String> digits) {
return digits != null ? StringUtils.join(digits.iterator(), DIGIT_SEPARATOR_STRING) : null;
}
/**
* Splits the string on "." and returns a list containing each digit.
*
* @param strDigits
*/
private List<String> parseDigits(String strDigits) {
return Arrays.asList(StringUtils.split(strDigits, DIGIT_SEPARATOR_STRING));
}
private static String nullIfEmpty(String s) {
return (s == null || s.isEmpty()) ? null : s;
}
/**
* <p>Getter for the field <code>digits</code>.</p>
*
* @return a {@link java.util.List} object
*/
public List<String> getDigits() {
return digits;
}
/**
* <p>Getter for the field <code>annotation</code>.</p>
*
* @return a {@link java.lang.String} object
*/
public String getAnnotation() {
return annotation;
}
/**
* <p>Getter for the field <code>annotationRevSeparator</code>.</p>
*
* @return a {@link java.lang.String} object
*/
public String getAnnotationRevSeparator() {
return annotationRevSeparator;
}
/**
* <p>Getter for the field <code>annotationRevision</code>.</p>
*
* @return a {@link java.lang.String} object
*/
public String getAnnotationRevision() {
return annotationRevision;
}
/**
* <p>Getter for the field <code>buildSeparator</code>.</p>
*
* @return a {@link java.lang.String} object
*/
public String getBuildSeparator() {
return buildSeparator;
}
/**
* <p>Getter for the field <code>buildSpecifier</code>.</p>
*
* @return a {@link java.lang.String} object
*/
public String getBuildSpecifier() {
return buildSpecifier;
}
/**
* <p>Setter for the field <code>digits</code>.</p>
*
* @param newDigits the new list of digits
* @return a new instance of Version
*/
public Version setDigits(List<String> newDigits) {
return new Version(
newDigits,
this.annotation,
this.annotationRevision,
this.buildSpecifier,
this.annotationSeparator,
this.annotationRevSeparator,
this.buildSeparator);
}
/**
* <p>Setter for the field <code>annotationRevision</code>.</p>
*
* @param newAnnotationRevision the new annotation revision
* @return a new instance of Version
*/
public Version setAnnotationRevision(String newAnnotationRevision) {
return new Version(
this.digits,
this.annotation,
newAnnotationRevision,
this.buildSpecifier,
this.annotationSeparator,
Objects.toString(this.annotationRevSeparator, DEFAULT_ANNOTATION_REV_SEPARATOR),
this.buildSeparator);
}
/**
* <p>Setter for the field <code>buildSpecifier</code>.</p>
*
* @param newBuildSpecifier the new build specifier
* @return a new instance of Version
*/
public Version setBuildSpecifier(String newBuildSpecifier) {
return new Version(
this.digits,
this.annotation,
this.annotationRevision,
newBuildSpecifier,
this.annotationSeparator,
this.annotationRevSeparator,
Objects.toString(this.buildSeparator, DEFAULT_BUILD_SEPARATOR));
}
/**
* <p>compareTo.</p>
*
* @throws org.apache.maven.shared.release.versions.VersionComparisonConflictException
* if {@link org.eclipse.aether.version.Version} and
* {@link org.apache.maven.artifact.versioning.ArtifactVersion ArtifactVersion} give different results
* @param other a {@link org.apache.maven.shared.release.versions.Version} object
* @return a int
*/
public int compareTo(Version other) throws VersionComparisonConflictException {
int aetherComparisonResult = this.aetherVersion.compareTo(other.aetherVersion);
int mavenComparisonResult = this.mavenArtifactVersion.compareTo(other.mavenArtifactVersion);
if (aetherComparisonResult < 0 && mavenComparisonResult < 0) {
return -1;
} else if (aetherComparisonResult == 0 && mavenComparisonResult == 0) {
return 0;
} else if (aetherComparisonResult > 0 && mavenComparisonResult > 0) {
return 1;
} else {
throw new VersionComparisonConflictException(
this.strVersion, other.strVersion, aetherComparisonResult, mavenComparisonResult);
}
}
}