Files
UnrealEngine/Engine/Source/Programs/UnrealBuildTool/System/ActionSerializer.cs
2025-05-18 13:04:45 +08:00

153 lines
4.7 KiB
C#

// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Reflection;
using EpicGames.Core;
namespace UnrealBuildTool
{
/// <summary>
/// Interface for a class that can serialize an action type
/// </summary>
interface IActionSerializer
{
/// <summary>
/// The action type
/// </summary>
Type Type { get; }
/// <summary>
/// Read the action from an archive
/// </summary>
/// <param name="Reader">Reader for the action</param>
/// <returns>New action</returns>
IExternalAction Read(BinaryArchiveReader Reader);
/// <summary>
/// Writes an action to an archive
/// </summary>
/// <param name="Writer">Writer for the archive</param>
/// <param name="Action">The action to write</param>
void Write(BinaryArchiveWriter Writer, IExternalAction Action);
}
/// <summary>
/// Generic base class for an action serializer
/// </summary>
/// <typeparam name="TAction"></typeparam>
abstract class ActionSerializerBase<TAction> : IActionSerializer where TAction : IExternalAction
{
/// <inheritdoc/>
public Type Type => typeof(TAction);
/// <inheritdoc/>
IExternalAction IActionSerializer.Read(BinaryArchiveReader Reader) => Read(Reader);
/// <inheritdoc/>
void IActionSerializer.Write(BinaryArchiveWriter Writer, IExternalAction Action) => Write(Writer, (TAction)Action);
/// <summary>
/// Read the action from an archive
/// </summary>
/// <param name="Reader">Reader for the action</param>
/// <returns>New action</returns>
public abstract TAction Read(BinaryArchiveReader Reader);
/// <summary>
/// Writes an action to an archive
/// </summary>
/// <param name="Writer">Writer for the archive</param>
/// <param name="Action">The action to write</param>
public abstract void Write(BinaryArchiveWriter Writer, TAction Action);
}
/// <summary>
/// Helper methods for registering serializers and serializing actions
/// </summary>
static class ActionSerialization
{
/// <summary>
/// Map from type name to deserializing constructor
/// </summary>
static IReadOnlyDictionary<Type, IActionSerializer> TypeToSerializer;
/// <summary>
/// Map from serializer name to instance
/// </summary>
static IReadOnlyDictionary<string, IActionSerializer> NameToSerializer;
/// <summary>
/// Creates a map of type name to constructor
/// </summary>
/// <returns></returns>
static ActionSerialization()
{
Dictionary<Type, IActionSerializer> TypeToSerializerDict = new Dictionary<Type, IActionSerializer>();
Dictionary<string, IActionSerializer> NameToSerializerDict = new Dictionary<string, IActionSerializer>(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;
}
/// <summary>
/// Read an action from the given archive
/// </summary>
/// <param name="Reader">Reader to deserialize from</param>
/// <returns>New action</returns>
public static IExternalAction ReadAction(this BinaryArchiveReader Reader)
{
IActionSerializer Serializer = Reader.ReadObjectReference(() => ReadSerializer(Reader))!;
return Serializer.Read(Reader);
}
/// <summary>
/// Reads a type name and find its registered constructor from an archive
/// </summary>
/// <param name="Reader">Archive to read from</param>
/// <returns>New constructor info</returns>
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;
}
/// <summary>
/// Writes an action to the given archive
/// </summary>
/// <param name="Writer">Writer to serialize the action to</param>
/// <param name="Action">Action to serialize</param>
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);
}
}
}