2023-02-02 22:48:27 -07:00

113 lines
3.5 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
namespace FileManager.NamingTemplate;
/// <summary>Represents one part of an evaluated <see cref="NamingTemplate"/>.</summary>
public class TemplatePart : IEnumerable<TemplatePart>
{
/// <summary>The <see cref="TemplatePart"/> name. If <see cref="TemplatePart"/> is
/// a registered property, this value is <see cref="ITemplateTag.TagName"/></summary>
public string TagName { get; }
/// <summary> The <see cref="IPropertyTag"/>'s <see cref="ITemplateTag"/> if <see cref="TemplatePart"/> is
/// a registered property, otherwise <see cref="null"/> for string literals. </summary>
public ITemplateTag TemplateTag { get; }
/// <summary>The evaluated string.</summary>
public string Value { get; set; }
private TemplatePart previous;
private TemplatePart next;
private TemplatePart(string name, string value)
{
TagName = name;
Value = value;
}
private TemplatePart(ITemplateTag templateTag, string value)
{
TemplateTag = templateTag;
TagName = templateTag.TagName;
Value = value;
}
internal static Expression Blank
=> CreateExpression("Blank", Expression.Constant(""));
internal static Expression CreateLiteral(string constant)
=> CreateExpression("Literal", Expression.Constant(constant));
internal static Expression CreateProperty(ITemplateTag templateTag, Expression property)
=> Expression.New(tagTemplateConstructorInfo, Expression.Constant(templateTag), property);
internal static Expression CreateConcatenation(Expression left, Expression right)
{
if (left.Type != typeof(TemplatePart) || right.Type != typeof(TemplatePart))
throw new InvalidOperationException($"Cannot concatenate expressions of types {left.Type.Name} and {right.Type.Name}");
return Expression.Add(left, right, addMethodInfo);
}
private static Expression CreateExpression(string name, Expression value)
=> Expression.New(constructorInfo, Expression.Constant(name), value);
private static readonly ConstructorInfo constructorInfo
= typeof(TemplatePart).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, new Type[] { typeof(string), typeof(string) });
private static readonly ConstructorInfo tagTemplateConstructorInfo
= typeof(TemplatePart).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, new Type[] { typeof(ITemplateTag), typeof(string) });
private static readonly MethodInfo addMethodInfo
= typeof(TemplatePart).GetMethod(nameof(Concatenate), BindingFlags.NonPublic | BindingFlags.Static, new Type[] { typeof(TemplatePart), typeof(TemplatePart) });
public IEnumerator<TemplatePart> GetEnumerator()
{
var firstPart = FirstPart;
do
{
if (firstPart.TemplateTag is not null || firstPart.TagName is not "Blank")
yield return firstPart;
firstPart = firstPart.next;
}
while (firstPart is not null);
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
internal TemplatePart FirstPart
{
get
{
var part = this;
while (part.previous is not null)
part = part.previous;
return part;
}
}
private TemplatePart LastPart
{
get
{
var part = this;
while (part.next is not null)
part = part.next;
return part;
}
}
private static TemplatePart Concatenate(TemplatePart left, TemplatePart right)
{
var last = left.LastPart;
last.next = right;
right.previous = last;
return left;
}
public static TemplatePart operator +(TemplatePart left, TemplatePart right)
=> Concatenate(left, right);
}