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 * Unicode 14.0 version 204 * @stable ICU 70 205 */ 206 public static final VersionInfo UNICODE_14_0; 207 208 /** 209 * Unicode 15.0 version 210 * @stable ICU 72 211 */ 212 public static final VersionInfo UNICODE_15_0; 213 214 /** 215 * ICU4J current release version 216 * @stable ICU 2.8 217 */ 218 public static final VersionInfo ICU_VERSION; 219 220 /** 221 * Data version string for ICU's internal data. 222 * Used for appending to data path (e.g. icudt43b) 223 * @internal 224 * @deprecated This API is ICU internal only. 225 */ 226 @Deprecated 227 public static final String ICU_DATA_VERSION_PATH = "72b"; 228 229 /** 230 * Data version in ICU4J. 231 * @internal 232 * @deprecated This API is ICU internal only. 233 */ 234 @Deprecated 235 public static final VersionInfo ICU_DATA_VERSION; 236 237 /** 238 * Collation runtime version (sort key generator, string comparisons). 239 * If the version is different, sort keys for the same string could be different. 240 * This value may change in subsequent releases of ICU. 241 * @stable ICU 2.8 242 */ 243 public static final VersionInfo UCOL_RUNTIME_VERSION; 244 245 /** 246 * Collation builder code version. 247 * When this is different, the same tailoring might result 248 * in assigning different collation elements to code points. 249 * This value may change in subsequent releases of ICU. 250 * @stable ICU 2.8 251 */ 252 public static final VersionInfo UCOL_BUILDER_VERSION; 253 254 /** 255 * Constant version 1. 256 * This was intended to be the version of collation tailorings, 257 * but instead the tailoring data carries a version number. 258 * @deprecated ICU 54 259 */ 260 @Deprecated 261 public static final VersionInfo UCOL_TAILORINGS_VERSION; 262 263 264 // public methods ------------------------------------------------------ 265 266 /** 267 * Returns an instance of VersionInfo with the argument version. 268 * @param version version String in the format of "major.minor.milli.micro" 269 * or "major.minor.milli" or "major.minor" or "major", 270 * where major, minor, milli, micro are non-negative numbers 271 * <= 255. If the trailing version numbers are 272 * not specified they are taken as 0s. E.g. Version "3.1" is 273 * equivalent to "3.1.0.0". 274 * @return an instance of VersionInfo with the argument version. 275 * @exception IllegalArgumentException when the argument version 276 * is not in the right format 277 * @stable ICU 2.6 278 */ getInstance(String version)279 public static VersionInfo getInstance(String version) 280 { 281 int length = version.length(); 282 int array[] = {0, 0, 0, 0}; 283 int count = 0; 284 int index = 0; 285 286 while (count < 4 && index < length) { 287 char c = version.charAt(index); 288 if (c == '.') { 289 count ++; 290 } 291 else { 292 c -= '0'; 293 if (c < 0 || c > 9) { 294 throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); 295 } 296 array[count] *= 10; 297 array[count] += c; 298 } 299 index ++; 300 } 301 if (index != length) { 302 throw new IllegalArgumentException( 303 "Invalid version number: String '" + version + "' exceeds version format"); 304 } 305 for (int i = 0; i < 4; i ++) { 306 if (array[i] < 0 || array[i] > 255) { 307 throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); 308 } 309 } 310 311 return getInstance(array[0], array[1], array[2], array[3]); 312 } 313 314 /** 315 * Returns an instance of VersionInfo with the argument version. 316 * @param major major version, non-negative number <= 255. 317 * @param minor minor version, non-negative number <= 255. 318 * @param milli milli version, non-negative number <= 255. 319 * @param micro micro version, non-negative number <= 255. 320 * @exception IllegalArgumentException when either arguments are negative or > 255 321 * @stable ICU 2.6 322 */ getInstance(int major, int minor, int milli, int micro)323 public static VersionInfo getInstance(int major, int minor, int milli, 324 int micro) 325 { 326 // checks if it is in the hashmap 327 // else 328 if (major < 0 || major > 255 || minor < 0 || minor > 255 || 329 milli < 0 || milli > 255 || micro < 0 || micro > 255) { 330 throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); 331 } 332 int version = getInt(major, minor, milli, micro); 333 Integer key = Integer.valueOf(version); 334 VersionInfo result = MAP_.get(key); 335 if (result == null) { 336 result = new VersionInfo(version); 337 VersionInfo tmpvi = MAP_.putIfAbsent(key, result); 338 if (tmpvi != null) { 339 result = tmpvi; 340 } 341 } 342 return result; 343 } 344 345 /** 346 * Returns an instance of VersionInfo with the argument version. 347 * Equivalent to getInstance(major, minor, milli, 0). 348 * @param major major version, non-negative number <= 255. 349 * @param minor minor version, non-negative number <= 255. 350 * @param milli milli version, non-negative number <= 255. 351 * @exception IllegalArgumentException when either arguments are 352 * negative or > 255 353 * @stable ICU 2.6 354 */ getInstance(int major, int minor, int milli)355 public static VersionInfo getInstance(int major, int minor, int milli) 356 { 357 return getInstance(major, minor, milli, 0); 358 } 359 360 /** 361 * Returns an instance of VersionInfo with the argument version. 362 * Equivalent to getInstance(major, minor, 0, 0). 363 * @param major major version, non-negative number <= 255. 364 * @param minor minor version, non-negative number <= 255. 365 * @exception IllegalArgumentException when either arguments are 366 * negative or > 255 367 * @stable ICU 2.6 368 */ getInstance(int major, int minor)369 public static VersionInfo getInstance(int major, int minor) 370 { 371 return getInstance(major, minor, 0, 0); 372 } 373 374 /** 375 * Returns an instance of VersionInfo with the argument version. 376 * Equivalent to getInstance(major, 0, 0, 0). 377 * @param major major version, non-negative number <= 255. 378 * @exception IllegalArgumentException when either arguments are 379 * negative or > 255 380 * @stable ICU 2.6 381 */ getInstance(int major)382 public static VersionInfo getInstance(int major) 383 { 384 return getInstance(major, 0, 0, 0); 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 */ 393 @Override toString()394 public String toString() 395 { 396 StringBuilder result = new StringBuilder(7); 397 result.append(getMajor()); 398 result.append('.'); 399 result.append(getMinor()); 400 result.append('.'); 401 result.append(getMilli()); 402 result.append('.'); 403 result.append(getMicro()); 404 return result.toString(); 405 } 406 407 /** 408 * Returns the major version number 409 * @return the major version number 410 * @stable ICU 2.6 411 */ getMajor()412 public int getMajor() 413 { 414 return (m_version_ >> 24) & LAST_BYTE_MASK_ ; 415 } 416 417 /** 418 * Returns the minor version number 419 * @return the minor version number 420 * @stable ICU 2.6 421 */ getMinor()422 public int getMinor() 423 { 424 return (m_version_ >> 16) & LAST_BYTE_MASK_ ; 425 } 426 427 /** 428 * Returns the milli version number 429 * @return the milli version number 430 * @stable ICU 2.6 431 */ getMilli()432 public int getMilli() 433 { 434 return (m_version_ >> 8) & LAST_BYTE_MASK_ ; 435 } 436 437 /** 438 * Returns the micro version number 439 * @return the micro version number 440 * @stable ICU 2.6 441 */ getMicro()442 public int getMicro() 443 { 444 return m_version_ & LAST_BYTE_MASK_ ; 445 } 446 447 /** 448 * Checks if this version information is equals to the argument version 449 * @param other object to be compared 450 * @return true if other is equals to this object's version information, 451 * false otherwise 452 * @stable ICU 2.6 453 */ 454 @Override equals(Object other)455 public boolean equals(Object other) 456 { 457 return other == this; 458 } 459 460 /** 461 * Returns the hash code value for this set. 462 * 463 * @return the hash code value for this set. 464 * @see java.lang.Object#hashCode() 465 * @stable ICU 2.6 466 */ 467 @Override hashCode()468 public int hashCode() { 469 return m_version_; 470 } 471 472 /** 473 * Compares other with this VersionInfo. 474 * @param other VersionInfo to be compared 475 * @return 0 if the argument is a VersionInfo object that has version 476 * information equals to this object. 477 * Less than 0 if the argument is a VersionInfo object that has 478 * version information greater than this object. 479 * Greater than 0 if the argument is a VersionInfo object that 480 * has version information less than this object. 481 * @stable ICU 2.6 482 */ 483 @Override compareTo(VersionInfo other)484 public int compareTo(VersionInfo other) 485 { 486 // m_version_ is an int, a signed 32-bit integer. 487 // When the major version is >=128, then the version int is negative. 488 // Compare it in two steps to simulate an unsigned-int comparison. 489 // (Alternatively we could turn each int into a long and reset the upper 32 bits.) 490 // Compare the upper bits first, using logical shift right (unsigned). 491 int diff = (m_version_ >>> 1) - (other.m_version_ >>> 1); 492 if (diff != 0) { return diff; } 493 // Compare the remaining bits. 494 return (m_version_ & 1) - (other.m_version_ & 1); 495 } 496 497 // private data members ---------------------------------------------- 498 499 /** 500 * Unicode data version used by the current release. 501 * Defined here privately for printing by the main() method in this class. 502 * Should be the same as {@link com.ibm.icu.lang.UCharacter#getUnicodeVersion()} 503 * which gets the version number from a data file. 504 * We do not want VersionInfo to have an import dependency on UCharacter. 505 */ 506 private static final VersionInfo UNICODE_VERSION; 507 508 /** 509 * Version number stored as a byte for each of the major, minor, milli and 510 * micro numbers in the 32 bit int. 511 * Most significant for the major and the least significant contains the 512 * micro numbers. 513 */ 514 private int m_version_; 515 /** 516 * Map of singletons 517 */ 518 private static final ConcurrentHashMap<Integer, VersionInfo> MAP_ = new ConcurrentHashMap<>(); 519 /** 520 * Last byte mask 521 */ 522 private static final int LAST_BYTE_MASK_ = 0xFF; 523 /** 524 * Error statement string 525 */ 526 private static final String INVALID_VERSION_NUMBER_ = 527 "Invalid version number: Version number may be negative or greater than 255"; 528 529 // static declaration ------------------------------------------------ 530 531 /** 532 * Initialize versions only after MAP_ has been created 533 */ 534 static { 535 UNICODE_1_0 = getInstance(1, 0, 0, 0); 536 UNICODE_1_0_1 = getInstance(1, 0, 1, 0); 537 UNICODE_1_1_0 = getInstance(1, 1, 0, 0); 538 UNICODE_1_1_5 = getInstance(1, 1, 5, 0); 539 UNICODE_2_0 = getInstance(2, 0, 0, 0); 540 UNICODE_2_1_2 = getInstance(2, 1, 2, 0); 541 UNICODE_2_1_5 = getInstance(2, 1, 5, 0); 542 UNICODE_2_1_8 = getInstance(2, 1, 8, 0); 543 UNICODE_2_1_9 = getInstance(2, 1, 9, 0); 544 UNICODE_3_0 = getInstance(3, 0, 0, 0); 545 UNICODE_3_0_1 = getInstance(3, 0, 1, 0); 546 UNICODE_3_1_0 = getInstance(3, 1, 0, 0); 547 UNICODE_3_1_1 = getInstance(3, 1, 1, 0); 548 UNICODE_3_2 = getInstance(3, 2, 0, 0); 549 UNICODE_4_0 = getInstance(4, 0, 0, 0); 550 UNICODE_4_0_1 = getInstance(4, 0, 1, 0); 551 UNICODE_4_1 = getInstance(4, 1, 0, 0); 552 UNICODE_5_0 = getInstance(5, 0, 0, 0); 553 UNICODE_5_1 = getInstance(5, 1, 0, 0); 554 UNICODE_5_2 = getInstance(5, 2, 0, 0); 555 UNICODE_6_0 = getInstance(6, 0, 0, 0); 556 UNICODE_6_1 = getInstance(6, 1, 0, 0); 557 UNICODE_6_2 = getInstance(6, 2, 0, 0); 558 UNICODE_6_3 = getInstance(6, 3, 0, 0); 559 UNICODE_7_0 = getInstance(7, 0, 0, 0); 560 UNICODE_8_0 = getInstance(8, 0, 0, 0); 561 UNICODE_9_0 = getInstance(9, 0, 0, 0); 562 UNICODE_10_0 = getInstance(10, 0, 0, 0); 563 UNICODE_11_0 = getInstance(11, 0, 0, 0); 564 UNICODE_12_0 = getInstance(12, 0, 0, 0); 565 UNICODE_12_1 = getInstance(12, 1, 0, 0); 566 UNICODE_13_0 = getInstance(13, 0, 0, 0); 567 UNICODE_14_0 = getInstance(14, 0, 0, 0); 568 UNICODE_15_0 = getInstance(15, 0, 0, 0); 569 570 ICU_VERSION = getInstance(72, 1, 0, 0); 571 ICU_DATA_VERSION = ICU_VERSION; 572 UNICODE_VERSION = UNICODE_15_0; 573 574 UCOL_RUNTIME_VERSION = getInstance(9); 575 UCOL_BUILDER_VERSION = getInstance(9); 576 UCOL_TAILORINGS_VERSION = getInstance(1); 577 } 578 579 // private constructor ----------------------------------------------- 580 581 /** 582 * Constructor with int 583 * @param compactversion a 32 bit int with each byte representing a number 584 */ VersionInfo(int compactversion)585 private VersionInfo(int compactversion) 586 { 587 m_version_ = compactversion; 588 } 589 590 /** 591 * Gets the int from the version numbers 592 * @param major non-negative version number 593 * @param minor non-negative version number 594 * @param milli non-negative version number 595 * @param micro non-negative version number 596 */ getInt(int major, int minor, int milli, int micro)597 private static int getInt(int major, int minor, int milli, int micro) 598 { 599 return (major << 24) | (minor << 16) | (milli << 8) | micro; 600 } 601 ///CLOVER:OFF 602 /** 603 * Main method prints out ICU version information 604 * @param args arguments (currently not used) 605 * @stable ICU 4.6 606 */ main(String[] args)607 public static void main(String[] args) { 608 String icuApiVer; 609 610 if (ICU_VERSION.getMajor() <= 4) { 611 if (ICU_VERSION.getMinor() % 2 != 0) { 612 // Development mile stone 613 int major = ICU_VERSION.getMajor(); 614 int minor = ICU_VERSION.getMinor() + 1; 615 if (minor >= 10) { 616 minor -= 10; 617 major++; 618 } 619 icuApiVer = "" + major + "." + minor + "M" + ICU_VERSION.getMilli(); 620 } else { 621 icuApiVer = ICU_VERSION.getVersionString(2, 2); 622 } 623 } else { 624 if (ICU_VERSION.getMinor() == 0) { 625 // Development mile stone 626 icuApiVer = "" + ICU_VERSION.getMajor() + "M" + ICU_VERSION.getMilli(); 627 } else { 628 icuApiVer = ICU_VERSION.getVersionString(2, 2); 629 } 630 } 631 632 633 System.out.println("International Components for Unicode for Java " + icuApiVer); 634 635 System.out.println(""); 636 System.out.println("Implementation Version: " + ICU_VERSION.getVersionString(2, 4)); 637 System.out.println("Unicode Data Version: " + UNICODE_VERSION.getVersionString(2, 4)); 638 System.out.println("CLDR Data Version: " + LocaleData.getCLDRVersion().getVersionString(2, 4)); 639 System.out.println("Time Zone Data Version: " + getTZDataVersion()); 640 } 641 642 /** 643 * Generate version string separated by dots with 644 * the specified digit width. Version digit 0 645 * after <code>minDigits</code> will be trimmed off. 646 * @param minDigits Minimum number of version digits 647 * @param maxDigits Maximum number of version digits 648 * @return A tailored version string 649 * @internal 650 * @deprecated This API is ICU internal only. (For use in CLDR, etc.) 651 */ 652 @Deprecated getVersionString(int minDigits, int maxDigits)653 public String getVersionString(int minDigits, int maxDigits) { 654 if (minDigits < 1 || maxDigits < 1 655 || minDigits > 4 || maxDigits > 4 || minDigits > maxDigits) { 656 throw new IllegalArgumentException("Invalid min/maxDigits range"); 657 } 658 659 int[] digits = new int[4]; 660 digits[0] = getMajor(); 661 digits[1] = getMinor(); 662 digits[2] = getMilli(); 663 digits[3] = getMicro(); 664 665 int numDigits = maxDigits; 666 while (numDigits > minDigits) { 667 if (digits[numDigits - 1] != 0) { 668 break; 669 } 670 numDigits--; 671 } 672 673 StringBuilder verStr = new StringBuilder(7); 674 verStr.append(digits[0]); 675 for (int i = 1; i < numDigits; i++) { 676 verStr.append("."); 677 verStr.append(digits[i]); 678 } 679 680 return verStr.toString(); 681 } 682 ///CLOVER:ON 683 684 685 // Moved from TimeZone class 686 private static volatile String TZDATA_VERSION = null; 687 getTZDataVersion()688 static String getTZDataVersion() { 689 if (TZDATA_VERSION == null) { 690 synchronized (VersionInfo.class) { 691 if (TZDATA_VERSION == null) { 692 UResourceBundle tzbundle = UResourceBundle.getBundleInstance("com/ibm/icu/impl/data/icudt" 693 + VersionInfo.ICU_DATA_VERSION_PATH, "zoneinfo64"); 694 TZDATA_VERSION = tzbundle.getString("TZVersion"); 695 } 696 } 697 } 698 return TZDATA_VERSION; 699 } 700 } 701