// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using EpicGames.Core; using EpicGames.UHT.Types; namespace EpicGames.UHT.Utils { /// /// Interface used to collect all the objects referenced by a given type. /// Not all types such as UhtPackage and UhtHeaderFile support collecting /// references due to assorted reasons. /// public interface IUhtReferenceCollector { /// /// Add a cross module reference to a given object type. /// /// Object type being referenced /// If true, the method being invoked must return the registered object. This only applies to classes. void AddCrossModuleReference(UhtObject? obj, bool registered); /// /// Add an object declaration /// /// Object in question /// If true, the method being invoked must return the registered object. This only applies to classes. void AddDeclaration(UhtObject obj, bool registered); /// /// Add a field as a singleton for exporting /// /// Field to be added void AddSingleton(UhtField field); /// /// Add a field as a type being exported /// /// Field to be added void AddExportType(UhtField field); /// /// Add a forward declaration. The string can contain multiple declarations but must only exist on one line. /// /// The declarations to add void AddForwardDeclaration(string? declaration); } /// /// Delegate used to fetch the string associated with a reference /// /// Index of the referenced object /// If true return the registered string, otherwise the unregistered string. Classes have an unregistered version. /// The requested string public delegate string GetReferenceStringDelegate(int objectIndex, bool registered); /// /// Maintains a list of referenced object indices. /// public class UhtUniqueReferenceCollection { /// /// Collection use to quickly detect if a reference is already in the collection /// private HashSet Uniques { get; } = new HashSet(); /// /// List of all unique reference keys. Use UngetKey to get the object index and the flag. /// public List References { get; } = new List(); /// /// Return an encoded key that represents the object and registered flag. /// If the object has the alternate object set (i.e. native interfaces), then /// that object's index is used to generate the key. /// /// Object being referenced /// If true, then the API that ensures the object is registered is returned. /// Integer key value. public static int GetKey(UhtObject obj, bool registered) { return obj.AlternateObject != null ? GetKey(obj.AlternateObject, registered) : (obj.ObjectTypeIndex << 1) + (registered ? 1 : 0); } /// /// Given a key, return the object index and registered flag /// /// The key in question /// Index of the referenced object /// True if referencing the registered API. public static void UngetKey(int key, out int objectIndex, out bool registered) { objectIndex = key >> 1; registered = (key & 1) != 0; } /// /// Add the given object to the references /// /// Object to be added /// True if the registered API is being returned. public void Add(UhtObject? obj, bool registered) { if (obj != null) { int key = GetKey(obj, registered); if (Uniques.Add(key)) { References.Add(key); } } } /// /// Return the collection of references sorted by the API string returned by the delegate. /// /// Delegate to invoke to return the requested object API string /// Read only memory region of all the string. public ReadOnlyMemory GetSortedReferences(GetReferenceStringDelegate referenceStringDelegate) { // Collect the unsorted array string[] sorted = new string[References.Count]; for (int index = 0; index < References.Count; ++index) { int key = References[index]; UngetKey(key, out int objectIndex, out bool registered); sorted[index] = referenceStringDelegate(objectIndex, registered); } // Sort the array Array.Sort(sorted, StringComparerUE.OrdinalIgnoreCase); // Remove duplicates. In some instances the different keys might return the same string. // This removes those duplicates if (References.Count > 1) { int priorOut = 0; for (int index = 1; index < sorted.Length; ++index) { if (sorted[index] != sorted[priorOut]) { ++priorOut; sorted[priorOut] = sorted[index]; } } return sorted.AsMemory(0, priorOut + 1); } else { return sorted.AsMemory(); } } } /// /// Standard implementation of the reference collector interface /// public class UhtReferenceCollector : IUhtReferenceCollector { /// /// Collection of unique cross module references /// public UhtUniqueReferenceCollection CrossModule { get; set; } = new UhtUniqueReferenceCollection(); /// /// Collection of unique declarations /// public UhtUniqueReferenceCollection Declaration { get; set; } = new UhtUniqueReferenceCollection(); /// /// Collection of singletons /// public List Singletons { get; } = new List(); /// /// Collection of types to export /// public List ExportTypes { get; } = new List(); /// /// Collection of forward declarations /// public HashSet ForwardDeclarations { get; } = new HashSet(); /// /// Collection of referenced headers /// public HashSet ReferencedHeaders { get; } = new HashSet(); /// /// Add a cross module reference /// /// Object being referenced /// True if the object being referenced must be registered public void AddCrossModuleReference(UhtObject? obj, bool registered) { CrossModule.Add(obj, registered); if (obj != null && obj is not UhtPackage && registered) { ReferencedHeaders.Add(obj.HeaderFile); } } /// /// Add a declaration /// /// Object being declared /// True if the object being declared must be registered public void AddDeclaration(UhtObject obj, bool registered) { Declaration.Add(obj, registered); } /// /// Add a singleton. These are added as forward declared functions in the package file. /// /// Field being added public void AddSingleton(UhtField field) { Singletons.Add(field); } /// /// Add a type to be exported. /// /// Type to be exported public void AddExportType(UhtField field) { ExportTypes.Add(field); } /// /// Add a symbol that must be forward declared /// /// Symbol to be forward declared. public void AddForwardDeclaration(string? declaration) { if (!String.IsNullOrEmpty(declaration)) { ForwardDeclarations.Add(declaration); } } } }