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