/* * 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; } }