File: 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;
using Microsoft.Build.Framework;
 
#nullable disable
 
namespace Microsoft.Build.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>
    /// 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>
        /// Returns the current serialization mode.
        /// </summary>
        TranslationDirection Mode
        {
            get;
        }
 
        /// <summary>
        /// Returns 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>
        /// Returns 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 unsigned integer.
        /// </summary>
        /// <param name="unsignedInteger">The unsigned integer to translate.</param>
        void Translate(ref uint unsignedInteger);
 
        /// <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 list of strings
        /// </summary>
        /// <param name="list">The list to be translated.</param>
        void Translate(ref List<string> list);
 
        /// <summary>
        /// Translates a set of strings
        /// </summary>
        /// <param name="set">The set to be translated.</param>
        void Translate(ref HashSet<string> set);
 
        /// <summary>
        /// Translates a list of T using an <see cref="ObjectTranslator{T}"/>
        /// </summary>
        /// <param name="list">The list to be translated.</param>
        /// <param name="objectTranslator">The translator to use for the items in the list</param>
        /// <typeparam name="T">A TaskItemType</typeparam>
        void Translate<T>(ref List<T> list, ObjectTranslator<T> objectTranslator);
 
        /// <summary>
        /// Translates a list of T using an <see cref="ObjectTranslator{T}"/> anda collection factory
        /// </summary>
        /// <param name="list">The list to be translated.</param>
        /// <param name="objectTranslator">The translator to use for the items in the list</param>
        /// <typeparam name="T">An ITranslatable subtype</typeparam>
        /// <typeparam name="L">An IList subtype</typeparam>
        /// <param name="collectionFactory">factory to create a collection</param>
        void Translate<T, L>(ref IList<T> list, ObjectTranslator<T> objectTranslator, NodePacketCollectionCreator<L> collectionFactory) where L : IList<T>;
 
        /// <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 a TimeSpan.
        /// </summary>
        /// <param name="value">The value to be translated.</param>
        void Translate(ref TimeSpan value);
 
        // MSBuildTaskHost is based on CLR 3.5, which does not have the 6-parameter constructor for BuildEventContext,
        // which is what current implementations of this method use.  However, it also does not ever need to translate
        // BuildEventContexts, so it should be perfectly safe to compile this method out of that assembly. I am compiling
        // the method out of the interface as well, instead of just making the method empty, so that if we ever do need
        // to translate BuildEventContexts from the CLR 3.5 task host, it will become immediately obvious, rather than
        // failing or misbehaving silently.
#if !CLR2COMPATIBILITY

        /// <summary>
        /// Translates a BuildEventContext
        /// </summary>
        /// <remarks>
        /// This method exists only because there is no serialization method built into the BuildEventContext
        /// class, and it lives in Framework and we don't want to add a public method to it.
        /// </remarks>
        /// <param name="value">The context to be translated.</param>
        void Translate(ref BuildEventContext value);
#endif
 
        /// <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;
 
        /// <summary>
        /// Translates a value using the .Net binary formatter.
        /// </summary>
        /// <typeparam name="T">The reference type.</typeparam>
        /// <param name="value">The value to be translated.</param>
        /// <remarks>
        /// The primary purpose of this method is to support serialization of Exceptions and
        /// custom build logging events, since these do not support our custom serialization
        /// methods.
        /// </remarks>
        void TranslateDotNet<T>(ref T value);
 
        void TranslateException(ref Exception value);
 
        /// <summary>
        /// Translates an object implementing INodePacketTranslatable.
        /// </summary>
        /// <typeparam name="T">The reference type.</typeparam>
        /// <param name="value">The value to be translated.</param>
        void Translate<T>(ref T value)
            where T : ITranslatable, new();
 
        /// <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 byte array
        /// </summary>
        /// <param name="byteArray">The array to be translated.</param>
        /// <param name="length">The length of array which will be used in translation</param>
        void Translate(ref byte[] byteArray, ref int length);
 
        /// <summary>
        /// Translates an array of objects implementing INodePacketTranslatable.
        /// </summary>
        /// <typeparam name="T">The reference type.</typeparam>
        /// <param name="array">The array to be translated.</param>
        void TranslateArray<T>(ref T[] array)
            where T : ITranslatable, new();
 
        /// <summary>
        /// Translates an array of objects using an <see cref="ObjectTranslator{T}"/>.
        /// </summary>
        /// <typeparam name="T">The reference type.</typeparam>
        /// <param name="array">The array to be translated.</param>
        /// <param name="objectTranslator">The translator to use for the elements in the array.</param>
        void TranslateArray<T>(ref T[] array, ObjectTranslator<T> objectTranslator);
 
        /// <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, string } adding additional entries.
        /// </summary>
        /// <param name="dictionary">The dictionary to be translated.</param>
        /// <param name="comparer">The comparer used to instantiate the dictionary.</param>
        /// <param name="additionalEntries">Additional entries to be translated</param>
        /// <param name="additionalEntriesKeys">Additional entries keys</param>
        /// <remarks>
        /// This overload is needed for a workaround concerning serializing BuildResult with a version.
        /// It serializes/deserializes additional entries together with the main dictionary.
        /// </remarks>
        void TranslateDictionary(ref Dictionary<string, string> dictionary, IEqualityComparer<string> comparer, ref Dictionary<string, string> additionalEntries, HashSet<string> additionalEntriesKeys);
 
        void TranslateDictionary(ref IDictionary<string, string> dictionary, NodePacketCollectionCreator<IDictionary<string, string>> collectionCreator);
 
        void TranslateDictionary(ref Dictionary<string, DateTime> dictionary, StringComparer comparer);
 
        void TranslateDictionary<K, V>(ref IDictionary<K, V> dictionary, ObjectTranslator<K> keyTranslator, ObjectTranslator<V> valueTranslator, NodePacketCollectionCreator<IDictionary<K, V>> dictionaryCreator);
 
        /// <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>
        void TranslateDictionary<T>(ref Dictionary<string, T> dictionary, IEqualityComparer<string> comparer, ObjectTranslator<T> objectTranslator)
            where T : class;
 
        /// <summary>
        /// Translates a dictionary of { string, T } for dictionaries with public parameterless constructors.
        /// </summary>
        /// <typeparam name="D">The reference type for the dictionary.</typeparam>
        /// <typeparam name="T">The reference type for values in the dictionary.</typeparam>
        /// <param name="dictionary">The dictionary to be translated.</param>
        /// <param name="objectTranslator">The translator to use for the values in the dictionary.</param>
        void TranslateDictionary<D, T>(ref D dictionary, ObjectTranslator<T> objectTranslator)
            where D : IDictionary<string, T>, new()
            where T : class;
 
        /// <summary>
        /// Translates a dictionary of { string, T } for dictionaries with public parameterless constructors.
        /// </summary>
        /// <typeparam name="D">The reference type for the dictionary.</typeparam>
        /// <typeparam name="T">The reference type for values in the dictionary.</typeparam>
        /// <param name="dictionary">The dictionary to be translated.</param>
        /// <param name="objectTranslator">The translator to use for the values in the dictionary</param>
        /// <param name="collectionCreator">A factory used to create the dictionary.</param>
        void TranslateDictionary<D, T>(ref D dictionary, ObjectTranslator<T> objectTranslator, NodePacketCollectionCreator<D> collectionCreator)
            where D : IDictionary<string, T>
            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);
    }
}