// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Reflection; using EpicGames.Core; 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 IReadOnlyDictionary TypeToSerializer; /// /// Map from serializer name to instance /// static IReadOnlyDictionary NameToSerializer; /// /// Creates a map of type name to constructor /// /// static ActionSerialization() { Dictionary TypeToSerializerDict = new Dictionary(); Dictionary NameToSerializerDict = 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)!; TypeToSerializerDict[Serializer.Type] = Serializer; NameToSerializerDict[Type.Name] = Serializer; } } TypeToSerializer = TypeToSerializerDict; NameToSerializer = NameToSerializerDict; } /// /// 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); } } }