// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text; using System.Threading.Tasks; using EpicGames.Core; using UnrealBuildBase; namespace UnrealBuildTool { /// /// Interface for a class that can serialize an action type /// interface IActionSerializer { /// /// The action type /// Type Type { get; } /// /// Read the action from an archive /// /// Reader for the action /// New action IExternalAction Read(BinaryArchiveReader Reader); /// /// Writes an action to an archive /// /// Writer for the archive /// The action to write void Write(BinaryArchiveWriter Writer, IExternalAction Action); } /// /// Generic base class for an action serializer /// /// abstract class ActionSerializerBase : IActionSerializer where TAction : IExternalAction { /// public Type Type => typeof(TAction); /// IExternalAction IActionSerializer.Read(BinaryArchiveReader Reader) => Read(Reader); /// void IActionSerializer.Write(BinaryArchiveWriter Writer, IExternalAction Action) => Write(Writer, (TAction)Action); /// /// Read the action from an archive /// /// Reader for the action /// New action public abstract TAction Read(BinaryArchiveReader Reader); /// /// Writes an action to an archive /// /// Writer for the archive /// The action to write public abstract void Write(BinaryArchiveWriter Writer, TAction Action); } /// /// Helper methods for registering serializers and serializing actions /// static class ActionSerialization { /// /// Map from type name to deserializing constructor /// static Dictionary TypeToSerializer; /// /// Map from serializer name to instance /// static Dictionary NameToSerializer; /// /// Creates a map of type name to constructor /// /// static ActionSerialization() { TypeToSerializer = new Dictionary(); NameToSerializer = new Dictionary(StringComparer.Ordinal); Type[] Types = Assembly.GetExecutingAssembly().GetTypes(); foreach (Type Type in Types) { if (Type.IsClass && !Type.IsAbstract && typeof(IActionSerializer).IsAssignableFrom(Type)) { IActionSerializer Serializer = (IActionSerializer)Activator.CreateInstance(Type)!; TypeToSerializer[Serializer.Type] = Serializer; NameToSerializer[Type.Name] = Serializer; } } } /// /// Read an action from the given archive /// /// Reader to deserialize from /// New action public static IExternalAction ReadAction(this BinaryArchiveReader Reader) { IActionSerializer Serializer = Reader.ReadObjectReference(() => ReadSerializer(Reader))!; return Serializer.Read(Reader); } /// /// Reads a type name and find its registered constructor from an archive /// /// Archive to read from /// New constructor info static IActionSerializer ReadSerializer(BinaryArchiveReader Reader) { string Name = Reader.ReadString()!; IActionSerializer? Serializer; if (!NameToSerializer.TryGetValue(Name, out Serializer)) { throw new BuildException("Unable to find action type '{0}'", Name); } return Serializer; } /// /// Writes an action to the given archive /// /// Writer to serialize the action to /// Action to serialize public static void WriteAction(this BinaryArchiveWriter Writer, IExternalAction Action) { Type Type = Action.GetType(); IActionSerializer? Serializer; if (!TypeToSerializer.TryGetValue(Type, out Serializer)) { throw new BuildException("Unable to find serializer for action type '{0}'", Type.Name); } Writer.WriteObjectReference(Serializer, () => Writer.WriteString(Serializer.GetType().Name)); Serializer.Write(Writer, Action); } } }