3939 */
4040public final class CloudStackVersion implements Comparable <CloudStackVersion > {
4141
42- private final static Pattern NUMBER_VERSION_FORMAT = Pattern .compile ("(\\ d+\\ .){2}(\\ d+\\ .)?\\ d+" );
43- private final static Pattern FULL_VERSION_FORMAT = Pattern .compile ("(\\ d+\\ .){2}(\\ d+\\ .)?\\ d+(-[a-zA-Z]+)?(-\\ d+)?(-SNAPSHOT)?" );
42+ private final static Pattern NUMBER_VERSION_FORMAT = Pattern .compile ("\\ d+\\ .\\ d+\\ .\\ d+(?:\\ .\\ d+)?" );
43+ private final static Pattern FULL_VERSION_FORMAT = Pattern .compile ("\\ d+\\ .\\ d+\\ .\\ d+(?:\\ .\\ d+)?(?:-[a-zA-Z]+)?(?:-\\ d+)?(?:-SNAPSHOT)?" );
44+ private final static int NEW_VERSIONING_CUTOVER_MAJOR_VERSION = 24 ;
4445
4546 private final int majorRelease ;
4647 private final int minorRelease ;
47- private final int patchRelease ;
48+ private final Integer patchRelease ;
4849 private final Integer securityRelease ;
4950
50- private CloudStackVersion (final int majorRelease , final int minorRelease , final int patchRelease , final Integer securityRelease ) {
51+ private CloudStackVersion (final int majorRelease , final int minorRelease , final Integer patchRelease , final Integer securityRelease ) {
5152
5253 super ();
5354
5455 checkArgument (majorRelease >= 0 , CloudStackVersion .class .getName () + "(int, int, int, Integer) requires a majorRelease greater than 0." );
5556 checkArgument (minorRelease >= 0 , CloudStackVersion .class .getName () + "(int, int, int, Integer) requires a minorRelease greater than 0." );
56- checkArgument (patchRelease >= 0 , CloudStackVersion .class .getName () + "(int, int, int, Integer) requires a patchRelease greater than 0." );
57- checkArgument (( securityRelease != null && securityRelease >= 0 ) || ( securityRelease == null ) ,
57+ checkArgument (patchRelease == null || patchRelease >= 0 , CloudStackVersion .class .getName () + "(int, int, int, Integer) requires a patchRelease greater than 0." );
58+ checkArgument (securityRelease == null || securityRelease >= 0 ,
5859 CloudStackVersion .class .getName () + "(int, int, int, Integer) requires a null securityRelease or a non-null value greater than 0." );
5960
6061 this .majorRelease = majorRelease ;
@@ -69,11 +70,13 @@ private CloudStackVersion(final int majorRelease, final int minorRelease, final
6970 * Parses a <code>String</code> representation of a version that conforms one of the following
7071 * formats into a <code>CloudStackVersion</code> instance:
7172 * <ul>
72- * <li><code><major>.<minor>.<patch>.<security> </code></li>
73- * <li><code><major>.<minor>.<patch>.<security>.<security> </code></li>
74- * <li><code><major>.<minor>.<patch>.< security>.<security>-<any string ></code></li>
73+ * <li><code><major>.<minor>.<patch></code> (legacy, deprecated since 24.0.0, allowed only below major version 24) </li>
74+ * <li><code><major>.<minor>.<patch>.<security></code> (legacy, deprecated since 24.0.0, allowed only below major version 24) </li>
75+ * <li><code><major>.<minor>.<security release ></code> (for versions >= 24.0.0) </li>
7576 * </ul>
7677 *
78+ * Legacy patch-based formats remain supported for backward compatibility.
79+ *
7780 * If the string contains a suffix that begins with a "-" character, then the "-" and all characters following it
7881 * will be dropped.
7982 *
@@ -91,7 +94,7 @@ public static CloudStackVersion parse(final String value) {
9194
9295 checkArgument (StringUtils .isNotBlank (trimmedValue ), CloudStackVersion .class .getName () + ".parse(String) requires a non-blank value" );
9396 checkArgument (NUMBER_VERSION_FORMAT .matcher (trimmedValue ).matches (), CloudStackVersion .class .getName () + ".parse(String) passed " +
94- value + ", but requires a value in the format of int.int.int(.int)(-<legacy patch >)" );
97+ value + ", but requires a value in the format of int.int.int(.int)(-<suffix >)" );
9598
9699 final String [] components = trimmedValue .split ("\\ ." );
97100
@@ -100,8 +103,26 @@ public static CloudStackVersion parse(final String value) {
100103
101104 final int majorRelease = Integer .valueOf (components [0 ]);
102105 final int minorRelease = Integer .valueOf (components [1 ]);
103- final int patchRelease = Integer .valueOf (components [2 ]);
104- final Integer securityRelease = components .length == 3 ? null : Integer .valueOf (components [3 ]);
106+ final int thirdComponent = Integer .valueOf (components [2 ]);
107+
108+ final int patchRelease ;
109+ final Integer securityRelease ;
110+
111+ if (components .length == 4 ) {
112+ checkArgument (isLegacyVersioning (majorRelease ), CloudStackVersion .class .getName () + ".parse(String) passed " + value +
113+ ", but major versions at or above 24 do not support legacy int.int.int.int format" );
114+ // Deprecated legacy format: major.minor.patch.security
115+ patchRelease = thirdComponent ;
116+ securityRelease = Integer .valueOf (components [3 ]);
117+ } else if (isNewVersioning (majorRelease )) {
118+ // New format: major.minor.securityRelease (patch dropped)
119+ patchRelease = 0 ;
120+ securityRelease = thirdComponent ;
121+ } else {
122+ // Deprecated legacy format: major.minor.patch
123+ patchRelease = thirdComponent ;
124+ securityRelease = null ;
125+ }
105126
106127 return new CloudStackVersion (majorRelease , minorRelease , patchRelease , securityRelease );
107128
@@ -207,6 +228,14 @@ private static ImmutableList<Integer> normalizeVersionValues(final ImmutableList
207228
208229 }
209230
231+ private static boolean isLegacyVersioning (final int majorRelease ) {
232+ return majorRelease < NEW_VERSIONING_CUTOVER_MAJOR_VERSION ;
233+ }
234+
235+ private static boolean isNewVersioning (final int majorRelease ) {
236+ return majorRelease >= NEW_VERSIONING_CUTOVER_MAJOR_VERSION ;
237+ }
238+
210239 /**
211240 *
212241 * @return The components of this version as an {@link ImmutableList} in order of major release, minor release,
@@ -244,6 +273,10 @@ public Integer getSecurityRelease() {
244273 return securityRelease ;
245274 }
246275
276+ public boolean usesNewVersioning () {
277+ return isNewVersioning (majorRelease );
278+ }
279+
247280 @ Override
248281 public boolean equals (final Object thatObject ) {
249282
@@ -270,6 +303,11 @@ public int hashCode() {
270303
271304 @ Override
272305 public String toString () {
306+ // Canonicalize cutover-and-later versions to major.minor.securityRelease.
307+ if (securityRelease != null && patchRelease == 0 && isNewVersioning (majorRelease )) {
308+ return Joiner .on ("." ).join (ImmutableList .of (majorRelease , minorRelease , securityRelease ));
309+ }
310+
273311 return Joiner .on ("." ).join (asList ());
274312 }
275313
0 commit comments