1 /*
   2  * Copyright (c) 2005, 2020, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 /*
  26  *******************************************************************************
  27  * (C) Copyright IBM Corp. and others, 1996-2009 - All Rights Reserved         *
  28  *                                                                             *
  29  * The original version of this source code and documentation is copyrighted   *
  30  * and owned by IBM, These materials are provided under terms of a License     *
  31  * Agreement between IBM and Sun. This technology is protected by multiple     *
  32  * US and International patents. This notice and attribution to IBM may not    *
  33  * to removed.                                                                 *
  34  *******************************************************************************
  35  */
  36 
  37 package jdk.internal.icu.util;
  38 
  39 import java.util.HashMap;
  40 
  41 /**
  42  * Class to store version numbers of the form major.minor.milli.micro.
  43  * @author synwee
  44  * @stable ICU 2.6
  45  */
  46 public final class VersionInfo
  47 {
  48     // public data members -------------------------------------------------
  49 
  50     /**
  51      * Data version string for ICU's internal data.
  52      * Used for appending to data path (e.g. icudt43b)
  53      * @internal
  54      * @deprecated This API is ICU internal only.
  55      */
  56     @Deprecated
  57     public static final String ICU_DATA_VERSION_PATH = "64b";
  58 
  59     // public methods ------------------------------------------------------
  60 
  61     /**
  62      * Returns an instance of VersionInfo with the argument version.
  63      * @param version version String in the format of "major.minor.milli.micro"
  64      *                or "major.minor.milli" or "major.minor" or "major",
  65      *                where major, minor, milli, micro are non-negative numbers
  66      *                {@literal <=} 255. If the trailing version numbers are
  67      *                not specified they are taken as 0s. E.g. Version "3.1" is
  68      *                equivalent to "3.1.0.0".
  69      * @return an instance of VersionInfo with the argument version.
  70      * @exception throws an IllegalArgumentException when the argument version
  71      *                is not in the right format
  72      * @stable ICU 2.6
  73      */
  74     public static VersionInfo getInstance(String version)
  75     {
  76         int length  = version.length();
  77         int array[] = {0, 0, 0, 0};
  78         int count   = 0;
  79         int index   = 0;
  80 
  81         while (count < 4 && index < length) {
  82             char c = version.charAt(index);
  83             if (c == '.') {
  84                 count ++;
  85             }
  86             else {
  87                 c -= '0';
  88                 if (c < 0 || c > 9) {
  89                     throw new IllegalArgumentException(INVALID_VERSION_NUMBER_);
  90                 }
  91                 array[count] *= 10;
  92                 array[count] += c;
  93             }
  94             index ++;
  95         }
  96         if (index != length) {
  97             throw new IllegalArgumentException(
  98                                                "Invalid version number: String '" + version + "' exceeds version format");
  99         }
 100         for (int i = 0; i < 4; i ++) {
 101             if (array[i] < 0 || array[i] > 255) {
 102                 throw new IllegalArgumentException(INVALID_VERSION_NUMBER_);
 103             }
 104         }
 105 
 106         return getInstance(array[0], array[1], array[2], array[3]);
 107     }
 108 
 109     /**
 110      * Returns an instance of VersionInfo with the argument version.
 111      * @param major major version, non-negative number {@literal <=} 255.
 112      * @param minor minor version, non-negative number {@literal <=} 255.
 113      * @param milli milli version, non-negative number {@literal <=} 255.
 114      * @param micro micro version, non-negative number {@literal <=} 255.
 115      * @exception throws an IllegalArgumentException when either arguments are
 116      *                                     negative or {@literal >} 255
 117      * @stable ICU 2.6
 118      */
 119     public static VersionInfo getInstance(int major, int minor, int milli,
 120                                           int micro)
 121     {
 122         // checks if it is in the hashmap
 123         // else
 124         if (major < 0 || major > 255 || minor < 0 || minor > 255 ||
 125             milli < 0 || milli > 255 || micro < 0 || micro > 255) {
 126             throw new IllegalArgumentException(INVALID_VERSION_NUMBER_);
 127         }
 128         int     version = getInt(major, minor, milli, micro);
 129         Integer key     = Integer.valueOf(version);
 130         Object  result  = MAP_.get(key);
 131         if (result == null) {
 132             result = new VersionInfo(version);
 133             MAP_.put(key, result);
 134         }
 135         return (VersionInfo)result;
 136     }
 137 
 138     /**
 139      * Compares other with this VersionInfo.
 140      * @param other VersionInfo to be compared
 141      * @return 0 if the argument is a VersionInfo object that has version
 142      *           information equal to this object.
 143      *           Less than 0 if the argument is a VersionInfo object that has
 144      *           version information greater than this object.
 145      *           Greater than 0 if the argument is a VersionInfo object that
 146      *           has version information less than this object.
 147      * @stable ICU 2.6
 148      */
 149     public int compareTo(VersionInfo other)
 150     {
 151         return m_version_ - other.m_version_;
 152     }
 153 
 154     // private data members ----------------------------------------------
 155 
 156     /**
 157      * Version number stored as a byte for each of the major, minor, milli and
 158      * micro numbers in the 32 bit int.
 159      * Most significant for the major and the least significant contains the
 160      * micro numbers.
 161      */
 162     private int m_version_;
 163     /**
 164      * Map of singletons
 165      */
 166     private static final HashMap<Integer, Object> MAP_ = new HashMap<>();
 167     /**
 168      * Error statement string
 169      */
 170     private static final String INVALID_VERSION_NUMBER_ =
 171         "Invalid version number: Version number may be negative or greater than 255";
 172 
 173     // private constructor -----------------------------------------------
 174 
 175     /**
 176      * Constructor with int
 177      * @param compactversion a 32 bit int with each byte representing a number
 178      */
 179     private VersionInfo(int compactversion)
 180     {
 181         m_version_ = compactversion;
 182     }
 183 
 184     /**
 185      * Gets the int from the version numbers
 186      * @param major non-negative version number
 187      * @param minor non-negativeversion number
 188      * @param milli non-negativeversion number
 189      * @param micro non-negativeversion number
 190      */
 191     private static int getInt(int major, int minor, int milli, int micro)
 192     {
 193         return (major << 24) | (minor << 16) | (milli << 8) | micro;
 194     }
 195 }