Продолжаем работать с билдером
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
using System.ComponentModel;
|
||||
using Pf2e.Database.Model;
|
||||
using Pf2e.Database.Model.Base.Keywords;
|
||||
using Pf2e.Database.Model.Base.Source;
|
||||
using Pf2e.Database.Model.Base.Targets;
|
||||
using Pf2e.Database.Model.Common.I18n;
|
||||
using Pf2e.Database.Model.Enum;
|
||||
|
||||
namespace Pf2e.Web.Helpers.Format;
|
||||
|
||||
public static class Formatters
|
||||
{
|
||||
/// <summary>
|
||||
/// Форматтер для перечислений (enum).
|
||||
/// </summary>
|
||||
public static readonly ValueFormatter EnumFormatter = new CustomValueFormatter(
|
||||
(type) => type.IsEnum,
|
||||
(value) =>
|
||||
{
|
||||
Enum enumValue = (Enum)value;
|
||||
string description = GetEnumDescription(enumValue);
|
||||
return $"{description} ({enumValue})";
|
||||
|
||||
string GetEnumDescription(Enum v)
|
||||
{
|
||||
Type type = v.GetType();
|
||||
string name = Enum.GetName(type, v) ?? v.ToString();
|
||||
|
||||
System.Reflection.FieldInfo? field = type.GetField(name);
|
||||
if (field == null)
|
||||
return v.ToString();
|
||||
|
||||
if (
|
||||
Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute))
|
||||
is DescriptionAttribute attr
|
||||
)
|
||||
{
|
||||
return attr.Description;
|
||||
}
|
||||
|
||||
return v.ToString();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Форматтер для примитивных типов и строк.
|
||||
/// </summary>
|
||||
public static readonly ValueFormatter PrimitiveFormatter = new CustomValueFormatter(
|
||||
(type) => type.IsPrimitive || type == typeof(string) || type == typeof(decimal),
|
||||
(value) => value.ToString() ?? "null"
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Форматтер для коллекций.
|
||||
/// </summary>
|
||||
public static readonly ValueFormatter CollectionFormatter = new CollectionFormatter();
|
||||
|
||||
/// <summary>
|
||||
/// Форматтеры для стандартных типов из базы данных.
|
||||
/// </summary>
|
||||
public static readonly List<ValueFormatter> DefaultTypeFormatters =
|
||||
[
|
||||
new TypeValueFormatter<LocaleString>(
|
||||
(value) =>
|
||||
{
|
||||
LocaleString localeString = (LocaleString)value;
|
||||
List<string> items = [];
|
||||
foreach (LocaleItem item in localeString)
|
||||
{
|
||||
items.Add($"{item.Locale}: {item.Value}");
|
||||
}
|
||||
return string.Join("<br/>", items);
|
||||
}
|
||||
),
|
||||
// IEntity
|
||||
new CustomValueFormatter(
|
||||
(type) => typeof(IEntity).IsAssignableFrom(type),
|
||||
(value) =>
|
||||
{
|
||||
IEntity entity = (IEntity)value;
|
||||
EntityType entityType = entity.Entity.Type;
|
||||
string typeName = PgNameHelper.GetPgName(entityType);
|
||||
string slug = entity.Entity.Slug;
|
||||
string name = entity.Entity.Name[LocaleType.English];
|
||||
return $"<a href=\"/{typeName}/{slug}\">{name}</a>";
|
||||
}
|
||||
),
|
||||
// EntitySource
|
||||
new TypeValueFormatter<EntitySource>(
|
||||
(value) =>
|
||||
{
|
||||
EntitySource entitySource = (EntitySource)value;
|
||||
string sourceName = entitySource.Source.Name;
|
||||
string suffix = !string.IsNullOrEmpty(entitySource.Suffix)
|
||||
? $" ({entitySource.Suffix})"
|
||||
: "";
|
||||
string isRemaster = entitySource.IsRemaster ? " [Remaster]" : "";
|
||||
return $"{sourceName}{suffix}{isRemaster}";
|
||||
}
|
||||
),
|
||||
// Keyword
|
||||
new TypeValueFormatter<Keyword>(
|
||||
(value) =>
|
||||
{
|
||||
Keyword keyword = (Keyword)value;
|
||||
string name = keyword.Name[LocaleType.English];
|
||||
return $"{name}";
|
||||
}
|
||||
),
|
||||
// KeywordAlias
|
||||
new TypeValueFormatter<KeywordAlias>(
|
||||
(value) =>
|
||||
{
|
||||
KeywordAlias alias = (KeywordAlias)value;
|
||||
return $"{alias.Value}";
|
||||
}
|
||||
),
|
||||
// ExternalTarget
|
||||
new TypeValueFormatter<ExternalTarget>(
|
||||
(value) =>
|
||||
{
|
||||
ExternalTarget target = (ExternalTarget)value;
|
||||
return $"{target.Name} (Icon: {target.Icon}, Prefix: {target.Prefix})";
|
||||
}
|
||||
),
|
||||
// EntityExternal
|
||||
new TypeValueFormatter<EntityExternal>(
|
||||
(value) =>
|
||||
{
|
||||
EntityExternal entityExternal = (EntityExternal)value;
|
||||
string targetName = entityExternal.Target?.Name ?? "Unknown";
|
||||
string externalId = !string.IsNullOrEmpty(entityExternal.ExternalId)
|
||||
? $" ID: {entityExternal.ExternalId}"
|
||||
: "";
|
||||
string suffix = !string.IsNullOrEmpty(entityExternal.Suffix)
|
||||
? $" Suffix: {entityExternal.Suffix}"
|
||||
: "";
|
||||
return $"{targetName}{externalId}{suffix}";
|
||||
}
|
||||
),
|
||||
// Source
|
||||
new TypeValueFormatter<Source>(
|
||||
(value) =>
|
||||
{
|
||||
Source source = (Source)value;
|
||||
string releaseDate = source.ReleaseDate?.ToString("yyyy-MM-dd") ?? "Unknown";
|
||||
return $"{source.Name} (Category: {source.Category}, Release: {releaseDate})";
|
||||
}
|
||||
),
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Набор всех стандартных форматтеров.
|
||||
/// </summary>
|
||||
public static readonly List<ValueFormatter> AllFormatters =
|
||||
[
|
||||
.. DefaultTypeFormatters,
|
||||
EnumFormatter,
|
||||
PrimitiveFormatter,
|
||||
CollectionFormatter,
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Добавить пользовательский форматтер в набор всех форматтеров.
|
||||
/// </summary>
|
||||
/// <param name="formatter">Добавляемый форматтер.</param>
|
||||
public static void AddFormatter(ValueFormatter formatter)
|
||||
{
|
||||
AllFormatters.Insert(0, formatter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Выбрать подходящий форматтер для типа.
|
||||
/// </summary>
|
||||
/// <param name="type">Тип значения.</param>
|
||||
/// <returns>Форматтер для типа или <c>null</c>, если подходящего не найдено.</returns>
|
||||
public static ValueFormatter? SelectFormatter(Type type) =>
|
||||
AllFormatters.FirstOrDefault(f => f.CanFormat(type)) ?? null;
|
||||
|
||||
/// <summary>
|
||||
/// Выбрать подходящий форматтер для значения.
|
||||
/// </summary>
|
||||
/// <param name="value">Значение.</param>
|
||||
/// <returns>Форматтер для значения или <c>null</c>, если значение равно <c>null</c> или подходящего не найдено.</returns>
|
||||
public static ValueFormatter? SelectFormatter(object? value) =>
|
||||
value == null ? null : SelectFormatter(value.GetType());
|
||||
|
||||
/// <summary>
|
||||
/// Форматировать значение в строку.
|
||||
/// </summary>
|
||||
/// <param name="value">Форматируемое значение.</param>
|
||||
/// <returns>Строковое представление отформатированного значения.</returns>
|
||||
public static string Format(object? value) =>
|
||||
SelectFormatter(value)?.Format(value!) ?? "[not implemented]";
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
using System.Collections;
|
||||
using static LinqToDB.Reflection.Methods.LinqToDB.Insert;
|
||||
|
||||
namespace Pf2e.Web.Helpers.Format;
|
||||
|
||||
/// <summary>
|
||||
/// Абстрактный класс форматтера значений.
|
||||
/// </summary>
|
||||
public abstract class ValueFormatter
|
||||
{
|
||||
/// <summary>
|
||||
/// Проверяет, может ли форматтер обработать указанный тип.
|
||||
/// </summary>
|
||||
/// <param name="type">Тип значения.</param>
|
||||
/// <returns><c>true</c>, если форматтер может обработать тип, иначе <c>false</c>.</returns>
|
||||
public abstract bool CanFormat(Type type);
|
||||
|
||||
/// <summary>
|
||||
/// Форматирует значение для отображения.
|
||||
/// </summary>
|
||||
/// <param name="value">Значение для форматирования.</param>
|
||||
/// <returns>Строковое представление отформатированного значения.</returns>
|
||||
public abstract string Format(object value);
|
||||
|
||||
/// <summary>
|
||||
/// Форматирует имя типа для отображения.
|
||||
/// </summary>
|
||||
/// <param name="type">Тип значения.</param>
|
||||
/// <returns>Строковое представление имени типа.</returns>
|
||||
public virtual string TypeName(Type type) => type.Name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Форматтер для конкретного типа.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Тип значения.</typeparam>
|
||||
/// <param name="formatter">Функция форматирования значения.</param>
|
||||
public class TypeValueFormatter<T>(Func<object, string> formatter) : ValueFormatter
|
||||
{
|
||||
public override bool CanFormat(Type type) => type == typeof(T);
|
||||
|
||||
public override string Format(object value) => formatter(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Форматтер с пользовательскими функциями проверки и форматирования.
|
||||
/// </summary>
|
||||
/// <param name="checker">Функция проверки типа.</param>
|
||||
/// <param name="formatter">Функция форматирования значения.</param>
|
||||
public class CustomValueFormatter(
|
||||
Func<Type, bool> checker,
|
||||
Func<object, string> formatter,
|
||||
Func<Type, string>? typeName = null
|
||||
) : ValueFormatter
|
||||
{
|
||||
public override bool CanFormat(Type type) => checker(type);
|
||||
|
||||
public override string Format(object value) => formatter(value);
|
||||
|
||||
public override string TypeName(Type type) => typeName?.Invoke(type) ?? base.TypeName(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Форматтер для коллекций.
|
||||
/// </summary>
|
||||
public class CollectionFormatter() : ValueFormatter
|
||||
{
|
||||
public override bool CanFormat(Type type) =>
|
||||
typeof(IEnumerable).IsAssignableFrom(type) && type != typeof(string);
|
||||
|
||||
public override string Format(object value)
|
||||
{
|
||||
IEnumerable collection = (IEnumerable)value;
|
||||
List<string> items = [];
|
||||
|
||||
foreach (object? item in collection)
|
||||
{
|
||||
items.Add(Formatters.Format(item));
|
||||
}
|
||||
|
||||
return items.Count == 0 ? "(empty)" : string.Join("<br/>", items);
|
||||
}
|
||||
|
||||
public override string TypeName(Type type)
|
||||
{
|
||||
return $"{type.Name[..^2]}<{string.Join(", ", type.GetGenericArguments().Select(t => t.Name))}>";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user