1095 * <a href="#verNum">version number</a> followed by pre-release and
1096 * build information.
1097 *
1098 * @param s
1099 * A string to interpret as a version
1100 *
1101 * @throws IllegalArgumentException
1102 * If the given string cannot be interpreted as a valid
1103 * version
1104 *
1105 * @throws NullPointerException
1106 * If the given string is {@code null}
1107 *
1108 * @throws NumberFormatException
1109 * If an element of the version number or the build number
1110 * cannot be represented as an {@link Integer}
1111 *
1112 * @return The Version of the given string
1113 */
1114 public static Version parse(String s) {
1115 return VersionBuilder.parse(s);
1116 }
1117
1118 /**
1119 * Returns the <a href="#major">major</a> version number.
1120 *
1121 * @return The major version number
1122 */
1123 public int major() {
1124 return version.get(0);
1125 }
1126
1127 /**
1128 * Returns the <a href="#minor">minor</a> version number or zero if it
1129 * was not set.
1130 *
1131 * @return The minor version number or zero if it was not set
1132 */
1133 public int minor() {
1134 return (version.size() > 1 ? version.get(1) : 0);
1135 }
1424 *
1425 * <p> This method satisfies the general contract of the {@link
1426 * Object#hashCode Object.hashCode} method.
1427 *
1428 * @return The hashcode of this version
1429 */
1430 @Override
1431 public int hashCode() {
1432 int h = 1;
1433 int p = 17;
1434
1435 h = p * h + version.hashCode();
1436 h = p * h + pre.hashCode();
1437 h = p * h + build.hashCode();
1438 h = p * h + optional.hashCode();
1439
1440 return h;
1441 }
1442 }
1443
1444 private static class VersionBuilder {
1445 // $VNUM(-$PRE)?(\+($BUILD)?(\-$OPT)?)?
1446 // RE limits the format of version strings
1447 // ([1-9][0-9]*(?:(?:\.0)*\.[1-9][0-9]*)*)(?:-([a-zA-Z0-9]+))?(?:(\+)(0|[1-9][0-9]*)?)?(?:-([-a-zA-Z0-9.]+))?
1448
1449 private static final String VNUM
1450 = "(?<VNUM>[1-9][0-9]*(?:(?:\\.0)*\\.[1-9][0-9]*)*)";
1451 private static final String VNUM_GROUP = "VNUM";
1452
1453 private static final String PRE = "(?:-(?<PRE>[a-zA-Z0-9]+))?";
1454 private static final String PRE_GROUP = "PRE";
1455
1456 private static final String BUILD
1457 = "(?:(?<PLUS>\\+)(?<BUILD>0|[1-9][0-9]*)?)?";
1458 private static final String PLUS_GROUP = "PLUS";
1459 private static final String BUILD_GROUP = "BUILD";
1460
1461 private static final String OPT = "(?:-(?<OPT>[-a-zA-Z0-9.]+))?";
1462 private static final String OPT_GROUP = "OPT";
1463
1464 private static final String VSTR_FORMAT
1465 = "^" + VNUM + PRE + BUILD + OPT + "$";
1466 private static final Pattern VSTR_PATTERN = Pattern.compile(VSTR_FORMAT);
1467
1468 /**
1469 * Constructs a valid <a href="verStr">version string</a> containing
1470 * a <a href="#verNum">version number</a> followed by pre-release and
1471 * build information.
1472 *
1473 * @param s
1474 * A string to be interpreted as a version
1475 *
1476 * @throws IllegalArgumentException
1477 * If the given string cannot be interpreted as a valid
1478 * version
1479 *
1480 * @throws NullPointerException
1481 * If {@code s} is {@code null}
1482 *
1483 * @throws NumberFormatException
1484 * If an element of the version number or the build number
1485 * cannot be represented as an {@link Integer}
1486 */
1487 static Version parse(String s) {
1488 if (s == null)
1489 throw new NullPointerException();
1490
1491 Matcher m = VSTR_PATTERN.matcher(s);
1492 if (!m.matches())
1493 throw new IllegalArgumentException("Invalid version string: '"
1494 + s + "'");
1495
1496 // $VNUM is a dot-separated list of integers of arbitrary length
1497 List<Integer> version = new ArrayList<>();
1498 for (String i : m.group(VNUM_GROUP).split("\\."))
1499 version.add(Integer.parseInt(i));
1500
1501 Optional<String> pre = Optional.ofNullable(m.group(PRE_GROUP));
1502
1503 String b = m.group(BUILD_GROUP);
1504 // $BUILD is an integer
1505 Optional<Integer> build = (b == null)
1506 ? Optional.empty()
1507 : Optional.of(Integer.parseInt(b));
1508
1509 Optional<String> optional = Optional.ofNullable(m.group(OPT_GROUP));
1510
1511 // empty '+'
1512 if ((m.group(PLUS_GROUP) != null) && !build.isPresent()) {
1513 if (optional.isPresent()) {
1514 if (pre.isPresent())
1515 throw new IllegalArgumentException("'+' found with"
1516 + " pre-release and optional components:'" + s
1517 + "'");
1518 } else {
1519 throw new IllegalArgumentException("'+' found with neither"
1520 + " build or optional components: '" + s + "'");
1521 }
1522 }
1523 return new Version(version, pre, build, optional);
1524 }
1525 }
1526 }
|
1095 * <a href="#verNum">version number</a> followed by pre-release and
1096 * build information.
1097 *
1098 * @param s
1099 * A string to interpret as a version
1100 *
1101 * @throws IllegalArgumentException
1102 * If the given string cannot be interpreted as a valid
1103 * version
1104 *
1105 * @throws NullPointerException
1106 * If the given string is {@code null}
1107 *
1108 * @throws NumberFormatException
1109 * If an element of the version number or the build number
1110 * cannot be represented as an {@link Integer}
1111 *
1112 * @return The Version of the given string
1113 */
1114 public static Version parse(String s) {
1115 if (s == null)
1116 throw new NullPointerException();
1117
1118 // Shortcut to avoid initializing VersionPattern when creating
1119 // major version constants during startup
1120 if (isSimpleNumber(s)) {
1121 return new Version(List.of(Integer.parseInt(s)),
1122 Optional.empty(), Optional.empty(), Optional.empty());
1123 }
1124 Matcher m = VersionPattern.VSTR_PATTERN.matcher(s);
1125 if (!m.matches())
1126 throw new IllegalArgumentException("Invalid version string: '"
1127 + s + "'");
1128
1129 // $VNUM is a dot-separated list of integers of arbitrary length
1130 List<Integer> version = new ArrayList<>();
1131 for (String i : m.group(VersionPattern.VNUM_GROUP).split("\\."))
1132 version.add(Integer.parseInt(i));
1133
1134 Optional<String> pre = Optional.ofNullable(
1135 m.group(VersionPattern.PRE_GROUP));
1136
1137 String b = m.group(VersionPattern.BUILD_GROUP);
1138 // $BUILD is an integer
1139 Optional<Integer> build = (b == null)
1140 ? Optional.empty()
1141 : Optional.of(Integer.parseInt(b));
1142
1143 Optional<String> optional = Optional.ofNullable(
1144 m.group(VersionPattern.OPT_GROUP));
1145
1146 // empty '+'
1147 if ((m.group(VersionPattern.PLUS_GROUP) != null)
1148 && !build.isPresent()) {
1149 if (optional.isPresent()) {
1150 if (pre.isPresent())
1151 throw new IllegalArgumentException("'+' found with"
1152 + " pre-release and optional components:'" + s
1153 + "'");
1154 } else {
1155 throw new IllegalArgumentException("'+' found with neither"
1156 + " build or optional components: '" + s + "'");
1157 }
1158 }
1159 return new Version(version, pre, build, optional);
1160 }
1161
1162 private static boolean isSimpleNumber(String s) {
1163 for (int i = 0; i < s.length(); i++) {
1164 char c = s.charAt(i);
1165 char lowerBound = (i > 0) ? '0' : '1';
1166 if (c < lowerBound || c > '9') {
1167 return false;
1168 }
1169 }
1170 return true;
1171 }
1172
1173 /**
1174 * Returns the <a href="#major">major</a> version number.
1175 *
1176 * @return The major version number
1177 */
1178 public int major() {
1179 return version.get(0);
1180 }
1181
1182 /**
1183 * Returns the <a href="#minor">minor</a> version number or zero if it
1184 * was not set.
1185 *
1186 * @return The minor version number or zero if it was not set
1187 */
1188 public int minor() {
1189 return (version.size() > 1 ? version.get(1) : 0);
1190 }
1479 *
1480 * <p> This method satisfies the general contract of the {@link
1481 * Object#hashCode Object.hashCode} method.
1482 *
1483 * @return The hashcode of this version
1484 */
1485 @Override
1486 public int hashCode() {
1487 int h = 1;
1488 int p = 17;
1489
1490 h = p * h + version.hashCode();
1491 h = p * h + pre.hashCode();
1492 h = p * h + build.hashCode();
1493 h = p * h + optional.hashCode();
1494
1495 return h;
1496 }
1497 }
1498
1499 private static class VersionPattern {
1500 // $VNUM(-$PRE)?(\+($BUILD)?(\-$OPT)?)?
1501 // RE limits the format of version strings
1502 // ([1-9][0-9]*(?:(?:\.0)*\.[1-9][0-9]*)*)(?:-([a-zA-Z0-9]+))?(?:(\+)(0|[1-9][0-9]*)?)?(?:-([-a-zA-Z0-9.]+))?
1503
1504 private static final String VNUM
1505 = "(?<VNUM>[1-9][0-9]*(?:(?:\\.0)*\\.[1-9][0-9]*)*)";
1506 private static final String PRE = "(?:-(?<PRE>[a-zA-Z0-9]+))?";
1507 private static final String BUILD
1508 = "(?:(?<PLUS>\\+)(?<BUILD>0|[1-9][0-9]*)?)?";
1509 private static final String OPT = "(?:-(?<OPT>[-a-zA-Z0-9.]+))?";
1510 private static final String VSTR_FORMAT
1511 = "^" + VNUM + PRE + BUILD + OPT + "$";
1512
1513 static final Pattern VSTR_PATTERN = Pattern.compile(VSTR_FORMAT);
1514
1515 static final String VNUM_GROUP = "VNUM";
1516 static final String PRE_GROUP = "PRE";
1517 static final String PLUS_GROUP = "PLUS";
1518 static final String BUILD_GROUP = "BUILD";
1519 static final String OPT_GROUP = "OPT";
1520 }
1521 }
|