113 lines
3.5 KiB
C#
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);
|
|
}
|