Implement internal DSL for SemVer Expressions
This commit is contained in:
parent
ebb6737f13
commit
e1aa90c2ec
26
README.md
26
README.md
@ -231,12 +231,26 @@ int result = v1.compareWithBuildsTo(v2); // < 0
|
||||
|
||||
SemVer Expressions API (Ranges)
|
||||
----------------------
|
||||
Since version 0.7.0 Java SemVer supports the SemVer Expressions API which is
|
||||
implemented as an external DSL. The BNF grammar for the SemVer Expressions DSL
|
||||
can be found in the corresponding issue
|
||||
"[Implement the SemVer Expressions API](https://github.com/zafarkhaja/java-semver/issues/1)".
|
||||
Java SemVer supports the SemVer Expressions API which is implemented as both
|
||||
internal DSL and external DSL. The entry point for the API are
|
||||
the `Version.satisfies` methods.
|
||||
|
||||
The entry point for the API is the `Version.satisfies` method.
|
||||
### Internal DSL ###
|
||||
The internal DSL is implemented by the `CompositeExpression` class using fluent
|
||||
interface. For convenience, it also provides the `Helper` class with static
|
||||
helper methods.
|
||||
|
||||
~~~ java
|
||||
import com.github.zafarkhaja.semver.Version;
|
||||
import static com.github.zafarkhaja.semver.expr.CompositeExpression.Helper.*;
|
||||
|
||||
Version v = Version.valueOf("1.0.0-beta");
|
||||
boolean result = v.satisfies(gte("1.0.0").and(lt("2.0.0"))); // false
|
||||
~~~
|
||||
|
||||
### External DSL ###
|
||||
The BNF grammar for the external DSL can be found in the corresponding
|
||||
[issue](https://github.com/zafarkhaja/java-semver/issues/1).
|
||||
|
||||
~~~ java
|
||||
import com.github.zafarkhaja.semver.Version;
|
||||
@ -246,7 +260,7 @@ boolean result = v.satisfies(">=1.0.0 & <2.0.0"); // false
|
||||
~~~
|
||||
|
||||
Below are examples of some common use cases, as well as syntactic sugar and some
|
||||
other interesting capabilities of the SemVer Expressions DSL.
|
||||
other interesting capabilities of the SemVer Expressions external DSL.
|
||||
* Wildcard - `1.*` which is equivalent to `>=1.0.0 & <2.0.0`
|
||||
* Tilde operator - `~1.5` which is equivalent to `>=1.5.0 & <2.0.0`
|
||||
* Range - `1.0-2.0` which is equivalent to `>=1.0.0 & <=2.0.0`
|
||||
|
@ -308,11 +308,11 @@ public class Version implements Comparable<Version> {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this version satisfies the specified SemVer Expression.
|
||||
* Checks if this version satisfies the specified SemVer Expression string.
|
||||
*
|
||||
* This method is a part of the SemVer Expressions API.
|
||||
*
|
||||
* @param expr the SemVer Expression
|
||||
* @param expr the SemVer Expression string
|
||||
* @return {@code true} if this version satisfies the specified
|
||||
* SemVer Expression or {@code false} otherwise
|
||||
* @throws ParseException in case of a general parse error
|
||||
@ -322,7 +322,21 @@ public class Version implements Comparable<Version> {
|
||||
*/
|
||||
public boolean satisfies(String expr) {
|
||||
Parser<Expression> parser = ExpressionParser.newInstance();
|
||||
return parser.parse(expr).interpret(this);
|
||||
return satisfies(parser.parse(expr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this version satisfies the specified SemVer Expression.
|
||||
*
|
||||
* This method is a part of the SemVer Expressions API.
|
||||
*
|
||||
* @param expr the SemVer Expression
|
||||
* @return {@code true} if this version satisfies the specified
|
||||
* SemVer Expression or {@code false} otherwise
|
||||
* @since 0.9.0
|
||||
*/
|
||||
public boolean satisfies(Expression expr) {
|
||||
return expr.interpret(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,266 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2012-2014 Zafar Khaja <zafarkhaja@gmail.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.github.zafarkhaja.semver.expr;
|
||||
|
||||
import com.github.zafarkhaja.semver.ParseException;
|
||||
import com.github.zafarkhaja.semver.UnexpectedCharacterException;
|
||||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* This class implements internal DSL for the
|
||||
* SemVer Expressions using fluent interface.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.9.0
|
||||
*/
|
||||
public class CompositeExpression implements Expression {
|
||||
|
||||
/**
|
||||
* A class with static helper methods.
|
||||
*/
|
||||
public static class Helper {
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with
|
||||
* an underlying {@code Not} expression.
|
||||
*
|
||||
* @param expr an {@code Expression} to negate
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
*/
|
||||
public static CompositeExpression not(Expression expr) {
|
||||
return new CompositeExpression(new Not(expr));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with
|
||||
* an underlying {@code Equal} expression.
|
||||
*
|
||||
* @param version a {@code Version} to check for equality
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
*/
|
||||
public static CompositeExpression eq(Version version) {
|
||||
return new CompositeExpression(new Equal(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with
|
||||
* an underlying {@code Equal} expression.
|
||||
*
|
||||
* @param version a {@code Version} string to check for equality
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
* @throws IllegalArgumentException if the input string is {@code NULL} or empty
|
||||
* @throws ParseException when invalid version string is provided
|
||||
* @throws UnexpectedCharacterException is a special case of {@code ParseException}
|
||||
*/
|
||||
public static CompositeExpression eq(String version) {
|
||||
return eq(Version.valueOf(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with
|
||||
* an underlying {@code NotEqual} expression.
|
||||
*
|
||||
* @param version a {@code Version} to check for non-equality
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
*/
|
||||
public static CompositeExpression neq(Version version) {
|
||||
return new CompositeExpression(new NotEqual(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with
|
||||
* an underlying {@code NotEqual} expression.
|
||||
*
|
||||
* @param version a {@code Version} string to check for non-equality
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
* @throws IllegalArgumentException if the input string is {@code NULL} or empty
|
||||
* @throws ParseException when invalid version string is provided
|
||||
* @throws UnexpectedCharacterException is a special case of {@code ParseException}
|
||||
*/
|
||||
public static CompositeExpression neq(String version) {
|
||||
return neq(Version.valueOf(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with
|
||||
* an underlying {@code Greater} expression.
|
||||
*
|
||||
* @param version a {@code Version} to compare with
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
*/
|
||||
public static CompositeExpression gt(Version version) {
|
||||
return new CompositeExpression(new Greater(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with
|
||||
* an underlying {@code Greater} expression.
|
||||
*
|
||||
* @param version a {@code Version} string to compare with
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
* @throws IllegalArgumentException if the input string is {@code NULL} or empty
|
||||
* @throws ParseException when invalid version string is provided
|
||||
* @throws UnexpectedCharacterException is a special case of {@code ParseException}
|
||||
*/
|
||||
public static CompositeExpression gt(String version) {
|
||||
return gt(Version.valueOf(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with an
|
||||
* underlying {@code GreaterOrEqual} expression.
|
||||
*
|
||||
* @param version a {@code Version} to compare with
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
*/
|
||||
public static CompositeExpression gte(Version version) {
|
||||
return new CompositeExpression(new GreaterOrEqual(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with an
|
||||
* underlying {@code GreaterOrEqual} expression.
|
||||
*
|
||||
* @param version a {@code Version} string to compare with
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
* @throws IllegalArgumentException if the input string is {@code NULL} or empty
|
||||
* @throws ParseException when invalid version string is provided
|
||||
* @throws UnexpectedCharacterException is a special case of {@code ParseException}
|
||||
*/
|
||||
public static CompositeExpression gte(String version) {
|
||||
return gte(Version.valueOf(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with
|
||||
* an underlying {@code Less} expression.
|
||||
*
|
||||
* @param version a {@code Version} to compare with
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
*/
|
||||
public static CompositeExpression lt(Version version) {
|
||||
return new CompositeExpression(new Less(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with
|
||||
* an underlying {@code Less} expression.
|
||||
*
|
||||
* @param version a {@code Version} string to compare with
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
* @throws IllegalArgumentException if the input string is {@code NULL} or empty
|
||||
* @throws ParseException when invalid version string is provided
|
||||
* @throws UnexpectedCharacterException is a special case of {@code ParseException}
|
||||
*/
|
||||
public static CompositeExpression lt(String version) {
|
||||
return lt(Version.valueOf(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with an
|
||||
* underlying {@code LessOrEqual} expression.
|
||||
*
|
||||
* @param version a {@code Version} to compare with
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
*/
|
||||
public static CompositeExpression lte(Version version) {
|
||||
return new CompositeExpression(new LessOrEqual(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code CompositeExpression} with an
|
||||
* underlying {@code LessOrEqual} expression.
|
||||
*
|
||||
* @param version a {@code Version} string to compare with
|
||||
* @return a newly created {@code CompositeExpression}
|
||||
* @throws IllegalArgumentException if the input string is {@code NULL} or empty
|
||||
* @throws ParseException when invalid version string is provided
|
||||
* @throws UnexpectedCharacterException is a special case of {@code ParseException}
|
||||
*/
|
||||
public static CompositeExpression lte(String version) {
|
||||
return lte(Version.valueOf(version));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The underlying expression tree.
|
||||
*/
|
||||
private Expression expr;
|
||||
|
||||
/**
|
||||
* Constructs a {@code CompositeExpression}
|
||||
* with an underlying {@code Expression}.
|
||||
*
|
||||
* @param expr the underlying expression
|
||||
*/
|
||||
public CompositeExpression(Expression expr) {
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds another {@code Expression} to {@code CompositeExpression}
|
||||
* using {@code And} logical expression.
|
||||
*
|
||||
* @param expr an expression to add
|
||||
* @return this {@code CompositeExpression}
|
||||
*/
|
||||
public CompositeExpression and(Expression expr) {
|
||||
this.expr = new And(this.expr, expr);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds another {@code Expression} to {@code CompositeExpression}
|
||||
* using {@code Or} logical expression.
|
||||
*
|
||||
* @param expr an expression to add
|
||||
* @return this {@code CompositeExpression}
|
||||
*/
|
||||
public CompositeExpression or(Expression expr) {
|
||||
this.expr = new Or(this.expr, expr);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interprets the expression.
|
||||
*
|
||||
* @param version a {@code Version} string to interpret against
|
||||
* @return the result of the expression interpretation
|
||||
* @throws IllegalArgumentException if the input string is {@code NULL} or empty
|
||||
* @throws ParseException when invalid version string is provided
|
||||
* @throws UnexpectedCharacterException is a special case of {@code ParseException}
|
||||
*/
|
||||
public boolean interpret(String version) {
|
||||
return interpret(Version.valueOf(version));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean interpret(Version version) {
|
||||
return expr.interpret(version);
|
||||
}
|
||||
}
|
@ -26,6 +26,7 @@ package com.github.zafarkhaja.semver;
|
||||
import org.junit.Test;
|
||||
import org.junit.experimental.runners.Enclosed;
|
||||
import org.junit.runner.RunWith;
|
||||
import static com.github.zafarkhaja.semver.expr.CompositeExpression.Helper.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
@ -307,11 +308,18 @@ public class VersionTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCheckIfVersionSatisfiesExpression() {
|
||||
public void shouldCheckIfVersionSatisfiesExpressionString() {
|
||||
Version v = Version.valueOf("2.0.0-beta");
|
||||
assertTrue(v.satisfies("~1.0"));
|
||||
assertFalse(v.satisfies(">=2.0 & <3.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldCheckIfVersionSatisfiesExpression() {
|
||||
Version v = Version.valueOf("2.0.0-beta");
|
||||
assertTrue(v.satisfies(gte("1.0.0").and(lt("2.0.0"))));
|
||||
assertFalse(v.satisfies(gte("2.0.0").and(lt("3.0.0"))));
|
||||
}
|
||||
}
|
||||
|
||||
public static class EqualsMethodTest {
|
||||
|
@ -0,0 +1,113 @@
|
||||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2012-2014 Zafar Khaja <zafarkhaja@gmail.com>.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
package com.github.zafarkhaja.semver.expr;
|
||||
|
||||
import org.junit.Test;
|
||||
import static com.github.zafarkhaja.semver.expr.CompositeExpression.Helper.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
*/
|
||||
public class CompositeExpressionTest {
|
||||
|
||||
@Test
|
||||
public void shouldSupportEqualExpression() {
|
||||
assertTrue(eq("1.0.0").interpret("1.0.0"));
|
||||
assertFalse(eq("1.0.0").interpret("2.0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportNotEqualExpression() {
|
||||
assertTrue(neq("1.0.0").interpret("2.0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportGreaterExpression() {
|
||||
assertTrue(gt("1.0.0").interpret("2.0.0"));
|
||||
assertFalse(gt("2.0.0").interpret("1.0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportGreaterOrEqualExpression() {
|
||||
assertTrue(gte("1.0.0").interpret("1.0.0"));
|
||||
assertTrue(gte("1.0.0").interpret("2.0.0"));
|
||||
assertFalse(gte("2.0.0").interpret("1.0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportLessExpression() {
|
||||
assertTrue(lt("2.0.0").interpret("1.0.0"));
|
||||
assertFalse(lt("1.0.0").interpret("2.0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportLessOrEqualExpression() {
|
||||
assertTrue(lte("1.0.0").interpret("1.0.0"));
|
||||
assertTrue(lte("2.0.0").interpret("1.0.0"));
|
||||
assertFalse(lte("1.0.0").interpret("2.0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportNotExpression() {
|
||||
assertTrue(not(eq("1.0.0")).interpret("2.0.0"));
|
||||
assertFalse(not(eq("1.0.0")).interpret("1.0.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportAndExpression() {
|
||||
assertTrue(gt("1.0.0").and(lt("2.0.0")).interpret("1.5.0"));
|
||||
assertFalse(gt("1.0.0").and(lt("2.0.0")).interpret("2.5.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportOrExpression() {
|
||||
assertTrue(lt("1.0.0").or(gt("1.0.0")).interpret("1.5.0"));
|
||||
assertFalse(gt("1.0.0").or(gt("2.0.0")).interpret("0.5.0"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSupportComplexExpressions() {
|
||||
/* ((>=1.0.1 & <2) | (>=3.0 & <4)) & ((1-1.5) & (~1.5)) */
|
||||
CompositeExpression expr =
|
||||
gte("1.0.1").and(
|
||||
lt("2.0.0").or(
|
||||
gte("3.0.0").and(
|
||||
lt("4.0.0").and(
|
||||
gte("1.0.0").and(
|
||||
lte("1.5.0").and(
|
||||
gte("1.5.0").and(
|
||||
lt("2.0.0")
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
assertTrue(expr.interpret("1.5.0"));
|
||||
assertFalse(expr.interpret("2.5.0"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user