1/*
2 *******************************************************************************
3 * Copyright (C) 1996-2015, International Business Machines Corporation and
4 * others. All Rights Reserved.
5 *******************************************************************************
6 */
7
8package com.ibm.icu.util;
9
10import 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 */
17public 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     */
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     */
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     */
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     */
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     */
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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
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
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