001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau; 018 019import java.util.*; 020 021import org.apache.juneau.common.utils.*; 022 023/** 024 * Represents a version string such as <js>"1.2"</js> or <js>"1.2.3"</js> 025 * 026 * <p> 027 * Used to compare version numbers. 028 * 029 * <h5 class='section'>See Also:</h5><ul> 030 * </ul> 031 */ 032public class Version implements Comparable<Version> { 033 034 private int[] parts; 035 036 /** 037 * Static creator. 038 * 039 * @param value 040 * A string of the form <js>"#.#..."</js> where there can be any number of parts. 041 * <br>Valid values: 042 * <ul> 043 * <li><js>"1.2"</js> 044 * <li><js>"1.2.3"</js> 045 * <li><js>"0.1"</js> 046 * <li><js>".1"</js> 047 * </ul> 048 * <br>Can be <jk>null</jk>. 049 * @return A new header bean, or <jk>null</jk> if the value is <jk>null</jk>. 050 */ 051 public static Version of(String value) { 052 if (value == null) 053 return null; 054 return new Version(value); 055 } 056 057 058 /** 059 * Constructor 060 * 061 * @param value 062 * A string of the form <js>"#.#..."</js> where there can be any number of parts. 063 * <br>Valid values: 064 * <ul> 065 * <li><js>"1.2"</js> 066 * <li><js>"1.2.3"</js> 067 * <li><js>"0.1"</js> 068 * <li><js>".1"</js> 069 * </ul> 070 * Any parts that are not numeric are interpreted as {@link Integer#MAX_VALUE} 071 */ 072 public Version(String value) { 073 if (Utils.isEmpty(value)) 074 value = "0"; 075 String[] sParts = Utils.splita(value, '.'); 076 parts = new int[sParts.length]; 077 for (int i = 0; i < sParts.length; i++) { 078 try { 079 parts[i] = sParts[i].isEmpty() ? 0 : Integer.parseInt(sParts[i]); 080 } catch (NumberFormatException e) { 081 parts[i] = Integer.MAX_VALUE; 082 } 083 } 084 } 085 086 /** 087 * Returns the version part at the specified zero-indexed value. 088 * 089 * @param index The index of the version part. 090 * @return The version part, never <jk>null</jk>. 091 */ 092 public Optional<Integer> getPart(int index) { 093 if (index < 0 || parts.length <= index) 094 return Utils.opte(); 095 return Utils.opt(parts[index]); 096 } 097 098 /** 099 * Returns the major version part (i.e. part at index 0). 100 * 101 * @return The version part, never <jk>null</jk>. 102 */ 103 public Optional<Integer> getMajor() { 104 return getPart(0); 105 } 106 107 /** 108 * Returns the minor version part (i.e. part at index 1). 109 * 110 * @return The version part, never <jk>null</jk>. 111 */ 112 public Optional<Integer> getMinor() { 113 return getPart(1); 114 } 115 116 /** 117 * Returns the maintenance version part (i.e. part at index 2). 118 * 119 * @return The version part, never <jk>null</jk>. 120 */ 121 public Optional<Integer> getMaintenance() { 122 return getPart(2); 123 } 124 125 /** 126 * Returns <jk>true</jk> if the specified version is at least this version. 127 * 128 * <h5 class='section'>Example:</h5> 129 * <p class='bjava'> 130 * <jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2"</js>).isAtLeast(Version.<jsm>of</jsm>(<js>"1"</js>))); 131 * <jsm>assertFalse</jsm>(Version.<jsm>of</jsm>(<js>"1.2"</js>).isAtLeast(Version.<jsm>of</jsm>(<js>"2"</js>))); 132 * <jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2"</js>).isAtLeast(Version.<jsm>of</jsm>(<js>"1.2.3"</js>))); 133 * <jsm>assertFalse</jsm>(Version.<jsm>of</jsm>(<js>"1.2.0"</js>).isAtLeast(Version.<jsm>of</jsm>(<js>"1.2.3"</js>))); 134 * </p> 135 * 136 * @param v The version to compare to. 137 * @return <jk>true</jk> if the specified version is at least this version. 138 */ 139 public boolean isAtLeast(Version v) { 140 return isAtLeast(v, false); 141 } 142 143 144 /** 145 * Returns <jk>true</jk> if the specified version is at least this version. 146 * 147 * <h5 class='section'>Example:</h5> 148 * <p class='bjava'> 149 * <jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isAtLeast(Version.<jsm>of</jsm>(<js>"1.2.3"</js>, <jk>false</jk>))); 150 * <jsm>assertFalse</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isAtLeast(Version.<jsm>of</jsm>(<js>"1.2.3"</js>, <jk>true</jk>))); 151 * </p> 152 * 153 * @param v The version to compare to. 154 * @param exclusive Match down-to-version but not including. 155 * @return <jk>true</jk> if the specified version is at least this version. 156 */ 157 public boolean isAtLeast(Version v, boolean exclusive) { 158 for (int i = 0; i < Math.min(parts.length, v.parts.length); i++) { 159 int c = v.parts[i] - parts[i]; 160 if (c > 0) 161 return false; 162 else if (c < 0) 163 return true; 164 } 165 for (int i = parts.length; i < v.parts.length; i++) 166 if (v.parts[i] != 0) 167 return false; 168 return ! exclusive; 169 } 170 171 /** 172 * Returns <jk>true</jk> if the specified version is at most this version. 173 * 174 * <h5 class='section'>Example:</h5> 175 * <p class='bjava'> 176 * <jsm>assertFalse</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isAtMost(Version.<jsm>of</jsm>(<js>"1"</js>))); 177 * <jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isAtMost(Version.<jsm>of</jsm>(<js>"2"</js>))); 178 * <jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isAtMost(Version.<jsm>of</jsm>(<js>"1.2"</js>))); 179 * <jsm>assertFalse</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isAtMost(Version.<jsm>of</jsm>(<js>"1.2.0"</js>))); 180 * </p> 181 * 182 * @param v The version to compare to. 183 * @return <jk>true</jk> if the specified version is at most this version. 184 */ 185 public boolean isAtMost(Version v) { 186 return isAtMost(v, false); 187 } 188 189 /** 190 * Returns <jk>true</jk> if the specified version is at most this version. 191 * 192 * <h5 class='section'>Example:</h5> 193 * <p class='bjava'> 194 * <jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isAtMost(Version.<jsm>of</jsm>(<js>"1.2.3"</js>, <jk>false</jk>))); 195 * <jsm>assertFalse</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isAtMost(Version.<jsm>of</jsm>(<js>"1.2.3"</js>, <jk>true</jk>))); 196 * </p> 197 * 198 * @param v The version to compare to. 199 * @param exclusive Match up-to-version but not including. 200 * @return <jk>true</jk> if the specified version is at most this version. 201 */ 202 public boolean isAtMost(Version v, boolean exclusive) { 203 for (int i = 0; i < Math.min(parts.length, v.parts.length); i++) { 204 int c = parts[i] - v.parts[i]; 205 if (c > 0) 206 return false; 207 else if (c < 0) 208 return true; 209 } 210 for (int i = parts.length; i < v.parts.length; i++) 211 if (v.parts[i] > 0) 212 return true; 213 return ! exclusive; 214 } 215 216 /** 217 * Returns <jk>true</jk> if the specified version is equal to this version. 218 * 219 * <h5 class='section'>Example:</h5> 220 * <p class='bjava'> 221 * <jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isEqualsTo(Version.<jsm>of</jsm>(<js>"1.2.3"</js>))); 222 * <jsm>assertTrue</jsm>(Version.<jsm>of</jsm>(<js>"1.2.3"</js>).isEqualsTo(Version.<jsm>of</jsm>(<js>"1.2"</js>))); 223 * </p> 224 * 225 * @param v The version to compare to. 226 * @return <jk>true</jk> if the specified version is equal to this version. 227 */ 228 public boolean equals(Version v) { 229 for (int i = 0; i < Math.min(parts.length, v.parts.length); i++) 230 if (v.parts[i] - parts[i] != 0) 231 return false; 232 return true; 233 } 234 235 @Override /* Object */ 236 public boolean equals(Object o) { 237 return o instanceof Version && equals((Version)o); 238 } 239 240 @Override /* Object */ 241 public String toString() { 242 return Utils.join(parts, '.'); 243 } 244 245 @Override 246 public int compareTo(Version v) { 247 for (int i = 0; i < Math.min(parts.length, v.parts.length); i++) { 248 int c = parts[i] - v.parts[i]; 249 if (c != 0) 250 return c; 251 } 252 return parts.length - v.parts.length; 253 } 254}