View Javadoc
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 }