File: BackEnd\ITranslator.cs
Web Access
Project: ..\..\..\src\MSBuildTaskHost\MSBuildTaskHost.csproj (MSBuildTaskHost)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
 
namespace Microsoft.Build.TaskHost.BackEnd;
 
/// <summary>
/// This delegate is used for objects which do not have public parameterless constructors and must be constructed using
/// another method.  When invoked, this delegate should return a new object which has been translated appropriately.
/// </summary>
/// <typeparam name="T">The type to be translated.</typeparam>
internal delegate T NodePacketValueFactory<T>(ITranslator translator);
 
/// <summary>
/// Delegate for users that want to translate an arbitrary structure that doesn't implement <see cref="ITranslatable"/> (e.g. translating a complex collection)
/// </summary>
/// <param name="translator">the translator</param>
/// <param name="objectToTranslate">the object to translate</param>
internal delegate void ObjectTranslator<T>(ITranslator translator, ref T objectToTranslate);
 
/// <summary>
/// Delegate for users that want to translate an arbitrary structure that doesn't implement <see cref="ITranslatable"/> (e.g. translating a complex collection)
/// </summary>
/// <param name="translator">the translator</param>
/// <param name="valueFactory">The factory to use to create the value.</param>
/// <param name="objectToTranslate">the object to translate</param>
internal delegate void ObjectTranslatorWithValueFactory<T>(ITranslator translator, NodePacketValueFactory<T> valueFactory, ref T objectToTranslate);
 
/// <summary>
/// This delegate is used to create arbitrary collection types for serialization.
/// </summary>
/// <typeparam name="T">The type of dictionary to be created.</typeparam>
internal delegate T NodePacketCollectionCreator<T>(int capacity);
 
/// <summary>
/// The serialization mode.
/// </summary>
internal enum TranslationDirection
{
    /// <summary>
    /// Indicates the serializer is operating in write mode.
    /// </summary>
    WriteToStream,
 
    /// <summary>
    /// Indicates the serializer is operating in read mode.
    /// </summary>
    ReadFromStream
}
 
/// <summary>
/// This interface represents an object which aids objects in serializing and
/// deserializing INodePackets.
/// </summary>
/// <remarks>
/// The reason we bother with a custom serialization mechanism at all is two fold:
/// 1. The .Net serialization mechanism is inefficient, even if you implement ISerializable
///    with your own custom mechanism.  This is because the serializer uses a bag called
///    SerializationInfo into which you are expected to drop all your data.  This adds
///    an unnecessary level of indirection to the serialization routines and prevents direct,
///    efficient access to the byte-stream.
/// 2. You have to implement both a reader and writer part, which introduces the potential for
///    error should the classes be later modified.  If the reader and writer methods are not
///    kept in perfect sync, serialization errors will occur.  Our custom serializer eliminates
///    that by ensuring a single Translate method on a given object can handle both reads and
///    writes without referencing any field more than once.
/// </remarks>
internal interface ITranslator : IDisposable
{
    /// <summary>
    /// Gets or sets the negotiated packet version between the communicating nodes.
    /// This represents the minimum packet version supported by both the sender and receiver,
    /// ensuring backward compatibility during cross-version communication.
    /// </summary>
    /// <remarks>
    /// This version is determined during the initial handshake between nodes and may differ
    /// from NodePacketTypeExtensions.PacketVersion when nodes are running different MSBuild versions.
    /// The negotiated version is used to conditionally serialize/deserialize fields that may
    /// not be supported by older packet versions.
    /// </remarks>
    byte NegotiatedPacketVersion { get; set; }
 
    /// <summary>
    /// Gets the current serialization mode.
    /// </summary>
    TranslationDirection Mode { get; }
 
    /// <summary>
    /// Gets the binary reader.
    /// </summary>
    /// <remarks>
    /// This should ONLY be used when absolutely necessary for translation.  It is generally unnecessary for the
    /// translating object to know the direction of translation.  Use one of the Translate methods instead.
    /// </remarks>
    BinaryReader Reader { get; }
 
    /// <summary>
    /// Gets the binary writer.
    /// </summary>
    /// <remarks>
    /// This should ONLY be used when absolutely necessary for translation.  It is generally unnecessary for the
    /// translating object to know the direction of translation.  Use one of the Translate methods instead.
    /// </remarks>
    BinaryWriter Writer { get; }
 
    /// <summary>
    /// Translates a boolean.
    /// </summary>
    /// <param name="value">The value to be translated.</param>
    void Translate(ref bool value);
 
    /// <summary>
    /// Translates an <see langword="bool"/> array.
    /// </summary>
    /// <param name="array">The array to be translated.</param>
    void Translate(ref bool[]? array);
 
    /// <summary>
    /// Translates a byte.
    /// </summary>
    /// <param name="value">The value to be translated.</param>
    void Translate(ref byte value);
 
