// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace UnrealBuildTool
{
///
/// Stores a numeric version consisting of any number of components.
///
[Serializable]
class VersionNumber : IComparable
{
///
/// Set of delimiters for version numbers
///
static readonly char[] Delimiters = { '.', ',' };
///
/// The individual version components
///
int[] Components;
///
/// Constructor
///
/// The individual version components. At least one value must be given.
public VersionNumber(params int[] Components)
{
if(Components.Length == 0)
{
throw new InvalidOperationException("Version number must have at least one component");
}
this.Components = Components;
}
///
/// Returns the component at the given index
///
/// The zero-based component index to return
/// The component at the given index
public int GetComponent(int Idx)
{
return Components[Idx];
}
///
/// Tests two objects for equality. VersionNumber behaves like a value type.
///
/// Object to compare against
/// True if the objects are equal, false otherwise.
public override bool Equals(object? Obj)
{
VersionNumber? Version = Obj as VersionNumber;
return !ReferenceEquals(Version, null) && this == Version;
}
///
/// Returns a hash of the version number.
///
/// A hash value for the version number.
public override int GetHashCode()
{
int Result = 5831;
for(int Idx = 0; Idx < Components.Length; Idx++)
{
Result = (Result * 33) + Components[Idx];
}
return Result;
}
///
/// Compares whether two versions are equal.
///
/// The first version number
/// The second version number
/// True if the versions are equal.
public static bool operator==(VersionNumber? Lhs, VersionNumber? Rhs)
{
if(Object.ReferenceEquals(Lhs, null))
{
return Object.ReferenceEquals(Rhs, null);
}
else
{
return !Object.ReferenceEquals(Rhs, null) && Compare(Lhs, Rhs) == 0;
}
}
///
/// Compares whether two versions are not equal.
///
/// The first version number
/// The second version number
/// True if the versions are not equal.
public static bool operator!=(VersionNumber? Lhs, VersionNumber? Rhs)
{
return !(Lhs == Rhs);
}
///
/// Compares whether one version is less than another.
///
/// The first version number
/// The second version number
/// True if the first version is less than the second.
public static bool operator<(VersionNumber Lhs, VersionNumber Rhs)
{
return Compare(Lhs, Rhs) < 0;
}
///
/// Compares whether one version is less or equal to another.
///
/// The first version number
/// The second version number
/// True if the first version is less or equal to the second.
public static bool operator<=(VersionNumber Lhs, VersionNumber Rhs)
{
return Compare(Lhs, Rhs) <= 0;
}
///
/// Compares whether one version is greater than another.
///
/// The first version number
/// The second version number
/// True if the first version is greater than the second.
public static bool operator>(VersionNumber Lhs, VersionNumber Rhs)
{
return Compare(Lhs, Rhs) > 0;
}
///
/// Compares whether one version is greater or equal to another.
///
/// The first version number
/// The second version number
/// True if the first version is greater or equal to the second.
public static bool operator>=(VersionNumber Lhs, VersionNumber Rhs)
{
return Compare(Lhs, Rhs) >= 0;
}
///
/// Comparison function for IComparable
///
/// Other version number to compare to
/// A negative value if this version is before Other, a positive value if this version is after Other, and zero otherwise.
public int CompareTo(VersionNumber? Other)
{
return ReferenceEquals(Other, null)? 1 : Compare(this, Other);
}
///
/// Compares two version numbers and returns an integer indicating their order
///
/// The first version to check
/// The second version to check
/// A negative value if Lhs is before Rhs, a positive value if Lhs is after Rhs, and zero otherwise.
public static int Compare(VersionNumber Lhs, VersionNumber Rhs)
{
for(int Idx = 0;;Idx++)
{
if(Idx == Lhs.Components.Length)
{
if(Idx == Rhs.Components.Length)
{
return 0;
}
else
{
return -1;
}
}
else
{
if(Idx == Rhs.Components.Length)
{
return +1;
}
else if(Lhs.Components[Idx] != Rhs.Components[Idx])
{
return Lhs.Components[Idx] - Rhs.Components[Idx];
}
}
}
}
///
/// Parses the version number from a string
///
/// The string to parse
/// A version number object
public static VersionNumber Parse(string Text)
{
List Components = new List();
foreach(string TextElement in Text.Split(Delimiters))
{
Components.Add(int.Parse(TextElement));
}
return new VersionNumber(Components.ToArray());
}
///
/// Parses the version number from a string
///
/// The string to parse
/// Variable to receive the parsed version number
/// A version number object
public static bool TryParse(string Text, [NotNullWhen(true)] out VersionNumber? OutNumber)
{
List Components = new List();
foreach(string TextElement in Text.Split(Delimiters))
{
int Component;
if(!int.TryParse(TextElement, out Component))
{
OutNumber = null;
return false;
}
Components.Add(Component);
}
OutNumber = new VersionNumber(Components.ToArray());
return true;
}
///
/// Returns a string version number, eg. 1.4
///
/// The stringized version number
public override string ToString()
{
StringBuilder Result = new StringBuilder();
if(Components.Length > 0)
{
Result.Append(Components[0]);
for(int Idx = 1; Idx < Components.Length; Idx++)
{
Result.Append('.');
Result.Append(Components[Idx]);
}
}
return Result.ToString();
}
}
}