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