/*
* Copyright 1998-2014 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package thredds.util;
/**
* Represent a version number formed by one or more non-negative integers
* with a period separating each integer. E.g., "1.0" or "1.0.1" but not "1.0a".
* <p>Augmented BNF:</p>
* <pre>
* nni = non-negative integer
* version = nni *("." nni)
* </pre>
*
* @author edavis
* @since 4.0
*/
public class Version implements Comparable<Version>
{
private final String versionString;
private final int[] versionSegments;
private final int hashCode;
/**
* Constructes a Version with the given version string.
*
* @param versionString the version string.
* @throws IllegalArgumentException if version string is null or not a valid version number.
*/
public Version( String versionString )
{
if ( versionString == null || versionString.equals( "" ))
throw new IllegalArgumentException( "Version string must be non-empty and non-null.");
if ( versionString.startsWith( "." ) || versionString.endsWith( "." ))
throw new IllegalArgumentException( "Version string [] may not start or end with a period ('.').");
// Split into segments.
String[] tmpVerStringSegs = versionString.split( "\\." );
if ( tmpVerStringSegs.length < 1 )
throw new IllegalArgumentException( "Version string [" + versionString + "] must have at least one numerical segment.");
// Parse segments making sure they are non-negative integers.
int[] tmpVerSegs = new int[tmpVerStringSegs.length];
for ( int i = 0; i < tmpVerStringSegs.length; i++ )
{
try
{
tmpVerSegs[i] = Integer.parseInt( tmpVerStringSegs[i] );
}
catch ( NumberFormatException e )
{
throw new IllegalArgumentException( "Version string [" + versionString + "] is not valid.", e);
}
if ( tmpVerSegs[i] < 0 )
throw new IllegalArgumentException( "Segments of version string [" + versionString + "] must all be integers greater than zero." );
}
this.versionString = versionString;
this.versionSegments = tmpVerSegs;
// Compute hash code (this is an immutable object, so hash won't change)
int tmpHashCode = 17;
for ( int i : this.versionSegments )
tmpHashCode = 37 * tmpHashCode + i;
this.hashCode = tmpHashCode;
}
public String getVersionString()
{
return this.versionString;
}
public int compareTo( Version thatVersion )
{
if ( thatVersion == null )
throw new IllegalArgumentException( "Version must be non-null.");
// Loop through shortest number of segments and compare.
int minLength = Math.min( this.versionSegments.length, thatVersion.versionSegments.length );
for ( int i = 0; i < minLength; i++ )
{
if ( this.versionSegments[i] < thatVersion.versionSegments[i] )
return -1; // this less than given
else if ( this.versionSegments[i] > thatVersion.versionSegments[i] )
return 1; // this greater than given
}
// If both versions have the same number of segments, they are equal.
if ( this.versionSegments.length == thatVersion.versionSegments.length )
return 0;
// Determine which version has more segments.
int[] tmpVerSegs;
boolean thisNotThat;
if ( this.versionSegments.length > minLength )
{
tmpVerSegs = this.versionSegments;
thisNotThat = true;
}
else
{
tmpVerSegs = thatVersion.versionSegments;
thisNotThat = false;
}
// If any of the extra segments are greater than zero, then
// the version with more segments is greater.
for ( int i = minLength; i < tmpVerSegs.length; i++ )
if ( tmpVerSegs[i] > 0 )
return thisNotThat ? 1 : -1;
// If all extra segments are zero, versions are equal.
return 0;
}
@Override
public int hashCode()
{
return this.hashCode;
}
@Override
public boolean equals( Object obj )
{
if ( obj == this )
return true;
if ( ! ( obj instanceof Version ))
return false;
Version v = (Version) obj;
return this.compareTo( v ) == 0;
}
@Override
public String toString()
{
return this.versionString;
}
}