/atlassian-plugins-core/src/main/java/com/atlassian/plugin/util/VersionRange.java
https://bitbucket.org/purewind/atlassian-plugins · Java · 438 lines · 325 code · 67 blank · 46 comment · 76 complexity · e47afbb863598f784d17f7206f79b82d MD5 · raw file
- package com.atlassian.plugin.util;
- import com.google.common.base.Objects;
- import java.util.Comparator;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- import static com.atlassian.plugin.util.VersionStringComparator.VALID_VERSION_PATTERN;
- import static com.google.common.base.Preconditions.checkNotNull;
- import static com.google.common.base.Preconditions.checkState;
- /**
- * Represents a version range. Version ranges can be built programmatically or parsed with the
- * following definition:
- * <table>
- * <thead>
- * <th>Range</th>
- * <th>Meaning</th>
- * </thead>
- * <tbody>
- * <tr>
- * <td>1.0</td>
- * <td>x >= 1.0</td>
- * </tr>
- * <tr>
- * <td>(,1.0]</td>
- * <td>x <= 1.0</td>
- * </tr>
- * <tr>
- * <td>(,1.0)</td>
- * <td>x < 1.0</td>
- * </tr>
- * <tr>
- * <td>[1.0]</td>
- * <td>x == 1.0</td>
- * </tr>
- * <tr>
- * <td>[1.0,)</td>
- * <td>x >= 1.0</td>
- * </tr>
- * <tr>
- * <td>(1.0,)</td>
- * <td>x > 1.0</td>
- * </tr>
- * <tr>
- * <td>(1.0,2.0)</td>
- * <td>1.0 < x < 2.0</td>
- * </tr>
- * <tr>
- * <td>[1.0,2.0]</td>
- * <td>1.0 <= x <= 2.0</td>
- * </tr>
- * </tbody>
- * </table>
- *
- * @since 3.0
- */
- public abstract class VersionRange {
- private static final Pattern RANGE_PATTERN = Pattern.compile("(\\(|\\[)?(" + VALID_VERSION_PATTERN + ")?(?:,(" + VALID_VERSION_PATTERN + ")?)?(\\)|\\])?");
- private VersionRange() {
- }
- abstract boolean isInRange(String version);
- public VersionRange or(VersionRange other) {
- return new OrVersionRange(this, other);
- }
- public static VersionRange empty() {
- return new EmptyVersionRange();
- }
- public static VersionRange all() {
- return new AllVersionRange();
- }
- public static VersionRange parse(String range) {
- final Matcher matcher = RANGE_PATTERN.matcher(range);
- checkState(matcher.matches(), "Range '" + range + "' doesn't match pattern " + RANGE_PATTERN.pattern());
- final String leftParenthesis = matcher.group(1);
- final String leftVersion = matcher.group(2);
- final String rightVersion = matcher.group(3);
- final String rightParenthesis = matcher.group(4);
- checkState(leftVersion != null || rightVersion != null, "No version configured for range!");
- if (leftParenthesis == null) {
- checkState(leftVersion != null);
- checkState(rightParenthesis == null);
- checkState(rightVersion == null);
- return VersionRange.include(leftVersion).unbounded();
- } else if (leftParenthesis.equals("[") && rightParenthesis.equals("]") && rightVersion == null) // single version
- {
- return VersionRange.single(leftVersion);
- } else {
- final ActualVersionRangeBuilder builder;
- if (leftParenthesis.equals("[")) {
- checkState(leftVersion != null);
- builder = VersionRange.include(leftVersion);
- } else if (leftParenthesis.equals("(")) {
- if (leftVersion != null) {
- builder = VersionRange.exclude(leftVersion);
- } else {
- builder = VersionRange.unbounded();
- }
- } else {
- throw new IllegalStateException("Incorrect start of range! " + leftParenthesis);
- }
- if (rightParenthesis.equals("]")) {
- checkState(rightVersion != null);
- return builder.include(rightVersion);
- } else if (rightParenthesis.equals(")")) {
- if (rightVersion != null) {
- return builder.exclude(rightVersion);
- } else {
- return builder.unbounded();
- }
- } else {
- throw new IllegalStateException("Incorrect ent of range! " + rightParenthesis);
- }
- }
- }
- public static VersionRange single(String version) {
- return new SingleVersionRange(version);
- }
- public static ActualVersionRangeBuilder include(String version) {
- return new ActualVersionRangeBuilder(true, version);
- }
- public static ActualVersionRangeBuilder exclude(String version) {
- return new ActualVersionRangeBuilder(false, version);
- }
- public static ActualVersionRangeBuilder unbounded() {
- return new ActualVersionRangeBuilder(true, null);
- }
- private static class SingleVersionRange extends VersionRange {
- private final String version;
- private SingleVersionRange(String version) {
- this.version = checkNotNull(version);
- }
- @Override
- boolean isInRange(String v) {
- return newVersionComparator().compare(this.version, v) == 0;
- }
- @Override
- public int hashCode() {
- return Objects.hashCode(version);
- }
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final SingleVersionRange that = (SingleVersionRange) obj;
- return Objects.equal(this.version, that.version);
- }
- @Override
- public String toString() {
- return "[" + version + "]";
- }
- }
- public static final class ActualVersionRangeBuilder {
- private final boolean leftIncluded;
- private final String leftVersion;
- public ActualVersionRangeBuilder(boolean leftIncluded, String leftVersion) {
- this.leftIncluded = leftIncluded;
- this.leftVersion = leftVersion;
- }
- public VersionRange include(String version) {
- return newRange(version, true);
- }
- public VersionRange exclude(String version) {
- return newRange(version, false);
- }
- private VersionRange newRange(String version, boolean rightIncluded) {
- if (leftVersion != null) {
- return newActualRange(version, rightIncluded);
- } else {
- return newLeftUnboundedRange(version, rightIncluded);
- }
- }
- private LeftUnboundedVersionRange newLeftUnboundedRange(String version, boolean rightIncluded) {
- return new LeftUnboundedVersionRange(rightIncluded, version);
- }
- private ActualVersionRange newActualRange(String version, boolean rightIncluded) {
- return new ActualVersionRange(leftIncluded, leftVersion, rightIncluded, version);
- }
- public VersionRange unbounded() {
- if (leftVersion == null) {
- throw new IllegalStateException();
- }
- return new RightUnboundedVersionRange(leftIncluded, leftVersion);
- }
- }
- private static final class ActualVersionRange extends VersionRange {
- private final boolean leftIncluded;
- private final String leftVersion;
- private final boolean rightIncluded;
- private final String rightVersion;
- private ActualVersionRange(
- boolean leftIncluded, String leftVersion,
- boolean rightIncluded, String rightVersion) {
- this.leftIncluded = leftIncluded;
- this.leftVersion = checkNotNull(leftVersion);
- this.rightIncluded = rightIncluded;
- this.rightVersion = checkNotNull(rightVersion);
- }
- @Override
- boolean isInRange(String v) {
- return isGreaterThan(leftIncluded, leftVersion, v)
- && isLowerThan(v, rightVersion, rightIncluded);
- }
- @Override
- public int hashCode() {
- return Objects.hashCode(leftIncluded, leftVersion, rightIncluded, rightVersion);
- }
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final ActualVersionRange that = (ActualVersionRange) obj;
- return Objects.equal(this.leftIncluded, that.leftIncluded)
- && Objects.equal(this.leftVersion, that.leftVersion)
- && Objects.equal(this.rightIncluded, that.rightIncluded)
- && Objects.equal(this.rightVersion, that.rightVersion);
- }
- @Override
- public String toString() {
- return (leftIncluded ? "[" : "(") + leftVersion + "," + rightVersion + (rightIncluded ? "]" : ")");
- }
- }
- private static final class LeftUnboundedVersionRange extends VersionRange {
- private final boolean rightIncluded;
- private final String rightVersion;
- private LeftUnboundedVersionRange(boolean rightIncluded, String rightVersion) {
- this.rightIncluded = rightIncluded;
- this.rightVersion = checkNotNull(rightVersion);
- }
- @Override
- boolean isInRange(String v) {
- return isLowerThan(v, rightVersion, rightIncluded);
- }
- @Override
- public int hashCode() {
- return Objects.hashCode(rightIncluded, rightVersion);
- }
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final LeftUnboundedVersionRange that = (LeftUnboundedVersionRange) obj;
- return Objects.equal(this.rightIncluded, that.rightIncluded)
- && Objects.equal(this.rightVersion, that.rightVersion);
- }
- @Override
- public String toString() {
- return "(," + rightVersion + (rightIncluded ? "]" : ")");
- }
- }
- private static final class RightUnboundedVersionRange extends VersionRange {
- private final boolean leftIncluded;
- private final String leftVersion;
- private RightUnboundedVersionRange(boolean leftIncluded, String leftVersion) {
- this.leftIncluded = leftIncluded;
- this.leftVersion = checkNotNull(leftVersion);
- }
- @Override
- boolean isInRange(String v) {
- return isGreaterThan(leftIncluded, leftVersion, v);
- }
- @Override
- public int hashCode() {
- return Objects.hashCode(leftIncluded, leftVersion);
- }
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final RightUnboundedVersionRange that = (RightUnboundedVersionRange) obj;
- return Objects.equal(this.leftIncluded, that.leftIncluded)
- && Objects.equal(this.leftVersion, that.leftVersion);
- }
- @Override
- public String toString() {
- return (leftIncluded ? "[" : "(") + leftVersion + ",)";
- }
- }
- private static final class AllVersionRange extends VersionRange {
- @Override
- boolean isInRange(String version) {
- return true;
- }
- @Override
- public int hashCode() {
- return 1;
- }
- @Override
- public boolean equals(Object obj) {
- return obj != null && getClass() == obj.getClass();
- }
- @Override
- public String toString() {
- return "(,)";
- }
- }
- private static final class EmptyVersionRange extends VersionRange {
- @Override
- boolean isInRange(String version) {
- return false;
- }
- @Override
- public int hashCode() {
- return 2;
- }
- @Override
- public boolean equals(Object obj) {
- return obj != null && getClass() == obj.getClass();
- }
- @Override
- public String toString() {
- return "()";
- }
- }
- private static final class OrVersionRange extends VersionRange {
- private final VersionRange or1;
- private final VersionRange or2;
- private OrVersionRange(VersionRange or1, VersionRange or2) {
- this.or1 = checkNotNull(or1);
- this.or2 = checkNotNull(or2);
- }
- @Override
- boolean isInRange(String v) {
- return or1.isInRange(v) || or2.isInRange(v);
- }
- @Override
- public int hashCode() {
- return Objects.hashCode(or1, or2);
- }
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (getClass() != obj.getClass()) {
- return false;
- }
- final OrVersionRange that = (OrVersionRange) obj;
- return Objects.equal(this.or1, that.or1)
- && Objects.equal(this.or2, that.or2);
- }
- @Override
- public String toString() {
- return or1 + "," + or2;
- }
- }
- private static boolean isLowerThan(String version, String rightVersion, boolean rightIncluded) {
- final int rightCompare = newVersionComparator().compare(rightVersion, version);
- return rightCompare > 0 || (rightIncluded && rightCompare == 0);
- }
- private static boolean isGreaterThan(boolean leftIncluded, String leftVersion, String version) {
- final int leftCompare = newVersionComparator().compare(version, leftVersion);
- return leftCompare > 0 || (leftIncluded && leftCompare == 0);
- }
- private static Comparator<String> newVersionComparator() {
- return new VersionStringComparator();
- }
- }