1 /* 2 ******************************************************************************* 3 * Copyright (C) 1996-2015, International Business Machines Corporation and 4 * others. All Rights Reserved. 5 ******************************************************************************* 6 */ 7 8 package com.ibm.icu.util; 9 10 import java.util.concurrent.ConcurrentHashMap; 11 12 /** 13 * Class to store version numbers of the form major.minor.milli.micro. 14 * @author synwee 15 * @stable ICU 2.6 16 */ 17 public final class VersionInfo implements Comparable<VersionInfo> 18 { 19 // public data members ------------------------------------------------- 20 21 /** 22 * Unicode 1.0 version 23 * @stable ICU 2.6 24 */ 25 public static final VersionInfo UNICODE_1_0; 26 /** 27 * Unicode 1.0.1 version 28 * @stable ICU 2.6 29 */ 30 public static final VersionInfo UNICODE_1_0_1; 31 /** 32 * Unicode 1.1.0 version 33 * @stable ICU 2.6 34 */ 35 public static final VersionInfo UNICODE_1_1_0; 36 /** 37 * Unicode 1.1.5 version 38 * @stable ICU 2.6 39 */ 40 public static final VersionInfo UNICODE_1_1_5; 41 /** 42 * Unicode 2.0 version 43 * @stable ICU 2.6 44 */ 45 public static final VersionInfo UNICODE_2_0; 46 /** 47 * Unicode 2.1.2 version 48 * @stable ICU 2.6 49 */ 50 public static final VersionInfo UNICODE_2_1_2; 51 /** 52 * Unicode 2.1.5 version 53 * @stable ICU 2.6 54 */ 55 public static final VersionInfo UNICODE_2_1_5; 56 /** 57 * Unicode 2.1.8 version 58 * @stable ICU 2.6 59 */ 60 public static final VersionInfo UNICODE_2_1_8; 61 /** 62 * Unicode 2.1.9 version 63 * @stable ICU 2.6 64 */ 65 public static final VersionInfo UNICODE_2_1_9; 66 /** 67 * Unicode 3.0 version 68 * @stable ICU 2.6 69 */ 70 public static final VersionInfo UNICODE_3_0; 71 /** 72 * Unicode 3.0.1 version 73 * @stable ICU 2.6 74 */ 75 public static final VersionInfo UNICODE_3_0_1; 76 /** 77 * Unicode 3.1.0 version 78 * @stable ICU 2.6 79 */ 80 public static final VersionInfo UNICODE_3_1_0; 81 /** 82 * Unicode 3.1.1 version 83 * @stable ICU 2.6 84 */ 85 public static final VersionInfo UNICODE_3_1_1; 86 /** 87 * Unicode 3.2 version 88 * @stable ICU 2.6 89 */ 90 public static final VersionInfo UNICODE_3_2; 91 92 /** 93 * Unicode 4.0 version 94 * @stable ICU 2.6 95 */ 96 public static final VersionInfo UNICODE_4_0; 97 98 /** 99 * Unicode 4.0.1 version 100 * @stable ICU 3.4 101 */ 102 public static final VersionInfo UNICODE_4_0_1; 103 104 /** 105 * Unicode 4.1 version 106 * @stable ICU 3.4 107 */ 108 public static final VersionInfo UNICODE_4_1; 109 110 /** 111 * Unicode 5.0 version 112 * @stable ICU 3.4 113 */ 114 public static final VersionInfo UNICODE_5_0; 115 116 /** 117 * Unicode 5.1 version 118 * @stable ICU 4.2 119 */ 120 public static final VersionInfo UNICODE_5_1; 121 122 /** 123 * Unicode 5.2 version 124 * @stable ICU 4.4 125 */ 126 public static final VersionInfo UNICODE_5_2; 127 128 /** 129 * Unicode 6.0 version 130 * @stable ICU 4.6 131 */ 132 public static final VersionInfo UNICODE_6_0; 133 134 /** 135 * Unicode 6.1 version 136 * @stable ICU 49 137 */ 138 public static final VersionInfo UNICODE_6_1; 139 140 /** 141 * Unicode 6.2 version 142 * @stable ICU 50 143 */ 144 public static final VersionInfo UNICODE_6_2; 145 146 /** 147 * Unicode 6.3 version 148 * @stable ICU 52 149 */ 150 public static final VersionInfo UNICODE_6_3; 151 152 /** 153 * Unicode 7.0 version 154 * @stable ICU 54 155 */ 156 public static final VersionInfo UNICODE_7_0; 157 158 /** 159 * Unicode 8.0 version 160 * @stable ICU 56 161 */ 162 public static final VersionInfo UNICODE_8_0; 163 164 /** 165 * ICU4J current release version 166 * @stable ICU 2.8 167 */ 168 public static final VersionInfo ICU_VERSION; 169 170 /** 171 * Data version string for ICU's internal data. 172 * Used for appending to data path (e.g. icudt43b) 173 * @internal 174 * @deprecated This API is ICU internal only. 175 */ 176 @Deprecated 177 public static final String ICU_DATA_VERSION_PATH = "56b"; 178 179 /** 180 * Data version in ICU4J. 181 * @internal 182 * @deprecated This API is ICU internal only. 183 */ 184 @Deprecated 185 public static final VersionInfo ICU_DATA_VERSION; 186 187 /** 188 * Collation runtime version (sort key generator, string comparisons). 189 * If the version is different, sort keys for the same string could be different. 190 * This value may change in subsequent releases of ICU. 191 * @stable ICU 2.8 192 */ 193 public static final VersionInfo UCOL_RUNTIME_VERSION; 194 195 /** 196 * Collation builder code version. 197 * When this is different, the same tailoring might result 198 * in assigning different collation elements to code points. 199 * This value may change in subsequent releases of ICU. 200 * @stable ICU 2.8 201 */ 202 public static final VersionInfo UCOL_BUILDER_VERSION; 203 204 /** 205 * Constant version 1. 206 * This was intended to be the version of collation tailorings, 207 * but instead the tailoring data carries a version number. 208 * @deprecated ICU 54 209 */ 210 @Deprecated 211 public static final VersionInfo UCOL_TAILORINGS_VERSION; 212 213 214 // public methods ------------------------------------------------------ 215 216 /** 217 * Returns an instance of VersionInfo with the argument version. 218 * @param version version String in the format of "major.minor.milli.micro" 219 * or "major.minor.milli" or "major.minor" or "major", 220 * where major, minor, milli, micro are non-negative numbers 221 * <= 255. If the trailing version numbers are 222 * not specified they are taken as 0s. E.g. Version "3.1" is 223 * equivalent to "3.1.0.0". 224 * @return an instance of VersionInfo with the argument version. 225 * @exception IllegalArgumentException when the argument version 226 * is not in the right format 227 * @stable ICU 2.6 228 */ getInstance(String version)229 public static VersionInfo getInstance(String version) 230 { 231 int length = version.length(); 232 int array[] = {0, 0, 0, 0}; 233 int count = 0; 234 int index = 0; 235 236 while (count < 4 && index < length) { 237 char c = version.charAt(index); 238 if (c == '.') { 239 count ++; 240 } 241 else { 242 c -= '0'; 243 if (c < 0 || c > 9) { 244 throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); 245 } 246 array[count] *= 10; 247 array[count] += c; 248 } 249 index ++; 250 } 251 if (index != length) { 252 throw new IllegalArgumentException( 253 "Invalid version number: String '" + version + "' exceeds version format"); 254 } 255 for (int i = 0; i < 4; i ++) { 256 if (array[i] < 0 || array[i] > 255) { 257 throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); 258 } 259 } 260 261 return getInstance(array[0], array[1], array[2], array[3]); 262 } 263 264 /** 265 * Returns an instance of VersionInfo with the argument version. 266 * @param major major version, non-negative number <= 255. 267 * @param minor minor version, non-negative number <= 255. 268 * @param milli milli version, non-negative number <= 255. 269 * @param micro micro version, non-negative number <= 255. 270 * @exception IllegalArgumentException when either arguments are negative or > 255 271 * @stable ICU 2.6 272 */ getInstance(int major, int minor, int milli, int micro)273 public static VersionInfo getInstance(int major, int minor, int milli, 274 int micro) 275 { 276 // checks if it is in the hashmap 277 // else 278 if (major < 0 || major > 255 || minor < 0 || minor > 255 || 279 milli < 0 || milli > 255 || micro < 0 || micro > 255) { 280 throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); 281 } 282 int version = getInt(major, minor, milli, micro); 283 Integer key = Integer.valueOf(version); 284 VersionInfo result = MAP_.get(key); 285 if (result == null) { 286 result = new VersionInfo(version); 287 VersionInfo tmpvi = MAP_.putIfAbsent(key, result); 288 if (tmpvi != null) { 289 result = tmpvi; 290 } 291 } 292 return result; 293 } 294 295 /** 296 * Returns an instance of VersionInfo with the argument version. 297 * Equivalent to getInstance(major, minor, milli, 0). 298 * @param major major version, non-negative number <= 255. 299 * @param minor minor version, non-negative number <= 255. 300 * @param milli milli version, non-negative number <= 255. 301 * @exception IllegalArgumentException when either arguments are 302 * negative or > 255 303 * @stable ICU 2.6 304 */ getInstance(int major, int minor, int milli)305 public static VersionInfo getInstance(int major, int minor, int milli) 306 { 307 return getInstance(major, minor, milli, 0); 308 } 309 310 /** 311 * Returns an instance of VersionInfo with the argument version. 312 * Equivalent to getInstance(major, minor, 0, 0). 313 * @param major major version, non-negative number <= 255. 314 * @param minor minor version, non-negative number <= 255. 315 * @exception IllegalArgumentException when either arguments are 316 * negative or > 255 317 * @stable ICU 2.6 318 */ getInstance(int major, int minor)319 public static VersionInfo getInstance(int major, int minor) 320 { 321 return getInstance(major, minor, 0, 0); 322 } 323 324 /** 325 * Returns an instance of VersionInfo with the argument version. 326 * Equivalent to getInstance(major, 0, 0, 0). 327 * @param major major version, non-negative number <= 255. 328 * @exception IllegalArgumentException when either arguments are 329 * negative or > 255 330 * @stable ICU 2.6 331 */ getInstance(int major)332 public static VersionInfo getInstance(int major) 333 { 334 return getInstance(major, 0, 0, 0); 335 } 336 337 private static volatile VersionInfo javaVersion; 338 339 /** 340 * @internal 341 * @deprecated This API is ICU internal only. 342 */ 343 @Deprecated javaVersion()344 public static VersionInfo javaVersion() { 345 if (javaVersion == null) { 346 synchronized(VersionInfo.class) { 347 if (javaVersion == null) { 348 String s = System.getProperty("java.version"); 349 // clean string 350 // preserve only digits, separated by single '.' 351 // ignore over 4 digit sequences 352 // does not test < 255, very odd... 353 354 char[] chars = s.toCharArray(); 355 int r = 0, w = 0, count = 0; 356 boolean numeric = false; // ignore leading non-numerics 357 while (r < chars.length) { 358 char c = chars[r++]; 359 if (c < '0' || c > '9') { 360 if (numeric) { 361 if (count == 3) { 362 // only four digit strings allowed 363 break; 364 } 365 numeric = false; 366 chars[w++] = '.'; 367 ++count; 368 } 369 } else { 370 numeric = true; 371 chars[w++] = c; 372 } 373 } 374 while (w > 0 && chars[w-1] == '.') { 375 --w; 376 } 377 378 String vs = new String(chars, 0, w); 379 380 javaVersion = VersionInfo.getInstance(vs); 381 } 382 } 383 } 384 return javaVersion; 385 } 386 387 /** 388 * Returns the String representative of VersionInfo in the format of 389 * "major.minor.milli.micro" 390 * @return String representative of VersionInfo 391 * @stable ICU 2.6 392 */ toString()393 public String toString() 394 { 395 StringBuilder result = new StringBuilder(7); 396 result.append(getMajor()); 397 result.append('.'); 398 result.append(getMinor()); 399 result.append('.'); 400 result.append(getMilli()); 401 result.append('.'); 402 result.append(getMicro()); 403 return result.toString(); 404 } 405 406 /** 407 * Returns the major version number 408 * @return the major version number 409 * @stable ICU 2.6 410 */ getMajor()411 public int getMajor() 412 { 413 return (m_version_ >> 24) & LAST_BYTE_MASK_ ; 414 } 415 416 /** 417 * Returns the minor version number 418 * @return the minor version number 419 * @stable ICU 2.6 420 */ getMinor()421 public int getMinor() 422 { 423 return (m_version_ >> 16) & LAST_BYTE_MASK_ ; 424 } 425 426 /** 427 * Returns the milli version number 428 * @return the milli version number 429 * @stable ICU 2.6 430 */ getMilli()431 public int getMilli() 432 { 433 return (m_version_ >> 8) & LAST_BYTE_MASK_ ; 434 } 435 436 /** 437 * Returns the micro version number 438 * @return the micro version number 439 * @stable ICU 2.6 440 */ getMicro()441 public int getMicro() 442 { 443 return m_version_ & LAST_BYTE_MASK_ ; 444 } 445 446 /** 447 * Checks if this version information is equals to the argument version 448 * @param other object to be compared 449 * @return true if other is equals to this object's version information, 450 * false otherwise 451 * @stable ICU 2.6 452 */ equals(Object other)453 public boolean equals(Object other) 454 { 455 return other == this; 456 } 457 458 /** 459 * Compares other with this VersionInfo. 460 * @param other VersionInfo to be compared 461 * @return 0 if the argument is a VersionInfo object that has version 462 * information equals to this object. 463 * Less than 0 if the argument is a VersionInfo object that has 464 * version information greater than this object. 465 * Greater than 0 if the argument is a VersionInfo object that 466 * has version information less than this object. 467 * @stable ICU 2.6 468 */ compareTo(VersionInfo other)469 public int compareTo(VersionInfo other) 470 { 471 return m_version_ - other.m_version_; 472 } 473 474 // private data members ---------------------------------------------- 475 476 /** 477 * Unicode data version used by the current release 478 */ 479 private static final VersionInfo UNICODE_VERSION; 480 481 /** 482 * Version number stored as a byte for each of the major, minor, milli and 483 * micro numbers in the 32 bit int. 484 * Most significant for the major and the least significant contains the 485 * micro numbers. 486 */ 487 private int m_version_; 488 /** 489 * Map of singletons 490 */ 491 private static final ConcurrentHashMap<Integer, VersionInfo> MAP_ = new ConcurrentHashMap<Integer, VersionInfo>(); 492 /** 493 * Last byte mask 494 */ 495 private static final int LAST_BYTE_MASK_ = 0xFF; 496 /** 497 * Error statement string 498 */ 499 private static final String INVALID_VERSION_NUMBER_ = 500 "Invalid version number: Version number may be negative or greater than 255"; 501 502 // static declaration ------------------------------------------------ 503 504 /** 505 * Initialize versions only after MAP_ has been created 506 */ 507 static { 508 UNICODE_1_0 = getInstance(1, 0, 0, 0); 509 UNICODE_1_0_1 = getInstance(1, 0, 1, 0); 510 UNICODE_1_1_0 = getInstance(1, 1, 0, 0); 511 UNICODE_1_1_5 = getInstance(1, 1, 5, 0); 512 UNICODE_2_0 = getInstance(2, 0, 0, 0); 513 UNICODE_2_1_2 = getInstance(2, 1, 2, 0); 514 UNICODE_2_1_5 = getInstance(2, 1, 5, 0); 515 UNICODE_2_1_8 = getInstance(2, 1, 8, 0); 516 UNICODE_2_1_9 = getInstance(2, 1, 9, 0); 517 UNICODE_3_0 = getInstance(3, 0, 0, 0); 518 UNICODE_3_0_1 = getInstance(3, 0, 1, 0); 519 UNICODE_3_1_0 = getInstance(3, 1, 0, 0); 520 UNICODE_3_1_1 = getInstance(3, 1, 1, 0); 521 UNICODE_3_2 = getInstance(3, 2, 0, 0); 522 UNICODE_4_0 = getInstance(4, 0, 0, 0); 523 UNICODE_4_0_1 = getInstance(4, 0, 1, 0); 524 UNICODE_4_1 = getInstance(4, 1, 0, 0); 525 UNICODE_5_0 = getInstance(5, 0, 0, 0); 526 UNICODE_5_1 = getInstance(5, 1, 0, 0); 527 UNICODE_5_2 = getInstance(5, 2, 0, 0); 528 UNICODE_6_0 = getInstance(6, 0, 0, 0); 529 UNICODE_6_1 = getInstance(6, 1, 0, 0); 530 UNICODE_6_2 = getInstance(6, 2, 0, 0); 531 UNICODE_6_3 = getInstance(6, 3, 0, 0); 532 UNICODE_7_0 = getInstance(7, 0, 0, 0); 533 UNICODE_8_0 = getInstance(8, 0, 0, 0); 534 535 ICU_VERSION = getInstance(56, 1, 0, 0); 536 ICU_DATA_VERSION = getInstance(56, 1, 0, 0); 537 UNICODE_VERSION = UNICODE_8_0; 538 539 UCOL_RUNTIME_VERSION = getInstance(9); 540 UCOL_BUILDER_VERSION = getInstance(9); 541 UCOL_TAILORINGS_VERSION = getInstance(1); 542 } 543 544 // private constructor ----------------------------------------------- 545 546 /** 547 * Constructor with int 548 * @param compactversion a 32 bit int with each byte representing a number 549 */ VersionInfo(int compactversion)550 private VersionInfo(int compactversion) 551 { 552 m_version_ = compactversion; 553 } 554 555 /** 556 * Gets the int from the version numbers 557 * @param major non-negative version number 558 * @param minor non-negative version number 559 * @param milli non-negative version number 560 * @param micro non-negative version number 561 */ getInt(int major, int minor, int milli, int micro)562 private static int getInt(int major, int minor, int milli, int micro) 563 { 564 return (major << 24) | (minor << 16) | (milli << 8) | micro; 565 } 566 ///CLOVER:OFF 567 /** 568 * Main method prints out ICU version information 569 * @param args arguments (currently not used) 570 * @stable ICU 4.6 571 */ main(String[] args)572 public static void main(String[] args) { 573 String icuApiVer; 574 575 if (ICU_VERSION.getMajor() <= 4) { 576 if (ICU_VERSION.getMinor() % 2 != 0) { 577 // Development mile stone 578 int major = ICU_VERSION.getMajor(); 579 int minor = ICU_VERSION.getMinor() + 1; 580 if (minor >= 10) { 581 minor -= 10; 582 major++; 583 } 584 icuApiVer = "" + major + "." + minor + "M" + ICU_VERSION.getMilli(); 585 } else { 586 icuApiVer = ICU_VERSION.getVersionString(2, 2); 587 } 588 } else { 589 if (ICU_VERSION.getMinor() == 0) { 590 // Development mile stone 591 icuApiVer = "" + ICU_VERSION.getMajor() + "M" + ICU_VERSION.getMilli(); 592 } else { 593 icuApiVer = ICU_VERSION.getVersionString(2, 2); 594 } 595 } 596 597 598 System.out.println("International Components for Unicode for Java " + icuApiVer); 599 600 System.out.println(""); 601 System.out.println("Implementation Version: " + ICU_VERSION.getVersionString(2, 4)); 602 System.out.println("Unicode Data Version: " + UNICODE_VERSION.getVersionString(2, 4)); 603 System.out.println("CLDR Data Version: " + LocaleData.getCLDRVersion().getVersionString(2, 4)); 604 System.out.println("Time Zone Data Version: " + getTZDataVersion()); 605 } 606 607 /** 608 * Generate version string separated by dots with 609 * the specified digit width. Version digit 0 610 * after <code>minDigits</code> will be trimmed off. 611 * @param minDigits Minimum number of version digits 612 * @param maxDigits Maximum number of version digits 613 * @return A tailored version string 614 * @internal 615 * @deprecated This API is ICU internal only. (For use in CLDR, etc.) 616 */ 617 @Deprecated getVersionString(int minDigits, int maxDigits)618 public String getVersionString(int minDigits, int maxDigits) { 619 if (minDigits < 1 || maxDigits < 1 620 || minDigits > 4 || maxDigits > 4 || minDigits > maxDigits) { 621 throw new IllegalArgumentException("Invalid min/maxDigits range"); 622 } 623 624 int[] digits = new int[4]; 625 digits[0] = getMajor(); 626 digits[1] = getMinor(); 627 digits[2] = getMilli(); 628 digits[3] = getMicro(); 629 630 int numDigits = maxDigits; 631 while (numDigits > minDigits) { 632 if (digits[numDigits - 1] != 0) { 633 break; 634 } 635 numDigits--; 636 } 637 638 StringBuilder verStr = new StringBuilder(7); 639 verStr.append(digits[0]); 640 for (int i = 1; i < numDigits; i++) { 641 verStr.append("."); 642 verStr.append(digits[i]); 643 } 644 645 return verStr.toString(); 646 } 647 ///CLOVER:ON 648 649 650 // Moved from TimeZone class 651 private static volatile String TZDATA_VERSION = null; 652 getTZDataVersion()653 static String getTZDataVersion() { 654 if (TZDATA_VERSION == null) { 655 synchronized (VersionInfo.class) { 656 if (TZDATA_VERSION == null) { 657 UResourceBundle tzbundle = UResourceBundle.getBundleInstance("com/ibm/icu/impl/data/icudt" 658 + VersionInfo.ICU_DATA_VERSION_PATH, "zoneinfo64"); 659 TZDATA_VERSION = tzbundle.getString("TZVersion"); 660 } 661 } 662 } 663 return TZDATA_VERSION; 664 } 665 } 666