001/* 002 * %% Ignore-License %% 003 * Copyright (c) OSGi Alliance (2004, 2009). All Rights Reserved. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package fr.ifremer.adagio.core.dao.technical; 019 020/* 021 * #%L 022 * SIH-Adagio Core Shared 023 * $Id: Version.java 11866 2013-12-02 15:53:58Z tc1fbb1 $ 024 * $HeadURL: https://forge.ifremer.fr/svn/sih-adagio/tags/adagio-3.5.6/core-shared/src/main/java/fr/ifremer/adagio/core/dao/technical/Version.java $ 025 * %% 026 * Copyright (C) 2012 - 2013 Ifremer 027 * %% 028 * This program is free software: you can redistribute it and/or modify 029 * it under the terms of the GNU Affero General Public License as published by 030 * the Free Software Foundation, either version 3 of the License, or 031 * (at your option) any later version. 032 * 033 * This program is distributed in the hope that it will be useful, 034 * but WITHOUT ANY WARRANTY; without even the implied warranty of 035 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 036 * GNU General Public License for more details. 037 * 038 * You should have received a copy of the GNU Affero General Public License 039 * along with this program. If not, see <http://www.gnu.org/licenses/>. 040 * #L% 041 */ 042 043import java.util.NoSuchElementException; 044import java.util.StringTokenizer; 045 046/** 047 * Version identifier for bundles and packages. 048 * <p/> 049 * <p/> 050 * Version identifiers have four components. 051 * <ol> 052 * <li>Major version. A non-negative integer.</li> 053 * <li>Minor version. A non-negative integer.</li> 054 * <li>Micro version. A non-negative integer.</li> 055 * <li>Qualifier. A text string. See <code>Version(String)</code> for the 056 * format of the qualifier string.</li> 057 * </ol> 058 * <p/> 059 * <p/> 060 * <code>Version</code> objects are immutable. 061 * 062 * @Immutable 063 */ 064 065public class Version implements Comparable<Version> { 066 private final int major; 067 068 private final int minor; 069 070 private final int micro; 071 072 private final String qualifier; 073 074 private static final String SEPARATOR = "."; //$NON-NLS-1$ 075 076 /** 077 * The empty version "0.0.0". 078 */ 079 public static final Version emptyVersion = new Version(0, 0, 0); 080 081 /** 082 * Creates a version identifier from the specified numerical components. 083 * <p/> 084 * <p/> 085 * The qualifier is set to the empty string. 086 * 087 * @param major Major component of the version identifier. 088 * @param minor Minor component of the version identifier. 089 * @param micro Micro component of the version identifier. 090 * @throws IllegalArgumentException If the numerical components are 091 * negative. 092 */ 093 public Version(int major, int minor, int micro) { 094 this(major, minor, micro, null); 095 } 096 097 /** 098 * Creates a version identifier from the specified components. 099 * 100 * @param major Major component of the version identifier. 101 * @param minor Minor component of the version identifier. 102 * @param micro Micro component of the version identifier. 103 * @param qualifier Qualifier component of the version identifier. If 104 * <code>null</code> is specified, then the qualifier will be set to 105 * the empty string. 106 * @throws IllegalArgumentException If the numerical components are negative 107 * or the qualifier string is invalid. 108 */ 109 public Version(int major, int minor, int micro, String qualifier) { 110 if (qualifier == null) { 111 qualifier = ""; //$NON-NLS-1$ 112 } 113 114 this.major = major; 115 this.minor = minor; 116 this.micro = micro; 117 this.qualifier = qualifier; 118 validate(); 119 } 120 121 /** 122 * Created a version identifier from the specified string. 123 * <p/> 124 * <p/> 125 * Here is the grammar for version strings. 126 * <p/> 127 * <pre> 128 * version ::= major('.'minor('.'micro('.'qualifier)?)?)? 129 * major ::= digit+ 130 * minor ::= digit+ 131 * micro ::= digit+ 132 * qualifier ::= (alpha|digit|'_'|'-')+ 133 * digit ::= [0..9] 134 * alpha ::= [a..zA..Z] 135 * </pre> 136 * <p/> 137 * There must be no whitespace in version. 138 * 139 * @param version String representation of the version identifier. 140 * @throws IllegalArgumentException If <code>version</code> is improperly 141 * formatted. 142 */ 143 public Version(String version) { 144 int maj = 0; 145 int min = 0; 146 int mic = 0; 147 String qual = ""; //$NON-NLS-1$ 148 149 try { 150 StringTokenizer st = new StringTokenizer(version, SEPARATOR, true); 151 maj = Integer.parseInt(st.nextToken()); 152 153 if (st.hasMoreTokens()) { 154 st.nextToken(); // consume delimiter 155 min = Integer.parseInt(st.nextToken()); 156 157 if (st.hasMoreTokens()) { 158 st.nextToken(); // consume delimiter 159 mic = Integer.parseInt(st.nextToken()); 160 161 if (st.hasMoreTokens()) { 162 st.nextToken(); // consume delimiter 163 qual = st.nextToken(); 164 165 if (st.hasMoreTokens()) { 166 throw new IllegalArgumentException("invalid format"); //$NON-NLS-1$ 167 } 168 } 169 } 170 } 171 } catch (NoSuchElementException e) { 172 throw new IllegalArgumentException("invalid format"); //$NON-NLS-1$ 173 } 174 175 major = maj; 176 minor = min; 177 micro = mic; 178 qualifier = qual; 179 validate(); 180 } 181 182 /** 183 * Called by the Version constructors to validate the version components. 184 * 185 * @throws IllegalArgumentException If the numerical components are negative 186 * or the qualifier string is invalid. 187 */ 188 private void validate() { 189 if (major < 0) { 190 throw new IllegalArgumentException("negative major"); //$NON-NLS-1$ 191 } 192 if (minor < 0) { 193 throw new IllegalArgumentException("negative minor"); //$NON-NLS-1$ 194 } 195 if (micro < 0) { 196 throw new IllegalArgumentException("negative micro"); //$NON-NLS-1$ 197 } 198 char[] chars = qualifier.toCharArray(); 199 for (int i = 0, length = chars.length; i < length; i++) { 200 char ch = chars[i]; 201 if (('A' <= ch) && (ch <= 'Z')) { 202 continue; 203 } 204 if (('a' <= ch) && (ch <= 'z')) { 205 continue; 206 } 207 if (('0' <= ch) && (ch <= '9')) { 208 continue; 209 } 210 if ((ch == '_') || (ch == '-')) { 211 continue; 212 } 213 throw new IllegalArgumentException( 214 "invalid qualifier: " + qualifier); //$NON-NLS-1$ 215 } 216 } 217 218 /** 219 * Parses a version identifier from the specified string. 220 * <p/> 221 * <p/> 222 * See <code>Version(String)</code> for the format of the version string. 223 * 224 * @param version String representation of the version identifier. Leading 225 * and trailing whitespace will be ignored. 226 * @return A <code>Version</code> object representing the version 227 * identifier. If <code>version</code> is <code>null</code> or 228 * the empty string then <code>emptyVersion</code> will be 229 * returned. 230 * @throws IllegalArgumentException If <code>version</code> is improperly 231 * formatted. 232 */ 233 public static Version parseVersion(String version) { 234 if (version == null) { 235 return emptyVersion; 236 } 237 238 version = version.trim(); 239 if (version.length() == 0) { 240 return emptyVersion; 241 } 242 243 return new Version(version); 244 } 245 246 /** 247 * Returns the major component of this version identifier. 248 * 249 * @return The major component. 250 */ 251 public int getMajor() { 252 return major; 253 } 254 255 /** 256 * Returns the minor component of this version identifier. 257 * 258 * @return The minor component. 259 */ 260 public int getMinor() { 261 return minor; 262 } 263 264 /** 265 * Returns the micro component of this version identifier. 266 * 267 * @return The micro component. 268 */ 269 public int getMicro() { 270 return micro; 271 } 272 273 /** 274 * Returns the qualifier component of this version identifier. 275 * 276 * @return The qualifier component. 277 */ 278 public String getQualifier() { 279 return qualifier; 280 } 281 282 /** 283 * Returns the string representation of this version identifier. 284 * <p/> 285 * <p/> 286 * The format of the version string will be <code>major.minor.micro</code> 287 * if qualifier is the empty string or 288 * <code>major.minor.micro.qualifier</code> otherwise. 289 * 290 * @return The string representation of this version identifier. 291 */ 292 public String toString() { 293 int q = qualifier.length(); 294 StringBuffer result = new StringBuffer(20 + q); 295 result.append(major); 296 result.append(SEPARATOR); 297 result.append(minor); 298 result.append(SEPARATOR); 299 result.append(micro); 300 if (q > 0) { 301 result.append(SEPARATOR); 302 result.append(qualifier); 303 } 304 return result.toString(); 305 } 306 307 /** 308 * Returns a hash code value for the object. 309 * 310 * @return An integer which is a hash code value for this object. 311 */ 312 public int hashCode() { 313 return (major << 24) + (minor << 16) + (micro << 8) 314 + qualifier.hashCode(); 315 } 316 317 /** 318 * Compares this <code>Version</code> object to another object. 319 * <p/> 320 * <p/> 321 * A version is considered to be <b>equal to </b> another version if the 322 * major, minor and micro components are equal and the qualifier component 323 * is equal (using <code>String.equals</code>). 324 * 325 * @param object The <code>Version</code> object to be compared. 326 * @return <code>true</code> if <code>object</code> is a 327 * <code>Version</code> and is equal to this object; 328 * <code>false</code> otherwise. 329 */ 330 public boolean equals(Object object) { 331 if (object == this) { // quicktest 332 return true; 333 } 334 335 if (!(object instanceof Version)) { 336 return false; 337 } 338 339 Version other = (Version) object; 340 return (major == other.major) && (minor == other.minor) 341 && (micro == other.micro) && qualifier.equals(other.qualifier); 342 } 343 344 /** 345 * Compares this <code>Version</code> object to another object. 346 * <p/> 347 * <p/> 348 * A version is considered to be <b>less than </b> another version if its 349 * major component is less than the other version's major component, or the 350 * major components are equal and its minor component is less than the other 351 * version's minor component, or the major and minor components are equal 352 * and its micro component is less than the other version's micro component, 353 * or the major, minor and micro components are equal and it's qualifier 354 * component is less than the other version's qualifier component (using 355 * <code>String.compareTo</code>). 356 * <p/> 357 * <p/> 358 * A version is considered to be <b>equal to</b> another version if the 359 * major, minor and micro components are equal and the qualifier component 360 * is equal (using <code>String.compareTo</code>). 361 * 362 * @param object The <code>Version</code> object to be compared. 363 * @return A negative integer, zero, or a positive integer if this object is 364 * less than, equal to, or greater than the specified 365 * <code>Version</code> object. 366 * @throws ClassCastException If the specified object is not a 367 * <code>Version</code>. 368 */ 369 public int compareTo(Version object) { 370 if (object == this) { // quicktest 371 return 0; 372 } 373 374 Version other = (Version) object; 375 376 int result = major - other.major; 377 if (result != 0) { 378 return result; 379 } 380 381 result = minor - other.minor; 382 if (result != 0) { 383 return result; 384 } 385 386 result = micro - other.micro; 387 if (result != 0) { 388 return result; 389 } 390 391 return qualifier.compareTo(other.qualifier); 392 } 393}