| /****************************************************************************  | 
| **  | 
| ** Copyright (C) 2020 The Qt Company Ltd.  | 
| ** Contact: https://www.qt.io/licensing/  | 
| **  | 
| ** This file is part of the Qt VS Tools.  | 
| **  | 
| ** $QT_BEGIN_LICENSE:GPL-EXCEPT$  | 
| ** Commercial License Usage  | 
| ** Licensees holding valid commercial Qt licenses may use this file in  | 
| ** accordance with the commercial license agreement provided with the  | 
| ** Software or, alternatively, in accordance with the terms contained in  | 
| ** a written agreement between you and The Qt Company. For licensing terms  | 
| ** and conditions see https://www.qt.io/terms-conditions. For further  | 
| ** information use the contact form at https://www.qt.io/contact-us.  | 
| **  | 
| ** GNU General Public License Usage  | 
| ** Alternatively, this file may be used under the terms of the GNU  | 
| ** General Public License version 3 as published by the Free Software  | 
| ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT  | 
| ** included in the packaging of this file. Please review the following  | 
| ** information to ensure the GNU General Public License requirements will  | 
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html.  | 
| **  | 
| ** $QT_END_LICENSE$  | 
| **  | 
| ****************************************************************************/  | 
|   | 
| using System;  | 
| using System.Collections.Generic;  | 
| using System.Diagnostics;  | 
| using System.Linq;  | 
| using System.Reflection;  | 
|   | 
| namespace QtVsTools.Common  | 
| {  | 
|     /// <summary>  | 
|     /// Extended enum support:  | 
|     ///   * Customized cast of enum values to arbitrary types  | 
|     /// </summary>  | 
|     public static class EnumExt  | 
|     {  | 
|         /// <summary>  | 
|         /// Wrapper for enum cast values.  | 
|         /// </summary>  | 
|         /// <typeparam name="T">Type of cast output</typeparam>  | 
|         /// <remarks>  | 
|         /// Cast attributes associated with enum values must implement this interface.  | 
|         /// </remarks>  | 
|         public interface ICast<T>  | 
|         {  | 
|             T Value { get; }  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// String cast attribute associated to an enum value.  | 
|         /// </summary>  | 
|         /// <example>  | 
|         ///     enum Foobar {  | 
|         ///         Foo,  | 
|         ///         [EnumExt.String("Bahr")] Bar  | 
|         ///     }  | 
|         /// </example>  | 
|         [AttributeUsage(AttributeTargets.All)]  | 
|         public sealed class StringAttribute : Attribute, ICast<string>  | 
|         {  | 
|             public string Value { get; private set; }  | 
|             public StringAttribute(string str) { Value = str; }  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// Cast enum value to type T.  | 
|         /// </summary>  | 
|         /// <typeparam name="T">Cast output type.</typeparam>  | 
|         /// <param name="value">Input enum value.</param>  | 
|         /// <returns>  | 
|         /// Value of type T associated to the enum value by an Attribute implementing  | 
|         /// ICast<typeparamref name="T"/>. If no attribute is found, returns a default value.  | 
|         /// </returns>  | 
|         /// <example>  | 
|         ///     enum Foobar  | 
|         ///     {  | 
|         ///         Foo,  | 
|         ///         [EnumExt.String("Bahr")] Bar  | 
|         ///     }  | 
|         ///     Foobar foo = Foobar.Foo;  | 
|         ///     Foobar bar = Foobar.Bar;  | 
|         ///     string fooCastString = foo.Cast<string>(); // "Foo"  | 
|         ///     string barCastString = bar.Cast<string>(); // "Bahr"  | 
|         ///     string fooToString = foo.ToString();       // "Foo"  | 
|         ///     string barToString = bar.ToString();       // "Bar"  | 
|         /// </example>  | 
|         public static T Cast<T>(this Enum value)  | 
|         {  | 
|             if (FindCastAttrib<T>(value) is ICast<T> cast)  | 
|                 return cast.Value;  | 
|             else  | 
|                 return Default<T>(value);  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// Compare enum value with instance/value of type T.  | 
|         /// </summary>  | 
|         /// <typeparam name="T">Cast/comparison type.</typeparam>  | 
|         /// <param name="valueT">Instance/value of type T to compare with.</param>  | 
|         /// <param name="valueEnum">Enum value to compare with.</param>  | 
|         /// <returns>true if cast of valueEnum is equal to valueT, false otherwise</returns>  | 
|         public static bool EqualTo<T>(this T valueT, Enum valueEnum)  | 
|         {  | 
|             return valueT.Equals(valueEnum.Cast<T>());  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// Convert type T to enum  | 
|         /// </summary>  | 
|         public static bool TryCast<T, TEnum>(this T valueT, out TEnum value) where TEnum : struct  | 
|         {  | 
|             value = default(TEnum);  | 
|             IEnumerable<Enum> enumValues = Enum.GetValues(typeof(TEnum)).OfType<Enum>()  | 
|                 .Where((Enum valueEnum) => valueEnum.Cast<T>().Equals(valueT));  | 
|             if (enumValues.Any())  | 
|                 value = (TEnum)Enum.ToObject(typeof(TEnum), enumValues.FirstOrDefault());  | 
|             return enumValues.Any();  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// Convert type T to enum  | 
|         /// </summary>  | 
|         public static TEnum Cast<T, TEnum>(this T valueT, TEnum defaultValue) where TEnum : struct  | 
|         {  | 
|             TEnum value;  | 
|             return TryCast(valueT, out value) ? value : defaultValue;  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// Get list of values of enum type  | 
|         /// </summary>  | 
|         public static IEnumerable<TEnum> GetValues<TEnum>() where TEnum : struct  | 
|         {  | 
|             Debug.Assert(typeof(TEnum).IsEnum);  | 
|             return Enum.GetValues(typeof(TEnum)).OfType<TEnum>();  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// Get list of values of enum type converted to type T  | 
|         /// </summary>  | 
|         public static IEnumerable<T> GetValues<T>(Type enumType)  | 
|         {  | 
|             return Enum.GetValues(enumType).OfType<Enum>()  | 
|                 .Select((Enum value) => value.Cast<T>());  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// Default cast of enum value to type T.  | 
|         /// </summary>  | 
|         /// <typeparam name="T">Cast output type.</typeparam>  | 
|         /// <param name="value">Input enum value.</param>  | 
|         /// <returns>  | 
|         /// Default value of type T associated with the enum value:  | 
|         ///   * if T is string: returns the enum value name as string;  | 
|         ///   * if T is an integer type: returns the underlying enum integer value;  | 
|         ///   * otherwise: default value for type T (e.g. null for reference types).  | 
|         /// </returns>  | 
|         static T Default<T>(Enum value)  | 
|         {  | 
|             Type enumType = value.GetType();  | 
|             Type baseType = Enum.GetUnderlyingType(enumType);  | 
|             Type outputType = typeof(T);  | 
|             if (outputType.IsAssignableFrom(enumType) || outputType.IsAssignableFrom(baseType))  | 
|                 return (T)(object)value;  | 
|             else if (outputType == typeof(string))  | 
|                 return (T)(object)Enum.GetName(value.GetType(), value);  | 
|             else  | 
|                 return default(T);  | 
|         }  | 
|   | 
|         /// <summary>  | 
|         /// Find cast attribute.  | 
|         /// </summary>  | 
|         /// <typeparam name="T">Cast output type.</typeparam>  | 
|         /// <param name="value">Input enum value.</param>  | 
|         /// <returns>  | 
|         /// First cast attribute of type T found associated with the enum value, or null in case a  | 
|         /// suitable attribute is not found.  | 
|         /// </returns>  | 
|         static ICast<T> FindCastAttrib<T>(Enum value)  | 
|         {  | 
|             Type enumType = value.GetType();  | 
|   | 
|             string valueName = Enum.GetName(enumType, value);  | 
|             if (string.IsNullOrEmpty(valueName))  | 
|                 return null;  | 
|   | 
|             FieldInfo enumField = enumType.GetField(valueName);  | 
|             if (enumField == null)  | 
|                 return null;  | 
|   | 
|             return CastAttribTypes  | 
|                 .Where(type => typeof(ICast<T>).IsAssignableFrom(type))  | 
|                 .Select(type => Attribute.GetCustomAttribute(enumField, type) as ICast<T>)  | 
|                 .FirstOrDefault();  | 
|         }  | 
|   | 
|         static IEnumerable<Type> _CastAttribTypes;  | 
|         /// <summary>  | 
|         /// List of cast attribute types.  | 
|         /// </summary>  | 
|         /// <remarks>  | 
|         /// Future cast attribute types need to be added to this list.  | 
|         /// </remarks>  | 
|         static IEnumerable<Type> CastAttribTypes => _CastAttribTypes  | 
|             ?? (_CastAttribTypes = new[]  | 
|             {  | 
|                 typeof(StringAttribute)  | 
|             });  | 
|   | 
|     }  | 
| }  |