001 /* 002 * Copyright 2004-2006 Stephen J. McConnell. 003 * Copyright 1999-2004 The Apache Software Foundation 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * 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 */ 017 package net.dpml.lang; 018 019 import java.io.Serializable; 020 021 import java.util.StringTokenizer; 022 023 /** 024 * This class is used to hold version information. 025 * <p /> 026 * 027 * The version number is made up of three dot-separated fields: 028 * <p /> 029 * "<b>major.minor.micro</b>" 030 * <p /> 031 * The <b>major</b>, <b>minor</b> and <b>micro</b> fields are 032 * <i>integer</i> numbers represented in decimal notation and have the 033 * following meaning: 034 * <ul> 035 * 036 * <p /><li><b>major</b> - When the major version changes (in ex. from 037 * "1.5.12" to "2.0.0"), then backward compatibility 038 * with previous releases is not granted.</li><p /> 039 * 040 * <p /><li><b>minor</b> - When the minor version changes (in ex. from 041 * "1.5.12" to "1.6.0"), then backward compatibility 042 * with previous releases is granted, but something changed in the 043 * implementation of the Component. (ie it methods could have been added)</li><p /> 044 * 045 * <p /><li><b>micro</b> - When the micro version changes (in ex. 046 * from "1.5.12" to "1.5.13"), then the the changes are 047 * small forward compatible bug fixes or documentation modifications etc. 048 * </li> 049 * </ul> 050 * 051 * @author <a href="http://www.dpml.net">Digital Product Management Laboratory</a> 052 * @version 2.0.0 053 */ 054 public final class Version implements Comparable, Serializable 055 { 056 /** 057 * Version -1.0.0. 058 */ 059 public static final Version NULL_VERSION = new Version( -1, 0, 0 ); 060 061 /** 062 * Serial version identifier. 063 */ 064 static final long serialVersionUID = 1L; 065 066 private int m_major; 067 private int m_minor; 068 private int m_micro; 069 070 /** 071 * Parse a version out of a string. 072 * The version string format is <major>.<minor>.<micro> where 073 * both minor and micro are optional. 074 * 075 * @param version The input version string 076 * @return the new Version object 077 * @throws NumberFormatException if an error occurs 078 * @throws IllegalArgumentException if an error occurs 079 * @throws NullPointerException if the version argument is <code>null</code>. 080 * @since 4.1 081 */ 082 public static Version parse( final String version ) 083 throws NumberFormatException, IllegalArgumentException, NullPointerException 084 { 085 if( version == null ) 086 { 087 throw new NullPointerException( "version" ); 088 } 089 090 final StringTokenizer tokenizer = new StringTokenizer( version, "." ); 091 final String[] levels = new String[ tokenizer.countTokens() ]; 092 for( int i = 0; i < levels.length; i++ ) 093 { 094 levels[ i ] = tokenizer.nextToken(); 095 } 096 097 int major = -1; 098 if( 0 < levels.length ) 099 { 100 major = Integer.parseInt( levels[ 0 ] ); 101 } 102 103 int minor = 0; 104 if( 1 < levels.length ) 105 { 106 minor = Integer.parseInt( levels[ 1 ] ); 107 } 108 109 int micro = 0; 110 if( 2 < levels.length ) 111 { 112 micro = Integer.parseInt( levels[ 2 ] ); 113 } 114 115 return new Version( major, minor, micro ); 116 } 117 118 /** 119 * Create a new instance of a <code>Version</code> object with the 120 * specified version numbers. 121 * 122 * @param major This <code>Version</code> major number. 123 * @param minor This <code>Version</code> minor number. 124 * @param micro This <code>Version</code> micro number. 125 */ 126 public Version( final int major, final int minor, final int micro ) 127 { 128 m_major = major; 129 m_minor = minor; 130 m_micro = micro; 131 } 132 133 /** 134 * Retrieve major component of version. 135 * 136 * @return the major component of version 137 * @since 4.1 138 */ 139 public int getMajor() 140 { 141 return m_major; 142 } 143 144 /** 145 * Retrieve minor component of version. 146 * 147 * @return the minor component of version 148 * @since 4.1 149 */ 150 public int getMinor() 151 { 152 return m_minor; 153 } 154 155 /** 156 * Retrieve micro component of version. 157 * 158 * @return the micro component of version. 159 * @since 4.1 160 */ 161 public int getMicro() 162 { 163 return m_micro; 164 } 165 166 /** 167 * Check this <code>Version</code> against another for equality. 168 * <p /> 169 * If this <code>Version</code> is compatible with the specified one, then 170 * <b>true</b> is returned, otherwise <b>false</b>. 171 * 172 * @param other The other <code>Version</code> object to be compared with this 173 * for equality. 174 * @return <b>true</b> if this <code>Version</code> is compatible with the specified one 175 * @since 4.1 176 */ 177 public boolean equals( final Version other ) 178 { 179 if( other == null ) 180 { 181 return false; 182 } 183 boolean isEqual = ( getMajor() == other.getMajor() ); 184 if( isEqual ) 185 { 186 isEqual = ( getMinor() == other.getMinor() ); 187 } 188 if( isEqual ) 189 { 190 isEqual = ( getMicro() == other.getMicro() ); 191 } 192 return isEqual; 193 } 194 195 /** 196 * Indicates whether some other object is "equal to" this <code>Version</code>. 197 * Returns <b>true</b> if the other object is an instance of <code>Version</code> 198 * and has the same major, minor, and micro components. 199 * 200 * @param other an <code>Object</code> value 201 * @return <b>true</b> if the other object is equal to this <code>Version</code> 202 */ 203 public boolean equals( final Object other ) 204 { 205 boolean isEqual = false; 206 if( other instanceof Version ) 207 { 208 isEqual = equals( (Version) other ); 209 } 210 return isEqual; 211 } 212 213 /** 214 * Add a hashing function to ensure the Version object is 215 * treated as expected in hashmaps and sets. NOTE: any 216 * time the equals() is overridden, hashCode() should also 217 * be overridden. 218 * 219 * @return the hashCode 220 */ 221 public int hashCode() 222 { 223 int hash = 61486123 * getMajor(); 224 hash = hash + 1273621 * getMinor(); 225 hash = hash + 8912738 * getMicro(); 226 return hash; 227 } 228 229 /** 230 * Check this <code>Version</code> against another for compliancy 231 * (compatibility). 232 * <p /> 233 * If this <code>Version</code> is compatible with the specified one, then 234 * <b>true</b> is returned, otherwise <b>false</b>. Be careful when using 235 * this method since, in example, version 1.3.7 is compliant to version 236 * 1.3.6, while the opposite is not. 237 * <p /> 238 * The following example displays the expected behaviour and results of version. 239 * <pre> 240 * final Version v1 = new Version( 1, 3, 6 ); 241 * final Version v2 = new Version( 1, 3, 7 ); 242 * final Version v3 = new Version( 1, 4, 0 ); 243 * final Version v4 = new Version( 2, 0, 1 ); 244 * 245 * assert( v1.complies( v1 ) ); 246 * assert( ! v1.complies( v2 ) ); 247 * assert( v2.complies( v1 ) ); 248 * assert( ! v1.complies( v3 ) ); 249 * assert( v3.complies( v1 ) ); 250 * assert( ! v1.complies( v4 ) ); 251 * assert( ! v4.complies( v1 ) ); 252 * </pre> 253 * 254 * @param other The other <code>Version</code> object to be compared with this 255 * for compliancy (compatibility). 256 * @return <b>true</b> if this <code>Version</code> is compatible with the specified one 257 */ 258 public boolean complies( final Version other ) 259 { 260 if( other == null ) 261 { 262 return false; 263 } 264 if( other.m_major == -1 ) 265 { 266 return true; 267 } 268 if( m_major != other.m_major ) 269 { 270 return false; 271 } 272 else if( m_minor < other.m_minor ) 273 { 274 //If of major version but lower minor version then incompatible 275 return false; 276 } 277 else 278 { 279 //If same major version, same minor version but lower micro level 280 //then incompatible 281 return !( m_minor == other.m_minor && m_micro < other.m_micro ); 282 } 283 } 284 285 /** 286 * Overload toString to report version correctly. 287 * 288 * @return the dot seperated version string 289 */ 290 public String toString() 291 { 292 return m_major + "." + m_minor + "." + m_micro; 293 } 294 295 /** 296 * Compare two versions together according to the 297 * {@link Comparable} interface. 298 * @param o the other object 299 * @return number indicating relative value (-1, 0, 1) 300 * @throws NullPointerException if the argument is null. 301 */ 302 public int compareTo( Object o ) 303 throws NullPointerException 304 { 305 if( o == null ) 306 { 307 throw new NullPointerException ( "o" ); 308 } 309 310 Version other = (Version) o; 311 312 if( getMajor() < other.getMajor() ) 313 { 314 return -1; 315 } 316 317 if( getMajor() > other.getMajor() ) 318 { 319 return 1; 320 } 321 322 if( getMinor() < other.getMinor() ) 323 { 324 return -1; 325 } 326 327 if( getMinor() > other.getMinor() ) 328 { 329 return 1; 330 } 331 332 if( getMicro() < other.getMicro() ) 333 { 334 return -1; 335 } 336 337 if( getMicro() > other.getMicro() ) 338 { 339 return 1; 340 } 341 342 return 0; 343 } 344 }