/****************************************************************************
**
** 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
{
///
/// Extended enum support:
/// * Customized cast of enum values to arbitrary types
///
public static class EnumExt
{
static LazyFactory StaticLazy { get; } = new LazyFactory();
///
/// Wrapper for enum cast values.
///
/// Type of cast output
///
/// Cast attributes associated with enum values must implement this interface.
///
public interface ICast
{
T Value { get; }
}
///
/// String cast attribute associated to an enum value.
///
///
/// enum Foobar {
/// Foo,
/// [EnumExt.String("Bahr")] Bar
/// }
///
[AttributeUsage(AttributeTargets.All)]
public sealed class StringAttribute : Attribute, ICast
{
public string Value { get; }
public StringAttribute(string str) { Value = str; }
}
///
/// Cast enum value to type T.
///
/// Cast output type.
/// Input enum value.
///
/// Value of type T associated to the enum value by an Attribute implementing
/// ICast. If no attribute is found, returns a default value.
///
///
/// enum Foobar
/// {
/// Foo,
/// [EnumExt.String("Bahr")] Bar
/// }
/// Foobar foo = Foobar.Foo;
/// Foobar bar = Foobar.Bar;
/// string fooCastString = foo.Cast(); // "Foo"
/// string barCastString = bar.Cast(); // "Bahr"
/// string fooToString = foo.ToString(); // "Foo"
/// string barToString = bar.ToString(); // "Bar"
///
public static T Cast(this Enum value)
{
if (FindCastAttrib(value) is ICast cast)
return cast.Value;
else
return Default(value);
}
///
/// Compare enum value with instance/value of type T.
///
/// Cast/comparison type.
/// Instance/value of type T to compare with.
/// Enum value to compare with.
/// true if cast of valueEnum is equal to valueT, false otherwise
public static bool EqualTo(this T valueT, Enum valueEnum)
{
return valueT.Equals(valueEnum.Cast());
}
///
/// Convert type T to enum
///
public static bool TryCast(this T valueT, out TEnum value) where TEnum : struct
{
value = default(TEnum);
IEnumerable enumValues = Enum.GetValues(typeof(TEnum)).OfType()
.Where((Enum valueEnum) => valueEnum.Cast().Equals(valueT));
if (enumValues.Any())
value = (TEnum)Enum.ToObject(typeof(TEnum), enumValues.FirstOrDefault());
return enumValues.Any();
}
///
/// Convert type T to enum
///
public static TEnum Cast(this T valueT, TEnum defaultValue) where TEnum : struct
{
return TryCast(valueT, out TEnum value) ? value : defaultValue;
}
///
/// Get list of values of enum type
///
public static IEnumerable GetValues() where TEnum : struct
{
Debug.Assert(typeof(TEnum).IsEnum);
return Enum.GetValues(typeof(TEnum)).OfType();
}
///
/// Get list of values of enum type converted to type T
///
public static IEnumerable GetValues(Type enumType)
{
return Enum.GetValues(enumType).OfType()
.Select((Enum value) => value.Cast());
}
///
/// Default cast of enum value to type T.
///
/// Cast output type.
/// Input enum value.
///
/// 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).
///
static T Default(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);
}
///
/// Find cast attribute.
///
/// Cast output type.
/// Input enum value.
///
/// First cast attribute of type T found associated with the enum value, or null in case a
/// suitable attribute is not found.
///
static ICast FindCastAttrib(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).IsAssignableFrom(type))
.Select(type => Attribute.GetCustomAttribute(enumField, type) as ICast)
.FirstOrDefault();
}
///
/// List of cast attribute types.
///
///
/// Future cast attribute types need to be added to this list.
///
static IEnumerable CastAttribTypes => StaticLazy.Get(() =>
CastAttribTypes, () => new[]
{
typeof(StringAttribute)
});
}
}