1 // 2016 and later: Unicode, Inc. and others. 2 // License & terms of use: http://www.unicode.org/copyright.html#License 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 * ICU4J current release version 180 * @stable ICU 2.8 181 */ 182 public static final VersionInfo ICU_VERSION; 183 184 /** 185 * Data version string for ICU's internal data. 186 * Used for appending to data path (e.g. icudt43b) 187 * @internal 188 * @deprecated This API is ICU internal only. 189 */ 190 @Deprecated 191 public static final String ICU_DATA_VERSION_PATH = "60b"; 192 193 /** 194 * Data version in ICU4J. 195 * @internal 196 * @deprecated This API is ICU internal only. 197 */ 198 @Deprecated 199 public static final VersionInfo ICU_DATA_VERSION; 200 201 /** 202 * Collation runtime version (sort key generator, string comparisons). 203 * If the version is different, sort keys for the same string could be different. 204 * This value may change in subsequent releases of ICU. 205 * @stable ICU 2.8 206 */ 207 public static final VersionInfo UCOL_RUNTIME_VERSION; 208 209 /** 210 * Collation builder code version. 211 * When this is different, the same tailoring might result 212 * in assigning different collation elements to code points. 213 * This value may change in subsequent releases of ICU. 214 * @stable ICU 2.8 215 */ 216 public static final VersionInfo UCOL_BUILDER_VERSION; 217 218 /** 219 * Constant version 1. 220 * This was intended to be the version of collation tailorings, 221 * but instead the tailoring data carries a version number. 222 * @deprecated ICU 54 223 */ 224 @Deprecated 225 public static final VersionInfo UCOL_TAILORINGS_VERSION; 226 227 228 // public methods ------------------------------------------------------ 229 230 /** 231 * Returns an instance of VersionInfo with the argument version. 232 * @param version version String in the format of "major.minor.milli.micro" 233 * or "major.minor.milli" or "major.minor" or "major", 234 * where major, minor, milli, micro are non-negative numbers 235 * <= 255. If the trailing version numbers are 236 * not specified they are taken as 0s. E.g. Version "3.1" is 237 * equivalent to "3.1.0.0". 238 * @return an instance of VersionInfo with the argument version. 239 * @exception IllegalArgumentException when the argument version 240 * is not in the right format 241 * @stable ICU 2.6 242 */ 243 public static VersionInfo getInstance(String version) 244 { 245 int length = version.length(); 246 int array[] = {0, 0, 0, 0}; 247 int count = 0; 248 int index = 0; 249 250 while (count < 4 && index < length) { 251 char c = version.charAt(index); 252 if (c == '.') { 253 count ++; 254 } 255 else { 256 c -= '0'; 257 if (c < 0 || c > 9) { 258 throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); 259 } 260 array[count] *= 10; 261 array[count] += c; 262 } 263 index ++; 264 } 265 if (index != length) { 266 throw new IllegalArgumentException( 267 "Invalid version number: String '" + version + "' exceeds version format"); 268 } 269 for (int i = 0; i < 4; i ++) { 270 if (array[i] < 0 || array[i] > 255) { 271 throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); 272 } 273 } 274 275 return getInstance(array[0], array[1], array[2], array[3]); 276 } 277 278 /** 279 * Returns an instance of VersionInfo with the argument version. 280 * @param major major version, non-negative number <= 255. 281 * @param minor minor version, non-negative number <= 255. 282 * @param milli milli version, non-negative number <= 255. 283 * @param micro micro version, non-negative number <= 255. 284 * @exception IllegalArgumentException when either arguments are negative or > 255 285 * @stable ICU 2.6 286 */ 287 public static VersionInfo getInstance(int major, int minor, int milli, 288 int micro) 289 { 290 // checks if it is in the hashmap 291 // else 292 if (major < 0 || major > 255 || minor < 0 || minor > 255 || 293 milli < 0 || milli > 255 || micro < 0 || micro > 255) { 294 throw new IllegalArgumentException(INVALID_VERSION_NUMBER_); 295 } 296 int version = getInt(major, minor, milli, micro); 297 Integer key = Integer.valueOf(version); 298 VersionInfo result = MAP_.get(key); 299 if (result == null) { 300 result = new VersionInfo(version); 301 VersionInfo tmpvi = MAP_.putIfAbsent(key, result); 302 if (tmpvi != null) { 303 result = tmpvi; 304 } 305 } 306 return result; 307 } 308 309 /** 310 * Returns an instance of VersionInfo with the argument version. 311 * Equivalent to getInstance(major, minor, milli, 0). 312 * @param major major version, non-negative number <= 255. 313 * @param minor minor version, non-negative number <= 255. 314 * @param milli milli version, non-negative number <= 255. 315 * @exception IllegalArgumentException when either arguments are 316 * negative or > 255 317 * @stable ICU 2.6 318 */ 319 public static VersionInfo getInstance(int major, int minor, int milli) 320 { 321 return getInstance(major, minor, milli, 0); 322 } 323 324 /** 325 * Returns an instance of VersionInfo with the argument version. 326 * Equivalent to getInstance(major, minor, 0, 0). 327 * @param major major version, non-negative number <= 255. 328 * @param minor minor version, non-negative number <= 255. 329 * @exception IllegalArgumentException when either arguments are 330 * negative or > 255 331 * @stable ICU 2.6 332 */ 333 public static VersionInfo getInstance(int major, int minor) 334 { 335 return getInstance(major, minor, 0, 0); 336 } 337 338 /** 339 * Returns an instance of VersionInfo with the argument version. 340 * Equivalent to getInstance(major, 0, 0, 0). 341 * @param major major version, non-negative number <= 255. 342 * @exception IllegalArgumentException when either arguments are 343 * negative or > 255 344 * @stable ICU 2.6 345 */ 346 public static VersionInfo getInstance(int major) 347 { 348 return getInstance(major, 0, 0, 0); 349 } 350 351 private static volatile VersionInfo javaVersion; 352 353 /** 354 * @internal 355 * @deprecated This API is ICU internal only. 356 */ 357 @Deprecated 358 public static VersionInfo javaVersion() { 359 if (javaVersion == null) { 360 synchronized(VersionInfo.class) { 361 if (javaVersion == null) { 362 String s = System.getProperty("java.version"); 363 // clean string 364 // preserve only digits, separated by single '.' 365 // ignore over 4 digit sequences 366 // does not test < 255, very odd... 367 368 char[] chars = s.toCharArray(); 369 int r = 0, w = 0, count = 0; 370 boolean numeric = false; // ignore leading non-numerics 371 while (r < chars.length) { 372 char c = chars[r++]; 373 if (c < '0' || c > '9') { 374 if (numeric) { 375 if (count == 3) { 376 // only four digit strings allowed 377 break; 378 } 379 numeric = false; 380 chars[w++] = '.'; 381 ++count; 382 } 383 } else { 384 numeric = true; 385 chars[w++] = c; 386 } 387 } 388 while (w > 0 && chars[w-1] == '.') { 389 --w; 390 } 391 392 String vs = new String(chars, 0, w); 393 394 javaVersion = VersionInfo.getInstance(vs); 395 } 396 } 397 } 398 return javaVersion; 399 } 400 401 /** 402 * Returns the String representative of VersionInfo in the format of 403 * "major.minor.milli.micro" 404 * @return String representative of VersionInfo 405 * @stable ICU 2.6 406 */ 407 @Override 408 public String toString() 409 { 410 StringBuilder result = new StringBuilder(7); 411 result.append(getMajor()); 412 result.append('.'); 413 result.append(getMinor()); 414 result.append('.'); 415 result.append(getMilli()); 416 result.append('.'); 417 result.append(getMicro()); 418 return result.toString(); 419 } 420 421 /** 422 * Returns the major version number 423 * @return the major version number 424 * @stable ICU 2.6 425 */ 426 public int getMajor() 427 { 428 return (m_version_ >> 24) & LAST_BYTE_MASK_ ; 429 } 430 431 /** 432 * Returns the minor version number 433 * @return the minor version number 434 * @stable ICU 2.6 435 */ 436 public int getMinor() 437 { 438 return (m_version_ >> 16) & LAST_BYTE_MASK_ ; 439 } 440 441 /** 442 * Returns the milli version number 443 * @return the milli version number 444 * @stable ICU 2.6 445 */ 446 public int getMilli() 447 { 448 return (m_version_ >> 8) & LAST_BYTE_MASK_ ; 449 } 450 451 /** 452 * Returns the micro version number 453 * @return the micro version number 454 * @stable ICU 2.6 455 */ 456 public int getMicro() 457 { 458 return m_version_ & LAST_BYTE_MASK_ ; 459 } 460 461 /** 462 * Checks if this version information is equals to the argument version 463 * @param other object to be compared 464 * @return true if other is equals to this object's version information, 465 * false otherwise 466 * @stable ICU 2.6 467 */ 468 @Override 469 public boolean equals(Object other) 470 { 471 return other == this; 472 } 473 474 /** 475 * Returns the hash code value for this set. 476 * 477 * @return the hash code value for this set. 478 * @see java.lang.Object#hashCode() 479 * @stable ICU 58 480 */ 481 @Override 482 public int hashCode() { 483 return m_version_; 484 } 485 486 /** 487 * Compares other with this VersionInfo. 488 * @param other VersionInfo to be compared 489 * @return 0 if the argument is a VersionInfo object that has version 490 * information equals to this object. 491 * Less than 0 if the argument is a VersionInfo object that has 492 * version information greater than this object. 493 * Greater than 0 if the argument is a VersionInfo object that 494 * has version information less than this object. 495 * @stable ICU 2.6 496 */ 497 @Override 498 public int compareTo(VersionInfo other) 499 { 500 return m_version_ - other.m_version_; 501 } 502 503 // private data members ---------------------------------------------- 504 505 /** 506 * Unicode data version used by the current release. 507 * Defined here privately for printing by the main() method in this class. 508 * Should be the same as {@link com.ibm.icu.lang.UCharacter#getUnicodeVersion()} 509 * which gets the version number from a data file. 510 * We do not want VersionInfo to have an import dependency on UCharacter. 511 */ 512 private static final VersionInfo UNICODE_VERSION; 513 514 /** 515 * Version number stored as a byte for each of the major, minor, milli and 516 * micro numbers in the 32 bit int. 517 * Most significant for the major and the least significant contains the 518 * micro numbers. 519 */ 520 private int m_version_; 521 /** 522 * Map of singletons 523 */ 524 private static final ConcurrentHashMap<Integer, VersionInfo> MAP_ = new ConcurrentHashMap<Integer, VersionInfo>(); 525 /** 526 * Last byte mask 527 */ 528 private static final int LAST_BYTE_MASK_ = 0xFF; 529 /** 530 * Error statement string 531 */ 532 private static final String INVALID_VERSION_NUMBER_ = 533 "Invalid version number: Version number may be negative or greater than 255"; 534 535 // static declaration ------------------------------------------------ 536 537 /** 538 * Initialize versions only after MAP_ has been created 539 */ 540 static { 541 UNICODE_1_0 = getInstance(1, 0, 0, 0); 542 UNICODE_1_0_1 = getInstance(1, 0, 1, 0); 543 UNICODE_1_1_0 = getInstance(1, 1, 0, 0); 544 UNICODE_1_1_5 = getInstance(1, 1, 5, 0); 545 UNICODE_2_0 = getInstance(2, 0, 0, 0); 546 UNICODE_2_1_2 = getInstance(2, 1, 2, 0); 547 UNICODE_2_1_5 = getInstance(2, 1, 5, 0); 548 UNICODE_2_1_8 = getInstance(2, 1, 8, 0); 549 UNICODE_2_1_9 = getInstance(2, 1, 9, 0); 550 UNICODE_3_0 = getInstance(3, 0, 0, 0); 551 UNICODE_3_0_1 = getInstance(3, 0, 1, 0); 552 UNICODE_3_1_0 = getInstance(3, 1, 0, 0); 553 UNICODE_3_1_1 = getInstance(3, 1, 1, 0); 554 UNICODE_3_2 = getInstance(3, 2, 0, 0); 555 UNICODE_4_0 = getInstance(4, 0, 0, 0); 556 UNICODE_4_0_1 = getInstance(4, 0, 1, 0); 557 UNICODE_4_1 = getInstance(4, 1, 0, 0); 558 UNICODE_5_0 = getInstance(5, 0, 0, 0); 559 UNICODE_5_1 = getInstance(5, 1, 0, 0); 560 UNICODE_5_2 = getInstance(5, 2, 0, 0); 561 UNICODE_6_0 = getInstance(6, 0, 0, 0); 562 UNICODE_6_1 = getInstance(6, 1, 0, 0); 563 UNICODE_6_2 = getInstance(6, 2, 0, 0); 564 UNICODE_6_3 = getInstance(6, 3, 0, 0); 565 UNICODE_7_0 = getInstance(7, 0, 0, 0); 566 UNICODE_8_0 = getInstance(8, 0, 0, 0); 567 UNICODE_9_0 = getInstance(9, 0, 0, 0); 568 UNICODE_10_0 = getInstance(10, 0, 0, 0); 569 570 ICU_VERSION = getInstance(60, 2, 0, 0); 571 ICU_DATA_VERSION = getInstance(60, 2, 0, 0); 572 UNICODE_VERSION = UNICODE_10_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 */ 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 */ 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 */ 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 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 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