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      *                &lt;= 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 &lt;= 255.
305      * @param minor minor version, non-negative number &lt;= 255.
306      * @param milli milli version, non-negative number &lt;= 255.
307      * @param micro micro version, non-negative number &lt;= 255.
308      * @exception IllegalArgumentException when either arguments are negative or &gt; 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 &lt;= 255.
337      * @param minor minor version, non-negative number &lt;= 255.
338      * @param milli milli version, non-negative number &lt;= 255.
339      * @exception IllegalArgumentException when either arguments are
340      *                                     negative or &gt; 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 &lt;= 255.
352      * @param minor minor version, non-negative number &lt;= 255.
353      * @exception IllegalArgumentException when either arguments are
354      *                                     negative or &gt; 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 &lt;= 255.
366      * @exception IllegalArgumentException when either arguments are
367      *                                     negative or &gt; 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