/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.usergrid.utils;
/*******************************************************************************
* Copyright (c) 2010, Schley Andrew Kutz All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* - Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* - Neither the name of the Schley Andrew Kutz nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
******************************************************************************/
import java.io.Serializable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang.StringUtils;
/**
* The Version class can be used to parse a standard version string into its four components,
* MAJOR.MINOR.BUILD.REVISION.
*/
public class Version implements Serializable, Cloneable, Comparable<Version> {
/** A serial version UID. */
private static final long serialVersionUID = -4316270526722986552L;
/** A pattern to match the standard version format MAJOR.MINOR.BUILD.REVISION. */
private static final Pattern STD_VERSION_PATT =
Pattern.compile( "^([^\\d]*?)(\\d+)(?:\\.(\\d+))?(?:\\.(\\d+))?(?:\\.(\\d+))?(.*)$" );
/** Initialize a new Version object that is set to "0.0.0.0". */
public Version() {
}
/** Everything before the version in the string that was parsed. */
private String prefix;
/** Everything after the version in the string that was parsed. */
private String suffix;
/** The String that was parsed to create this version object. */
private String rawVersion;
/**
* Gets everything before the version in the string that was parsed.
*
* @return Everything before the version in the string that was parsed.
*/
public String getPrefix() {
return prefix;
}
/**
* Parses a new Version object from a String.
*
* @param toParse The String object to parse.
*
* @return A new Version object.
*
* @throws Exception When there is an error parsing the String.
*/
public static Version parse( String toParse ) throws Exception {
Matcher m = STD_VERSION_PATT.matcher( toParse );
if ( !m.find() ) {
throw new Exception( String.format( "Error parsing version from '%s'", toParse ) );
}
Version v = new Version();
v.rawVersion = toParse;
v.prefix = m.group( 1 );
if ( StringUtils.isNotEmpty( m.group( 2 ) ) ) {
v.setMajor( m.group( 2 ) );
}
if ( StringUtils.isNotEmpty( m.group( 3 ) ) ) {
v.setMinor( m.group( 3 ) );
}
if ( StringUtils.isNotEmpty( m.group( 4 ) ) ) {
v.setBuild( m.group( 4 ) );
}
if ( StringUtils.isNotEmpty( m.group( 5 ) ) ) {
v.setRevision( m.group( 5 ) );
}
v.suffix = m.group( 6 );
return v;
}
/** The version's MAJOR component. */
private String major = "0";
/**
* Sets the version's MAJOR component.
*
* @param toSet The version's MAJOR component.
*
* @throws IllegalArgumentException When a null or non-numeric value is given.
*/
public void setMajor( String toSet ) throws IllegalArgumentException {
if ( StringUtils.isEmpty( toSet ) ) {
throw new IllegalArgumentException( "Argument is null" );
}
if ( !toSet.matches( "\\d+" ) ) {
throw new IllegalArgumentException( "Argument is not numeric" );
}
if ( numberOfComponents < 1 ) {
numberOfComponents = 1;
}
major = toSet;
}
/** The version's MAJOR component as an integer. */
private int getMajorAsInt() {
return Integer.parseInt( major );
}
/** The version's MINOR component. */
private String minor = "0";
/**
* Sets the version's MINOR component.
*
* @param toSet The version's MINOR component.
*
* @throws IllegalArgumentException When a null or non-numeric value is given.
*/
public void setMinor( String toSet ) throws IllegalArgumentException {
if ( StringUtils.isEmpty( toSet ) ) {
throw new IllegalArgumentException( "Argument is null" );
}
if ( !toSet.matches( "\\d+" ) ) {
throw new IllegalArgumentException( "Argument is not numeric" );
}
if ( numberOfComponents < 2 ) {
numberOfComponents = 2;
}
minor = toSet;
}
/** The version's MINOR component as an integer. */
private int getMinorAsInt() {
return Integer.parseInt( minor );
}
/** The version's BUILD component. */
private String build = "0";
/** The version's BUILD component as an integer. */
private int getBuildAsInt() {
return Integer.parseInt( build );
}
/**
* Gets the version's BUILD component.
*
* @return The version's BUILD component.
*/
public String getBuild() {
return build;
}
/**
* Sets the version's BUILD component.
*
* @param toSet The version's BUILD component.
*
* @throws IllegalArgumentException When a null or non-numeric value is given.
*/
public void setBuild( String toSet ) throws IllegalArgumentException {
if ( StringUtils.isEmpty( toSet ) ) {
throw new IllegalArgumentException( "Argument is null" );
}
if ( !toSet.matches( "\\d+" ) ) {
throw new IllegalArgumentException( "Argument is not numeric" );
}
if ( numberOfComponents < 3 ) {
numberOfComponents = 3;
}
build = toSet;
}
/**
* Sets the version's BUILD component.
*
* @param toSet The version's BUILD component.
*/
public void setBuild( int toSet ) {
setBuild( String.valueOf( toSet ) );
}
/** The version's REVISION component. */
private String revision = "0";
/** The version's REVISION component as an integer. */
private int getRevisionAsInt() {
return Integer.parseInt( revision );
}
/**
* Sets the version's REVISION component.
*
* @param toSet The version's REVISION component.
*
* @throws IllegalArgumentException When a null or non-numeric value is given.
*/
public void setRevision( String toSet ) throws IllegalArgumentException {
if ( StringUtils.isEmpty( toSet ) ) {
throw new IllegalArgumentException( "Argument is null" );
}
if ( !toSet.matches( "\\d+" ) ) {
throw new IllegalArgumentException( "Argument is not numeric" );
}
if ( numberOfComponents < 4 ) {
numberOfComponents = 4;
}
revision = toSet;
}
/**
* The number of components that make up the version. The value will always be between 1 (inclusive) and 4
* (inclusive).
*/
private int numberOfComponents;
@Override
@SuppressWarnings("all")
public Object clone() throws CloneNotSupportedException {
Version v = new Version();
v.rawVersion = rawVersion;
v.prefix = prefix;
v.suffix = suffix;
v.numberOfComponents = numberOfComponents;
v.major = major;
v.minor = minor;
v.build = build;
v.revision = revision;
return v;
}
@Override
public boolean equals( Object toCompare ) {
// Compare pointers
if ( toCompare == this ) {
return true;
}
// Compare types
if ( !( toCompare instanceof Version ) ) {
return false;
}
return compareTo( ( Version ) toCompare ) == 0;
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public String toString() {
return String.format( "%s.%s.%s.%s", major, minor, build, revision );
}
/**
* Gets the version as a string using the specified number of components.
*
* @param components The number of components. Values less than 1 will be treated as 1 and values greater than 4
* will be treated as 4.
*
* @return The version as a string using the specified number of components.
*/
public String toString( int components ) {
StringBuilder buff = new StringBuilder();
buff.append( major );
if ( components > 4 ) {
components = 4;
}
switch ( components ) {
case 2:
buff.append( String.format( ".%s", minor ) );
break;
case 3:
buff.append( String.format( ".%s.%s", minor, build ) );
break;
case 4:
buff.append( String.format( ".%s.%s.%s", minor, build, revision ) );
break;
default:
break;
}
return buff.toString();
}
private int compareInts( int x, int y ) {
if ( x == y ) {
return 0;
}
if ( x < y ) {
return -1;
}
return 1;
}
@Override
public int compareTo( Version toCompare ) {
int result = toString().compareTo( toCompare.toString() );
if ( result == 0 ) {
return result;
}
result = compareInts( getMajorAsInt(), toCompare.getMajorAsInt() );
if ( result != 0 ) {
return result;
}
result = compareInts( getMinorAsInt(), toCompare.getMinorAsInt() );
if ( result != 0 ) {
return result;
}
result = compareInts( getBuildAsInt(), toCompare.getBuildAsInt() );
if ( result != 0 ) {
return result;
}
result = compareInts( getRevisionAsInt(), toCompare.getRevisionAsInt() );
if ( result != 0 ) {
return result;
}
return result;
}
}