All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.phloc.commons.version.Version Maven / Gradle / Ivy

There is a newer version: 5.0.0
Show newest version
/**
 * Copyright (C) 2006-2015 phloc systems
 * http://www.phloc.com
 * office[at]phloc[dot]com
 *
 * Licensed 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 com.phloc.commons.version;

import java.io.Serializable;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;

import com.phloc.commons.IHasStringRepresentation;
import com.phloc.commons.ValueEnforcer;
import com.phloc.commons.equals.EqualsUtils;
import com.phloc.commons.hash.HashCodeGenerator;
import com.phloc.commons.string.StringHelper;
import com.phloc.commons.string.StringParser;
import com.phloc.commons.string.ToStringGenerator;

/**
 * This class represents a single version object. It supports 4 elements: major
 * version (integer), minor version (integer), micro version (integer) and a
 * qualifier (string).
 * 
 * @author Philip Helger
 */
@Immutable
public final class Version implements Comparable , IHasStringRepresentation, Serializable
{
  /** default version if nothing is specified. */
  public static final String DEFAULT_VERSION_STRING = "0";

  /** Default value for printing zero elements in getAsString */
  public static final boolean DEFAULT_PRINT_ZERO_ELEMENTS = false;

  /** major version. */
  private final int m_nMajor;

  /** minor version. */
  private final int m_nMinor;

  /** micro version. */
  private final int m_nMicro;

  /** version build qualifier. */
  private final String m_sQualifier;

  /**
   * Create a new version with major version only.
   * 
   * @param nMajor
   *        major version
   * @throws IllegalArgumentException
   *         if the parameter is < 0
   */
  public Version (@Nonnegative final int nMajor)
  {
    this (nMajor, 0, 0, null);
  }

  /**
   * Create a new version with major and minor version only.
   * 
   * @param nMajor
   *        major version
   * @param nMinor
   *        minor version
   * @throws IllegalArgumentException
   *         if any of the parameters is < 0
   */
  public Version (@Nonnegative final int nMajor, @Nonnegative final int nMinor)
  {
    this (nMajor, nMinor, 0, null);
  }

  /**
   * Create a new version with major, minor and micro version number. The
   * qualifier remains null.
   * 
   * @param nMajor
   *        major version
   * @param nMinor
   *        minor version
   * @param nMicro
   *        micro version
   * @throws IllegalArgumentException
   *         if any of the parameters is < 0
   */
  public Version (@Nonnegative final int nMajor, @Nonnegative final int nMinor, @Nonnegative final int nMicro)
  {
    this (nMajor, nMinor, nMicro, null);
  }

  public Version (@Nonnegative final int nMajor,
                  @Nonnegative final int nMinor,
                  @Nonnegative final int nMicro,
                  @Nonnegative final int nQualifier)
  {
    this (nMajor, nMinor, nMicro, Integer.toString (nQualifier));
  }

  /**
   * Create a new version with 3 integer values and a qualifier.
   * 
   * @param nMajor
   *        major version
   * @param nMinor
   *        minor version
   * @param nMicro
   *        micro version
   * @param sQualifier
   *        the version qualifier - may be null. If a qualifier is supplied, it
   *        may neither contain the "." or the "," character since they are used
   *        to determine the fields of a version and to separate 2 versions in a
   *        VersionRange.
   * @throws IllegalArgumentException
   *         if any of the numeric parameters is < 0 or if the qualifier
   *         contains a forbidden character
   */
  public Version (@Nonnegative final int nMajor,
                  @Nonnegative final int nMinor,
                  @Nonnegative final int nMicro,
                  @Nullable final String sQualifier)
  {
    ValueEnforcer.isGE0 (nMajor, "Major");
    ValueEnforcer.isGE0 (nMinor, "Minor");
    ValueEnforcer.isGE0 (nMicro, "Micro");
    m_nMajor = nMajor;
    m_nMinor = nMinor;
    m_nMicro = nMicro;
    m_sQualifier = StringHelper.hasNoText (sQualifier) ? null : sQualifier;
  }

