jaulib v1.3.0
Jau Support Library (C++, Java, ..)
VersionNumber.java
Go to the documentation of this file.
1/**
2 * Author: Sven Gothel <sgothel@jausoft.com>
3 * Copyright (c) 2021 Gothel Software e.K.
4 * Copyright (c) 2010 Gothel Software e.K.
5 * Copyright (c) 2010 JogAmp Community.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining
8 * a copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sublicense, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be
16 * included in all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27package org.jau.util;
28
29import java.util.regex.Matcher;
30
31/**
32 * Simple version number class containing a version number
33 * either being {@link #VersionNumber(int, int, int) defined explicit}
34 * or {@link #VersionNumber(String, String) derived from a string}.
35 * <p>
36 * For the latter case, you can query whether a component has been defined explicitly by the given <code>versionString</code>,
37 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
38 * </p>
39 * <p>
40 * The state whether a component is defined explicitly <i>is not considered</i>
41 * in the {@link #hashCode()}, {@link #equals(Object)} or {@link #compareTo(Object)} methods,
42 * since the version number itself is treated regardless.
43 * </p>
44 */
45public class VersionNumber implements Comparable<Object> {
46
47 /**
48 * A {@link #isZero() zero} version instance, w/o any component defined explicitly.
49 * @see #hasMajor()
50 * @see #hasMinor()
51 * @see #hasSub()
52 */
53 public static final VersionNumber zeroVersion = new VersionNumber(0, 0, 0, -1, (short)0);
54
55 /**
56 * Returns the {@link java.util.regex.Pattern pattern}
57 * with Perl regular expression:
58 * <pre>
59 * "\\D*(\\d+)[^\\"+delim+"\\s]*(?:\\"+delim+"\\D*(\\d+)[^\\"+delim+"\\s]*(?:\\"+delim+"\\D*(\\d+))?)?"
60 * </pre>
61 * </p>
62 * <p>
63 * A whitespace within the version number will end the parser.
64 * </p>
65 * <p>
66 * Capture groups represent the major (1), optional minor (2) and optional sub version number (3) component in this order.
67 * </p>
68 * <p>
69 * Each capture group ignores any leading non-digit and uses only contiguous digits, i.e. ignores pending non-digits.
70 * </p>
71 * @param delim the delimiter, e.g. "."
72 */
73 public static java.util.regex.Pattern getVersionNumberPattern(final String delim) {
74 return java.util.regex.Pattern.compile("\\D*(\\d+)[^\\"+delim+"\\s]*(?:\\"+delim+"\\D*(\\d+)[^\\"+delim+"\\s]*(?:\\"+delim+"\\D*(\\d+))?)?");
75 }
76
77 /**
78 * Returns the default {@link java.util.regex.Pattern pattern} using {@link #getVersionNumberPattern(String)}
79 * with delimiter "<b>.</b>".
80 * <p>
81 * Instance is cached.
82 * </p>
83 */
84 public static java.util.regex.Pattern getDefaultVersionNumberPattern() {
85 if( null == defPattern ) { // volatile dbl-checked-locking OK
86 synchronized( VersionNumber.class ) {
87 if( null == defPattern ) {
88 defPattern = getVersionNumberPattern(".");
89 }
90 }
91 }
92 return defPattern;
93 }
94 private static volatile java.util.regex.Pattern defPattern = null;
95
96 protected final int major, minor, sub, strEnd;
97
98 protected final short state;
99 protected final static short HAS_MAJOR = 1 << 0 ;
100 protected final static short HAS_MINOR = 1 << 1 ;
101 protected final static short HAS_SUB = 1 << 2 ;
102
103 protected VersionNumber(final int majorRev, final int minorRev, final int subMinorRev, final int _strEnd, final short _state) {
104 major = majorRev;
105 minor = minorRev;
106 sub = subMinorRev;
107 strEnd = _strEnd;
108 state = _state;
109 }
110
111 /**
112 * Explicit version number instantiation, with all components defined explicitly.
113 * @see #hasMajor()
114 * @see #hasMinor()
115 * @see #hasSub()
116 */
117 public VersionNumber(final int majorRev, final int minorRev, final int subMinorRev) {
118 this(majorRev, minorRev, subMinorRev, -1, (short)(HAS_MAJOR | HAS_MINOR | HAS_SUB));
119 }
120
121 /**
122 * String derived version number instantiation.
123 * <p>
124 * Utilizing the default {@link java.util.regex.Pattern pattern} parser with delimiter "<b>.</b>", see {@link #getDefaultVersionNumberPattern()}.
125 * </p>
126 * <p>
127 * You can query whether a component has been defined explicitly by the given <code>versionString</code>,
128 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
129 * </p>
130 * @param versionString should be given as [MAJOR[.MINOR[.SUB]]]
131 *
132 * @see #hasMajor()
133 * @see #hasMinor()
134 * @see #hasSub()
135 */
136 public VersionNumber(final String versionString) {
137 this(versionString, getDefaultVersionNumberPattern());
138 }
139
140 /**
141 * String derived version number instantiation.
142 * <p>
143 * Utilizing {@link java.util.regex.Pattern pattern} parser created via {@link #getVersionNumberPattern(String)}.
144 * </p>
145 * <p>
146 * You can query whether a component has been defined explicitly by the given <code>versionString</code>,
147 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
148 * </p>
149 * @param versionString should be given as [MAJOR[.MINOR[.SUB]]]
150 * @param delim the delimiter, e.g. "."
151 *
152 * @see #hasMajor()
153 * @see #hasMinor()
154 * @see #hasSub()
155 */
156 public VersionNumber(final String versionString, final String delim) {
157 this(versionString, getVersionNumberPattern(delim));
158 }
159
160 /**
161 * String derived version number instantiation.
162 * <p>
163 * You can query whether a component has been defined explicitly by the given <code>versionString</code>,
164 * via {@link #hasMajor()}, {@link #hasMinor()} and {@link #hasSub()}.
165 * </p>
166 * @param versionString should be given as [MAJOR[.MINOR[.SUB]]]
167 * @param versionPattern the {@link java.util.regex.Pattern pattern} parser, must be compatible w/ {@link #getVersionNumberPattern(String)}
168 *
169 * @see #hasMajor()
170 * @see #hasMinor()
171 * @see #hasSub()
172 */
173 public VersionNumber(final String versionString, final java.util.regex.Pattern versionPattern) {
174 // group1: \d* == digits major
175 // group2: \d* == digits minor
176 // group3: \d* == digits sub
177 final int[] val = new int[3];
178 int _strEnd = 0;
179 short _state = 0;
180 try {
181 final Matcher matcher = versionPattern.matcher( versionString );
182 if( matcher.lookingAt() ) {
183 _strEnd = matcher.end();
184 final int groupCount = matcher.groupCount();
185 if( 1 <= groupCount ) {
186 val[0] = Integer.parseInt(matcher.group(1));
187 _state = HAS_MAJOR;
188 if( 2 <= groupCount ) {
189 val[1] = Integer.parseInt(matcher.group(2));
190 _state |= HAS_MINOR;
191 if( 3 <= groupCount ) {
192 val[2] = Integer.parseInt(matcher.group(3));
193 _state |= HAS_SUB;
194 }
195 }
196 }
197 }
198 } catch (final Exception e) { }
199
200 major = val[0];
201 minor = val[1];
202 sub = val[2];
203 strEnd = _strEnd;
204 state = _state;
205 }
206
207 /** Returns <code>true</code>, if all version components are zero, otherwise <code>false</code>. */
208 public final boolean isZero() {
209 return major == 0 && minor == 0 && sub == 0;
210 }
211
212 /** Returns <code>true</code>, if the major component is defined explicitly, otherwise <code>false</code>. Undefined components has the value <code>0</code>. */
213 public final boolean hasMajor() { return 0 != ( HAS_MAJOR & state ); }
214 /** Returns <code>true</code>, if the optional minor component is defined explicitly, otherwise <code>false</code>. Undefined components has the value <code>0</code>. */
215 public final boolean hasMinor() { return 0 != ( HAS_MINOR & state ); }
216 /** Returns <code>true</code>, if the optional sub component is defined explicitly, otherwise <code>false</code>. Undefined components has the value <code>0</code>. */
217 public final boolean hasSub() { return 0 != ( HAS_SUB & state ); }
218
219 /**
220 * If constructed with <code>version-string</code>, returns the string offset <i>after</i> the last matching character,
221 * or <code>0</code> if none matched, or <code>-1</code> if not constructed with a string.
222 */
223 public final int endOfStringMatch() { return strEnd; }
224
225 @Override
226 public final int hashCode() {
227 // 31 * x == (x << 5) - x
228 int hash = 31 + major;
229 hash = ((hash << 5) - hash) + minor;
230 return ((hash << 5) - hash) + sub;
231 }
232
233 @Override
234 public final boolean equals(final Object o) {
235 if ( o instanceof VersionNumber ) {
236 return 0 == compareTo( (VersionNumber) o );
237 }
238 return false;
239 }
240
241 @Override
242 public final int compareTo(final Object o) {
243 if ( ! ( o instanceof VersionNumber ) ) {
244 final Class<?> c = (null != o) ? o.getClass() : null ;
245 throw new ClassCastException("Not a VersionNumber object: " + c);
246 }
247 return compareTo( (VersionNumber) o );
248 }
249
250 public final int compareTo(final VersionNumber vo) {
251 if (major > vo.major) {
252 return 1;
253 } else if (major < vo.major) {
254 return -1;
255 } else if (minor > vo.minor) {
256 return 1;
257 } else if (minor < vo.minor) {
258 return -1;
259 } else if (sub > vo.sub) {
260 return 1;
261 } else if (sub < vo.sub) {
262 return -1;
263 }
264 return 0;
265 }
266
267 public final int getMajor() {
268 return major;
269 }
270
271 public final int getMinor() {
272 return minor;
273 }
274
275 public final int getSub() {
276 return sub;
277 }
278
279 @Override
280 public String toString() {
281 return major + "." + minor + "." + sub ;
282 }
283}
Simple version number class containing a version number either being defined explicit or derived from...
final boolean hasSub()
Returns true, if the optional sub component is defined explicitly, otherwise false.
final boolean isZero()
Returns true, if all version components are zero, otherwise false.
final int endOfStringMatch()
If constructed with version-string, returns the string offset after the last matching character,...
final int compareTo(final VersionNumber vo)
VersionNumber(final String versionString, final java.util.regex.Pattern versionPattern)
String derived version number instantiation.
static final short HAS_SUB
VersionNumber(final String versionString, final String delim)
String derived version number instantiation.
static final short HAS_MINOR
VersionNumber(final String versionString)
String derived version number instantiation.
VersionNumber(final int majorRev, final int minorRev, final int subMinorRev, final int _strEnd, final short _state)
VersionNumber(final int majorRev, final int minorRev, final int subMinorRev)
Explicit version number instantiation, with all components defined explicitly.
static final short HAS_MAJOR
final boolean hasMajor()
Returns true, if the major component is defined explicitly, otherwise false.
static final VersionNumber zeroVersion
A zero version instance, w/o any component defined explicitly.
static java.util.regex.Pattern getVersionNumberPattern(final String delim)
Returns the pattern with Perl regular expression:
final int compareTo(final Object o)
final boolean equals(final Object o)
static java.util.regex.Pattern getDefaultVersionNumberPattern()
Returns the default pattern using getVersionNumberPattern(String) with delimiter "<b>....
final boolean hasMinor()
Returns true, if the optional minor component is defined explicitly, otherwise false.