blob: 73174959e7b07b0a5c2bf78af49d58a68c51c952 [file] [log] [blame]
Richard S. Hallc88fca32006-10-18 22:01:22 +00001/*
Richard S. Hallf28d6762009-06-08 19:31:06 +00002 * Copyright (c) OSGi Alliance (2004, 2009). All Rights Reserved.
Richard S. Hallc88fca32006-10-18 22:01:22 +00003 *
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
17package org.osgi.framework;
18
19import java.util.NoSuchElementException;
20import java.util.StringTokenizer;
21
22/**
23 * Version identifier for bundles and packages.
24 *
25 * <p>
26 * Version identifiers have four components.
27 * <ol>
28 * <li>Major version. A non-negative integer.</li>
29 * <li>Minor version. A non-negative integer.</li>
30 * <li>Micro version. A non-negative integer.</li>
31 * <li>Qualifier. A text string. See <code>Version(String)</code> for the
32 * format of the qualifier string.</li>
33 * </ol>
34 *
35 * <p>
36 * <code>Version</code> objects are immutable.
37 *
Richard S. Hallc88fca32006-10-18 22:01:22 +000038 * @since 1.3
Richard S. Hall53e70d32008-08-01 19:31:32 +000039 * @Immutable
Richard S. Hallf28d6762009-06-08 19:31:06 +000040 * @version $Revision: 6860 $
Richard S. Hallc88fca32006-10-18 22:01:22 +000041 */
42
43public class Version implements Comparable {
44 private final int major;
45 private final int minor;
46 private final int micro;
47 private final String qualifier;
48 private static final String SEPARATOR = "."; //$NON-NLS-1$
49
50 /**
Richard S. Hallf28d6762009-06-08 19:31:06 +000051 * The empty version "0.0.0".
Richard S. Hallc88fca32006-10-18 22:01:22 +000052 */
53 public static final Version emptyVersion = new Version(0, 0, 0);
54
55 /**
56 * Creates a version identifier from the specified numerical components.
57 *
58 * <p>
59 * The qualifier is set to the empty string.
60 *
61 * @param major Major component of the version identifier.
62 * @param minor Minor component of the version identifier.
63 * @param micro Micro component of the version identifier.
64 * @throws IllegalArgumentException If the numerical components are
65 * negative.
66 */
67 public Version(int major, int minor, int micro) {
68 this(major, minor, micro, null);
69 }
70
71 /**
Richard S. Hallf28d6762009-06-08 19:31:06 +000072 * Creates a version identifier from the specified components.
Richard S. Hallc88fca32006-10-18 22:01:22 +000073 *
74 * @param major Major component of the version identifier.
75 * @param minor Minor component of the version identifier.
76 * @param micro Micro component of the version identifier.
77 * @param qualifier Qualifier component of the version identifier. If
Richard S. Hallf28d6762009-06-08 19:31:06 +000078 * <code>null</code> is specified, then the qualifier will be set to
79 * the empty string.
Richard S. Hallc88fca32006-10-18 22:01:22 +000080 * @throws IllegalArgumentException If the numerical components are negative
81 * or the qualifier string is invalid.
82 */
83 public Version(int major, int minor, int micro, String qualifier) {
84 if (qualifier == null) {
85 qualifier = ""; //$NON-NLS-1$
86 }
87
88 this.major = major;
89 this.minor = minor;
90 this.micro = micro;
91 this.qualifier = qualifier;
92 validate();
93 }
94
95 /**
96 * Created a version identifier from the specified string.
97 *
98 * <p>
99 * Here is the grammar for version strings.
100 *
101 * <pre>
102 * version ::= major('.'minor('.'micro('.'qualifier)?)?)?
103 * major ::= digit+
104 * minor ::= digit+
105 * micro ::= digit+
106 * qualifier ::= (alpha|digit|'_'|'-')+
107 * digit ::= [0..9]
108 * alpha ::= [a..zA..Z]
109 * </pre>
110 *
111 * There must be no whitespace in version.
112 *
113 * @param version String representation of the version identifier.
114 * @throws IllegalArgumentException If <code>version</code> is improperly
115 * formatted.
116 */
117 public Version(String version) {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000118 int maj = 0;
119 int min = 0;
120 int mic = 0;
121 String qual = ""; //$NON-NLS-1$
Richard S. Hallc88fca32006-10-18 22:01:22 +0000122
123 try {
124 StringTokenizer st = new StringTokenizer(version, SEPARATOR, true);
Richard S. Hallf28d6762009-06-08 19:31:06 +0000125 maj = Integer.parseInt(st.nextToken());
Richard S. Hallc88fca32006-10-18 22:01:22 +0000126
127 if (st.hasMoreTokens()) {
128 st.nextToken(); // consume delimiter
Richard S. Hallf28d6762009-06-08 19:31:06 +0000129 min = Integer.parseInt(st.nextToken());
Richard S. Hallc88fca32006-10-18 22:01:22 +0000130
131 if (st.hasMoreTokens()) {
132 st.nextToken(); // consume delimiter
Richard S. Hallf28d6762009-06-08 19:31:06 +0000133 mic = Integer.parseInt(st.nextToken());
Richard S. Hallc88fca32006-10-18 22:01:22 +0000134
135 if (st.hasMoreTokens()) {
136 st.nextToken(); // consume delimiter
Richard S. Hallf28d6762009-06-08 19:31:06 +0000137 qual = st.nextToken();
Richard S. Hallc88fca32006-10-18 22:01:22 +0000138
139 if (st.hasMoreTokens()) {
140 throw new IllegalArgumentException("invalid format"); //$NON-NLS-1$
141 }
142 }
143 }
144 }
145 }
146 catch (NoSuchElementException e) {
147 throw new IllegalArgumentException("invalid format"); //$NON-NLS-1$
148 }
149
Richard S. Hallf28d6762009-06-08 19:31:06 +0000150 major = maj;
151 minor = min;
152 micro = mic;
153 qualifier = qual;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000154 validate();
155 }
156
157 /**
158 * Called by the Version constructors to validate the version components.
159 *
160 * @throws IllegalArgumentException If the numerical components are negative
161 * or the qualifier string is invalid.
162 */
163 private void validate() {
164 if (major < 0) {
165 throw new IllegalArgumentException("negative major"); //$NON-NLS-1$
166 }
167 if (minor < 0) {
168 throw new IllegalArgumentException("negative minor"); //$NON-NLS-1$
169 }
170 if (micro < 0) {
171 throw new IllegalArgumentException("negative micro"); //$NON-NLS-1$
172 }
Richard S. Hallf28d6762009-06-08 19:31:06 +0000173 char[] chars = qualifier.toCharArray();
174 for (int i = 0, length = chars.length; i < length; i++) {
175 char ch = chars[i];
176 if (('A' <= ch) && (ch <= 'Z')) {
177 continue;
Richard S. Hallc88fca32006-10-18 22:01:22 +0000178 }
Richard S. Hallf28d6762009-06-08 19:31:06 +0000179 if (('a' <= ch) && (ch <= 'z')) {
180 continue;
181 }
182 if (('0' <= ch) && (ch <= '9')) {
183 continue;
184 }
185 if ((ch == '_') || (ch == '-')) {
186 continue;
187 }
188 throw new IllegalArgumentException(
189 "invalid qualifier: " + qualifier); //$NON-NLS-1$
Richard S. Hallc88fca32006-10-18 22:01:22 +0000190 }
191 }
192
193 /**
194 * Parses a version identifier from the specified string.
195 *
196 * <p>
197 * See <code>Version(String)</code> for the format of the version string.
198 *
199 * @param version String representation of the version identifier. Leading
200 * and trailing whitespace will be ignored.
201 * @return A <code>Version</code> object representing the version
202 * identifier. If <code>version</code> is <code>null</code> or
203 * the empty string then <code>emptyVersion</code> will be
204 * returned.
205 * @throws IllegalArgumentException If <code>version</code> is improperly
206 * formatted.
207 */
208 public static Version parseVersion(String version) {
209 if (version == null) {
210 return emptyVersion;
211 }
212
213 version = version.trim();
214 if (version.length() == 0) {
215 return emptyVersion;
216 }
217
218 return new Version(version);
219 }
220
221 /**
222 * Returns the major component of this version identifier.
223 *
224 * @return The major component.
225 */
226 public int getMajor() {
227 return major;
228 }
229
230 /**
231 * Returns the minor component of this version identifier.
232 *
233 * @return The minor component.
234 */
235 public int getMinor() {
236 return minor;
237 }
238
239 /**
240 * Returns the micro component of this version identifier.
241 *
242 * @return The micro component.
243 */
244 public int getMicro() {
245 return micro;
246 }
247
248 /**
249 * Returns the qualifier component of this version identifier.
250 *
251 * @return The qualifier component.
252 */
253 public String getQualifier() {
254 return qualifier;
255 }
256
257 /**
258 * Returns the string representation of this version identifier.
259 *
260 * <p>
261 * The format of the version string will be <code>major.minor.micro</code>
262 * if qualifier is the empty string or
263 * <code>major.minor.micro.qualifier</code> otherwise.
264 *
265 * @return The string representation of this version identifier.
266 */
267 public String toString() {
Richard S. Hallf28d6762009-06-08 19:31:06 +0000268 int q = qualifier.length();
269 StringBuffer result = new StringBuffer(20 + q);
270 result.append(major);
271 result.append(SEPARATOR);
272 result.append(minor);
273 result.append(SEPARATOR);
274 result.append(micro);
275 if (q > 0) {
276 result.append(SEPARATOR);
277 result.append(qualifier);
Richard S. Hallc88fca32006-10-18 22:01:22 +0000278 }
Richard S. Hallf28d6762009-06-08 19:31:06 +0000279 return result.toString();
Richard S. Hallc88fca32006-10-18 22:01:22 +0000280 }
281
282 /**
283 * Returns a hash code value for the object.
284 *
285 * @return An integer which is a hash code value for this object.
286 */
287 public int hashCode() {
288 return (major << 24) + (minor << 16) + (micro << 8)
289 + qualifier.hashCode();
290 }
291
292 /**
293 * Compares this <code>Version</code> object to another object.
294 *
295 * <p>
296 * A version is considered to be <b>equal to </b> another version if the
297 * major, minor and micro components are equal and the qualifier component
298 * is equal (using <code>String.equals</code>).
299 *
300 * @param object The <code>Version</code> object to be compared.
301 * @return <code>true</code> if <code>object</code> is a
302 * <code>Version</code> and is equal to this object;
303 * <code>false</code> otherwise.
304 */
305 public boolean equals(Object object) {
306 if (object == this) { // quicktest
307 return true;
308 }
309
310 if (!(object instanceof Version)) {
311 return false;
312 }
313
314 Version other = (Version) object;
315 return (major == other.major) && (minor == other.minor)
316 && (micro == other.micro) && qualifier.equals(other.qualifier);
317 }
318
319 /**
320 * Compares this <code>Version</code> object to another object.
321 *
322 * <p>
323 * A version is considered to be <b>less than </b> another version if its
324 * major component is less than the other version's major component, or the
325 * major components are equal and its minor component is less than the other
326 * version's minor component, or the major and minor components are equal
327 * and its micro component is less than the other version's micro component,
328 * or the major, minor and micro components are equal and it's qualifier
329 * component is less than the other version's qualifier component (using
330 * <code>String.compareTo</code>).
331 *
332 * <p>
333 * A version is considered to be <b>equal to</b> another version if the
334 * major, minor and micro components are equal and the qualifier component
335 * is equal (using <code>String.compareTo</code>).
336 *
337 * @param object The <code>Version</code> object to be compared.
338 * @return A negative integer, zero, or a positive integer if this object is
339 * less than, equal to, or greater than the specified
340 * <code>Version</code> object.
341 * @throws ClassCastException If the specified object is not a
342 * <code>Version</code>.
343 */
344 public int compareTo(Object object) {
345 if (object == this) { // quicktest
346 return 0;
347 }
348
349 Version other = (Version) object;
350
351 int result = major - other.major;
352 if (result != 0) {
353 return result;
354 }
355
356 result = minor - other.minor;
357 if (result != 0) {
358 return result;
359 }
360
361 result = micro - other.micro;
362 if (result != 0) {
363 return result;
364 }
365
366 return qualifier.compareTo(other.qualifier);
367 }
368}