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