org.gradle.api.internalivyservice.ivyresolve.strategy.VersionRangeSelector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-api Show documentation
Show all versions of gradle-api Show documentation
Gradle 6.5 API redistribution.
/*
* Copyright 2013 the original author or authors.
*
* 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
*
* 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.gradle.api.internal.artifacts.ivyservice.ivyresolve.strategy;
import java.util.Comparator;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Matches version ranges:
*
* - [1.0,2.0] matches all versions greater or equal to 1.0 and lower or equal to 2.0
* - [1.0,2.0[ matches all versions greater or equal to 1.0 and lower than 2.0
* - ]1.0,2.0] matches all versions greater than 1.0 and lower or equal to 2.0
* - ]1.0,2.0[ matches all versions greater than 1.0 and lower than 2.0
* - [1.0,) matches all versions greater or equal to 1.0
* - ]1.0,) matches all versions greater than 1.0
* - (,2.0] matches all versions lower or equal to 2.0
* - (,2.0[matches all versions lower than 2.0
*
* This class uses a latest strategy to compare revisions. If
* none is set, it uses the default one of the ivy instance set through setIvy(). If neither a
* latest strategy nor a ivy instance is set, an IllegalStateException will be thrown when calling
* accept(). Note that it can't work with latest time strategy, cause no time is known for the
* limits of the range. Therefore only purely revision based LatestStrategy can be used.
*/
public class VersionRangeSelector extends AbstractVersionVersionSelector {
private static final String OPEN_INC = "[";
private static final String OPEN_EXC = "]";
private static final String OPEN_EXC_MAVEN = "(";
private static final String CLOSE_INC = "]";
private static final String CLOSE_EXC = "[";
private static final String CLOSE_EXC_MAVEN = ")";
private static final String LOWER_INFINITE = "(";
private static final String UPPER_INFINITE = ")";
private static final String SEPARATOR = ",";
// following patterns are built upon constants above and should not be modified
private static final String OPEN_INC_PATTERN = "\\" + OPEN_INC;
private static final String OPEN_EXC_PATTERN = "\\" + OPEN_EXC + "\\" + OPEN_EXC_MAVEN;
private static final String CLOSE_INC_PATTERN = "\\" + CLOSE_INC;
private static final String CLOSE_EXC_PATTERN = "\\" + CLOSE_EXC + "\\" + CLOSE_EXC_MAVEN;
private static final String LI_PATTERN = "\\" + LOWER_INFINITE;
private static final String UI_PATTERN = "\\" + UPPER_INFINITE;
private static final String SEP_PATTERN = "\\s*\\" + SEPARATOR + "\\s*";
private static final String OPEN_PATTERN = "[" + OPEN_INC_PATTERN + OPEN_EXC_PATTERN + "]";
private static final String CLOSE_PATTERN = "[" + CLOSE_INC_PATTERN + CLOSE_EXC_PATTERN + "]";
private static final String ANY_NON_SPECIAL_PATTERN = "[^\\s" + SEPARATOR + OPEN_INC_PATTERN
+ OPEN_EXC_PATTERN + CLOSE_INC_PATTERN + CLOSE_EXC_PATTERN + LI_PATTERN + UI_PATTERN
+ "]";
private static final String FINITE_PATTERN = OPEN_PATTERN + "\\s*(" + ANY_NON_SPECIAL_PATTERN
+ "+)" + SEP_PATTERN + "(" + ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN;
private static final String LOWER_INFINITE_PATTERN = LI_PATTERN + SEP_PATTERN + "("
+ ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN;
private static final String UPPER_INFINITE_PATTERN = OPEN_PATTERN + "\\s*("
+ ANY_NON_SPECIAL_PATTERN + "+)" + SEP_PATTERN + UI_PATTERN;
private static final String SINGLE_VALUE_PATTERN = OPEN_INC_PATTERN + "\\s*(" + ANY_NON_SPECIAL_PATTERN + "+)" + CLOSE_INC_PATTERN;
private static final Pattern FINITE_RANGE = Pattern.compile(FINITE_PATTERN);
private static final Pattern LOWER_INFINITE_RANGE = Pattern.compile(LOWER_INFINITE_PATTERN);
private static final Pattern UPPER_INFINITE_RANGE = Pattern.compile(UPPER_INFINITE_PATTERN);
private static final Pattern SINGLE_VALUE_RANGE = Pattern.compile(SINGLE_VALUE_PATTERN);
public static final Pattern ALL_RANGE = Pattern.compile(FINITE_PATTERN + "|"
+ LOWER_INFINITE_PATTERN + "|" + UPPER_INFINITE_PATTERN + "|" + SINGLE_VALUE_RANGE);
private final String upperBound;
private final Version upperBoundVersion;
private final boolean upperInclusive;
private final String lowerBound;
private final boolean lowerInclusive;
private final Version lowerBoundVersion;
private final Comparator comparator;
private final boolean smartExcludeUpperBound;
public VersionRangeSelector(String selector, Comparator comparator, VersionParser versionParser) {
this(selector, comparator, versionParser, false);
}
public VersionRangeSelector(String selector, Comparator comparator, VersionParser versionParser, boolean smartExcludeUpperBound) {
super(versionParser, selector);
this.comparator = comparator;
this.smartExcludeUpperBound = smartExcludeUpperBound;
Matcher matcher;
matcher = FINITE_RANGE.matcher(selector);
if (matcher.matches()) {
lowerBound = matcher.group(1);
lowerInclusive = selector.startsWith(OPEN_INC);
upperBound = matcher.group(2);
upperInclusive = selector.endsWith(CLOSE_INC);
} else {
matcher = LOWER_INFINITE_RANGE.matcher(selector);
if (matcher.matches()) {
lowerBound = null;
lowerInclusive = true;
upperBound = matcher.group(1);
upperInclusive = selector.endsWith(CLOSE_INC);
} else {
matcher = UPPER_INFINITE_RANGE.matcher(selector);
if (matcher.matches()) {
lowerBound = matcher.group(1);
lowerInclusive = selector.startsWith(OPEN_INC);
upperBound = null;
upperInclusive = true;
} else {
matcher = SINGLE_VALUE_RANGE.matcher(selector);
if (matcher.matches()) {
lowerBound = matcher.group(1);
lowerInclusive = true;
upperBound = lowerBound;
upperInclusive = true;
} else {
throw new IllegalArgumentException("Not a version range selector: " + selector);
}
}
}
}
lowerBoundVersion = lowerBound == null ? null : versionParser.transform(lowerBound);
upperBoundVersion = upperBound == null ? null : versionParser.transform(upperBound);
}
@Override
public boolean isDynamic() {
return true;
}
@Override
public boolean requiresMetadata() {
return false;
}
@Override
public boolean matchesUniqueVersion() {
return false;
}
@Override
public boolean accept(Version candidate) {
if (lowerBound != null && !isHigher(candidate, lowerBoundVersion, lowerInclusive)) {
return false;
}
return upperBound == null || isLower(candidate, upperBoundVersion, upperInclusive);
}
/**
* Tells if version1 is lower than version2.
*/
private boolean isLower(Version version1, Version version2, boolean inclusive) {
int result = comparator.compare(version1, version2);
if (inclusive) {
return result <= 0;
} else {
// For non inclusive upper bound, we also check that the prefix does not match
if (result <= -1) {
if (smartExcludeUpperBound) {
return !version1.toString().startsWith(version2.toString());
} else {
return true;
}
} else {
return false;
}
}
}
/**
* Tells if version1 is higher than version2.
*/
private boolean isHigher(Version version1, Version version2, boolean inclusive) {
int result = comparator.compare(version1, version2);
return result >= (inclusive ? 0 : 1);
}
public String getUpperBound() {
return upperBound;
}
public Version getUpperBoundVersion() {
return upperBoundVersion;
}
public boolean isUpperInclusive() {
return upperInclusive;
}
public String getLowerBound() {
return lowerBound;
}
public boolean isLowerInclusive() {
return lowerInclusive;
}
public Version getLowerBoundVersion() {
return lowerBoundVersion;
}
@Override
public String toString() {
return getSelector();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
VersionRangeSelector that = (VersionRangeSelector) o;
if (upperInclusive != that.upperInclusive) {
return false;
}
if (lowerInclusive != that.lowerInclusive) {
return false;
}
if (!Objects.equals(upperBound, that.upperBound)) {
return false;
}
if (!Objects.equals(upperBoundVersion, that.upperBoundVersion)) {
return false;
}
if (!Objects.equals(lowerBound, that.lowerBound)) {
return false;
}
if (!Objects.equals(lowerBoundVersion, that.lowerBoundVersion)) {
return false;
}
return comparator.equals(that.comparator);
}
@Override
public int hashCode() {
int result = upperBound != null ? upperBound.hashCode() : 0;
result = 31 * result + (upperBoundVersion != null ? upperBoundVersion.hashCode() : 0);
result = 31 * result + (upperInclusive ? 1 : 0);
result = 31 * result + (lowerBound != null ? lowerBound.hashCode() : 0);
result = 31 * result + (lowerInclusive ? 1 : 0);
result = 31 * result + (lowerBoundVersion != null ? lowerBoundVersion.hashCode() : 0);
result = 31 * result + comparator.hashCode();
return result;
}
}