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