// 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(); } } }