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