1 package org.codehaus.gmavenplus.model.internal;
2
3 import java.util.Arrays;
4
5
6 /**
7 * Container for Version information in the form of <i>major.minor.revision-tag</i>.
8 *
9 * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
10 * @author Keegan Witt
11 */
12 public class Version implements Comparable<Version> {
13
14 /**
15 * The version major number.
16 */
17 private int major;
18
19 /**
20 * The version minor number.
21 */
22 private int minor;
23
24 /**
25 * The version revision.
26 */
27 private int revision;
28
29 /**
30 * The version tag.
31 */
32 private String tag;
33
34 /**
35 * Constructs a new version object with the specified parameters.
36 *
37 * @param newMajor The version major number
38 * @param newMinor The version minor number
39 * @param newRevision The version revision number
40 * @param newTag The version tag string
41 */
42 public Version(final int newMajor, final int newMinor, final int newRevision, final String newTag) {
43 if (newMajor < 0 || newMinor < 0 || newRevision < 0) {
44 // note we don't check the tag since it can be null
45 throw new IllegalArgumentException("Major must be >= 0 and minor >= 0 and revision >= 0.");
46 }
47
48 major = newMajor;
49 minor = newMinor;
50 revision = newRevision;
51 if (newTag == null || !newTag.isEmpty()) {
52 tag = newTag;
53 } else {
54 tag = null;
55 }
56 }
57
58 /**
59 * Constructs a new Version object with the specified parameters.
60 *
61 * @param newMajor The version major number
62 * @param newMinor The version minor number
63 * @param newRevision The version revision number
64 */
65 public Version(final int newMajor, final int newMinor, final int newRevision) {
66 this(newMajor, newMinor, newRevision, null);
67 }
68
69 /**
70 * Constructs a new Version object with the specified parameters.
71 *
72 * @param newMajor The version major number
73 * @param newMinor The version minor number
74 */
75 public Version(final int newMajor, final int newMinor) {
76 this(newMajor, newMinor, 0);
77 }
78
79 /**
80 * Constructs a new Version object with the specified parameters.
81 *
82 * @param newMajor The version major number
83 */
84 public Version(final int newMajor) {
85 this(newMajor, 0);
86 }
87
88 /**
89 * Parses a new Version object from a string.
90 *
91 * @param version The version string to parse
92 * @return The version parsed from the string
93 */
94 public static Version parseFromString(final String version) {
95 if (version == null || version.isEmpty()) {
96 throw new IllegalArgumentException("Version must not be null or empty.");
97 }
98 String[] split = version.split("[._-]", 4);
99 try {
100 int tagIdx = 3;
101 int major = Integer.parseInt(split[0]);
102 int minor = 0;
103 int revision = 0;
104 StringBuilder tag = new StringBuilder();
105 if (split.length >= 2) {
106 try {
107 minor = Integer.parseInt(split[1]);
108 } catch (NumberFormatException nfe) {
109 // version string must not have specified a minor version, leave minor as 0 and append to tag instead
110 tag.append(split[1]);
111 tagIdx = 1;
112 tag.append("-");
113 }
114 }
115 if (split.length >= 3) {
116 try {
117 revision = Integer.parseInt(split[2]);
118 } catch (NumberFormatException nfe) {
119 // version string must not have specified a revision version, leave revision as 0 and append to tag instead
120 tag.append(split[2]);
121 tagIdx = 2;
122 tag.append("-");
123 }
124 }
125 if (split.length >= 4) {
126 for (int i = tagIdx; i < split.length; i++) {
127 if (i > tagIdx) {
128 tag.append("-");
129 }
130 tag.append(split[i]);
131 }
132 }
133 return new Version(major, minor, revision, tag.toString());
134 } catch (NumberFormatException e) {
135 throw new IllegalArgumentException("Major, minor, and revision must be integers.", e);
136 }
137 }
138
139 /**
140 * Returns a hash code for this object.
141 *
142 * @return The hash code for this object
143 * @see java.lang.Object#hashCode()
144 */
145 @Override
146 public final int hashCode() {
147 return Arrays.hashCode(new Object[]{major, minor, revision, tag});
148 }
149
150 /**
151 * Determines whether the specified object is equal to this object.
152 *
153 * @param obj The object to compare to this object
154 * @return <code>true</code> if the specified object is equal to this object, <code>false</code> otherwise
155 * @see java.lang.Object#equals(java.lang.Object)
156 */
157 @Override
158 public final boolean equals(final Object obj) {
159 if (obj == null) {
160 return false;
161 }
162 if (obj == this) {
163 return true;
164 }
165 if (obj.getClass() != getClass()) {
166 return false;
167 }
168 return compareTo((Version) obj) == 0;
169 }
170
171 /**
172 * Returns a String representation of this object.
173 *
174 * @return The String representation of this object
175 * @see java.lang.Object#toString()
176 */
177 @Override
178 public final String toString() {
179 StringBuilder buff = new StringBuilder();
180
181 buff.append(major)
182 .append(".").append(minor)
183 .append(".").append(revision);
184 if (tag != null) {
185 buff.append("-").append(tag);
186 }
187
188 return buff.toString();
189 }
190
191 /**
192 * Compares two versions objects. Note that if the major, minor, and revision are all the same, tags are compared with
193 * {@link java.lang.String#compareTo(String) String.compareTo()}. Having no tag is considered a newer version than a version with a tag.
194 *
195 * @param version The version to compare this version to
196 * @return <code>0</code> if the version is equal to this version, <code>1</code> if the version is greater than
197 * this version, or <code>-1</code> if the version is lower than this version.
198 */
199 @Override
200 public final int compareTo(final Version version) {
201 return compareTo(version, true);
202 }
203
204 /**
205 * Compares two versions objects. Note that if the major, minor, and revision are all the same, tags are compared with
206 * {@link java.lang.String#compareTo(String) String.compareTo()}.
207 *
208 * @param version The version to compare this version to
209 * @param noTagsAreNewer Whether versions with no tag are considered newer than those that have tags
210 * @return <code>0</code> if the version is equal to this version, <code>1</code> if the version is greater than
211 * this version, or <code>-1</code> if the version is lower than this version.
212 */
213 public final int compareTo(final Version version, final boolean noTagsAreNewer) {
214 // "beta" is replaced with " beta" to make sure RCs are considered newer than betas (by moving beta to back of order)
215 int comp = Integer.compare(major, version.major);
216 if (comp == 0) {
217 comp = Integer.compare(minor, version.minor);
218 }
219 if (comp == 0) {
220 comp = Integer.compare(revision, version.revision);
221 }
222 if (comp == 0) {
223 if (tag != null && version.tag != null) {
224 return tag.replace("beta", " beta").replace("alpha", " alpha")
225 .compareTo(version.tag.replace("beta", " beta").replace("alpha", " alpha"));
226 } else if (tag == null ^ version.tag == null) {
227 if (tag == null) {
228 return noTagsAreNewer ? 1 : -1;
229 } else {
230 return noTagsAreNewer ? -1 : 1;
231 }
232 } else {
233 return comp;
234 }
235 } else {
236 return comp;
237 }
238 }
239
240 /**
241 * Gets the version major number.
242 *
243 * @return The major version number
244 */
245 public int getMajor() {
246 return major;
247 }
248
249 /**
250 * Sets the version major number.
251 *
252 * @param newMajor The major version number to set
253 * @return This object (for fluent invocation)
254 */
255 public Version setMajor(final int newMajor) {
256 major = newMajor;
257 return this;
258 }
259
260 /**
261 * Gets the version minor number.
262 *
263 * @return The version minor number
264 */
265 public int getMinor() {
266 return minor;
267 }
268
269 /**
270 * Sets the version minor number.
271 *
272 * @param newMinor The version minor number to set
273 * @return This object (for fluent invocation)
274 */
275 public Version setMinor(final int newMinor) {
276 minor = newMinor;
277 return this;
278 }
279
280 /**
281 * Gets the version revision number.
282 *
283 * @return The version revision number
284 */
285 public int getRevision() {
286 return revision;
287 }
288
289 /**
290 * Sets the version revision number.
291 *
292 * @param newRevision The revision number to set
293 * @return This object (for fluent invocation)
294 */
295 public Version setRevision(final int newRevision) {
296 revision = newRevision;
297 return this;
298 }
299
300 /**
301 * Gets the version tag string.
302 *
303 * @return The version tag string
304 */
305 public String getTag() {
306 return tag;
307 }
308
309 /**
310 * Sets the version tag string.
311 *
312 * @param newTag The version tag string to set
313 * @return This object (for fluent invocation)
314 */
315 public Version setTag(final String newTag) {
316 tag = newTag;
317 return this;
318 }
319
320 }