    /// <summary>
    /// Translates a short.
    /// </summary>
    /// <param name="value">The value to be translated.</param>
    void Translate(ref short value);
 
    /// <summary>
    /// Translates a unsigned short.
    /// </summary>
    /// <param name="value">The value to be translated.</param>
    void Translate(ref ushort value);
 
    /// <summary>
    /// Translates an integer.
    /// </summary>
    /// <param name="value">The value to be translated.</param>
    void Translate(ref int value);
 
    /// <summary>
    /// Translates an <see langword="int"/> array.
    /// </summary>
    /// <param name="array">The array to be translated.</param>
    void Translate(ref int[]? array);
 
    /// <summary>
    /// Translates a long.
    /// </summary>
    /// <param name="value">The value to be translated.</param>
    void Translate(ref long value);
 
    /// <summary>
    /// Translates a string.
    /// </summary>
    /// <param name="value">The value to be translated.</param>
    void Translate(ref string? value);
 
    /// <summary>
    /// Translates a double.
    /// </summary>
    /// <param name="value">The value to be translated.</param>
    void Translate(ref double value);
 
    /// <summary>
    /// Translates a string array.
    /// </summary>
    /// <param name="array">The array to be translated.</param>
    void Translate(ref string[]? array);
 
    /// <summary>
    /// Translates a collection of T into the specified type using an <see cref="ObjectTranslator{T}"/> and <see cref="NodePacketCollectionCreator{L}"/>.
    /// </summary>
    /// <param name="collection">The collection to be translated.</param>
    /// <param name="objectTranslator">The translator to use for the values in the collection.</param>
    /// <param name="collectionFactory">The factory to create the ICollection.</param>
    /// <typeparam name="T">The type contained in the collection.</typeparam>
    /// <typeparam name="L">The type of collection to be created.</typeparam>
    void Translate<T, L>(
        ref ICollection<T>? collection,
        ObjectTranslator<T> objectTranslator,
        NodePacketCollectionCreator<L> collectionFactory)
        where L : ICollection<T>;
 
    /// <summary>
    /// Translates a DateTime.
    /// </summary>
    /// <param name="value">The value to be translated.</param>
    void Translate(ref DateTime value);
 
    /// <summary>
    /// Translates an enumeration.
    /// </summary>
    /// <typeparam name="T">The enumeration type.</typeparam>
    /// <param name="value">The enumeration instance to be translated.</param>
    /// <param name="numericValue">The enumeration value as an integer.</param>
    /// <remarks>This is a bit ugly, but it doesn't seem like a nice method signature is possible because
    /// you can't pass the enum type as a reference and constrain the generic parameter to Enum.  Nor
    /// can you simply pass as ref Enum, because an enum instance doesn't match that function signature.
    /// Finally, converting the enum to an int assumes that we always want to transport enums as ints.  This
    /// works in all of our current cases, but certainly isn't perfectly generic.</remarks>
    void TranslateEnum<T>(ref T value, int numericValue)
        where T : struct, Enum;
 
    void TranslateException(ref Exception? value);
 
    /// <summary>
    /// Translates a culture.
    /// </summary>
    /// <param name="culture">The culture.</param>
    void TranslateCulture(ref CultureInfo? culture);
 
    /// <summary>
    /// Translates a byte array.
    /// </summary>
    /// <param name="byteArray">The array to be translated.</param>
    void Translate(ref byte[]? byteArray);
 
    /// <summary>
    /// Translates a dictionary of { string, string }.
    /// </summary>
    /// <param name="dictionary">The dictionary to be translated.</param>
    /// <param name="comparer">The comparer used to instantiate the dictionary.</param>
    void TranslateDictionary(ref Dictionary<string, string?>? dictionary, IEqualityComparer<string> comparer);
 
    /// <summary>
    /// Translates a dictionary of { string, T }.
    /// </summary>
    /// <typeparam name="T">The reference type for the values, which implements INodePacketTranslatable.</typeparam>
    /// <param name="dictionary">The dictionary to be translated.</param>
    /// <param name="comparer">The comparer used to instantiate the dictionary.</param>
    /// <param name="objectTranslator">The translator to use for the values in the dictionary.</param>
    /// /// <param name="valueFactory">The factory to use to create the value.</param>
    void TranslateDictionary<T>(
        ref Dictionary<string, T>? dictionary,
        IEqualityComparer<string> comparer,
        ObjectTranslatorWithValueFactory<T> objectTranslator,
        NodePacketValueFactory<T> valueFactory)
        where T : class;
 
    /// <summary>
    /// Translates the boolean that says whether this value is null or not.
    /// </summary>
    /// <param name="value">The object to test.</param>
    /// <typeparam name="T">The type of object to test.</typeparam>
    /// <returns>True if the object should be written, false otherwise.</returns>
    bool TranslateNullable<T>(T? value)
        where T : class;
}