001// ***************************************************************************************************************************
002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
003// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
005// * with the License.  You may obtain a copy of the License at                                                              *
006// *                                                                                                                         *
007// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
008// *                                                                                                                         *
009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
011// * specific language governing permissions and limitations under the License.                                              *
012// ***************************************************************************************************************************
013package org.apache.juneau.internal;
014
015import static org.apache.juneau.internal.StringUtils.*;
016
017/**
018 * Represents a version string such as <js>"1.2"</js> or <js>"1.2.3"</js>
019 *
020 * <p>
021 * Used to compare version numbers.
022 */
023public class Version {
024
025   private int[] parts;
026
027   /**
028    * Constructor
029    *
030    * @param versionString
031    *    A string of the form <js>"#.#..."</js> where there can be any number of parts.
032    *    <br>Valid values:
033    *    <ul>
034    *       <li><js>"1.2"</js>
035    *       <li><js>"1.2.3"</js>
036    *       <li><js>"0.1"</js>
037    *       <li><js>".1"</js>
038    *    </ul>
039    *    Any parts that are not numeric are interpreted as {@link Integer#MAX_VALUE}
040    */
041   public Version(String versionString) {
042      if (isEmpty(versionString))
043         versionString = "0";
044      String[] sParts = split(versionString, '.');
045      parts = new int[sParts.length];
046      for (int i = 0; i < sParts.length; i++) {
047         try {
048            parts[i] = sParts[i].isEmpty() ? 0 : Integer.parseInt(sParts[i]);
049         } catch (NumberFormatException e) {
050            parts[i] = Integer.MAX_VALUE;
051         }
052      }
053   }
054
055   /**
056    * Returns <jk>true</jk> if the specified version is at least this version.
057    *
058    * <p>
059    * Note that the following is true:
060    * <p class='bcode w800'>
061    *    boolean b;
062    *    b = <jk>new</jk> Version(<js>"1.2"</js>).isAtLeast(<jk>new</jk> Version(<js>"1.2.3"</js>)); <jc>// == true </jc>
063    *    b = <jk>new</jk> Version(<js>"1.2.0"</js>).isAtLeast(<jk>new</jk> Version(<js>"1.2.3"</js>)); <jc>// == false</jc>
064    * </p>
065    *
066    * @param v The version to compare to.
067    * @param exclusive Match down-to-version but not including.
068    * @return <jk>true</jk> if the specified version is at least this version.
069    */
070   public boolean isAtLeast(Version v, boolean exclusive) {
071      for (int i = 0; i < Math.min(parts.length, v.parts.length); i++) {
072         int c = v.parts[i] - parts[i];
073         if (c > 0)
074            return false;
075         else if (c < 0)
076            return true;
077      }
078      for (int i = parts.length; i < v.parts.length; i++)
079         if (v.parts[i] != 0)
080            return false;
081      return ! exclusive;
082   }
083
084   /**
085    * Returns <jk>true</jk> if the specified version is at most this version.
086    *
087    * <p>
088    * Note that the following is true:
089    * <p class='bcode w800'>
090    *    boolean b;
091    *    b = <jk>new</jk> Version(<js>"1.2.3"</js>).isAtMost(<jk>new</jk> Version(<js>"1.2"</js>)); <jc>// == true </jc>
092    *    b = <jk>new</jk> Version(<js>"1.2.3"</js>).isAtMost(<jk>new</jk> Version(<js>"1.2.0"</js>)); <jc>// == false</jc>
093    * </p>
094    *
095    * @param v The version to compare to.
096    * @param exclusive Match up-to-version but not including.
097    * @return <jk>true</jk> if the specified version is at most this version.
098    */
099   public boolean isAtMost(Version v, boolean exclusive) {
100      for (int i = 0; i < Math.min(parts.length, v.parts.length); i++) {
101         int c = parts[i] - v.parts[i];
102         if (c > 0)
103            return false;
104         else if (c < 0)
105            return true;
106      }
107      for (int i = parts.length; i < v.parts.length; i++)
108         if (v.parts[i] > 0)
109            return false;
110      return ! exclusive;
111   }
112
113   /**
114    * Returns <jk>true</jk> if the specified version is equal to this version.
115    *
116    * <p>
117    * Note that the following is true:
118    * <p class='bcode w800'>
119    *    boolean b;
120    *    b = <jk>new</jk> Version(<js>"1.2.3"</js>).equals(<jk>new</jk> Version(<js>"1.2"</js>)); <jc>// == true </jc>
121    *    b = <jk>new</jk> Version(<js>"1.2"</js>).equals(<jk>new</jk> Version(<js>"1.2.3"</js>)); <jc>// == true</jc>
122    * </p>
123    *
124    * @param v The version to compare to.
125    * @return <jk>true</jk> if the specified version is equal to this version.
126    */
127   public boolean isEqualsTo(Version v) {
128      for (int i = 0; i < Math.min(parts.length, v.parts.length); i++)
129         if (v.parts[i] - parts[i] != 0)
130            return false;
131      return true;
132   }
133
134   @Override /* Object */
135   public String toString() {
136      return join(parts, '.');
137   }
138}