  /**
   * Construct a version object from a string.
* EBNF:
* version ::= major( '.' minor ( '.' micro ( ( '.' | '-' ) qualifier )? )? )?
* major ::= number
* minor ::= number
* micro ::= number
* qualifier ::= .+ * * @param sVersionString * the version string to be interpreted as a version * @throws IllegalArgumentException * if any of the parameters is < 0 */ public Version (@Nullable final String sVersionString) { // For backward compatibility the old version should be used this (sVersionString, true); } @Nonnull private static String [] _extSplit (final String s) { final String [] aDotParts = StringHelper.getExplodedArray ('.', s, 2); if (aDotParts.length == 2) { // Dots always take precedence return aDotParts; } if (StringParser.isInt (aDotParts[0])) { // If it is numeric, use the dot parts anyway (e.g. for "5" or "-1") return aDotParts; } final String [] aDashParts = StringHelper.getExplodedArray ('-', s, 2); if (aDashParts.length == 1) { // Neither dot nor dash present return aDotParts; } // More matches for dash split! (e.g. "0-RC1") return aDashParts; } /** * Construct a version object from a string. * * @param sVersionString * the version string to be interpreted as a version * @param bOldVersion * true to use the old version to parse a string, meaning * splitting only by dot; or false to indicate that the * more complex parsing should be used. * @throws IllegalArgumentException * if any of the parameters is < 0 */ public Version (@Nullable final String sVersionString, final boolean bOldVersion) { String s = sVersionString == null ? "" : sVersionString.trim (); if (s.length () == 0) s = DEFAULT_VERSION_STRING; if (bOldVersion) { // old version // split each token final String [] aParts = StringHelper.getExplodedArray ('.', s, 4); if (aParts.length > 0) m_nMajor = StringParser.parseInt (aParts[0], 0); else m_nMajor = 0; if (aParts.length > 1) m_nMinor = StringParser.parseInt (aParts[1], 0); else m_nMinor = 0; if (aParts.length > 2) m_nMicro = StringParser.parseInt (aParts[2], 0); else m_nMicro = 0; if (aParts.length > 3) m_sQualifier = StringHelper.hasNoText (aParts[3]) ? null : aParts[3]; else m_sQualifier = null; } else { // Complex parsing Integer aMajor; Integer aMinor = null; Integer aMicro = null; String sQualifier = null; boolean bDone = false; // Extract major version number String [] aParts = _extSplit (s); aMajor = StringParser.parseIntObj (aParts[0]); if (aMajor == null && StringHelper.hasText (aParts[0])) { // Major version is not numeric, so everything is the qualifier sQualifier = s; bDone = true; } String sRest = !bDone && aParts.length > 1 ? aParts[1] : null; if (sRest != null) { // Parse minor version number part aParts = _extSplit (sRest); aMinor = StringParser.parseIntObj (aParts[0]); if (aMinor == null && StringHelper.hasText (aParts[0])) { // Minor version is not numeric, so everything is the qualifier sQualifier = sRest; bDone = true; } sRest = !bDone && aParts.length > 1 ? aParts[1] : null; if (sRest != null) { // Parse micro version number part aParts = _extSplit (sRest); aMicro = StringParser.parseIntObj (aParts[0]); if (aMicro == null && StringHelper.hasText (aParts[0])) { // Micro version is not numeric, so everything is the qualifier sQualifier = sRest; bDone = true; } if (!bDone && aParts.length > 1) { // Some qualifier left! sQualifier = aParts[1]; } } } m_nMajor = aMajor == null ? 0 : aMajor.intValue (); m_nMinor = aMinor == null ? 0 : aMinor.intValue (); m_nMicro = aMicro == null ? 0 : aMicro.intValue (); m_sQualifier = StringHelper.hasNoText (sQualifier) ? null : sQualifier; } // check consistency ValueEnforcer.isGE0 (m_nMajor, "Major"); ValueEnforcer.isGE0 (m_nMinor, "Minor"); ValueEnforcer.isGE0 (m_nMicro, "Micro"); } @Nonnegative public int getMajor () { return m_nMajor; } @Nonnegative public int getMinor () { return m_nMinor; } @Nonnegative public int getMicro () { return m_nMicro; } @Nullable public String getQualifier () { return m_sQualifier; } /** * Compares two Version objects. * * @param rhs * the version to compare to * @return < 0 if this is less than rhs; > 0 if this is greater than * rhs, and 0 if they are equal. * @throws IllegalArgumentException * if the parameter is null */ public int compareTo (@Nonnull final Version rhs) { ValueEnforcer.notNull (rhs, "Rhs"); // compare major version int ret = m_nMajor - rhs.m_nMajor; if (ret == 0) { // compare minor version ret = m_nMinor - rhs.m_nMinor; if (ret == 0) { // compare micro version ret = m_nMicro - rhs.m_nMicro; if (ret == 0) { // check qualifier if (m_sQualifier != null) { if (rhs.m_sQualifier != null) { ret = m_sQualifier.compareTo (rhs.m_sQualifier); // convert to -1/0/+1 if (ret < 0) ret = -1; else if (ret > 0) ret = +1; } else ret = 1; } else if (rhs.m_sQualifier != null) { // only this qualifier == null ret = -1; } else { // both qualifier are null ret = 0; } } } } return ret; } public boolean isGreaterThan (@Nonnull final Version aVersion) { return compareTo (aVersion) > 0; } public boolean isGreaterOrEqualThan (@Nonnull final Version aVersion) { return compareTo (aVersion) >= 0; } public boolean isLowerThan (@Nonnull final Version aVersion) { return compareTo (aVersion) < 0; } public boolean isLowerOrEqualThan (@Nonnull final Version aVersion) { return compareTo (aVersion) <= 0; } @Nonnull public String getAsString () { return getAsString (DEFAULT_PRINT_ZERO_ELEMENTS); } /** * Get the string representation of the version number. * * @param bPrintZeroElements * If true than trailing zeroes are printed, otherwise * printed zeroes are not printed. * @return Never null. */ @Nonnull public String getAsString (final boolean bPrintZeroElements) { // Build from back to front final StringBuilder aSB = new StringBuilder (m_sQualifier != null ? m_sQualifier : ""); if (m_nMicro > 0 || aSB.length () > 0 || bPrintZeroElements) { // Micro version if (aSB.length () > 0) aSB.insert (0, '.'); aSB.insert (0, m_nMicro); } if (m_nMinor > 0 || aSB.length () > 0 || bPrintZeroElements) { // Minor version if (aSB.length () > 0) aSB.insert (0, '.'); aSB.insert (0, m_nMinor); } if (m_nMajor > 0 || aSB.length () > 0 || bPrintZeroElements) { // Major version if (aSB.length () > 0) aSB.insert (0, '.'); aSB.insert (0, m_nMajor); } return aSB.length () > 0 ? aSB.toString () : DEFAULT_VERSION_STRING; } @Override public boolean equals (final Object o) { if (o == this) return true; if (!(o instanceof Version)) return false; final Version rhs = (Version) o; return m_nMajor == rhs.m_nMajor && m_nMinor == rhs.m_nMinor && m_nMicro == rhs.m_nMicro && EqualsUtils.equals (m_sQualifier, rhs.m_sQualifier); } @Override public int hashCode () { return new HashCodeGenerator (this).append (m_nMajor) .append (m_nMinor) .append (m_nMicro) .append (m_sQualifier) .getHashCode (); } @Override public String toString () { return new ToStringGenerator (this).append ("major", m_nMajor) .append ("minor", m_nMinor) .append ("micro", m_nMicro) .appendIfNotNull ("qualifier", m_sQualifier) .toString (); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy