Source code: javax/ide/util/Version.java
1 package javax.ide.util;
2
3 import java.util.HashMap;
4 import java.util.StringTokenizer;
5
6 /**
7 * Represents the version number of an extension or component. A version
8 * number is an immutable ordered sequence of integer values.
9 * <p>
10 * Version numbers can be represented in two forms. When you construct a
11 * new Version object using a String representation of the version number, this
12 * String representation is stored and returned by the {@link #toString()} method.
13 * This form of the version number will retain any redundant leading zeros in
14 * any of the integer values which make up the version number.
15 * <p>
16 * Conversely, in the <i>canonical</i> form, a version number does not retain
17 * leading zeros. The canonical form of <tt>new Version( "1.02.03" )</tt> would
18 * be <tt>"1.2.3"</tt>. You can retrieve the canonical form of a Version object
19 * using the {@link #toCanonicalString()} method.
20 */
21 public final class Version implements Comparable
22 {
23 private static final HashMap _convertCache = new HashMap( 100 );
24 private final int[] _numbers;
25 private final String _versionLabel;
26
27 /**
28 * Constructs a Version object from a String representation.
29 *
30 * @param versionLabel a String representation of a version number. This must
31 * start with an integer and contain only integers and periods.
32 * @throws NumberFormatException if the specified version label contains
33 * non-numeric characters other than periods.
34 */
35 public Version( String versionLabel ) throws NumberFormatException
36 {
37 _versionLabel = versionLabel;
38 _numbers = convert( versionLabel );
39 }
40
41 /**
42 * Converts this Version to an int array.
43 *
44 * @return a newly allocated int array whose length is equal to the number
45 * of integer values in this version object and whose contents are
46 * the integer values represented by this version.
47 */
48 public int[] toIntArray()
49 {
50 int[] numbers = new int[ _numbers.length ];
51 System.arraycopy( _numbers, 0, numbers, 0, _numbers.length );
52
53 return numbers;
54 }
55
56
57 //--------------------------------------------------------------------------
58 // Comparable interface.
59 //--------------------------------------------------------------------------
60
61 /**
62 * Compare this version object with another version object.
63 * Comparison of two version numbers applies to the canonical form of the
64 * version number. For example, the result of evaluating the expression
65 * <tt>new Version( "1.05.07" ).compareTo( new Version( "1.5.0006" ) )</tt> is
66 * a positive integer indicating that version 1.5.7 is greater than 1.5.6.
67 *
68 * @param other another instance of Version.
69 * @return true if this version is equal to the other version.
70 */
71 public int compareTo( Object other )
72 {
73 if ( this == other )
74 {
75 return 0;
76 }
77
78 int[] otherNumbers = ((Version) other)._numbers;
79 int len1 = _numbers.length;
80 int len2 = otherNumbers.length;
81 int max = Math.max(len1, len2);
82
83 for ( int i = 0; i < max; i++ )
84 {
85 int d1 = (i < len1 ? _numbers[i] : 0);
86 int d2 = (i < len2 ? otherNumbers[i] : 0);
87
88 if (d1 != d2)
89 {
90 return d1 - d2;
91 }
92 }
93 return 0;
94 }
95
96 //--------------------------------------------------------------------------
97 // Object overrides.
98 //--------------------------------------------------------------------------
99
100 /**
101 * Get this version number as a string. The string returned is equal to the
102 * string provided in the constructor. For example,
103 * <tt>new Version( "1.05.06" ).toString();</tt> would return
104 * <tt>"1.05.06"</tt>.
105 *
106 * @return the string representation of this version.
107 * @see #toCanonicalString()
108 */
109 public String toString()
110 {
111 return _versionLabel;
112 }
113
114 /**
115 * Get this version number as a canonicalized string. Leading zeros will be
116 * removed from all numerical components of the version. For example,
117 * <tt>new Version( "1.05.06" ).toString();</tt> would return
118 * <tt>"1.5.6"</tt>.
119 *
120 * @return a canonical string representation of this version.
121 * @see #toString()
122 */
123 public String toCanonicalString()
124 {
125 final int len = _numbers.length;
126 final StringBuffer rtn = new StringBuffer( len * 3 ).append( _numbers[0] );
127
128 for ( int i = 1; i < len; i++ )
129 {
130 rtn.append('.'); // NORES
131 rtn.append( _numbers[ i ] );
132 }
133
134 return rtn.toString();
135 }
136
137 /**
138 * Compare this version object with another version object for equality.
139 * Equality of two version numbers applies to the canonical form of the
140 * version number. For example, the result of evaluating the expression
141 * <tt>new Version( "1.05.06" ).equals( new Version( "1.5.0006" ) )</tt> is
142 * <tt>true</tt>.
143 *
144 * @param other another instance of Version.
145 * @return true if this version is equal to the other version.
146 */
147 public boolean equals( Object other )
148 {
149 if ( other == this )
150 {
151 return true;
152 }
153 if ( !(other instanceof Version) )
154 {
155 return false;
156 }
157
158 final Version version = (Version)other;
159 final int len = _numbers.length;
160
161 if ( len != version._numbers.length )
162 {
163 return false;
164 }
165
166 for ( int i = 0; i < len; i++ )
167 {
168 if ( _numbers[ i ] != version._numbers[ i ] )
169 {
170 return false;
171 }
172 }
173
174 return true;
175 }
176
177 public int hashCode()
178 {
179 int hash = 925295;
180 final int len = _numbers.length;
181
182 for ( int i = 0; i < len; i++ )
183 {
184 hash = 37*hash + i;
185 }
186
187 return hash;
188 }
189
190 //---------------------------------------------------------------------------
191 // Private methods.
192 //---------------------------------------------------------------------------
193
194 private int[] convert( String versionLabel ) throws NumberFormatException
195 {
196 int[] numbers = (int[])_convertCache.get( versionLabel );
197
198 if ( numbers != null )
199 {
200 return numbers;
201 }
202
203 final StringTokenizer tokenizer =
204 new StringTokenizer(versionLabel, ".", true); // NORES
205 final int count = tokenizer.countTokens() + 1;
206
207 if ( count % 2 != 0 )
208 {
209 throw new NumberFormatException(
210 " Malformed version specification: `" //NORES
211 + versionLabel + "`."); //NORES
212 }
213
214 numbers = new int[ count / 2 ];
215 boolean expectingNumber = true;
216 int i = 0;
217
218 while ( tokenizer.hasMoreTokens() )
219 {
220 final String token = tokenizer.nextToken();
221
222 if (expectingNumber)
223 {
224 expectingNumber = false;
225 int piece = Integer.parseInt( token );
226
227 if (piece < 0)
228 {
229 throw new NumberFormatException(
230 "Malformed version specification: `" //NORES
231 + piece + "`." //NORES
232 + "Version number must be > 0."); //NORES
233 }
234 numbers[ i++ ] = piece;
235 }
236 else
237 {
238 expectingNumber = true;
239 }
240 }
241
242 return numbers;
243 }
244
245 }