
name.remal.version.VersionRange Maven / Gradle / Ivy
package name.remal.version;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonValue;
import net.jcip.annotations.Immutable;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.Serializable;
import java.util.Objects;
import static java.lang.String.format;
@Immutable
public final class VersionRange implements Serializable, Cloneable {
@NotNull
public static final VersionRange ANY_VERSION = new VersionRange(null, false, null, false);
private static final long serialVersionUID = 1;
private static final String VERSION_RAGE_CANT_BE_PARSED_MESSAGE_FORMAT = "Version range can't be parsed: '%s'. Only these formats are supported: '1.1+', '1.1.+', '[1,2]', '[1,2)', '(1,2]', '(1,2)'.";
private static final String SUFFIXED_VERSIONS_CANT_BE_USED_MESSAGE_FORMAT = "Suffixed versions can't be used for version range: '%s'";
@NotNull
public static VersionRange create(@Nullable Version lowerBound, boolean lowerBoundInclusive, @Nullable Version upperBound, boolean upperBoundInclusive) {
if (lowerBound == null && upperBound == null) return ANY_VERSION;
return new VersionRange(lowerBound, lowerBoundInclusive, upperBound, upperBoundInclusive);
}
@NotNull
public static VersionRange create(@NotNull Version version) {
return new VersionRange(version, true, version, true);
}
@NotNull
public static VersionRange greaterThan(@NotNull Version version) {
return create(version, false, null, false);
}
@NotNull
public static VersionRange greaterThanOrEqual(@NotNull Version version) {
return create(version, true, null, false);
}
@NotNull
public static VersionRange lessThan(@NotNull Version version) {
return create(null, false, version, false);
}
@NotNull
public static VersionRange lessThanOrEqual(@NotNull Version version) {
return create(null, false, version, true);
}
@NotNull
public static VersionRange parse(@NotNull String string) throws VersionRangeParsingException {
string = string.trim();
if (string.isEmpty()) {
throw new VersionRangeParsingException("Blank string");
}
try {
if ("+".equals(string)) {
return ANY_VERSION;
}
if (string.contains("+")) {
if (!string.endsWith("+")) {
throw new VersionRangeParsingException(format(VERSION_RAGE_CANT_BE_PARSED_MESSAGE_FORMAT, string));
}
if (string.contains("[") || string.contains("(") || string.contains("]") || string.contains(")")) {
throw new VersionRangeParsingException(format(VERSION_RAGE_CANT_BE_PARSED_MESSAGE_FORMAT, string));
}
String versionString = string.substring(0, string.length() - 1).trim();
boolean endsWithDot = versionString.endsWith(".");
if (endsWithDot) {
versionString = versionString.substring(0, versionString.length() - 1).trim();
}
Version version = Version.parse(versionString);
if (version.hasSuffix()) {
throw new VersionRangeParsingException(format(SUFFIXED_VERSIONS_CANT_BE_USED_MESSAGE_FORMAT, string));
}
return create(
endsWithDot ? version.withNumber(version.getNumbersCount(), 0) : version,
true,
endsWithDot ? version.incrementNumber(version.getNumbersCount() - 1) : null,
false
);
}
if (string.contains("[") || string.contains("(") || string.contains("]") || string.contains(")")) {
if (!string.startsWith("[") && !string.startsWith("(")) {
throw new VersionRangeParsingException(format(VERSION_RAGE_CANT_BE_PARSED_MESSAGE_FORMAT, string));
}
if (!string.endsWith("]") && !string.endsWith(")")) {
throw new VersionRangeParsingException(format(VERSION_RAGE_CANT_BE_PARSED_MESSAGE_FORMAT, string));
}
int delimPos = string.indexOf(';');
if (delimPos < 0) delimPos = string.indexOf(',');
if (delimPos < 0) {
throw new VersionRangeParsingException(format(VERSION_RAGE_CANT_BE_PARSED_MESSAGE_FORMAT, string));
}
final Version lowerBound;
{
String lowerBoundString = string.substring(1, delimPos).trim();
if (!lowerBoundString.isEmpty()) {
lowerBound = Version.parse(lowerBoundString);
if (lowerBound.hasSuffix()) {
throw new VersionRangeParsingException(format(SUFFIXED_VERSIONS_CANT_BE_USED_MESSAGE_FORMAT, string));
}
} else {
lowerBound = null;
}
}
final Version upperBound;
{
String upperBoundString = string.substring(delimPos + 1, string.length() - 1).trim();
if (!upperBoundString.isEmpty()) {
upperBound = Version.parse(upperBoundString);
if (upperBound.hasSuffix()) {
throw new VersionRangeParsingException(format(SUFFIXED_VERSIONS_CANT_BE_USED_MESSAGE_FORMAT, string));
}
} else {
upperBound = null;
}
}
return create(
lowerBound,
string.startsWith("["),
upperBound,
string.endsWith("]")
);
}
{
Version version = Version.parse(string);
return create(version);
}
} catch (Exception e) {
throw e instanceof VersionRangeParsingException ? (VersionRangeParsingException) e : new VersionRangeParsingException(e);
}
}
@Nullable
@Contract("null->null")
@JsonCreator
public static VersionRange parseOrNull(@Nullable String string) {
if (string == null || string.isEmpty()) return null;
try {
return parse(string);
} catch (VersionRangeParsingException ignored) {
return null;
}
}
@Nullable
private final Version lowerBound;
private final boolean lowerBoundInclusive;
@Nullable
private final Version upperBound;
private final boolean upperBoundInclusive;
private VersionRange(@Nullable Version lowerBound, boolean lowerBoundInclusive, @Nullable Version upperBound, boolean upperBoundInclusive) {
if (lowerBound != null && upperBound != null) {
if (lowerBound.compareTo(upperBound) > 0) {
throw new IllegalArgumentException(format("Lower bound version '%s' greater then upper bound '%s'", lowerBound, upperBound));
}
if (lowerBound.equals(upperBound) && (lowerBoundInclusive || upperBoundInclusive)) {
lowerBoundInclusive = true;
upperBoundInclusive = true;
}
}
this.lowerBound = lowerBound;
this.lowerBoundInclusive = lowerBound != null && lowerBoundInclusive;
this.upperBound = upperBound;
this.upperBoundInclusive = upperBound != null && upperBoundInclusive;
}
public boolean contains(@NotNull Version version) {
if (lowerBound != null) {
int comparisonResult = lowerBound.compareTo(version);
if (lowerBoundInclusive && 0 < comparisonResult) return false;
if (!lowerBoundInclusive && 0 <= comparisonResult) return false;
}
if (upperBound != null) {
int comparisonResult = upperBound.compareTo(version);
if (upperBoundInclusive && comparisonResult < 0) return false;
if (!upperBoundInclusive && comparisonResult <= 0) return false;
}
return true;
}
public boolean contains(@NotNull VersionRange versionRange) {
if (versionRange.lowerBound == null && versionRange.upperBound == null) {
return lowerBound == null && upperBound == null;
}
if (versionRange.lowerBound != null) {
if (!versionRange.lowerBoundInclusive && !lowerBoundInclusive) {
if (!versionRange.lowerBound.equals(lowerBound) && !contains(versionRange.lowerBound)) return false;
} else {
if (!contains(versionRange.lowerBound)) return false;
}
}
if (versionRange.upperBound != null) {
if (!versionRange.upperBoundInclusive && !upperBoundInclusive) {
if (!versionRange.upperBound.equals(upperBound) && !contains(versionRange.upperBound)) return false;
} else {
if (!contains(versionRange.upperBound)) return false;
}
}
return true;
}
public boolean isBoundToVersion() {
return lowerBoundInclusive && upperBoundInclusive && lowerBound != null && lowerBound.equals(upperBound);
}
@Nullable
public Version getLowerBound() {
return lowerBound;
}
public boolean isLowerBoundInclusive() {
return lowerBoundInclusive;
}
@Nullable
public Version getUpperBound() {
return upperBound;
}
public boolean isUpperBoundInclusive() {
return upperBoundInclusive;
}
@Override
@NotNull
@JsonValue
public String toString() {
if (lowerBound == null && upperBound == null) {
return "+";
}
if (lowerBoundInclusive && upperBoundInclusive && lowerBound != null && lowerBound.equals(upperBound)) {
return lowerBound.toString();
}
if (lowerBoundInclusive && !upperBoundInclusive && lowerBound != null && upperBound != null) {
int lowerBoundNumberCount = lowerBound.getNumbersCount();
int upperBoundNumberCount = upperBound.getNumbersCount();
if (lowerBoundNumberCount - 1 == upperBoundNumberCount && 0 == lowerBound.getNumberOr0(lowerBoundNumberCount - 1)) {
Version cutLowerBound = lowerBound.withoutNumber(lowerBoundNumberCount - 1);
if (upperBound.equals(cutLowerBound.incrementNumber(upperBoundNumberCount - 1))) {
return cutLowerBound + ".+";
}
}
}
StringBuilder sb = new StringBuilder();
if (lowerBoundInclusive) {
sb.append('[');
} else {
sb.append('(');
}
if (lowerBound != null) {
sb.append(lowerBound);
}
sb.append(',');
if (upperBound != null) {
sb.append(upperBound);
}
if (upperBoundInclusive) {
sb.append(']');
} else {
sb.append(')');
}
return sb.toString();
}
@Override
public boolean equals(@Nullable Object obj) {
if (this == obj) return true;
if (!(obj instanceof VersionRange)) return false;
VersionRange other = (VersionRange) obj;
if (lowerBoundInclusive != other.lowerBoundInclusive) return false;
if (upperBoundInclusive != other.upperBoundInclusive) return false;
if (!Objects.equals(lowerBound, other.lowerBound)) return false;
if (!Objects.equals(upperBound, other.upperBound)) return false;
return true;
}
@Override
public int hashCode() {
int result = 1;
final int prime = 31;
result = prime * result + Objects.hashCode(lowerBound);
result = prime * result + Boolean.hashCode(lowerBoundInclusive);
result = prime * result + Objects.hashCode(upperBound);
result = prime * result + Boolean.hashCode(upperBoundInclusive);
return result;
}
@Override
@NotNull
public VersionRange clone() {
return new VersionRange(lowerBound, lowerBoundInclusive, upperBound, upperBoundInclusive);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy