This commit is contained in:
Sean Mullan 2016-08-01 17:00:26 -04:00
commit 4130468fcd

View File

@ -1112,7 +1112,62 @@ public class Runtime {
* @return The Version of the given string
*/
public static Version parse(String s) {
return VersionBuilder.parse(s);
if (s == null)
throw new NullPointerException();
// Shortcut to avoid initializing VersionPattern when creating
// major version constants during startup
if (isSimpleNumber(s)) {
return new Version(List.of(Integer.parseInt(s)),
Optional.empty(), Optional.empty(), Optional.empty());
}
Matcher m = VersionPattern.VSTR_PATTERN.matcher(s);
if (!m.matches())
throw new IllegalArgumentException("Invalid version string: '"
+ s + "'");
// $VNUM is a dot-separated list of integers of arbitrary length
List<Integer> version = new ArrayList<>();
for (String i : m.group(VersionPattern.VNUM_GROUP).split("\\."))
version.add(Integer.parseInt(i));
Optional<String> pre = Optional.ofNullable(
m.group(VersionPattern.PRE_GROUP));
String b = m.group(VersionPattern.BUILD_GROUP);
// $BUILD is an integer
Optional<Integer> build = (b == null)
? Optional.empty()
: Optional.of(Integer.parseInt(b));
Optional<String> optional = Optional.ofNullable(
m.group(VersionPattern.OPT_GROUP));
// empty '+'
if ((m.group(VersionPattern.PLUS_GROUP) != null)
&& !build.isPresent()) {
if (optional.isPresent()) {
if (pre.isPresent())
throw new IllegalArgumentException("'+' found with"
+ " pre-release and optional components:'" + s
+ "'");
} else {
throw new IllegalArgumentException("'+' found with neither"
+ " build or optional components: '" + s + "'");
}
}
return new Version(version, pre, build, optional);
}
private static boolean isSimpleNumber(String s) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
char lowerBound = (i > 0) ? '0' : '1';
if (c < lowerBound || c > '9') {
return false;
}
}
return true;
}
/**
@ -1441,86 +1496,26 @@ public class Runtime {
}
}
private static class VersionBuilder {
private static class VersionPattern {
// $VNUM(-$PRE)?(\+($BUILD)?(\-$OPT)?)?
// RE limits the format of version strings
// ([1-9][0-9]*(?:(?:\.0)*\.[1-9][0-9]*)*)(?:-([a-zA-Z0-9]+))?(?:(\+)(0|[1-9][0-9]*)?)?(?:-([-a-zA-Z0-9.]+))?
private static final String VNUM
= "(?<VNUM>[1-9][0-9]*(?:(?:\\.0)*\\.[1-9][0-9]*)*)";
private static final String VNUM_GROUP = "VNUM";
private static final String PRE = "(?:-(?<PRE>[a-zA-Z0-9]+))?";
private static final String PRE_GROUP = "PRE";
private static final String BUILD
= "(?:(?<PLUS>\\+)(?<BUILD>0|[1-9][0-9]*)?)?";
private static final String PLUS_GROUP = "PLUS";
private static final String BUILD_GROUP = "BUILD";
private static final String OPT = "(?:-(?<OPT>[-a-zA-Z0-9.]+))?";
private static final String OPT_GROUP = "OPT";
private static final String VSTR_FORMAT
= "^" + VNUM + PRE + BUILD + OPT + "$";
private static final Pattern VSTR_PATTERN = Pattern.compile(VSTR_FORMAT);
/**
* Constructs a valid <a href="verStr">version string</a> containing
* a <a href="#verNum">version number</a> followed by pre-release and
* build information.
*
* @param s
* A string to be interpreted as a version
*
* @throws IllegalArgumentException
* If the given string cannot be interpreted as a valid
* version
*
* @throws NullPointerException
* If {@code s} is {@code null}
*
* @throws NumberFormatException
* If an element of the version number or the build number
* cannot be represented as an {@link Integer}
*/
static Version parse(String s) {
if (s == null)
throw new NullPointerException();
static final Pattern VSTR_PATTERN = Pattern.compile(VSTR_FORMAT);
Matcher m = VSTR_PATTERN.matcher(s);
if (!m.matches())
throw new IllegalArgumentException("Invalid version string: '"
+ s + "'");
// $VNUM is a dot-separated list of integers of arbitrary length
List<Integer> version = new ArrayList<>();
for (String i : m.group(VNUM_GROUP).split("\\."))
version.add(Integer.parseInt(i));
Optional<String> pre = Optional.ofNullable(m.group(PRE_GROUP));
String b = m.group(BUILD_GROUP);
// $BUILD is an integer
Optional<Integer> build = (b == null)
? Optional.empty()
: Optional.of(Integer.parseInt(b));
Optional<String> optional = Optional.ofNullable(m.group(OPT_GROUP));
// empty '+'
if ((m.group(PLUS_GROUP) != null) && !build.isPresent()) {
if (optional.isPresent()) {
if (pre.isPresent())
throw new IllegalArgumentException("'+' found with"
+ " pre-release and optional components:'" + s
+ "'");
} else {
throw new IllegalArgumentException("'+' found with neither"
+ " build or optional components: '" + s + "'");
}
}
return new Version(version, pre, build, optional);
}
static final String VNUM_GROUP = "VNUM";
static final String PRE_GROUP = "PRE";
static final String PLUS_GROUP = "PLUS";
static final String BUILD_GROUP = "BUILD";
static final String OPT_GROUP = "OPT";
}
}