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