1 /*
2 Copyright (C) 2000 - 2007 Grid Systems, S.A.
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16 */
17 package com.gridsystems.utils;
18
19 import java.util.ArrayList;
20
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23
24 /**
25 * General Utilities.
26 *
27 * @author nporcel
28 * @version 1.0
29 */
30 public class Version implements Comparable<Version> {
31
32 /**
33 * List of values.
34 */
35 private ArrayList<Integer> values = new ArrayList<Integer>();
36
37 /**
38 * Logger for this class.
39 */
40 private static Log log = LogFactory.getLog(Version.class);
41
42 /**
43 * Constructor.
44 *
45 * @param version the String from which the version information is obtained.
46 * It is interpreted as an arbitrarily long series of integers separated by
47 * non-digit characters.
48 */
49 public Version(String version) {
50 if (version == null) {
51 version = "";
52 }
53 final int v10 = 10;
54 int length = version.length();
55 int value = 0;
56 boolean valueSet = false;
57 for (int i = 0; i < length; i++) {
58 char versionChar = version.charAt(i);
59 if (Character.isDigit(versionChar)) {
60 value = value * v10 + Integer.parseInt(Character.toString(versionChar));
61 valueSet = true;
62 } else {
63 if (valueSet) {
64 this.values.add(new Integer(value));
65 valueSet = false;
66 value = 0;
67 }
68 }
69 }
70 if (value != 0) {
71 this.values.add(new Integer(value));
72 }
73 }
74
75 /**
76 * Creates an instance.
77 *
78 * @param values version numbers (major, minor, release, build, etc.)
79 */
80 public Version(int... values) {
81 for (int v : values) {
82 if (v < 0) {
83 break;
84 } else {
85 this.values.add(v);
86 }
87 }
88 }
89
90 /**
91 * Gets a slice of the version numbers.
92 *
93 * @param i an int with the position (ranging from 0 to
94 * <code>this.getSize() - 1</code> of the part of the version number wanted;
95 * 0 means the major version number; 1 the minor version number and so on.
96 *
97 * @return an int with the value of that part of the version number or, if
98 * the index is out of the valid range, 0.
99 */
100 public int getValue(int i) {
101 if ((i >= 0) && (i < this.values.size())) {
102 return ((Integer) this.values.get(i)).intValue();
103 }
104 return 0;
105 }
106
107 /**
108 * Get the size (number of elements) of the version.
109 *
110 * @return an int with the size (number of elements) of the version.
111 */
112 public int getSize() {
113 return this.values.size();
114 }
115
116 /**
117 * Compares this object to another JavaVersion.
118 *
119 * Compares this object to another JavaVersion. Objects related to older
120 * versions are minor that objects related to newer versions.
121 *
122 * @param v the instance to compare to
123 * @return an int that is -1 if <code>this</code> is minor than
124 * <code>obj</code>, 0 if both objects represent the same version and 1
125 * otherwise.
126 */
127 public int compareTo(Version v) {
128
129 int sizeA = v.getSize();
130 int sizeB = this.getSize();
131
132 if (sizeA < sizeB) {
133 sizeA = sizeB;
134 }
135
136 for (int i = 0; i < sizeA; i++) {
137 int valueThis = this.getValue(i);
138 int valueObj = v.getValue(i);
139 if (valueThis < valueObj) {
140 return -1;
141 } else {
142 if (valueThis > valueObj) {
143 return 1;
144 }
145 }
146 }
147 return 0;
148 }
149
150 /**
151 * {@inheritDoc}
152 */
153 @Override public boolean equals(Object obj) {
154 if (obj == null) {
155 return false;
156 } else if (obj instanceof Version) {
157 return this.compareTo((Version)obj) == 0;
158 } else {
159 return false;
160 }
161 }
162
163 /**
164 * {@inheritDoc}
165 */
166 @Override public int hashCode() {
167 final int factor = 37;
168 final int initial = 17;
169
170 int hash = initial;
171 for (int v : this.values) {
172 hash = hash * factor + v;
173 }
174 return hash;
175 }
176
177 /**
178 * Gets the major version.
179 *
180 * @return an int with the major number of the version.
181 *
182 * @deprecated Use #getValue(int) instead.
183 */
184 public int getMajorVersion() {
185 return this.getValue(0);
186 }
187
188 /**
189 * Gets the minor version.
190 *
191 * @return an int with the the minor number of the version.
192 *
193 * @deprecated Use #getValue(int) instead.
194 */
195 public int getMinorVersion() {
196 return this.getValue(1);
197 }
198
199 /**
200 * Gets the release version.
201 *
202 * @return an int with the release number of the minor version.
203 *
204 * @deprecated Use #getValue(int) instead.
205 */
206 public int getRelease() {
207 return this.getValue(2);
208 }
209
210
211 /**
212 * Returns a String representation of this object.
213 *
214 * @return a String representation of this version number as a series of
215 * integers separated with dots ".".
216 */
217 @Override public String toString() {
218
219 StringBuffer returnValue = new StringBuffer();
220
221 int size = this.getSize();
222 for (int i = 0; i < size; i++) {
223 if (i != 0) {
224 returnValue.append(".");
225 }
226 returnValue.append(((Integer) this.values.get(i)).toString());
227 }
228 return returnValue.toString();
229 }
230
231 /**
232 * Gets the representation as a double of a version String.
233 *
234 * Gets the representation as a double of a version String. The String is
235 * parsed in the same way it is when invoking #Version(String). The return
236 * value is in the form of {d0}.{d1}{d2}{d3}..., being d0 the value of the
237 * first part of the version number and d1, d2 and remainders are two-digits
238 * representation of the other parts of the version number. If any of the
239 * later is greater than 99 (and thus, it cannot be written with two digits)
240 * it is converted to 99. Examples<br>
241 * <ul>
242 * <li>5.8.10.66 -> 5.081066</li>
243 * <li>216.14.3.108 -> 216.140399</li>
244 * </ul>
245 *
246 * @param version the String with the version description.
247 *
248 * @return a double with a representation of <code>version</code>.
249 *
250 * @warning Due to the nature of the representation of numbers using
251 * fixed-size types, it can happen that
252 * getDoubleRepresentation(getStringRepresentation(x)) != x.
253 *
254 * @see #Version(String)
255 * @deprecated For comparisons, use #compare(Object)
256 */
257 public static double getDoubleRepresentation(String version) {
258 double returnValue = 0d;
259
260 Version tmpVersion = new Version(version);
261 final int v99 = 99;
262 final int v100 = 100;
263 double divider = 1;
264 for (int i = 0; i <= tmpVersion.getSize(); i++) {
265 int versionPart = tmpVersion.getValue(i);
266 if ((versionPart > v99) && (i != 0)) {
267 log.warn("The element at position " + i + " of the version number "
268 + "is greater than 99; replacing it with 99");
269 versionPart = v99;
270 }
271 returnValue += ((double)versionPart) / divider;
272 divider = divider * v100;
273 }
274
275 return returnValue;
276 }
277
278 /**
279 * Gets the representation as a string of a version double.
280 *
281 * Gets the representation as a string of a version double. This is the
282 * opposite process to #getDoubleRepresentation(String), splicing the double
283 * value in groups of two digits and using the values as parts of the version
284 * number. Parts are separated using an underscore "_" character.
285 * Examples:<br>
286 * <ul>
287 * <li>5.081066 -> 5_8_10_66</li>
288 * <li>216.140399 -> 216_14_3_99</li>
289 * </ul>
290 *
291 * @param version a double with the version data.
292 * @param fields an int with the number of fields that the resulting String
293 * is expected to have. Setting this value properly allows avoiding rounding
294 * errors due to the use of double.
295 *
296 * @return a String with a representation of <code>version</code>.
297 * @deprecated Use #toString()
298 */
299 public static String getStringRepresentation(double version, int fields) {
300 StringBuffer returnValue = new StringBuffer();
301
302 // To pow and other uses, we need the number of fields - 1
303 fields--;
304 final int v100 = 100;
305 double factor = Math.pow(v100, fields);
306 long validVersionDigits = Math.round(version * factor);
307
308 for (int i = 0; i <= fields; i++) {
309 long part = validVersionDigits % v100;
310 returnValue.insert(0, part);
311 validVersionDigits = validVersionDigits / v100;
312 if (i != fields) {
313 returnValue.insert(0, "_");
314 }
315 }
316 return returnValue.toString();
317 }
318
319 /**
320 * Gets the representation as a string (three fields) of a version double.
321 *
322 * Gets the representation as a string of a version double. This is the
323 * opposite process to #getDoubleRepresentation(String), splicing the double
324 * value in groups of two digits and using the values as parts of the version
325 * number. Parts are separated using an underscore "_" character.
326 * Examples:<br>
327 * <ul>
328 * <li>5.0810 -> 5_8_10</li>
329 * <li>216.1403 -> 216_14_3</li>
330 * </ul>
331 * This method always return a String representation that contains the three
332 * first fields.
333 *
334 * @param version a double with the version data.
335 *
336 * @return a String with a representation of <code>version</code>.
337 *
338 * @see #getStringRepresentation(double, int)
339 * @deprecated Use #toString()
340 */
341 public static String getStringRepresentation(double version) {
342 final int v3 = 3;
343 return getStringRepresentation(version, v3);
344 }
345 }