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