Add Javadoc to the source code
This commit is contained in:
parent
388f94915f
commit
cf8d6b1960
|
@ -218,11 +218,6 @@ other interesting capabilities of the SemVer Expressions DSL.
|
|||
* Parenthesized expression - `~1.3 | (1.4.* & !=1.4.5) | ~2`
|
||||
|
||||
|
||||
TODO
|
||||
----
|
||||
* [Write doc comments for all API classes and methods](https://github.com/zafarkhaja/java-semver/issues/2)
|
||||
|
||||
|
||||
Bugs and Features
|
||||
-----------------
|
||||
Bug reports and feature requests can be submitted at https://github.com/zafarkhaja/java-semver/issues.
|
||||
|
|
|
@ -24,11 +24,19 @@
|
|||
package com.github.zafarkhaja.semver;
|
||||
|
||||
/**
|
||||
* Thrown when an error occurs during the parsing specified
|
||||
* by the SemVer or the formal grammar of the parsed string.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public class GrammarException extends ParserException {
|
||||
|
||||
/**
|
||||
* Constructs a {@code GrammarException} instance with an error message.
|
||||
*
|
||||
* @param message the error message
|
||||
*/
|
||||
GrammarException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
|
|
@ -26,39 +26,66 @@ package com.github.zafarkhaja.semver;
|
|||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* The {@code MetadataVersion} class is used to represent
|
||||
* the pre-release version and the build metadata.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.2.0
|
||||
*/
|
||||
class MetadataVersion implements Comparable<MetadataVersion> {
|
||||
|
||||
/**
|
||||
* Null metadata, the implementation of the Null Object design pattern.
|
||||
*/
|
||||
static final MetadataVersion NULL = new NullMetadataVersion();
|
||||
|
||||
/**
|
||||
* The implementation of the Null Object design pattern.
|
||||
*/
|
||||
private static class NullMetadataVersion extends MetadataVersion {
|
||||
|
||||
/**
|
||||
* Constructs a {@code NullMetadataVersion} instance.
|
||||
*/
|
||||
public NullMetadataVersion() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws NullPointerException as Null metadata cannot be incremented
|
||||
*/
|
||||
@Override
|
||||
MetadataVersion increment() {
|
||||
throw new NullPointerException("Metadata version is NULL");
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof NullMetadataVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(MetadataVersion other) {
|
||||
if (!equals(other)) {
|
||||
|
@ -72,12 +99,24 @@ class MetadataVersion implements Comparable<MetadataVersion> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The array containing the version's identifiers.
|
||||
*/
|
||||
private final String[] idents;
|
||||
|
||||
/**
|
||||
* Constructs a {@code MetadataVersion} instance with identifiers.
|
||||
* @param identifiers the version's identifiers
|
||||
*/
|
||||
MetadataVersion(String[] identifiers) {
|
||||
idents = identifiers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the metadata version.
|
||||
*
|
||||
* @return a new instance of the {@code MetadataVersion} class
|
||||
*/
|
||||
MetadataVersion increment() {
|
||||
String[] ids = idents;
|
||||
String lastId = ids[ids.length - 1];
|
||||
|
@ -91,6 +130,9 @@ class MetadataVersion implements Comparable<MetadataVersion> {
|
|||
return new MetadataVersion(ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
|
@ -102,11 +144,17 @@ class MetadataVersion implements Comparable<MetadataVersion> {
|
|||
return compareTo((MetadataVersion) other) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(idents);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
@ -116,6 +164,9 @@ class MetadataVersion implements Comparable<MetadataVersion> {
|
|||
return sb.deleteCharAt(sb.lastIndexOf(".")).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(MetadataVersion other) {
|
||||
if (other == MetadataVersion.NULL) {
|
||||
|
@ -127,11 +178,23 @@ class MetadataVersion implements Comparable<MetadataVersion> {
|
|||
}
|
||||
int result = compareIdentifierArrays(other.idents);
|
||||
if (result == 0) {
|
||||
/**
|
||||
* A larger set of pre-release fields has a higher
|
||||
* precedence than a smaller set, if all of the
|
||||
* preceding identifiers are equal. (SemVer p.11)
|
||||
*/
|
||||
result = idents.length - other.idents.length;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two arrays of identifiers.
|
||||
*
|
||||
* @param otherIdents the identifiers of the other version
|
||||
* @return integer result of comparison compatible with
|
||||
* the {@code Comparable.compareTo} method
|
||||
*/
|
||||
private int compareIdentifierArrays(String[] otherIdents) {
|
||||
int result = 0;
|
||||
int length = getLeastCommonArrayLength(idents, otherIdents);
|
||||
|
@ -144,10 +207,25 @@ class MetadataVersion implements Comparable<MetadataVersion> {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the smallest array.
|
||||
*
|
||||
* @param arr1 the first array
|
||||
* @param arr2 the second array
|
||||
* @return the size of the smallest array
|
||||
*/
|
||||
private int getLeastCommonArrayLength(String[] arr1, String[] arr2) {
|
||||
return arr1.length <= arr2.length ? arr1.length : arr2.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two identifiers.
|
||||
*
|
||||
* @param ident1 the first identifier
|
||||
* @param ident2 the second identifier
|
||||
* @return integer result of comparison compatible with
|
||||
* the {@code Comparable.compareTo} method
|
||||
*/
|
||||
private int compareIdentifiers(String ident1, String ident2) {
|
||||
if (isInt(ident1) && isInt(ident2)) {
|
||||
return Integer.parseInt(ident1) - Integer.parseInt(ident2);
|
||||
|
@ -156,6 +234,13 @@ class MetadataVersion implements Comparable<MetadataVersion> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the specified string is an integer.
|
||||
*
|
||||
* @param str the string to check
|
||||
* @return {@code true} if the specified string is an integer
|
||||
* or {@code false} otherwise
|
||||
*/
|
||||
private boolean isInt(String str) {
|
||||
try {
|
||||
Integer.parseInt(str);
|
||||
|
|
|
@ -24,15 +24,39 @@
|
|||
package com.github.zafarkhaja.semver;
|
||||
|
||||
/**
|
||||
* The {@code NormalVersion} class represents the version core.
|
||||
*
|
||||
* This class is immutable and hence thread-safe.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.2.0
|
||||
*/
|
||||
class NormalVersion implements Comparable<NormalVersion> {
|
||||
|
||||
/**
|
||||
* The major version number.
|
||||
*/
|
||||
private final int major;
|
||||
|
||||
/**
|
||||
* The minor version number.
|
||||
*/
|
||||
private final int minor;
|
||||
|
||||
/**
|
||||
* The patch version number.
|
||||
*/
|
||||
private final int patch;
|
||||
|
||||
/**
|
||||
* Constructs a {@code NormalVersion} with the
|
||||
* major, minor and patch version numbers.
|
||||
*
|
||||
* @param major the major version number
|
||||
* @param minor the minor version number
|
||||
* @param patch the patch version number
|
||||
* @throws IllegalArgumentException if one of the version numbers is a negative integer
|
||||
*/
|
||||
NormalVersion(int major, int minor, int patch) {
|
||||
if (major < 0 || minor < 0 || patch < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
|
@ -44,30 +68,63 @@ class NormalVersion implements Comparable<NormalVersion> {
|
|||
this.patch = patch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the major version number.
|
||||
*
|
||||
* @return the major version number
|
||||
*/
|
||||
int getMajor() {
|
||||
return major;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minor version number.
|
||||
*
|
||||
* @return the minor version number
|
||||
*/
|
||||
int getMinor() {
|
||||
return minor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the patch version number.
|
||||
*
|
||||
* @return the patch version number
|
||||
*/
|
||||
int getPatch() {
|
||||
return patch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the major version number.
|
||||
*
|
||||
* @return a new instance of the {@code NormalVersion} class
|
||||
*/
|
||||
NormalVersion incrementMajor() {
|
||||
return new NormalVersion(major + 1, 0, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the minor version number.
|
||||
*
|
||||
* @return a new instance of the {@code NormalVersion} class
|
||||
*/
|
||||
NormalVersion incrementMinor() {
|
||||
return new NormalVersion(major, minor + 1, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the patch version number.
|
||||
*
|
||||
* @return a new instance of the {@code NormalVersion} class
|
||||
*/
|
||||
NormalVersion incrementPatch() {
|
||||
return new NormalVersion(major, minor, patch + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(NormalVersion other) {
|
||||
int result = major - other.major;
|
||||
|
@ -80,6 +137,9 @@ class NormalVersion implements Comparable<NormalVersion> {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
|
@ -91,6 +151,9 @@ class NormalVersion implements Comparable<NormalVersion> {
|
|||
return compareTo((NormalVersion) other) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 17;
|
||||
|
@ -106,6 +169,8 @@ class NormalVersion implements Comparable<NormalVersion> {
|
|||
* A normal version number MUST take the form X.Y.Z where X, Y, and Z are
|
||||
* non-negative integers. X is the major version, Y is the minor version,
|
||||
* and Z is the patch version. (SemVer p.2)
|
||||
*
|
||||
* @return the string representation of this normal version
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
|
|
@ -24,9 +24,20 @@
|
|||
package com.github.zafarkhaja.semver;
|
||||
|
||||
/**
|
||||
* A parser interface.
|
||||
*
|
||||
* @param <T> the type of parser's output
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public interface Parser<T> {
|
||||
|
||||
/**
|
||||
* Parses the input string.
|
||||
*
|
||||
* @param input the string to parse
|
||||
* @return the Abstract Syntax Tree
|
||||
*/
|
||||
T parse(String input);
|
||||
}
|
||||
|
|
|
@ -24,15 +24,25 @@
|
|||
package com.github.zafarkhaja.semver;
|
||||
|
||||
/**
|
||||
* Thrown to indicate an error during the parsing.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public class ParserException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* Constructs a {@code ParserException} instance with an error message.
|
||||
*
|
||||
* @param message the error message
|
||||
*/
|
||||
public ParserException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code ParserException} instance with no error message.
|
||||
*/
|
||||
public ParserException() {
|
||||
|
||||
}
|
||||
|
|
|
@ -28,24 +28,70 @@ import com.github.zafarkhaja.semver.expr.ExpressionParser;
|
|||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* The {@code Version} class is the main class of the Java SemVer library.
|
||||
*
|
||||
* This class implements the Facade design pattern.
|
||||
* It is also immutable, which makes the class thread-safe.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
public class Version implements Comparable<Version> {
|
||||
|
||||
/**
|
||||
* The normal version.
|
||||
*/
|
||||
private final NormalVersion normal;
|
||||
|
||||
/**
|
||||
* The pre-release version.
|
||||
*/
|
||||
private final MetadataVersion preRelease;
|
||||
|
||||
/**
|
||||
* The build metadata.
|
||||
*/
|
||||
private final MetadataVersion build;
|
||||
|
||||
/**
|
||||
* A separator that separates the pre-release
|
||||
* version from the normal version.
|
||||
*/
|
||||
private static final String PRE_RELEASE_PREFIX = "-";
|
||||
|
||||
/**
|
||||
* A separator that separates the build metadata from
|
||||
* the normal version or the pre-release version.
|
||||
*/
|
||||
private static final String BUILD_PREFIX = "+";
|
||||
|
||||
/**
|
||||
* A mutable builder for the immutable {@code Version} class.
|
||||
*/
|
||||
public static class Builder {
|
||||
|
||||
/**
|
||||
* The normal version string.
|
||||
*/
|
||||
private String normal;
|
||||
|
||||
/**
|
||||
* The pre-release version string.
|
||||
*/
|
||||
private String preRelease;
|
||||
|
||||
/**
|
||||
* The build metadata string.
|
||||
*/
|
||||
private String build;
|
||||
|
||||
/**
|
||||
* Constructs a {@code Builder} instance with the
|
||||
* string representation of the normal version.
|
||||
*
|
||||
* @param normal the string representation of the normal version
|
||||
* @throws NullPointerException if the specified normal version is null
|
||||
*/
|
||||
public Builder(String normal) {
|
||||
if (normal == null) {
|
||||
throw new NullPointerException(
|
||||
|
@ -55,14 +101,29 @@ public class Version implements Comparable<Version> {
|
|||
this.normal = normal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pre-release version.
|
||||
*
|
||||
* @param preRelease the string representation of the pre-release version
|
||||
*/
|
||||
public void setPreReleaseVersion(String preRelease) {
|
||||
this.preRelease = preRelease;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the build metadata.
|
||||
*
|
||||
* @param build the string representation of the build metadata
|
||||
*/
|
||||
public void setBuildMetadata(String build) {
|
||||
this.build = build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a {@code Version} object.
|
||||
*
|
||||
* @return a newly built {@code Version} instance
|
||||
*/
|
||||
public Version build() {
|
||||
return new Version(
|
||||
VersionParser.parseVersionCore(normal),
|
||||
|
@ -72,10 +133,30 @@ public class Version implements Comparable<Version> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparator that respects the build metadata when comparing versions.
|
||||
*/
|
||||
public static final Comparator BUILD_AWARE_ORDER = new BuildAwareOrder();
|
||||
|
||||
/**
|
||||
* A build-aware comparator.
|
||||
*/
|
||||
private static class BuildAwareOrder implements Comparator<Version> {
|
||||
|
||||
/**
|
||||
* Compares two {@code Version} instances taking
|
||||
* into account their build metadata.
|
||||
*
|
||||
* When compared build metadata is divided into identifiers. The
|
||||
* numeric identifiers are compared numerically, and the alphanumeric
|
||||
* identifiers are compared in the ASCII sort order.
|
||||
*
|
||||
* If one of the compared versions has no defined build
|
||||
* metadata, this version is considered to have a lower
|
||||
* precedence than that of the other.
|
||||
*
|
||||
* @return {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int compare(Version v1, Version v2) {
|
||||
int result = v1.compareTo(v2);
|
||||
|
@ -96,14 +177,34 @@ public class Version implements Comparable<Version> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code Version} instance with the normal version.
|
||||
*
|
||||
* @param normal the normal version
|
||||
*/
|
||||
Version(NormalVersion normal) {
|
||||
this(normal, MetadataVersion.NULL, MetadataVersion.NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code Version} instance with the
|
||||
* normal version and the pre-release version.
|
||||
*
|
||||
* @param normal the normal version
|
||||
* @param preRelease the pre-release version
|
||||
*/
|
||||
Version(NormalVersion normal, MetadataVersion preRelease) {
|
||||
this(normal, preRelease, MetadataVersion.NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code Version} instance with the normal
|
||||
* version, the pre-release version and the build metadata.
|
||||
*
|
||||
* @param normal the normal version
|
||||
* @param preRelease the pre-release version
|
||||
* @param build the build metadata
|
||||
*/
|
||||
Version(
|
||||
NormalVersion normal,
|
||||
MetadataVersion preRelease,
|
||||
|
@ -114,31 +215,86 @@ public class Version implements Comparable<Version> {
|
|||
this.build = build;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code Version} as a
|
||||
* result of parsing the specified version string.
|
||||
*
|
||||
* @param version the version string to parse
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public static Version valueOf(String version) {
|
||||
return VersionParser.parseValidSemVer(version);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code Version}
|
||||
* for the specified version numbers.
|
||||
*
|
||||
* @param major the major version number
|
||||
* @return a new instance of the {@code Version} class
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public static Version forIntegers(int major) {
|
||||
return new Version(new NormalVersion(major, 0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code Version}
|
||||
* for the specified version numbers.
|
||||
*
|
||||
* @param major the major version number
|
||||
* @param minor the minor version number
|
||||
* @return a new instance of the {@code Version} class
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public static Version forIntegers(int major, int minor) {
|
||||
return new Version(new NormalVersion(major, minor, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of {@code Version}
|
||||
* for the specified version numbers.
|
||||
*
|
||||
* @param major the major version number
|
||||
* @param minor the minor version number
|
||||
* @param patch the patch version number
|
||||
* @return a new instance of the {@code Version} class
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public static Version forIntegers(int major, int minor, int patch) {
|
||||
return new Version(new NormalVersion(major, minor, patch));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.7.0
|
||||
*/
|
||||
public boolean satisfies(String expr) {
|
||||
Parser<Expression> parser = ExpressionParser.newInstance();
|
||||
return parser.parse(expr).interpret(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the major version.
|
||||
*
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public Version incrementMajorVersion() {
|
||||
return new Version(normal.incrementMajor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the major version and appends the pre-release version.
|
||||
*
|
||||
* @param preRelease the pre-release version to append
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public Version incrementMajorVersion(String preRelease) {
|
||||
return new Version(
|
||||
normal.incrementMajor(),
|
||||
|
@ -146,10 +302,21 @@ public class Version implements Comparable<Version> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the minor version.
|
||||
*
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public Version incrementMinorVersion() {
|
||||
return new Version(normal.incrementMinor());
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the minor version and appends the pre-release version.
|
||||
*
|
||||
* @param preRelease the pre-release version to append
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public Version incrementMinorVersion(String preRelease) {
|
||||
return new Version(
|
||||
normal.incrementMinor(),
|
||||
|
@ -157,10 +324,21 @@ public class Version implements Comparable<Version> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the patch version.
|
||||
*
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public Version incrementPatchVersion() {
|
||||
return new Version(normal.incrementPatch());
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the patch version and appends the pre-release version.
|
||||
*
|
||||
* @param preRelease the pre-release version to append
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public Version incrementPatchVersion(String preRelease) {
|
||||
return new Version(
|
||||
normal.incrementPatch(),
|
||||
|
@ -168,14 +346,30 @@ public class Version implements Comparable<Version> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the pre-release version.
|
||||
*
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public Version incrementPreReleaseVersion() {
|
||||
return new Version(normal, preRelease.increment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the build metadata.
|
||||
*
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public Version incrementBuildMetadata() {
|
||||
return new Version(normal, preRelease, build.increment());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the pre-release version.
|
||||
*
|
||||
* @param preRelease the pre-release version to set
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public Version setPreReleaseVersion(String preRelease) {
|
||||
return new Version(
|
||||
normal,
|
||||
|
@ -183,6 +377,12 @@ public class Version implements Comparable<Version> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the build metadata.
|
||||
*
|
||||
* @param build the build metadata to set
|
||||
* @return a new instance of the {@code Version} class
|
||||
*/
|
||||
public Version setBuildMetadata(String build) {
|
||||
return new Version(
|
||||
normal,
|
||||
|
@ -191,46 +391,118 @@ public class Version implements Comparable<Version> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the major version number.
|
||||
*
|
||||
* @return the major version number
|
||||
*/
|
||||
public int getMajorVersion() {
|
||||
return normal.getMajor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minor version number.
|
||||
*
|
||||
* @return the minor version number
|
||||
*/
|
||||
public int getMinorVersion() {
|
||||
return normal.getMinor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the patch version number.
|
||||
*
|
||||
* @return the patch version number
|
||||
*/
|
||||
public int getPatchVersion() {
|
||||
return normal.getPatch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the normal version.
|
||||
*
|
||||
* @return the string representation of the normal version
|
||||
*/
|
||||
public String getNormalVersion() {
|
||||
return normal.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the pre-release version.
|
||||
*
|
||||
* @return the string representation of the pre-release version
|
||||
*/
|
||||
public String getPreReleaseVersion() {
|
||||
return preRelease.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the build metadata.
|
||||
*
|
||||
* @return the string representation of the build metadata
|
||||
*/
|
||||
public String getBuildMetadata() {
|
||||
return build.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this version is greater than the other version.
|
||||
*
|
||||
* @param other the other version to compare to
|
||||
* @return {@code true} if this version is greater than the other version
|
||||
* or {@code false} otherwise
|
||||
* @see #compareTo(Version other)
|
||||
*/
|
||||
public boolean greaterThan(Version other) {
|
||||
return compareTo(other) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this version is greater than or equal to the other version.
|
||||
*
|
||||
* @param other the other version to compare to
|
||||
* @return {@code true} if this version is greater than or equal
|
||||
* to the other version or {@code false} otherwise
|
||||
* @see #compareTo(Version other)
|
||||
*/
|
||||
public boolean greaterThanOrEqualTo(Version other) {
|
||||
return compareTo(other) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this version is less than the other version.
|
||||
*
|
||||
* @param other the other version to compare to
|
||||
* @return {@code true} if this version is less than the other version
|
||||
* or {@code false} otherwise
|
||||
* @see #compareTo(Version other)
|
||||
*/
|
||||
public boolean lessThan(Version other) {
|
||||
return compareTo(other) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this version is less than or equal to the other version.
|
||||
*
|
||||
* @param other the other version to compare to
|
||||
* @return {@code true} if this version is less than or equal
|
||||
* to the other version or {@code false} otherwise
|
||||
* @see #compareTo(Version other)
|
||||
*/
|
||||
public boolean lessThanOrEqualTo(Version other) {
|
||||
return compareTo(other) <= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this version equals the other version.
|
||||
*
|
||||
* The comparison is done by the {@code Version.compareTo} method.
|
||||
*
|
||||
* @param other the other version to compare to
|
||||
* @return {@code true} if this version equals the other version
|
||||
* or {@code false} otherwise
|
||||
* @see #compareTo(Version other)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
|
@ -242,6 +514,9 @@ public class Version implements Comparable<Version> {
|
|||
return compareTo((Version) other) == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
|
@ -251,6 +526,9 @@ public class Version implements Comparable<Version> {
|
|||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder(getNormalVersion());
|
||||
|
@ -263,6 +541,20 @@ public class Version implements Comparable<Version> {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this version to the other version.
|
||||
*
|
||||
* This method does not take into account the versions' build
|
||||
* metadata. If you want to compare the versions' build metadata
|
||||
* use the {@code Version.compareWithBuildsTo} method or the
|
||||
* {@code Version.BUILD_AWARE_ORDER} comparator.
|
||||
*
|
||||
* @param other the other version to compare to
|
||||
* @return a negative integer, zero or a positive integer if this version
|
||||
* is less than, equal to or greater the the specified version
|
||||
* @see #BUILD_AWARE_ORDER
|
||||
* @see #compareWithBuildsTo(Version other)
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(Version other) {
|
||||
int result = normal.compareTo(other.normal);
|
||||
|
@ -272,6 +564,17 @@ public class Version implements Comparable<Version> {
|
|||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this version to the other version
|
||||
* taking into account the build metadata.
|
||||
*
|
||||
* The method makes use of the {@code Version.BUILD_AWARE_ORDER} comparator.
|
||||
*
|
||||
* @param other the other version to compare to
|
||||
* @return integer result of comparison compatible with
|
||||
* that of the {@code Comparable.compareTo} method
|
||||
* @see #BUILD_AWARE_ORDER
|
||||
*/
|
||||
public int compareWithBuildsTo(Version other) {
|
||||
return BUILD_AWARE_ORDER.compare(this, other);
|
||||
}
|
||||
|
|
|
@ -27,16 +27,25 @@ import com.github.zafarkhaja.semver.util.Stream;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import static com.github.zafarkhaja.semver.VersionParser.Char.*;
|
||||
import com.github.zafarkhaja.semver.util.UnexpectedElementTypeException;
|
||||
|
||||
/**
|
||||
* A parser for the SemVer Version.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class VersionParser implements Parser<Version> {
|
||||
|
||||
/**
|
||||
* Valid character types.
|
||||
*/
|
||||
static enum Char implements Stream.ElementType<Character> {
|
||||
|
||||
DIGIT {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isMatchedBy(Character chr) {
|
||||
if (chr == null) {
|
||||
|
@ -46,6 +55,9 @@ class VersionParser implements Parser<Version> {
|
|||
}
|
||||
},
|
||||
LETTER {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isMatchedBy(Character chr) {
|
||||
if (chr == null) {
|
||||
|
@ -56,6 +68,9 @@ class VersionParser implements Parser<Version> {
|
|||
}
|
||||
},
|
||||
DOT {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isMatchedBy(Character chr) {
|
||||
if (chr == null) {
|
||||
|
@ -65,6 +80,9 @@ class VersionParser implements Parser<Version> {
|
|||
}
|
||||
},
|
||||
HYPHEN {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isMatchedBy(Character chr) {
|
||||
if (chr == null) {
|
||||
|
@ -74,6 +92,9 @@ class VersionParser implements Parser<Version> {
|
|||
}
|
||||
},
|
||||
PLUS {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isMatchedBy(Character chr) {
|
||||
if (chr == null) {
|
||||
|
@ -83,6 +104,9 @@ class VersionParser implements Parser<Version> {
|
|||
}
|
||||
},
|
||||
EOL {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isMatchedBy(Character chr) {
|
||||
return chr == null;
|
||||
|
@ -90,8 +114,17 @@ class VersionParser implements Parser<Version> {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The stream of characters.
|
||||
*/
|
||||
private final Stream<Character> chars;
|
||||
|
||||
/**
|
||||
* Constructs a {@code VersionParser} instance
|
||||
* with the input string to parse.
|
||||
*
|
||||
* @param input the input string to parse
|
||||
*/
|
||||
VersionParser(String input) {
|
||||
Character[] elements = new Character[input.length()];
|
||||
for (int i = 0; i < input.length(); i++) {
|
||||
|
@ -100,21 +133,59 @@ class VersionParser implements Parser<Version> {
|
|||
chars = new Stream<Character>(elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the input string.
|
||||
*
|
||||
* @param input the input string to parse
|
||||
* @return a valid version object
|
||||
* @throws GrammarException when there is an error defined in
|
||||
* the SemVer or the formal grammar
|
||||
* @throws UnexpectedElementTypeException when encounters an unexpected
|
||||
* character type
|
||||
*/
|
||||
@Override
|
||||
public Version parse(String input) {
|
||||
return parseValidSemVer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the whole version including pre-release version and build metadata.
|
||||
*
|
||||
* @param version the version string to parse
|
||||
* @return a valid version object
|
||||
* @throws GrammarException when there is an error defined in
|
||||
* the SemVer or the formal grammar
|
||||
* @throws UnexpectedElementTypeException when encounters an unexpected
|
||||
* character type
|
||||
*/
|
||||
static Version parseValidSemVer(String version) {
|
||||
VersionParser parser = new VersionParser(version);
|
||||
return parser.parseValidSemVer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the version core.
|
||||
*
|
||||
* @param versionCore the version core string to parse
|
||||
* @return a valid normal version object
|
||||
* @throws GrammarException when there is an error defined in
|
||||
* the SemVer or the formal grammar
|
||||
* @throws UnexpectedElementTypeException when encounters an unexpected
|
||||
* character type
|
||||
*/
|
||||
static NormalVersion parseVersionCore(String versionCore) {
|
||||
VersionParser parser = new VersionParser(versionCore);
|
||||
return parser.parseVersionCore();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the pre-release version.
|
||||
*
|
||||
* @param preRelease the pre-release version string to parse
|
||||
* @return a valid pre-release version object
|
||||
* @throws GrammarException when there is an error defined in
|
||||
* the SemVer or the formal grammar
|
||||
*/
|
||||
static MetadataVersion parsePreRelease(String preRelease) {
|
||||
if (preRelease == null) {
|
||||
return MetadataVersion.NULL;
|
||||
|
@ -123,6 +194,14 @@ class VersionParser implements Parser<Version> {
|
|||
return parser.parsePreRelease();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the build metadata.
|
||||
*
|
||||
* @param build the build metadata string to parse
|
||||
* @return a valid build metadata object
|
||||
* @throws GrammarException when there is an error defined in
|
||||
* the SemVer or the formal grammar
|
||||
*/
|
||||
static MetadataVersion parseBuild(String build) {
|
||||
if (build == null) {
|
||||
return MetadataVersion.NULL;
|
||||
|
@ -131,6 +210,20 @@ class VersionParser implements Parser<Version> {
|
|||
return parser.parseBuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <valid semver>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <valid semver> ::= <version core>
|
||||
* | <version core> "-" <pre-release>
|
||||
* | <version core> "+" <build>
|
||||
* | <version core> "-" <pre-release> "+" <build>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return a valid version object
|
||||
*/
|
||||
private Version parseValidSemVer() {
|
||||
NormalVersion normalVersion = parseVersionCore();
|
||||
MetadataVersion preReleaseVersion = MetadataVersion.NULL;
|
||||
|
@ -150,6 +243,17 @@ class VersionParser implements Parser<Version> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <version core>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <version core> ::= <major> "." <minor> "." <patch>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return a valid normal version object
|
||||
*/
|
||||
private NormalVersion parseVersionCore() {
|
||||
int major = Integer.parseInt(numericIdentifier());
|
||||
chars.consume(DOT);
|
||||
|
@ -159,6 +263,24 @@ class VersionParser implements Parser<Version> {
|
|||
return new NormalVersion(major, minor, patch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <pre-release>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <pre-release> ::= <dot-separated pre-release identifiers>
|
||||
*
|
||||
* <dot-separated pre-release identifiers> ::= <pre-release identifier>
|
||||
* | <pre-release identifier> "." <dot-separated pre-release identifiers>
|
||||
*
|
||||
* <pre-release identifier> ::= <alphanumeric identifier>
|
||||
* | <numeric identifier>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return a valid pre-release version object
|
||||
* @throws GrammarException if the pre-release version has empty identifier(s)
|
||||
*/
|
||||
private MetadataVersion parsePreRelease() {
|
||||
Char end = closestEndpoint(PLUS, EOL);
|
||||
Char before = closestEndpoint(DOT, end);
|
||||
|
@ -182,6 +304,24 @@ class VersionParser implements Parser<Version> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <build>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <build> ::= <dot-separated build identifiers>
|
||||
*
|
||||
* <dot-separated build identifiers> ::= <build identifier>
|
||||
* | <build identifier> "." <dot-separated build identifiers>
|
||||
*
|
||||
* <build identifier> ::= <alphanumeric identifier>
|
||||
* | <digits>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return a valid build metadata object
|
||||
* @throws GrammarException if the build metadata has empty identifier(s)
|
||||
*/
|
||||
private MetadataVersion parseBuild() {
|
||||
Char end = EOL;
|
||||
Char before = closestEndpoint(DOT, end);
|
||||
|
@ -205,6 +345,20 @@ class VersionParser implements Parser<Version> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <numeric identifier>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <numeric identifier> ::= "0"
|
||||
* | <positive digit>
|
||||
* | <positive digit> <digits>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return a string representing the numeric identifier
|
||||
* @throws GrammarException if the numeric identifier has leading zero(es)
|
||||
*/
|
||||
private String numericIdentifier() {
|
||||
checkForLeadingZeroes();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
@ -215,6 +369,20 @@ class VersionParser implements Parser<Version> {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <alphanumeric identifier>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <alphanumeric identifier> ::= <non-digit>
|
||||
* | <non-digit> <identifier characters>
|
||||
* | <identifier characters> <non-digit>
|
||||
* | <identifier characters> <non-digit> <identifier characters>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return a string representing the alphanumeric identifier
|
||||
*/
|
||||
private String alphanumericIdentifier() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(chars.consume(DIGIT, LETTER, HYPHEN));
|
||||
|
@ -224,6 +392,18 @@ class VersionParser implements Parser<Version> {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <digits>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <digits> ::= <digit>
|
||||
* | <digit> <digits>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return a string representing the digits
|
||||
*/
|
||||
private String digits() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(chars.consume(DIGIT));
|
||||
|
@ -233,6 +413,13 @@ class VersionParser implements Parser<Version> {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses the closest character.
|
||||
*
|
||||
* @param tryThis the character to try first
|
||||
* @param orThis the character to fallback to
|
||||
* @return the closest character
|
||||
*/
|
||||
private Char closestEndpoint(Char tryThis, Char orThis) {
|
||||
if (chars.positiveLookaheadBefore(orThis, tryThis)) {
|
||||
return tryThis;
|
||||
|
@ -240,6 +427,11 @@ class VersionParser implements Parser<Version> {
|
|||
return orThis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for leading zeroes in the numeric identifiers.
|
||||
*
|
||||
* @throws GrammarException if a numeric identifier has leading zero(es)
|
||||
*/
|
||||
private void checkForLeadingZeroes() {
|
||||
Character la1 = chars.lookahead(1);
|
||||
Character la2 = chars.lookahead(2);
|
||||
|
@ -250,6 +442,12 @@ class VersionParser implements Parser<Version> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for empty identifiers in the pre-release version or build metadata.
|
||||
*
|
||||
* @throws GrammarException if the pre-release version or build
|
||||
* metadata have empty identifier(s)
|
||||
*/
|
||||
private void checkForEmptyIdentifier() {
|
||||
if (DOT.isMatchedBy(chars.lookahead(1))) {
|
||||
throw new GrammarException("Identifiers MUST NOT be empty");
|
||||
|
|
|
@ -26,19 +26,42 @@ package com.github.zafarkhaja.semver.expr;
|
|||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* Expression for the logical "and" operator.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class And implements Expression {
|
||||
|
||||
/**
|
||||
* The left-hand operand of expression.
|
||||
*/
|
||||
private final Expression left;
|
||||
|
||||
/**
|
||||
* The right-hand operand of expression.
|
||||
*/
|
||||
private final Expression right;
|
||||
|
||||
/**
|
||||
* Constructs a {@code And} expression with
|
||||
* the left-hand and right-hand operands.
|
||||
*
|
||||
* @param left the left-hand operand of expression
|
||||
* @param right the right-hand operand of expression
|
||||
*/
|
||||
And(Expression left, Expression right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if both operands evaluate to {@code true}.
|
||||
*
|
||||
* @param version the version to interpret against
|
||||
* @return {@code true} if both operands evaluate to {@code true}
|
||||
* or {@code false} otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean interpret(Version version) {
|
||||
return left.interpret(version) && right.interpret(version);
|
||||
|
|
|
@ -26,17 +26,35 @@ package com.github.zafarkhaja.semver.expr;
|
|||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* Expression for the comparison "equal" operator.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class Equal implements Expression {
|
||||
|
||||
/**
|
||||
* The parsed version, the right-hand operand of the "equal" operator.
|
||||
*/
|
||||
private final Version parsedVersion;
|
||||
|
||||
/**
|
||||
* Constructs a {@code Equal} expression with the parsed version.
|
||||
*
|
||||
* @param parsedVersion the parsed version
|
||||
*/
|
||||
Equal(Version parsedVersion) {
|
||||
this.parsedVersion = parsedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current version equals the parsed version.
|
||||
*
|
||||
* @param version the version to compare to, the left-hand
|
||||
* operand of the "equal" operator
|
||||
* @return {@code true} if the version equals the
|
||||
* parsed version or {@code false} otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean interpret(Version version) {
|
||||
return version.equals(parsedVersion);
|
||||
|
|
|
@ -26,9 +26,20 @@ package com.github.zafarkhaja.semver.expr;
|
|||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* The {@code Expression} interface is to be implemented
|
||||
* by the nodes of the Abstract Syntax Tree produced by
|
||||
* the {@code ExpressionParser} class.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public interface Expression {
|
||||
|
||||
/**
|
||||
* Interprets the expression.
|
||||
*
|
||||
* @param version the version to interpret against
|
||||
* @return the result of the expression interpretation
|
||||
*/
|
||||
boolean interpret(Version version);
|
||||
}
|
||||
|
|
|
@ -33,28 +33,72 @@ import java.util.Iterator;
|
|||
import static com.github.zafarkhaja.semver.expr.Lexer.Token.Type.*;
|
||||
|
||||
/**
|
||||
* A parser for the SemVer Expressions.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public class ExpressionParser implements Parser<Expression> {
|
||||
|
||||
/**
|
||||
* The lexer instance used for tokenization of the input string.
|
||||
*/
|
||||
private final Lexer lexer;
|
||||
|
||||
/**
|
||||
* The stream of tokens produced by the lexer.
|
||||
*/
|
||||
private Stream<Token> tokens;
|
||||
|
||||
/**
|
||||
* Constructs a {@code ExpressionParser} instance
|
||||
* with the corresponding lexer.
|
||||
*
|
||||
* @param lexer the lexer to use for tokenization of the input string
|
||||
*/
|
||||
ExpressionParser(Lexer lexer) {
|
||||
this.lexer = lexer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns new instance of the {@code ExpressionParser} class.
|
||||
*
|
||||
* This method implements the Static Factory Method pattern.
|
||||
*
|
||||
* @return a new instance of the {@code ExpressionParser} class
|
||||
*/
|
||||
public static Parser<Expression> newInstance() {
|
||||
return new ExpressionParser(new Lexer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the SemVer Expressions.
|
||||
*
|
||||
* @param input a string representing the SemVer Expression
|
||||
* @return the AST for the SemVer Expressions
|
||||
* @throws LexerException when encounters an illegal character
|
||||
* @throws UnexpectedTokenException when encounters an unexpected token type
|
||||
*/
|
||||
@Override
|
||||
public Expression parse(String input) {
|
||||
tokens = lexer.tokenize(input);
|
||||
return parseSemVerExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <semver-expr>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <semver-expr> ::= "!" "(" <semver-expr> ")"
|
||||
* | "(" <semver-expr> ")"
|
||||
* | <semver-expr> <boolean-expr>
|
||||
* | <expr>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return the expression AST
|
||||
*/
|
||||
private Expression parseSemVerExpression() {
|
||||
Expression expr;
|
||||
if (tokens.positiveLookahead(NOT)) {
|
||||
|
@ -72,6 +116,19 @@ public class ExpressionParser implements Parser<Expression> {
|
|||
return parseBooleanExpression(expr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <boolean-expr>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <boolean-expr> ::= <boolean-op> <semver-expr>
|
||||
* | <epsilon>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @param expr the left-hand expression of the logical operators
|
||||
* @return the expression AST
|
||||
*/
|
||||
private Expression parseBooleanExpression(Expression expr) {
|
||||
if (tokens.positiveLookahead(AND)) {
|
||||
tokens.consume();
|
||||
|
@ -83,6 +140,20 @@ public class ExpressionParser implements Parser<Expression> {
|
|||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <expr>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <expr> ::= <comparison-expr>
|
||||
* | <version-expr>
|
||||
* | <tilde-expr>
|
||||
* | <range-expr>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return the expression AST
|
||||
*/
|
||||
private Expression parseExpression() {
|
||||
if (tokens.positiveLookahead(TILDE)) {
|
||||
return parseTildeExpression();
|
||||
|
@ -94,6 +165,18 @@ public class ExpressionParser implements Parser<Expression> {
|
|||
return parseComparisonExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <comparison-expr>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <comparison-expr> ::= <comparison-op> <version>
|
||||
* | <version>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return the expression AST
|
||||
*/
|
||||
private Expression parseComparisonExpression() {
|
||||
Token token = tokens.lookahead();
|
||||
Expression expr;
|
||||
|
@ -128,6 +211,17 @@ public class ExpressionParser implements Parser<Expression> {
|
|||
return expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <tilde-expr>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <tilde-expr> ::= "~" <version>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return the expression AST
|
||||
*/
|
||||
private Expression parseTildeExpression() {
|
||||
tokens.consume(TILDE);
|
||||
int major = intOf(tokens.consume(NUMERIC).lexeme);
|
||||
|
@ -150,10 +244,30 @@ public class ExpressionParser implements Parser<Expression> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the following version terminals are part
|
||||
* of the {@literal <version-expr>} not-terminal.
|
||||
*
|
||||
* @return {@code true} if the following version terminals are
|
||||
* part of the {@literal <version-expr>} not-terminal or
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
private boolean isVersionExpression() {
|
||||
return isVersionFollowedBy(STAR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <version-expr>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <version-expr> ::= <major> "." "*"
|
||||
* | <major> "." <minor> "." "*"
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return the expression AST
|
||||
*/
|
||||
private Expression parseVersionExpression() {
|
||||
int major = intOf(tokens.consume(NUMERIC).lexeme);
|
||||
tokens.consume(DOT);
|
||||
|
@ -173,10 +287,29 @@ public class ExpressionParser implements Parser<Expression> {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the following version terminals are
|
||||
* part of the {@literal <range-expr>} not-terminal.
|
||||
*
|
||||
* @return {@code true} if the following version terminals are
|
||||
* part of the {@literal <range-expr>} not-terminal or
|
||||
* {@code false} otherwise
|
||||
*/
|
||||
private boolean isRangeExpression() {
|
||||
return isVersionFollowedBy(HYPHEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <range-expr>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <range-expr> ::= <version> "-" <version>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return the expression AST
|
||||
*/
|
||||
private Expression parseRangeExpression() {
|
||||
Expression ge = new GreaterOrEqual(parseVersion());
|
||||
tokens.consume(HYPHEN);
|
||||
|
@ -184,6 +317,19 @@ public class ExpressionParser implements Parser<Expression> {
|
|||
return new And(ge, le);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the {@literal <version>} non-terminal.
|
||||
*
|
||||
* <pre>
|
||||
* {@literal
|
||||
* <version> ::= <major>
|
||||
* | <major> "." <minor>
|
||||
* | <major> "." <minor> "." <patch>
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* @return the parsed version
|
||||
*/
|
||||
private Version parseVersion() {
|
||||
int major = intOf(tokens.consume(NUMERIC).lexeme);
|
||||
int minor = 0;
|
||||
|
@ -199,6 +345,17 @@ public class ExpressionParser implements Parser<Expression> {
|
|||
return versionOf(major, minor, patch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the version terminals are
|
||||
* followed by the specified token type.
|
||||
*
|
||||
* This method is essentially a {@code lookahead(k)} method
|
||||
* which allows to solve the grammar's ambiguities.
|
||||
*
|
||||
* @param type the token type to check
|
||||
* @return {@code true} if the version terminals are followed by
|
||||
* the specified token type or {@code false} otherwise
|
||||
*/
|
||||
private boolean isVersionFollowedBy(ElementType<Token> type) {
|
||||
EnumSet<Token.Type> expected = EnumSet.of(NUMERIC, DOT);
|
||||
Iterator<Token> it = tokens.iterator();
|
||||
|
@ -212,10 +369,24 @@ public class ExpressionParser implements Parser<Expression> {
|
|||
return type.isMatchedBy(lookahead);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code Version} instance for the specified integers.
|
||||
*
|
||||
* @param major the major version number
|
||||
* @param minor the minor version number
|
||||
* @param patch the patch version number
|
||||
* @return the version for the specified integers
|
||||
*/
|
||||
private Version versionOf(int major, int minor, int patch) {
|
||||
return Version.forIntegers(major, minor, patch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@code int} representation of the specified string.
|
||||
*
|
||||
* @param value the string to convert into an integer
|
||||
* @return the integer value of the specified string
|
||||
*/
|
||||
private int intOf(String value) {
|
||||
return Integer.parseInt(value);
|
||||
}
|
||||
|
|
|
@ -26,17 +26,36 @@ package com.github.zafarkhaja.semver.expr;
|
|||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* Expression for the comparison "greater than" operator.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class Greater implements Expression {
|
||||
|
||||
/**
|
||||
* The parsed version, the right-hand
|
||||
* operand of the "greater than" operator.
|
||||
*/
|
||||
private final Version parsedVersion;
|
||||
|
||||
/**
|
||||
* Constructs a {@code Greater} expression with the parsed version.
|
||||
*
|
||||
* @param parsedVersion the parsed version
|
||||
*/
|
||||
Greater(Version parsedVersion) {
|
||||
this.parsedVersion = parsedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current version is greater than the parsed version.
|
||||
*
|
||||
* @param version the version to compare to, the left-hand
|
||||
* operand of the "greater than" operator
|
||||
* @return {@code true} if the version is greater than the
|
||||
* parsed version or {@code false} otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean interpret(Version version) {
|
||||
return version.greaterThan(parsedVersion);
|
||||
|
|
|
@ -26,17 +26,37 @@ package com.github.zafarkhaja.semver.expr;
|
|||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* Expression for the comparison "greater than or equal to" operator.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class GreaterOrEqual implements Expression {
|
||||
|
||||
/**
|
||||
* The parsed version, the right-hand operand
|
||||
* of the "greater than or equal to" operator.
|
||||
*/
|
||||
private final Version parsedVersion;
|
||||
|
||||
/**
|
||||
* Constructs a {@code GreaterOrEqual} expression with the parsed version.
|
||||
*
|
||||
* @param parsedVersion the parsed version
|
||||
*/
|
||||
GreaterOrEqual(Version parsedVersion) {
|
||||
this.parsedVersion = parsedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current version is greater
|
||||
* than or equal to the parsed version.
|
||||
*
|
||||
* @param version the version to compare to, the left-hand operand
|
||||
* of the "greater than or equal to" operator
|
||||
* @return {@code true} if the version is greater than or equal
|
||||
* to the parsed version or {@code false} otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean interpret(Version version) {
|
||||
return version.greaterThanOrEqualTo(parsedVersion);
|
||||
|
|
|
@ -26,17 +26,36 @@ package com.github.zafarkhaja.semver.expr;
|
|||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* Expression for the comparison "less than" operator.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class Less implements Expression {
|
||||
|
||||
/**
|
||||
* The parsed version, the right-hand
|
||||
* operand of the "less than" operator.
|
||||
*/
|
||||
private final Version parsedVersion;
|
||||
|
||||
/**
|
||||
* Constructs a {@code Less} expression with the parsed version.
|
||||
*
|
||||
* @param parsedVersion the parsed version
|
||||
*/
|
||||
Less(Version parsedVersion) {
|
||||
this.parsedVersion = parsedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current version is less than the parsed version.
|
||||
*
|
||||
* @param version the version to compare to, the left-hand
|
||||
* operand of the "less than" operator
|
||||
* @return {@code true} if the version is less than the
|
||||
* parsed version or {@code false} otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean interpret(Version version) {
|
||||
return version.lessThan(parsedVersion);
|
||||
|
|
|
@ -26,17 +26,37 @@ package com.github.zafarkhaja.semver.expr;
|
|||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* Expression for the comparison "less than or equal to" operator.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class LessOrEqual implements Expression {
|
||||
|
||||
/**
|
||||
* The parsed version, the right-hand operand
|
||||
* of the "less than or equal to" operator.
|
||||
*/
|
||||
private final Version parsedVersion;
|
||||
|
||||
/**
|
||||
* Constructs a {@code LessOrEqual} expression with the parsed version.
|
||||
*
|
||||
* @param parsedVersion the parsed version
|
||||
*/
|
||||
LessOrEqual(Version parsedVersion) {
|
||||
this.parsedVersion = parsedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current version is less
|
||||
* than or equal to the parsed version.
|
||||
*
|
||||
* @param version the version to compare to, the left-hand operand
|
||||
* of the "less than or equal to" operator
|
||||
* @return {@code true} if the version is less than or equal
|
||||
* to the parsed version or {@code false} otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean interpret(Version version) {
|
||||
return version.lessThanOrEqualTo(parsedVersion);
|
||||
|
|
|
@ -30,13 +30,21 @@ import java.util.regex.Matcher;
|
|||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A lexer for the SemVer Expressions.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class Lexer {
|
||||
|
||||
/**
|
||||
* This class holds the information about lexemes in the input stream.
|
||||
*/
|
||||
static class Token {
|
||||
|
||||
/**
|
||||
* Valid token types.
|
||||
*/
|
||||
enum Type implements Stream.ElementType<Token> {
|
||||
|
||||
NUMERIC("0|[1-9][0-9]*"),
|
||||
|
@ -57,23 +65,44 @@ class Lexer {
|
|||
RIGHT_PAREN("\\)"),
|
||||
WHITESPACE("\\s+"),
|
||||
EOL("?!") {
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isMatchedBy(Token token) {
|
||||
return token == null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* A pattern matching this type.
|
||||
*/
|
||||
final Pattern pattern;
|
||||
|
||||
/**
|
||||
* Constructs a token type with a regular
|
||||
* expression for the pattern.
|
||||
*
|
||||
* @param regexp the regular expression for the pattern
|
||||
* @see #pattern
|
||||
*/
|
||||
private Type(String regexp) {
|
||||
pattern = Pattern.compile("^(" + regexp + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of this type.
|
||||
*
|
||||
* @return the string representation of this type
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return name() + "(" + pattern + ")";
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isMatchedBy(Token token) {
|
||||
if (token == null) {
|
||||
|
@ -83,14 +112,30 @@ class Lexer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of this token.
|
||||
*/
|
||||
final Type type;
|
||||
|
||||
/**
|
||||
* The lexeme of this token.
|
||||
*/
|
||||
final String lexeme;
|
||||
|
||||
/**
|
||||
* Constructs a {@code Token} instance with the type and lexeme.
|
||||
*
|
||||
* @param type the type of this token
|
||||
* @param lexeme the lexeme of this token
|
||||
*/
|
||||
Token(Type type, String lexeme) {
|
||||
this.type = type;
|
||||
this.lexeme = (lexeme == null) ? "" : lexeme;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
|
@ -103,6 +148,9 @@ class Lexer {
|
|||
return type.equals(token.type) && lexeme.equals(token.lexeme);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 5;
|
||||
|
@ -111,16 +159,31 @@ class Lexer {
|
|||
return hash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of this token.
|
||||
*
|
||||
* @return the string representation of this token
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return type.name() + "(" + lexeme + ")";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@code Lexer} instance.
|
||||
*/
|
||||
Lexer() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes the specified input string.
|
||||
*
|
||||
* @param input the input string to tokenize
|
||||
* @return a stream of tokens
|
||||
* @throws LexerException when encounters an illegal character
|
||||
*/
|
||||
Stream<Token> tokenize(String input) {
|
||||
List<Token> tokens = new ArrayList<Token>();
|
||||
while (!input.isEmpty()) {
|
||||
|
|
|
@ -24,17 +24,34 @@
|
|||
package com.github.zafarkhaja.semver.expr;
|
||||
|
||||
/**
|
||||
* Thrown during the lexical analysis when
|
||||
* an illegal character is encountered.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public class LexerException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* The string being analyzed starting from an illegal character.
|
||||
*/
|
||||
private final String expr;
|
||||
|
||||
/**
|
||||
* Constructs a {@code LexerException} instance with
|
||||
* a string starting from an illegal character.
|
||||
*
|
||||
* @param expr the string starting from an illegal character
|
||||
*/
|
||||
LexerException(String expr) {
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of this exception.
|
||||
*
|
||||
* @return the string representation of this exception
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Illegal character near '" + expr + "'";
|
||||
|
|
|
@ -26,17 +26,34 @@ package com.github.zafarkhaja.semver.expr;
|
|||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* Expression for the logical "negation" operator.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class Not implements Expression {
|
||||
|
||||
/**
|
||||
* The expression to negate.
|
||||
*/
|
||||
private final Expression expr;
|
||||
|
||||
/**
|
||||
* Constructs a {@code Not} expression with an expression to negate.
|
||||
*
|
||||
* @param expr the expression to negate
|
||||
*/
|
||||
Not(Expression expr) {
|
||||
this.expr = expr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates the given expression.
|
||||
*
|
||||
* @param version the version to interpret against
|
||||
* @return {@code true} if the given expression evaluates to
|
||||
* {@code false} and {@code false} otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean interpret(Version version) {
|
||||
return !expr.interpret(version);
|
||||
|
|
|
@ -26,17 +26,35 @@ package com.github.zafarkhaja.semver.expr;
|
|||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* Expression for the comparison "not equal" operator.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class NotEqual implements Expression {
|
||||
|
||||
/**
|
||||
* The parsed version, the right-hand operand of the "not equal" operator.
|
||||
*/
|
||||
private final Version parsedVersion;
|
||||
|
||||
/**
|
||||
* Constructs a {@code NotEqual} expression with the parsed version.
|
||||
*
|
||||
* @param parsedVersion the parsed version
|
||||
*/
|
||||
NotEqual(Version parsedVersion) {
|
||||
this.parsedVersion = parsedVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current version does not equal the parsed version.
|
||||
*
|
||||
* @param version the version to compare with, the left-hand
|
||||
* operand of the "not equal" operator
|
||||
* @return {@code true} if the version does not equal the
|
||||
* parsed version or {@code false} otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean interpret(Version version) {
|
||||
return !version.equals(parsedVersion);
|
||||
|
|
|
@ -26,19 +26,42 @@ package com.github.zafarkhaja.semver.expr;
|
|||
import com.github.zafarkhaja.semver.Version;
|
||||
|
||||
/**
|
||||
* Expression for the logical "or" operator.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
class Or implements Expression {
|
||||
|
||||
/**
|
||||
* The left-hand operand of expression.
|
||||
*/
|
||||
private final Expression left;
|
||||
|
||||
/**
|
||||
* The right-hand operand of expression.
|
||||
*/
|
||||
private final Expression right;
|
||||
|
||||
/**
|
||||
* Constructs a {@code Or} expression with
|
||||
* the left-hand and right-hand operands.
|
||||
*
|
||||
* @param left the left-hand operand of expression
|
||||
* @param right the right-hand operand of expression
|
||||
*/
|
||||
Or(Expression left, Expression right) {
|
||||
this.left = left;
|
||||
this.right = right;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if one of the operands evaluates to {@code true}.
|
||||
*
|
||||
* @param version the version to interpret against
|
||||
* @return {@code true} if one of the operands evaluates to {@code true}
|
||||
* or {@code false} otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean interpret(Version version) {
|
||||
return left.interpret(version) || right.interpret(version);
|
||||
|
|
|
@ -28,19 +28,42 @@ import com.github.zafarkhaja.semver.expr.Lexer.*;
|
|||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Thrown when a token of unexpected types is encountered during the parsing.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public class UnexpectedTokenException extends ParserException {
|
||||
|
||||
/**
|
||||
* The unexpected token.
|
||||
*/
|
||||
private final Token unexpected;
|
||||
|
||||
/**
|
||||
* The array of the expected token types.
|
||||
*/
|
||||
private final Token.Type[] expected;
|
||||
|
||||
/**
|
||||
* Constructs a {@code UnexpectedTokenException} instance
|
||||
* with the unexpected token and the expected types.
|
||||
*
|
||||
* @param element the unexpected token
|
||||
* @param expected an array of the expected token types
|
||||
*/
|
||||
UnexpectedTokenException(Token token, Token.Type... expected) {
|
||||
unexpected = token;
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of this exception
|
||||
* containing the information about the unexpected
|
||||
* token and, if available, about the expected types.
|
||||
*
|
||||
* @return the string representation of this exception
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
String message = "Unexpected token '" + unexpected + "'";
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2013 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This package contains classes that implement the SemVer Expressions.
|
||||
*
|
||||
* The main class of the package is the {@code ExpressionParser} class which
|
||||
* parses the specified expressions and returns the Abstract Syntax Tree.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
package com.github.zafarkhaja.semver.expr;
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2013 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is the root package of the Java SemVer library.
|
||||
*
|
||||
* The package exports most of the public API. The main entry point of the
|
||||
* package is the {@code Version} class, which implements the Facade design
|
||||
* pattern.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.1.0
|
||||
*/
|
||||
package com.github.zafarkhaja.semver;
|
|
@ -28,23 +28,65 @@ import java.util.Iterator;
|
|||
import java.util.NoSuchElementException;
|
||||
|
||||
/**
|
||||
* A simple stream class used to represent a stream of characters or tokens.
|
||||
*
|
||||
* @param <E> the type of elements held in this stream
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @see com.github.zafarkhaja.semver.VersionParser
|
||||
* @see com.github.zafarkhaja.semver.expr.Lexer
|
||||
* @see com.github.zafarkhaja.semver.expr.ExpressionParser
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public class Stream<E> implements Iterable<E> {
|
||||
|
||||
/**
|
||||
* The {@code ElementType} interface represents types of the elements
|
||||
* held by this stream and can be used for stream filtering.
|
||||
*
|
||||
* @param <E> type of elements held by this stream
|
||||
*/
|
||||
public static interface ElementType<E> {
|
||||
|
||||
/**
|
||||
* Checks if the specified element matches this type.
|
||||
*
|
||||
* @param element the element to be tested
|
||||
* @return {@code true} if the element matches this type
|
||||
* or {@code false} otherwise
|
||||
*/
|
||||
boolean isMatchedBy(E element);
|
||||
}
|
||||
|
||||
/**
|
||||
* The array holding all the elements of this stream.
|
||||
*/
|
||||
private final E[] elements;
|
||||
|
||||
/**
|
||||
* The current offset which is incremented when an element is consumed.
|
||||
*
|
||||
* @see #consume()
|
||||
*/
|
||||
private int offset = 0;
|
||||
|
||||
/**
|
||||
* Constructs a stream containing the specified elements.
|
||||
*
|
||||
* The stream does not store the real elements but the defensive copy.
|
||||
*
|
||||
* @param elements the elements to be streamed
|
||||
*/
|
||||
public Stream(E[] elements) {
|
||||
this.elements = elements.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes the next element in this stream.
|
||||
*
|
||||
* @return the next element in this stream
|
||||
* or {@code null} if no more elements left
|
||||
*/
|
||||
public E consume() {
|
||||
if (offset >= elements.length) {
|
||||
return null;
|
||||
|
@ -52,6 +94,14 @@ public class Stream<E> implements Iterable<E> {
|
|||
return elements[offset++];
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes the next element in this stream
|
||||
* only if it is of the expected types.
|
||||
*
|
||||
* @param expected the types which are expected
|
||||
* @return the next element in this stream
|
||||
* @throws UnexpectedElementTypeException if the next element is of an unexpected type
|
||||
*/
|
||||
public E consume(ElementType<E>... expected) {
|
||||
E lookahead = lookahead(1);
|
||||
for (ElementType<E> type : expected) {
|
||||
|
@ -62,10 +112,23 @@ public class Stream<E> implements Iterable<E> {
|
|||
throw new UnexpectedElementTypeException(lookahead, expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next element in this stream without consuming it.
|
||||
*
|
||||
* @return the next element in this stream
|
||||
*/
|
||||
public E lookahead() {
|
||||
return lookahead(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the element at the specified position
|
||||
* in this stream without consuming it.
|
||||
*
|
||||
* @param position the position of the element to return
|
||||
* @return the element at the specified position
|
||||
* or {@code null} if no more elements left
|
||||
*/
|
||||
public E lookahead(int position) {
|
||||
int idx = offset + position - 1;
|
||||
if (idx < elements.length) {
|
||||
|
@ -74,6 +137,13 @@ public class Stream<E> implements Iterable<E> {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the next element in this stream is of the expected types.
|
||||
*
|
||||
* @param expected the expected types
|
||||
* @return {@code true} if the next element is of the expected types
|
||||
* or {@code false} otherwise
|
||||
*/
|
||||
public boolean positiveLookahead(ElementType<E>... expected) {
|
||||
for (ElementType<E> type : expected) {
|
||||
if (type.isMatchedBy(lookahead(1))) {
|
||||
|
@ -83,6 +153,15 @@ public class Stream<E> implements Iterable<E> {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there exists an element in this stream of
|
||||
* the expected types before the specified type.
|
||||
*
|
||||
* @param before the type before which to search
|
||||
* @param expected the expected types
|
||||
* @return {@code true} if there is an element of the expected types
|
||||
* before the specified type or {@code false} otherwise
|
||||
*/
|
||||
public boolean positiveLookaheadBefore(
|
||||
ElementType<E> before,
|
||||
ElementType<E>... expected
|
||||
|
@ -102,6 +181,15 @@ public class Stream<E> implements Iterable<E> {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there is an element in this stream of
|
||||
* the expected types until the specified position.
|
||||
*
|
||||
* @param until the position until which to search
|
||||
* @param expected the expected types
|
||||
* @return {@code true} if there is an element of the expected types
|
||||
* until the specified position or {@code false} otherwise
|
||||
*/
|
||||
public boolean positiveLookaheadUntil(
|
||||
int until,
|
||||
ElementType<E>... expected
|
||||
|
@ -116,17 +204,28 @@ public class Stream<E> implements Iterable<E> {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over elements that are left in this stream.
|
||||
*
|
||||
* @return an iterator of the remaining elements in this stream
|
||||
*/
|
||||
@Override
|
||||
public Iterator<E> iterator() {
|
||||
return new Iterator<E>() {
|
||||
|
||||
private int index = offset;
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return index < elements.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public E next() {
|
||||
if (index >= elements.length) {
|
||||
|
@ -135,6 +234,9 @@ public class Stream<E> implements Iterable<E> {
|
|||
return elements[index++];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -142,6 +244,14 @@ public class Stream<E> implements Iterable<E> {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing all of the
|
||||
* elements that are left in this stream.
|
||||
*
|
||||
* The returned array is a safe copy.
|
||||
*
|
||||
* @return an array containing all of elements in this stream
|
||||
*/
|
||||
public E[] toArray() {
|
||||
return Arrays.copyOfRange(elements, offset, elements.length);
|
||||
}
|
||||
|
|
|
@ -27,19 +27,43 @@ import com.github.zafarkhaja.semver.util.Stream.ElementType;
|
|||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Thrown when attempting to consume a stream element of unexpected types.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @see Stream#consume(Stream.ElementType...)
|
||||
* @since 0.7.0
|
||||
*/
|
||||
public class UnexpectedElementTypeException extends RuntimeException {
|
||||
|
||||
/**
|
||||
* The unexpected element in the stream.
|
||||
*/
|
||||
private final Object unexpected;
|
||||
|
||||
/**
|
||||
* The array of the expected element types.
|
||||
*/
|
||||
private final ElementType<?>[] expected;
|
||||
|
||||
/**
|
||||
* Constructs a {@code UnexpectedElementTypeException} instance
|
||||
* with the unexpected element and the expected types.
|
||||
*
|
||||
* @param element the unexpected element in the stream
|
||||
* @param expected an array of the expected element types
|
||||
*/
|
||||
UnexpectedElementTypeException(Object element, ElementType<?>... expected) {
|
||||
unexpected = element;
|
||||
this.expected = expected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of this exception
|
||||
* containing the information about the unexpected
|
||||
* element and, if available, about the expected types.
|
||||
*
|
||||
* @return the string representation of this exception
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
String message = "Unexpected element '" + unexpected + "'";
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2013 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This package provides some useful utility classes.
|
||||
*
|
||||
* @author Zafar Khaja <zafarkhaja@gmail.com>
|
||||
* @since 0.7.0
|
||||
*/
|
||||
package com.github.zafarkhaja.semver.util;
|
Loading…
Reference in New Issue