diff --git a/src/main/java/com/vdurmont/semver4j/Range.java b/src/main/java/com/vdurmont/semver4j/Range.java index ab9e94a..fa73acd 100644 --- a/src/main/java/com/vdurmont/semver4j/Range.java +++ b/src/main/java/com/vdurmont/semver4j/Range.java @@ -35,6 +35,10 @@ public boolean isSatisfiedBy(Semver version) { throw new RuntimeException("Code error. Unknown RangeOperator: " + this.op); // Should never happen } + @Override public String toString() { + return "(" + this.op + ", " + this.version + ")"; + } + public enum RangeOperator { /** * The version and the requirement are equivalent diff --git a/src/main/java/com/vdurmont/semver4j/Requirement.java b/src/main/java/com/vdurmont/semver4j/Requirement.java index 0877ac7..23643ab 100644 --- a/src/main/java/com/vdurmont/semver4j/Requirement.java +++ b/src/main/java/com/vdurmont/semver4j/Requirement.java @@ -6,6 +6,8 @@ import java.util.List; import java.util.NoSuchElementException; import java.util.Stack; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * A requirement will provide an easy way to check if a version is satisfying. @@ -14,6 +16,27 @@ * - NPM: follows the rules of NPM */ public class Requirement { + private static final Pattern IVY_DYNAMIC_PATCH_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\.\\+"); + private static final Pattern IVY_DYNAMIC_MINOR_PATTERN = Pattern.compile("(\\d+)\\.\\+"); + private static final Pattern IVY_LATEST_PATTERN = Pattern.compile("latest\\.\\w+"); + private static final Pattern IVY_MATH_BOUNDED_PATTERN = Pattern.compile( + "(\\[|\\])" + // 1ST GROUP: a square bracket + "([\\d\\.]+)" + // 2ND GROUP: a version + "," + // a comma separator + "([\\d\\.]+)" + // 3RD GROUP: a version + "(\\[|\\])" // 4TH GROUP: a square bracket + ); + private static final Pattern IVY_MATH_LOWER_UNBOUNDED_PATTERN = Pattern.compile( + "\\(," + // a parenthesis and a comma separator + "([\\d\\.]+)" + // 1ST GROUP: a version + "(\\[|\\])" // 2ND GROUP: a square bracket + ); + private static final Pattern IVY_MATH_UPPER_UNBOUNDED_PATTERN = Pattern.compile( + "(\\[|\\])" + // 1ST GROUP: a square bracket + "([\\d\\.]+)" + // 2ND GROUP: a version + ",\\)" // a comma separator and a parenthesis + ); + protected final Range range; protected final Requirement req1; protected final RequirementOperator op; @@ -108,7 +131,54 @@ private static Requirement buildWithTokenizer(String requirement, Semver.SemverT * @return the generated requirement */ public static Requirement buildIvy(String requirement) { - return new Requirement(null, null, null, null); + try { + return buildLoose(requirement); + } catch (SemverException ignored) { + } + + Matcher matcher = IVY_DYNAMIC_PATCH_PATTERN.matcher(requirement); + if (matcher.find()) { + int major = Integer.valueOf(matcher.group(1)); + int minor = Integer.valueOf(matcher.group(2)); + Requirement lower = new Requirement(new Range(major + "." + minor + ".0", Range.RangeOperator.GTE), null, null, null); + Requirement upper = new Requirement(new Range(major + "." + (minor + 1) + ".0", Range.RangeOperator.LT), null, null, null); + return new Requirement(null, lower, RequirementOperator.AND, upper); + } + matcher = IVY_DYNAMIC_MINOR_PATTERN.matcher(requirement); + if (matcher.find()) { + int major = Integer.valueOf(matcher.group(1)); + Requirement lower = new Requirement(new Range(major + ".0.0", Range.RangeOperator.GTE), null, null, null); + Requirement upper = new Requirement(new Range((major + 1) + ".0.0", Range.RangeOperator.LT), null, null, null); + return new Requirement(null, lower, RequirementOperator.AND, upper); + } + matcher = IVY_LATEST_PATTERN.matcher(requirement); + if (matcher.find()) { + return new Requirement(new Range("0.0.0", Range.RangeOperator.GTE), null, null, null); + } + matcher = IVY_MATH_BOUNDED_PATTERN.matcher(requirement); + if (matcher.find()) { + Range.RangeOperator lowerOp = "[".equals(matcher.group(1)) ? Range.RangeOperator.GTE : Range.RangeOperator.GT; + Semver lowerVersion = new Semver(matcher.group(2), Semver.SemverType.LOOSE); + Semver upperVersion = new Semver(matcher.group(3), Semver.SemverType.LOOSE); + Range.RangeOperator upperOp = "]".equals(matcher.group(4)) ? Range.RangeOperator.LTE : Range.RangeOperator.LT; + Requirement lower = new Requirement(new Range(extrapolateVersion(lowerVersion), lowerOp), null, null, null); + Requirement upper = new Requirement(new Range(extrapolateVersion(upperVersion), upperOp), null, null, null); + return new Requirement(null, lower, RequirementOperator.AND, upper); + } + matcher = IVY_MATH_LOWER_UNBOUNDED_PATTERN.matcher(requirement); + if (matcher.find()) { + Semver version = new Semver(matcher.group(1), Semver.SemverType.LOOSE); + Range.RangeOperator op = "]".equals(matcher.group(2)) ? Range.RangeOperator.LTE : Range.RangeOperator.LT; + return new Requirement(new Range(extrapolateVersion(version), op), null, null, null); + } + matcher = IVY_MATH_UPPER_UNBOUNDED_PATTERN.matcher(requirement); + if (matcher.find()) { + Range.RangeOperator op = "[".equals(matcher.group(1)) ? Range.RangeOperator.GTE : Range.RangeOperator.GT; + Semver version = new Semver(matcher.group(2), Semver.SemverType.LOOSE); + return new Requirement(new Range(extrapolateVersion(version), op), null, null, null); + } + + throw new SemverException("Invalid requirement"); } /** @@ -390,6 +460,13 @@ public boolean isSatisfiedBy(Semver version) { } } + @Override public String toString() { + if (this.range != null) { + return "Requirement{" + this.range + "}"; + } + return "Requirement{" + this.req1 + " " + this.op + " " + this.req2 + "}"; + } + /** * The operators that can be used in a requirement. */ diff --git a/src/main/java/com/vdurmont/semver4j/Semver.java b/src/main/java/com/vdurmont/semver4j/Semver.java index 229abdd..8e1d0b7 100644 --- a/src/main/java/com/vdurmont/semver4j/Semver.java +++ b/src/main/java/com/vdurmont/semver4j/Semver.java @@ -147,6 +147,9 @@ public boolean satisfies(String requirement) { case COCOAPODS: req = Requirement.buildCocoapods(requirement); break; + case IVY: + req = Requirement.buildIvy(requirement); + break; default: throw new SemverException("Invalid requirement type: " + type); } diff --git a/src/test/java/com/vdurmont/semver4j/RequirementTest.java b/src/test/java/com/vdurmont/semver4j/RequirementTest.java index cd042bf..261dbef 100644 --- a/src/test/java/com/vdurmont/semver4j/RequirementTest.java +++ b/src/test/java/com/vdurmont/semver4j/RequirementTest.java @@ -260,9 +260,6 @@ public class RequirementTest { @Test public void buildIvy_with_latest() { Requirement req = Requirement.buildIvy("latest.integration"); - assertNull(req.op); - assertNull(req.req1); - assertNull(req.req2); assertIsRange(req, "0.0.0", Range.RangeOperator.GTE); }