Merge pull request #473 from Mbucari/master
Made changes discussed in previous PR and fixed #472
This commit is contained in:
commit
85769d797b
@ -23,7 +23,7 @@ These tags will be replaced in the template with the audiobook's values.
|
||||
|
||||
|Tag|Description|Type|
|
||||
|-|-|-|
|
||||
|\<id\>|Audible book ID (ASIN)|Text|
|
||||
|\<id\> **†**|Audible book ID (ASIN)|Text|
|
||||
|\<title\>|Full title|Text|
|
||||
|\<title short\>|Title. Stop at first colon|Text|
|
||||
|\<author\>|Author(s)|Text|
|
||||
@ -39,16 +39,18 @@ These tags will be replaced in the template with the audiobook's values.
|
||||
|\<locale\>|Region/country|Text|
|
||||
|\<year\>|Year published|Integer|
|
||||
|\<language\>|Book's language|Text|
|
||||
|\<language short\>|Book's language abbreviated. Eg: ENG|Text|
|
||||
|\<language short\> **†**|Book's language abbreviated. Eg: ENG|Text|
|
||||
|\<file date\>|File creation date/time.|DateTime|
|
||||
|\<pub date\>|Audiobook publication date|DateTime|
|
||||
|\<date added\>|Date the book added to your Audible account|DateTime|
|
||||
|\<ch count\>|Number of chapters **†**|Integer|
|
||||
|\<ch title\>|Chapter title **†**|Text|
|
||||
|\<ch#\>|Chapter number **†**|Integer|
|
||||
|\<ch# 0\>|Chapter number with leading zeros **†**|Integer|
|
||||
|\<ch count\> **‡**|Number of chapters|Integer|
|
||||
|\<ch title\> **‡**|Chapter title|Text|
|
||||
|\<ch#\> **‡**|Chapter number|Integer|
|
||||
|\<ch# 0\> **‡**|Chapter number with leading zeros|Integer|
|
||||
|
||||
**†** Only valid for Chapter Filename and Chapter Tile Metadata
|
||||
**†** Does not support custom formatting
|
||||
|
||||
**‡** Only valid for Chapter Filename and Chapter Tile Metadata
|
||||
|
||||
To change how these properties are displayed, [read about custom formatters](#tag-formatters)
|
||||
|
||||
|
||||
@ -20,17 +20,21 @@ internal interface IClosingPropertyTag : IPropertyTag
|
||||
bool StartsWithClosing(string templateString, out string exactName, out IClosingPropertyTag propertyTag);
|
||||
}
|
||||
|
||||
public class ConditionalTagClass<TClass> : TagClass
|
||||
public class ConditionalTagCollection<TClass> : TagCollection
|
||||
{
|
||||
public ConditionalTagClass(bool caseSensative = true) :base(typeof(TClass), caseSensative) { }
|
||||
public ConditionalTagCollection(bool caseSensative = true) :base(typeof(TClass), caseSensative) { }
|
||||
|
||||
public void RegisterCondition(ITemplateTag templateTag, Func<TClass, bool> propertyGetter)
|
||||
/// <summary>
|
||||
/// Register a conditional tag.
|
||||
/// </summary>
|
||||
/// <param name="propertyGetter">A Func to get the condition's <see cref="bool"/> value from <see cref="TClass"/></param>
|
||||
public void Add(ITemplateTag templateTag, Func<TClass, bool> propertyGetter)
|
||||
{
|
||||
var expr = Expression.Call(Expression.Constant(propertyGetter.Target), propertyGetter.Method, Parameter);
|
||||
|
||||
AddPropertyTag(new ConditionalTag(templateTag, Options, expr));
|
||||
}
|
||||
|
||||
|
||||
private class ConditionalTag : TagBase, IClosingPropertyTag
|
||||
{
|
||||
public Regex NameCloseMatcher { get; }
|
||||
@ -51,14 +55,12 @@ public class ConditionalTagClass<TClass> : TagClass
|
||||
propertyTag = this;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
exactName = null;
|
||||
propertyTag = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
exactName = null;
|
||||
propertyTag = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
protected override Expression GetTagExpression(string exactName, string formatter) => formatter == "!" ? Expression.Not(ExpressionValue) : ExpressionValue;
|
||||
protected override Expression GetTagExpression(string exactName, string formatter) => formatter == "!" ? Expression.Not(ValueExpression) : ValueExpression;
|
||||
}
|
||||
}
|
||||
@ -9,14 +9,14 @@ public class NamingTemplate
|
||||
{
|
||||
public string TemplateText { get; private set; }
|
||||
public IEnumerable<ITemplateTag> TagsInUse => _tagsInUse;
|
||||
public IEnumerable<ITemplateTag> TagsRegistered => Classes.SelectMany(p => p.TemplateTags).DistinctBy(f => f.TagName);
|
||||
public IEnumerable<ITemplateTag> TagsRegistered => Classes.SelectMany(t => t).DistinctBy(t => t.TagName);
|
||||
public IEnumerable<string> Warnings => errors.Concat(warnings);
|
||||
public IEnumerable<string> Errors => errors;
|
||||
|
||||
private Delegate templateToString;
|
||||
private readonly List<string> warnings = new();
|
||||
private readonly List<string> errors = new();
|
||||
private readonly IEnumerable<TagClass> Classes;
|
||||
private readonly IEnumerable<TagCollection> Classes;
|
||||
private readonly List<ITemplateTag> _tagsInUse = new();
|
||||
|
||||
public const string ERROR_NULL_IS_INVALID = "Null template is invalid.";
|
||||
@ -27,7 +27,7 @@ public class NamingTemplate
|
||||
/// <summary>
|
||||
/// Invoke the <see cref="NamingTemplate"/> to
|
||||
/// </summary>
|
||||
/// <param name="propertyClasses">Instances of the TClass used in <see cref="PropertyTagClass{TClass}"/> and <see cref="ConditionalTagClass{TClass}"/></param>
|
||||
/// <param name="propertyClasses">Instances of the TClass used in <see cref="PropertyTagCollection{TClass}"/> and <see cref="ConditionalTagCollection{TClass}"/></param>
|
||||
/// <returns></returns>
|
||||
public TemplatePart Evaluate(params object[] propertyClasses)
|
||||
{
|
||||
@ -47,9 +47,9 @@ public class NamingTemplate
|
||||
|
||||
/// <summary>Parse a template string to a <see cref="NamingTemplate"/></summary>
|
||||
/// <param name="template">The template string to parse</param>
|
||||
/// <param name="tagClasses">A collection of <see cref="TagClass"/> with
|
||||
/// <param name="tagClasses">A collection of <see cref="TagCollection"/> with
|
||||
/// properties registered to match to the <paramref name="template"/></param>
|
||||
public static NamingTemplate Parse(string template, IEnumerable<TagClass> tagClasses)
|
||||
public static NamingTemplate Parse(string template, IEnumerable<TagCollection> tagClasses)
|
||||
{
|
||||
var namingTemplate = new NamingTemplate(tagClasses);
|
||||
try
|
||||
@ -71,7 +71,7 @@ public class NamingTemplate
|
||||
return namingTemplate;
|
||||
}
|
||||
|
||||
private NamingTemplate(IEnumerable<TagClass> properties)
|
||||
private NamingTemplate(IEnumerable<TagCollection> properties)
|
||||
{
|
||||
Classes = properties;
|
||||
}
|
||||
@ -183,6 +183,7 @@ public class NamingTemplate
|
||||
if (pc.StartsWith(template, out exactName, out propertyTag, out valueExpression))
|
||||
return true;
|
||||
}
|
||||
|
||||
exactName = null;
|
||||
valueExpression = null;
|
||||
propertyTag = null;
|
||||
@ -196,6 +197,7 @@ public class NamingTemplate
|
||||
if (pc.StartsWithClosing(template, out exactName, out closingPropertyTag))
|
||||
return true;
|
||||
}
|
||||
|
||||
exactName = null;
|
||||
closingPropertyTag = null;
|
||||
return false;
|
||||
|
||||
@ -1,88 +0,0 @@
|
||||
using System;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FileManager.NamingTemplate;
|
||||
|
||||
public delegate string PropertyFormatter<T>(ITemplateTag templateTag, T value, string formatString);
|
||||
|
||||
public class PropertyTagClass<TClass> : TagClass
|
||||
{
|
||||
public PropertyTagClass(bool caseSensative = true) : base(typeof(TClass), caseSensative) { }
|
||||
|
||||
/// <summary>
|
||||
/// Register a nullable value type property.
|
||||
/// </summary>
|
||||
/// <typeparam name="U">Type of the property from <see cref="TClass"/></typeparam>
|
||||
/// <param name="propertyGetter">A Func to get the property value from <see cref="TClass"/></param>
|
||||
/// <param name="formatter">Optional formatting function that accepts the <typeparamref name="U"/> property and a formatting string and returnes the value formatted to string</param>
|
||||
public void RegisterProperty<U>(ITemplateTag templateTag, Func<TClass, U?> propertyGetter, PropertyFormatter<U> formatter = null)
|
||||
where U : struct
|
||||
=> RegisterPropertyInternal(templateTag, propertyGetter, formatter);
|
||||
|
||||
/// <summary>
|
||||
/// Register a non-nullable value type property
|
||||
/// </summary>
|
||||
/// <typeparam name="U">Type of the property from <see cref="TClass"/></typeparam>
|
||||
/// <param name="propertyGetter">A Func to get the property value from <see cref="TClass"/></param>
|
||||
/// <param name="formatter">Optional formatting function that accepts the <typeparamref name="U"/> property and a formatting string and returnes the value formatted to string</param>
|
||||
public void RegisterProperty<U>(ITemplateTag templateTag, Func<TClass, U> propertyGetter, PropertyFormatter<U> formatter = null)
|
||||
where U : struct
|
||||
=> RegisterPropertyInternal(templateTag, propertyGetter, formatter);
|
||||
|
||||
/// <summary>
|
||||
/// Register a string type property.
|
||||
/// </summary>
|
||||
/// <param name="propertyGetter">A Func to get the string property from <see cref="TClass"/></param>
|
||||
/// <param name="formatter">Optional formatting function that accepts the string property and a formatting string and returnes the value formatted to string</param>
|
||||
public void RegisterProperty(ITemplateTag templateTag, Func<TClass, string> propertyGetter, PropertyFormatter<string> formatter = null)
|
||||
=> RegisterPropertyInternal(templateTag, propertyGetter, formatter);
|
||||
|
||||
private void RegisterPropertyInternal(ITemplateTag templateTag, Delegate propertyGetter, Delegate formatter)
|
||||
{
|
||||
if (formatter?.Target is not null)
|
||||
throw new ArgumentException($"{nameof(formatter)} must be a static method");
|
||||
|
||||
var expr = Expression.Call(Expression.Constant(propertyGetter.Target), propertyGetter.Method, Parameter);
|
||||
|
||||
AddPropertyTag(new PropertyTag(templateTag, Options, expr, formatter?.Method));
|
||||
}
|
||||
|
||||
private class PropertyTag : TagBase
|
||||
{
|
||||
private readonly Func<Expression, Type, string, Expression> createToStringExpression;
|
||||
|
||||
public PropertyTag(ITemplateTag templateTag, RegexOptions options, Expression propertyExpression, MethodInfo formatter)
|
||||
: base(templateTag, propertyExpression)
|
||||
{
|
||||
var regexStr = formatter is null ? @$"^<{TemplateTag.TagName}>" : @$"^<{TemplateTag.TagName.Replace(" ", "\\s*?")}\s*?(?:\[([^\[\]]*?)\]\s*?)?>";
|
||||
NameMatcher = new Regex(regexStr, options);
|
||||
|
||||
//Create the ToString() expression for the TagBase.ExpressionValue's type.
|
||||
//If a formatter delegate was registered for this property, use that.
|
||||
//Otherwise use the object.Tostring() method.
|
||||
createToStringExpression
|
||||
= formatter is null
|
||||
? (expValue, retTyp, format) => Expression.Call(expValue, retTyp.GetMethod(nameof(object.ToString), Array.Empty<Type>()))
|
||||
: (expValue, retTyp, format) => Expression.Call(null, formatter, Expression.Constant(templateTag), expValue, Expression.Constant(format));
|
||||
}
|
||||
|
||||
protected override Expression GetTagExpression(string exactName, string formatString)
|
||||
{
|
||||
var underlyingType = Nullable.GetUnderlyingType(ReturnType);
|
||||
|
||||
Expression toStringExpression
|
||||
= ReturnType == typeof(string)
|
||||
? createToStringExpression(Expression.Coalesce(ExpressionValue, Expression.Constant("")), ReturnType, formatString)
|
||||
: underlyingType is null
|
||||
? createToStringExpression(ExpressionValue, ReturnType, formatString)
|
||||
: Expression.Condition(
|
||||
Expression.PropertyOrField(ExpressionValue, "HasValue"),
|
||||
createToStringExpression(Expression.PropertyOrField(ExpressionValue, "Value"), underlyingType, formatString),
|
||||
Expression.Constant(""));
|
||||
|
||||
return Expression.TryCatch(toStringExpression, Expression.Catch(typeof(Exception), Expression.Constant(exactName)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,164 @@
|
||||
using Dinah.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FileManager.NamingTemplate;
|
||||
|
||||
public delegate string PropertyFormatter<T>(ITemplateTag templateTag, T value, string formatString);
|
||||
|
||||
public class PropertyTagCollection<TClass> : TagCollection
|
||||
{
|
||||
private readonly Dictionary<Type, MulticastDelegate> defaultFormatters = new();
|
||||
|
||||
public PropertyTagCollection(bool caseSensative = true, params MulticastDelegate[] defaultFormatters) : base(typeof(TClass), caseSensative)
|
||||
{
|
||||
foreach (var formatter in defaultFormatters)
|
||||
{
|
||||
var parameters = formatter.Method.GetParameters();
|
||||
|
||||
if (formatter.Method.ReturnType != typeof(string)
|
||||
|| parameters.Length != 3
|
||||
|| parameters[0].ParameterType != typeof(ITemplateTag)
|
||||
|| parameters[2].ParameterType != typeof(string))
|
||||
throw new ArgumentException($"{nameof(defaultFormatters)} must have a signature of [{nameof(String)} PropertyFormatter<T>({nameof(ITemplateTag)}, T, {nameof(String)})]");
|
||||
|
||||
this.defaultFormatters[parameters[1].ParameterType] = formatter;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a nullable value type <typeparamref name="TClass"/> property.
|
||||
/// </summary>
|
||||
/// <typeparam name="TProperty">Type of the property from <see cref="TClass"/></typeparam>
|
||||
/// <param name="propertyGetter">A Func to get the property value from <see cref="TClass"/></param>
|
||||
/// <param name="formatter">Optional formatting function that accepts the <typeparamref name="TProperty"/> property
|
||||
/// and a formatting string and returnes the value the formatted string. If <see cref="null"/>, use the default
|
||||
/// <typeparamref name="TProperty"/> formatter if present, or <see cref="object.ToString"/></param>
|
||||
public void Add<TProperty>(ITemplateTag templateTag, Func<TClass, TProperty?> propertyGetter, PropertyFormatter<TProperty> formatter = null)
|
||||
where TProperty : struct
|
||||
=> RegisterWithFormatter(templateTag, propertyGetter, formatter);
|
||||
|
||||
/// <summary>
|
||||
/// Register a nullable value type <typeparamref name="TClass"/> property.
|
||||
/// </summary>
|
||||
/// <param name="propertyGetter">A Func to get the string property from <see cref="TClass"/></param>
|
||||
/// <param name="toString">ToString function that accepts the <typeparamref name="TProperty"/> property and returnes a string</param>
|
||||
public void Add<TProperty>(ITemplateTag templateTag, Func<TClass, TProperty?> propertyGetter, Func<TProperty, string> toString)
|
||||
where TProperty : struct
|
||||
=> RegisterWithToString(templateTag, propertyGetter, toString);
|
||||
|
||||
/// <summary>
|
||||
/// Register a <typeparamref name="TClass"/> property
|
||||
/// </summary>
|
||||
/// <typeparam name="TProperty">Type of the property from <see cref="TClass"/></typeparam>
|
||||
/// <param name="propertyGetter">A Func to get the property value from <see cref="TClass"/></param>
|
||||
/// <param name="formatter">Optional formatting function that accepts the <typeparamref name="TProperty"/> property
|
||||
/// and a formatting string and returnes the value formatted to string. If <see cref="null"/>, use the default
|
||||
/// <typeparamref name="TProperty"/> formatter if present, or <see cref="object.ToString"/></param>
|
||||
public void Add<TProperty>(ITemplateTag templateTag, Func<TClass, TProperty> propertyGetter, PropertyFormatter<TProperty> formatter = null)
|
||||
=> RegisterWithFormatter(templateTag, propertyGetter, formatter);
|
||||
|
||||
/// <summary>
|
||||
/// Register a <typeparamref name="TClass"/> property.
|
||||
/// </summary>
|
||||
/// <param name="propertyGetter">A Func to get the string property from <see cref="TClass"/></param>
|
||||
/// <param name="toString">ToString function that accepts the <typeparamref name="TProperty"/> property and returnes a string</param>
|
||||
public void Add<TProperty>(ITemplateTag templateTag, Func<TClass, TProperty> propertyGetter, Func<TProperty, string> toString)
|
||||
=> RegisterWithToString(templateTag, propertyGetter, toString);
|
||||
|
||||
private void RegisterWithFormatter<TProperty, TPropertyValue>
|
||||
(ITemplateTag templateTag, Func<TClass, TProperty> propertyGetter, PropertyFormatter<TPropertyValue> formatter)
|
||||
{
|
||||
ArgumentValidator.EnsureNotNull(templateTag, nameof(templateTag));
|
||||
ArgumentValidator.EnsureNotNull(propertyGetter, nameof(propertyGetter));
|
||||
|
||||
formatter ??= GetDefaultFormatter<TPropertyValue>();
|
||||
|
||||
if (formatter is null)
|
||||
RegisterWithToString<TProperty, TPropertyValue>(templateTag, propertyGetter, null);
|
||||
else
|
||||
{
|
||||
var expr = Expression.Call(Expression.Constant(propertyGetter.Target), propertyGetter.Method, Parameter);
|
||||
AddPropertyTag(PropertyTag.Create(templateTag, Options, expr, formatter));
|
||||
}
|
||||
}
|
||||
|
||||
private PropertyFormatter<T> GetDefaultFormatter<T>()
|
||||
{
|
||||
try
|
||||
{
|
||||
var del = defaultFormatters.FirstOrDefault(kvp => kvp.Key == typeof(T)).Value;
|
||||
return del is null ? null : Delegate.CreateDelegate(typeof(PropertyFormatter<T>), del.Target, del.Method) as PropertyFormatter<T>;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterWithToString<TProperty, TPropertyValue>
|
||||
(ITemplateTag templateTag, Func<TClass, TProperty> propertyGetter, Func<TPropertyValue, string> toString)
|
||||
{
|
||||
static string ToStringFunc(TPropertyValue value) => value?.ToString() ?? "";
|
||||
ArgumentValidator.EnsureNotNull(templateTag, nameof(templateTag));
|
||||
ArgumentValidator.EnsureNotNull(propertyGetter, nameof(propertyGetter));
|
||||
|
||||
var expr = Expression.Call(Expression.Constant(propertyGetter.Target), propertyGetter.Method, Parameter);
|
||||
AddPropertyTag(PropertyTag.Create(templateTag, Options, expr, toString ?? ToStringFunc));
|
||||
}
|
||||
|
||||
private class PropertyTag : TagBase
|
||||
{
|
||||
private Func<Expression, string, Expression> CreateToStringExpression { get; init; }
|
||||
private PropertyTag(ITemplateTag templateTag, Expression propertyGetter) : base(templateTag, propertyGetter) { }
|
||||
|
||||
public static PropertyTag Create<TPropertyValue>(ITemplateTag templateTag, RegexOptions options, Expression propertyGetter, PropertyFormatter<TPropertyValue> formatter)
|
||||
{
|
||||
return new PropertyTag(templateTag, propertyGetter)
|
||||
{
|
||||
NameMatcher = new Regex(@$"^<{templateTag.TagName.Replace(" ", "\\s*?")}\s*?(?:\[([^\[\]]*?)\]\s*?)?>", options),
|
||||
CreateToStringExpression = (expVal, format) =>
|
||||
Expression.Call(
|
||||
formatter.Target is null ? null : Expression.Constant(formatter.Target),
|
||||
formatter.Method,
|
||||
Expression.Constant(templateTag),
|
||||
expVal,
|
||||
Expression.Constant(format))
|
||||
};
|
||||
}
|
||||
|
||||
public static PropertyTag Create<TPropertyValue>(ITemplateTag templateTag, RegexOptions options, Expression propertyGetter, Func<TPropertyValue, string> toString)
|
||||
{
|
||||
return new PropertyTag(templateTag, propertyGetter)
|
||||
{
|
||||
NameMatcher = new Regex(@$"^<{templateTag.TagName}>", options),
|
||||
CreateToStringExpression = (expVal, _) =>
|
||||
Expression.Call(
|
||||
toString.Target is null ? null : Expression.Constant(toString.Target),
|
||||
toString.Method,
|
||||
expVal)
|
||||
};
|
||||
}
|
||||
|
||||
protected override Expression GetTagExpression(string exactName, string formatString)
|
||||
{
|
||||
Expression toStringExpression
|
||||
= !ReturnType.IsValueType
|
||||
? Expression.Condition(
|
||||
Expression.Equal(ValueExpression, Expression.Constant(null)),
|
||||
Expression.Constant(""),
|
||||
CreateToStringExpression(ValueExpression, formatString))
|
||||
: Nullable.GetUnderlyingType(ReturnType) is null
|
||||
? CreateToStringExpression(ValueExpression, formatString)
|
||||
: Expression.Condition(
|
||||
Expression.PropertyOrField(ValueExpression, "HasValue"),
|
||||
CreateToStringExpression(Expression.PropertyOrField(ValueExpression, "Value"), formatString),
|
||||
Expression.Constant(""));
|
||||
|
||||
return Expression.TryCatch(toStringExpression, Expression.Catch(typeof(Exception), Expression.Constant(exactName)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@ internal interface IPropertyTag
|
||||
Type ReturnType { get; }
|
||||
|
||||
/// <summary>The <see cref="Regex"/> used to match <see cref="TemplateTag"/> in template strings.</summary>
|
||||
public Regex NameMatcher { get; }
|
||||
Regex NameMatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determine if the template string starts with <see cref="TemplateTag"/>, and if it does parse the tag to an <see cref="Expression"/>
|
||||
@ -29,13 +29,13 @@ internal abstract class TagBase : IPropertyTag
|
||||
{
|
||||
public ITemplateTag TemplateTag { get; }
|
||||
public Regex NameMatcher { get; protected init; }
|
||||
public Type ReturnType => ExpressionValue.Type;
|
||||
protected Expression ExpressionValue { get; }
|
||||
public Type ReturnType => ValueExpression.Type;
|
||||
protected Expression ValueExpression { get; }
|
||||
|
||||
protected TagBase(ITemplateTag templateTag, Expression propertyExpression)
|
||||
{
|
||||
TemplateTag = templateTag;
|
||||
ExpressionValue = propertyExpression;
|
||||
ValueExpression = propertyExpression;
|
||||
}
|
||||
|
||||
/// <summary>Create an <see cref="Expression"/> that returns the property's value.</summary>
|
||||
@ -52,12 +52,10 @@ internal abstract class TagBase : IPropertyTag
|
||||
propertyValue = GetTagExpression(exactName, match.Groups.Count == 2 ? match.Groups[1].Value.Trim() : "");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
exactName = null;
|
||||
propertyValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
exactName = null;
|
||||
propertyValue = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
@ -6,19 +7,18 @@ using System.Text.RegularExpressions;
|
||||
|
||||
namespace FileManager.NamingTemplate;
|
||||
|
||||
|
||||
/// <summary>A collection of <see cref="IPropertyTag"/>s registered to a single <see cref="Type"/>.</summary>
|
||||
public abstract class TagClass
|
||||
public abstract class TagCollection : IEnumerable<ITemplateTag>
|
||||
{
|
||||
/// <summary>The <see cref="ParameterExpression"/> of the <see cref="TagClass"/>'s TClass type.</summary>
|
||||
/// <summary>The <see cref="ParameterExpression"/> of the <see cref="TagCollection"/>'s TClass type.</summary>
|
||||
public ParameterExpression Parameter { get; }
|
||||
/// <summary>The <see cref="ITemplateTag"/>s registered with this <see cref="TagClass"/> </summary>
|
||||
public IEnumerable<ITemplateTag> TemplateTags => PropertyTags.Select(p => p.TemplateTag);
|
||||
/// <summary>The <see cref="ITemplateTag"/>s registered with this <see cref="TagCollection"/> </summary>
|
||||
public IEnumerator<ITemplateTag> GetEnumerator() => PropertyTags.Select(p => p.TemplateTag).GetEnumerator();
|
||||
|
||||
protected RegexOptions Options { get; } = RegexOptions.Compiled;
|
||||
private protected List<IPropertyTag> PropertyTags { get; } = new();
|
||||
private List<IPropertyTag> PropertyTags { get; } = new();
|
||||
|
||||
protected TagClass(Type classType, bool caseSensative = true)
|
||||
protected TagCollection(Type classType, bool caseSensative = true)
|
||||
{
|
||||
Parameter = Expression.Parameter(classType, classType.Name);
|
||||
Options |= caseSensative ? RegexOptions.None : RegexOptions.IgnoreCase;
|
||||
@ -42,6 +42,7 @@ public abstract class TagClass
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
propertyValue = null;
|
||||
propertyTag = null;
|
||||
exactName = null;
|
||||
@ -74,4 +75,6 @@ public abstract class TagClass
|
||||
if (!PropertyTags.Any(c => c.TemplateTag.TagName == propertyTag.TemplateTag.TagName))
|
||||
PropertyTags.Add(propertyTag);
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
@ -18,7 +18,7 @@ public class TemplatePart : IEnumerable<TemplatePart>
|
||||
public ITemplateTag TemplateTag { get; }
|
||||
|
||||
/// <summary>The evaluated string.</summary>
|
||||
public string Value { get; set; }
|
||||
public string Value { get; }
|
||||
|
||||
private TemplatePart previous;
|
||||
private TemplatePart next;
|
||||
|
||||
@ -19,7 +19,7 @@ namespace LibationAvalonia.Views
|
||||
var options = new FilePickerSaveOptions
|
||||
{
|
||||
Title = "Where to export Library",
|
||||
SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Configuration.Instance.Books),
|
||||
SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Configuration.Instance.Books.PathWithoutPrefix),
|
||||
SuggestedFileName = $"Libation Library Export {DateTime.Now:yyyy-MM-dd}.xlsx",
|
||||
DefaultExtension = "xlsx",
|
||||
ShowOverwritePrompt = true,
|
||||
|
||||
@ -111,7 +111,7 @@ namespace LibationAvalonia.Views
|
||||
{
|
||||
Title = $"Locate the audio file for '{entry.Book.Title}'",
|
||||
AllowMultiple = false,
|
||||
SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Configuration.Instance.Books),
|
||||
SuggestedStartLocation = new Avalonia.Platform.Storage.FileIO.BclStorageFolder(Configuration.Instance.Books.PathWithoutPrefix),
|
||||
FileTypeFilter = new FilePickerFileType[]
|
||||
{
|
||||
new("All files (*.*)") { Patterns = new[] { "*" } },
|
||||
|
||||
@ -6,14 +6,13 @@ using AaxDecrypter;
|
||||
using Dinah.Core;
|
||||
using FileManager;
|
||||
using FileManager.NamingTemplate;
|
||||
using Serilog.Formatting;
|
||||
|
||||
namespace LibationFileManager
|
||||
{
|
||||
public interface ITemplate
|
||||
{
|
||||
static abstract string DefaultTemplate { get; }
|
||||
static abstract IEnumerable<TagClass> TagClass { get; }
|
||||
static abstract IEnumerable<TagCollection> TagCollections { get; }
|
||||
}
|
||||
|
||||
public abstract class Templates
|
||||
@ -21,7 +20,7 @@ namespace LibationFileManager
|
||||
public const string ERROR_FULL_PATH_IS_INVALID = @"No colons or full paths allowed. Eg: should not start with C:\";
|
||||
public const string WARNING_NO_CHAPTER_NUMBER_TAG = "Should include chapter number tag in template used for naming files which are split by chapter. Ie: <ch#> or <ch# 0>";
|
||||
|
||||
//Assign the properties in the static constructor will require all
|
||||
//Assigning the properties in the static constructor will require all
|
||||
//Templates users to have a valid configuration file. To allow tests
|
||||
//to work without access to Configuration, only load templates on demand.
|
||||
private static FolderTemplate _folder;
|
||||
@ -35,41 +34,44 @@ namespace LibationFileManager
|
||||
public static ChapterTitleTemplate ChapterTitle => _chapterTitle ??= GetTemplate<ChapterTitleTemplate>(Configuration.Instance.ChapterTitleTemplate);
|
||||
|
||||
#region Template Parsing
|
||||
|
||||
public static T GetTemplate<T>(string templateText) where T : Templates, ITemplate, new()
|
||||
=> TryGetTemplate<T>(templateText, out var template) ? template : GetDefaultTemplate<T>();
|
||||
|
||||
public static bool TryGetTemplate<T>(string templateText, out T template) where T : Templates, ITemplate, new()
|
||||
{
|
||||
var namingTemplate = NamingTemplate.Parse(templateText, T.TagClass);
|
||||
var namingTemplate = NamingTemplate.Parse(templateText, T.TagCollections);
|
||||
|
||||
template = new() { Template = namingTemplate };
|
||||
return !namingTemplate.Errors.Any();
|
||||
}
|
||||
|
||||
private static T GetDefaultTemplate<T>() where T : Templates, ITemplate, new()
|
||||
=> new() { Template = NamingTemplate.Parse(T.DefaultTemplate, T.TagClass) };
|
||||
=> new() { Template = NamingTemplate.Parse(T.DefaultTemplate, T.TagCollections) };
|
||||
|
||||
static Templates()
|
||||
{
|
||||
Configuration.Instance.PropertyChanged +=
|
||||
[PropertyChangeFilter(nameof(Configuration.FolderTemplate))]
|
||||
(_,e) => _folder = GetTemplate<FolderTemplate>((string)e.NewValue);
|
||||
(_,e) => _folder = GetTemplate<FolderTemplate>((string)e.NewValue);
|
||||
|
||||
Configuration.Instance.PropertyChanged
|
||||
+= [PropertyChangeFilter(nameof(Configuration.FileTemplate))]
|
||||
(_, e) => _file = GetTemplate<FileTemplate>((string)e.NewValue);
|
||||
Configuration.Instance.PropertyChanged +=
|
||||
[PropertyChangeFilter(nameof(Configuration.FileTemplate))]
|
||||
(_, e) => _file = GetTemplate<FileTemplate>((string)e.NewValue);
|
||||
|
||||
Configuration.Instance.PropertyChanged
|
||||
+= [PropertyChangeFilter(nameof(Configuration.ChapterFileTemplate))]
|
||||
(_, e) => _chapterFile = GetTemplate<ChapterFileTemplate>((string)e.NewValue);
|
||||
Configuration.Instance.PropertyChanged +=
|
||||
[PropertyChangeFilter(nameof(Configuration.ChapterFileTemplate))]
|
||||
(_, e) => _chapterFile = GetTemplate<ChapterFileTemplate>((string)e.NewValue);
|
||||
|
||||
Configuration.Instance.PropertyChanged
|
||||
+= [PropertyChangeFilter(nameof(Configuration.ChapterTitleTemplate))]
|
||||
(_, e) => _chapterTitle = GetTemplate<ChapterTitleTemplate>((string)e.NewValue);
|
||||
Configuration.Instance.PropertyChanged +=
|
||||
[PropertyChangeFilter(nameof(Configuration.ChapterTitleTemplate))]
|
||||
(_, e) => _chapterTitle = GetTemplate<ChapterTitleTemplate>((string)e.NewValue);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Template Properties
|
||||
|
||||
public IEnumerable<TemplateTags> TagsRegistered => Template.TagsRegistered.Cast<TemplateTags>();
|
||||
public IEnumerable<TemplateTags> TagsInUse => Template.TagsInUse.Cast<TemplateTags>();
|
||||
public abstract string Name { get; }
|
||||
@ -77,7 +79,6 @@ namespace LibationFileManager
|
||||
public string TemplateText => Template.TemplateText;
|
||||
protected NamingTemplate Template { get; private set; }
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region validation
|
||||
@ -152,7 +153,7 @@ namespace LibationFileManager
|
||||
part.Insert(maxIndex, maxEntry.Remove(maxLength - 1, 1));
|
||||
}
|
||||
}
|
||||
//Any
|
||||
|
||||
var fullPath = Path.Combine(pathParts.Select(fileParts => string.Join("", fileParts)).Prepend(baseDir).ToArray());
|
||||
|
||||
return FileUtility.GetValidFilename(fullPath, replacements, fileExtension, returnFirstExisting);
|
||||
@ -163,7 +164,7 @@ namespace LibationFileManager
|
||||
/// returned as empty directories and are taken care of by Path.Combine()
|
||||
/// </summary>
|
||||
/// <returns>A List of template directories. Each directory is a list of template part strings</returns>
|
||||
private List<List<string>> GetPathParts(IEnumerable<string> templateParts)
|
||||
private static List<List<string>> GetPathParts(IEnumerable<string> templateParts)
|
||||
{
|
||||
List<List<string>> directories = new();
|
||||
List<string> dir = new();
|
||||
@ -190,69 +191,65 @@ namespace LibationFileManager
|
||||
|
||||
#region Registered Template Properties
|
||||
|
||||
private static readonly PropertyTagClass<LibraryBookDto> filePropertyTags = GetFilePropertyTags();
|
||||
private static readonly ConditionalTagClass<LibraryBookDto> conditionalTags = GetConditionalTags();
|
||||
private static readonly List<TagClass> chapterPropertyTags = GetChapterPropertyTags();
|
||||
|
||||
private static ConditionalTagClass<LibraryBookDto> GetConditionalTags()
|
||||
private static readonly PropertyTagCollection<LibraryBookDto> filePropertyTags =
|
||||
new(caseSensative: true, StringFormatter, DateTimeFormatter, IntegerFormatter)
|
||||
{
|
||||
ConditionalTagClass<LibraryBookDto> lbConditions = new();
|
||||
//Don't allow formatting of Id
|
||||
{ TemplateTags.Id, lb => lb.AudibleProductId, v => v },
|
||||
{ TemplateTags.Title, lb => lb.Title },
|
||||
{ TemplateTags.TitleShort, lb => getTitleShort(lb.Title) },
|
||||
{ TemplateTags.Author, lb => lb.AuthorNames },
|
||||
{ TemplateTags.FirstAuthor, lb => lb.FirstAuthor },
|
||||
{ TemplateTags.Narrator, lb => lb.NarratorNames },
|
||||
{ TemplateTags.FirstNarrator, lb => lb.FirstNarrator },
|
||||
{ TemplateTags.Series, lb => lb.SeriesName },
|
||||
{ TemplateTags.SeriesNumber, lb => lb.SeriesNumber },
|
||||
{ TemplateTags.Language, lb => lb.Language },
|
||||
//Don't allow formatting of LanguageShort
|
||||
{ TemplateTags.LanguageShort, lb =>lb.Language, getLanguageShort },
|
||||
{ TemplateTags.Bitrate, lb => lb.BitRate },
|
||||
{ TemplateTags.SampleRate, lb => lb.SampleRate },
|
||||
{ TemplateTags.Channels, lb => lb.Channels },
|
||||
{ TemplateTags.Account, lb => lb.Account },
|
||||
{ TemplateTags.Locale, lb => lb.Locale },
|
||||
{ TemplateTags.YearPublished, lb => lb.YearPublished },
|
||||
{ TemplateTags.DatePublished, lb => lb.DatePublished },
|
||||
{ TemplateTags.DateAdded, lb => lb.DateAdded },
|
||||
{ TemplateTags.FileDate, lb => lb.FileDate },
|
||||
};
|
||||
|
||||
lbConditions.RegisterCondition(TemplateTags.IfSeries, lb => lb.IsSeries);
|
||||
lbConditions.RegisterCondition(TemplateTags.IfPodcast, lb => lb.IsPodcast);
|
||||
lbConditions.RegisterCondition(TemplateTags.IfBookseries, lb => lb.IsSeries && !lb.IsPodcast);
|
||||
|
||||
return lbConditions;
|
||||
}
|
||||
|
||||
private static PropertyTagClass<LibraryBookDto> GetFilePropertyTags()
|
||||
private static readonly List<TagCollection> chapterPropertyTags = new()
|
||||
{
|
||||
PropertyTagClass<LibraryBookDto> lbProperties = new();
|
||||
lbProperties.RegisterProperty(TemplateTags.Id, lb => lb.AudibleProductId);
|
||||
lbProperties.RegisterProperty(TemplateTags.Title, lb => lb.Title, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.TitleShort, lb => lb.Title.IndexOf(':') < 1 ? lb.Title : lb.Title.Substring(0, lb.Title.IndexOf(':')), StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.Author, lb => lb.AuthorNames, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.FirstAuthor, lb => lb.FirstAuthor, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.Narrator, lb => lb.NarratorNames, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.FirstNarrator, lb => lb.FirstNarrator, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.Series, lb => lb.SeriesName, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.SeriesNumber, lb => lb.SeriesNumber, IntegerFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.Language, lb => lb.Language, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.LanguageShort, lb => getLanguageShort(lb.Language), StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.Bitrate, lb => lb.BitRate, IntegerFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.SampleRate, lb => lb.SampleRate, IntegerFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.Channels, lb => lb.Channels, IntegerFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.Account, lb => lb.Account, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.Locale, lb => lb.Locale, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.YearPublished, lb => lb.YearPublished, IntegerFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.DatePublished, lb => lb.DatePublished, DateTimeFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.DateAdded, lb => lb.DateAdded, DateTimeFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.FileDate, lb => lb.FileDate, DateTimeFormatter);
|
||||
return lbProperties;
|
||||
}
|
||||
new PropertyTagCollection<LibraryBookDto>(caseSensative: true, StringFormatter)
|
||||
{
|
||||
{ TemplateTags.Title, lb => lb.Title },
|
||||
{ TemplateTags.TitleShort, lb => getTitleShort(lb.Title) },
|
||||
{ TemplateTags.Series, lb => lb.SeriesName },
|
||||
},
|
||||
new PropertyTagCollection<MultiConvertFileProperties>(caseSensative: true, StringFormatter, IntegerFormatter, DateTimeFormatter)
|
||||
{
|
||||
{ TemplateTags.ChCount, m => m.PartsTotal },
|
||||
{ TemplateTags.ChNumber, m => m.PartsPosition },
|
||||
{ TemplateTags.ChNumber0, m => m.PartsPosition.ToString("D" + ((int)Math.Log10(m.PartsTotal) + 1)) },
|
||||
{ TemplateTags.ChTitle, m => m.Title },
|
||||
{ TemplateTags.FileDate, m => m.FileDate }
|
||||
}
|
||||
};
|
||||
|
||||
private static List<TagClass> GetChapterPropertyTags()
|
||||
private static readonly ConditionalTagCollection<LibraryBookDto> conditionalTags = new()
|
||||
{
|
||||
PropertyTagClass<LibraryBookDto> lbProperties = new();
|
||||
PropertyTagClass<MultiConvertFileProperties> multiConvertProperties = new();
|
||||
|
||||
lbProperties.RegisterProperty(TemplateTags.Title, lb => lb.Title, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.TitleShort, lb => lb?.Title?.IndexOf(':') > 0 ? lb.Title.Substring(0, lb.Title.IndexOf(':')) : lb.Title, StringFormatter);
|
||||
lbProperties.RegisterProperty(TemplateTags.Series, lb => lb.SeriesName, StringFormatter);
|
||||
|
||||
multiConvertProperties.RegisterProperty(TemplateTags.ChCount, lb => lb.PartsTotal, IntegerFormatter);
|
||||
multiConvertProperties.RegisterProperty(TemplateTags.ChNumber, lb => lb.PartsPosition, IntegerFormatter);
|
||||
multiConvertProperties.RegisterProperty(TemplateTags.ChNumber0, m => m.PartsPosition.ToString("D" + ((int)Math.Log10(m.PartsTotal) + 1)));
|
||||
multiConvertProperties.RegisterProperty(TemplateTags.ChTitle, m => m.Title, StringFormatter);
|
||||
multiConvertProperties.RegisterProperty(TemplateTags.FileDate, lb => lb.FileDate, DateTimeFormatter);
|
||||
|
||||
return new List<TagClass> { lbProperties, multiConvertProperties };
|
||||
}
|
||||
{ TemplateTags.IfSeries, lb => lb.IsSeries },
|
||||
{ TemplateTags.IfPodcast, lb => lb.IsPodcast },
|
||||
{ TemplateTags.IfBookseries, lb => lb.IsSeries && !lb.IsPodcast },
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
#region Tag Formatters
|
||||
|
||||
private static string getTitleShort(string title)
|
||||
=> title?.IndexOf(':') > 0 ? title.Substring(0, title.IndexOf(':')) : title;
|
||||
|
||||
private static string getLanguageShort(string language)
|
||||
{
|
||||
if (language is null)
|
||||
@ -292,23 +289,18 @@ namespace LibationFileManager
|
||||
public override string Name => "Folder Template";
|
||||
public override string Description => Configuration.GetDescription(nameof(Configuration.FolderTemplate));
|
||||
public static string DefaultTemplate { get; } = "<title short> [<id>]";
|
||||
public static IEnumerable<TagClass> TagClass => new TagClass[] { filePropertyTags, conditionalTags };
|
||||
public static IEnumerable<TagCollection> TagCollections => new TagCollection[] { filePropertyTags, conditionalTags };
|
||||
|
||||
public override IEnumerable<string> Errors
|
||||
=> TemplateText?.Length >= 2 && Path.IsPathFullyQualified(TemplateText) ? base.Errors.Append(ERROR_FULL_PATH_IS_INVALID) : base.Errors;
|
||||
|
||||
protected override List<string> GetTemplatePartsStrings(List<TemplatePart> parts, ReplacementCharacters replacements)
|
||||
{
|
||||
foreach (var tp in parts)
|
||||
{
|
||||
=> parts
|
||||
.Select(tp => tp.TemplateTag is null
|
||||
//FolderTemplate literals can have directory separator characters
|
||||
if (tp.TemplateTag is null)
|
||||
tp.Value = replacements.ReplacePathChars(tp.Value.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar));
|
||||
else
|
||||
tp.Value = replacements.ReplaceFilenameChars(tp.Value);
|
||||
}
|
||||
return parts.Select(p => p.Value).ToList();
|
||||
}
|
||||
? replacements.ReplacePathChars(tp.Value.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar))
|
||||
: replacements.ReplaceFilenameChars(tp.Value)
|
||||
).ToList();
|
||||
}
|
||||
|
||||
public class FileTemplate : Templates, ITemplate
|
||||
@ -316,7 +308,7 @@ namespace LibationFileManager
|
||||
public override string Name => "File Template";
|
||||
public override string Description => Configuration.GetDescription(nameof(Configuration.FileTemplate));
|
||||
public static string DefaultTemplate { get; } = "<title> [<id>]";
|
||||
public static IEnumerable<TagClass> TagClass { get; } = new TagClass[] { filePropertyTags, conditionalTags };
|
||||
public static IEnumerable<TagCollection> TagCollections { get; } = new TagCollection[] { filePropertyTags, conditionalTags };
|
||||
}
|
||||
|
||||
public class ChapterFileTemplate : Templates, ITemplate
|
||||
@ -324,8 +316,7 @@ namespace LibationFileManager
|
||||
public override string Name => "Chapter File Template";
|
||||
public override string Description => Configuration.GetDescription(nameof(Configuration.ChapterFileTemplate));
|
||||
public static string DefaultTemplate { get; } = "<title> [<id>] - <ch# 0> - <ch title>";
|
||||
public static IEnumerable<TagClass> TagClass { get; }
|
||||
= chapterPropertyTags.Append(filePropertyTags).Append(conditionalTags);
|
||||
public static IEnumerable<TagCollection> TagCollections { get; } = chapterPropertyTags.Append(filePropertyTags).Append(conditionalTags);
|
||||
|
||||
public override IEnumerable<string> Warnings
|
||||
=> Template.TagsInUse.Any(t => t.TagName.In(TemplateTags.ChNumber.TagName, TemplateTags.ChNumber0.TagName))
|
||||
@ -338,8 +329,7 @@ namespace LibationFileManager
|
||||
public override string Name => "Chapter Title Template";
|
||||
public override string Description => Configuration.GetDescription(nameof(Configuration.ChapterTitleTemplate));
|
||||
public static string DefaultTemplate => "<ch#> - <title short>: <ch title>";
|
||||
public static IEnumerable<TagClass> TagClass { get; }
|
||||
= chapterPropertyTags.Append(conditionalTags);
|
||||
public static IEnumerable<TagCollection> TagCollections { get; } = chapterPropertyTags.Append(conditionalTags);
|
||||
|
||||
protected override IEnumerable<string> GetTemplatePartsStrings(List<TemplatePart> parts, ReplacementCharacters replacements)
|
||||
=> parts.Select(p => p.Value);
|
||||
|
||||
@ -33,20 +33,56 @@ namespace NamingTemplateTests
|
||||
public string Item2 { get; set; }
|
||||
public string Item3 { get; set; }
|
||||
public string Item4 { get; set; }
|
||||
public ReferenceType RefType { get; set; }
|
||||
public int? Int2 { get; set; }
|
||||
public bool Condition { get; set; }
|
||||
}
|
||||
class ReferenceType
|
||||
{
|
||||
public override string ToString()
|
||||
{
|
||||
return nameof(ReferenceType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[TestClass]
|
||||
public class GetPortionFilename
|
||||
{
|
||||
PropertyTagClass<PropertyClass1> props1 = new();
|
||||
PropertyTagClass<PropertyClass2> props2 = new();
|
||||
PropertyTagClass<PropertyClass3> props3 = new();
|
||||
ConditionalTagClass<PropertyClass1> conditional1 = new();
|
||||
ConditionalTagClass<PropertyClass2> conditional2 = new();
|
||||
ConditionalTagClass<PropertyClass3> conditional3 = new();
|
||||
PropertyTagCollection<PropertyClass1> props1 = new()
|
||||
{
|
||||
{ new TemplateTag { TagName = "item1" }, i => i.Item1 },
|
||||
{ new TemplateTag { TagName = "item2" }, i => i.Item2 },
|
||||
{ new TemplateTag { TagName = "item3" }, i => i.Item3 }
|
||||
};
|
||||
|
||||
PropertyTagCollection<PropertyClass2> props2 = new()
|
||||
{
|
||||
{ new TemplateTag { TagName = "item1" }, i => i.Item1 },
|
||||
{ new TemplateTag { TagName = "item2" }, i => i.Item2 },
|
||||
{ new TemplateTag { TagName = "item3" }, i => i.Item3 },
|
||||
{ new TemplateTag { TagName = "item4" }, i => i.Item4 },
|
||||
};
|
||||
PropertyTagCollection<PropertyClass3> props3 = new(true, GetVal)
|
||||
{
|
||||
{ new TemplateTag { TagName = "item3_1" }, i => i.Item1 },
|
||||
{ new TemplateTag { TagName = "item3_2" }, i => i.Item2 },
|
||||
{ new TemplateTag { TagName = "item3_3" }, i => i.Item3 },
|
||||
{ new TemplateTag { TagName = "item3_4" }, i => i.Item4 },
|
||||
{ new TemplateTag { TagName = "reftype" }, i => i.RefType },
|
||||
};
|
||||
ConditionalTagCollection<PropertyClass1> conditional1 = new()
|
||||
{
|
||||
{ new TemplateTag { TagName = "ifc1" }, i => i.Condition },
|
||||
};
|
||||
ConditionalTagCollection<PropertyClass2> conditional2 = new()
|
||||
{
|
||||
{ new TemplateTag { TagName = "ifc2" }, i => i.Condition },
|
||||
};
|
||||
ConditionalTagCollection<PropertyClass3> conditional3 = new()
|
||||
{
|
||||
{ new TemplateTag { TagName = "ifc3" }, i => i.Condition },
|
||||
};
|
||||
|
||||
PropertyClass1 propertyClass1 = new()
|
||||
{
|
||||
@ -74,27 +110,6 @@ namespace NamingTemplateTests
|
||||
Condition = true
|
||||
};
|
||||
|
||||
public GetPortionFilename()
|
||||
{
|
||||
props1.RegisterProperty(new TemplateTag { TagName = "item1" }, i => i.Item1);
|
||||
props1.RegisterProperty(new TemplateTag { TagName = "item2" }, i => i.Item2);
|
||||
props1.RegisterProperty(new TemplateTag { TagName = "item3" }, i => i.Item3);
|
||||
|
||||
props2.RegisterProperty(new TemplateTag { TagName = "item1" }, i => i.Item1);
|
||||
props2.RegisterProperty(new TemplateTag { TagName = "item2" }, i => i.Item2);
|
||||
props2.RegisterProperty(new TemplateTag { TagName = "item3" }, i => i.Item3);
|
||||
props2.RegisterProperty(new TemplateTag { TagName = "item4" }, i => i.Item4);
|
||||
|
||||
props3.RegisterProperty(new TemplateTag { TagName = "item3_1" }, i => i.Item1);
|
||||
props3.RegisterProperty(new TemplateTag { TagName = "item3_2" }, i => i.Item2);
|
||||
props3.RegisterProperty(new TemplateTag { TagName = "item3_3" }, i => i.Item3);
|
||||
props3.RegisterProperty(new TemplateTag { TagName = "item3_4" }, i => i.Item4);
|
||||
|
||||
conditional1.RegisterCondition(new TemplateTag { TagName = "ifc1" }, i => i.Condition);
|
||||
conditional2.RegisterCondition(new TemplateTag { TagName = "ifc2" }, i => i.Condition);
|
||||
conditional3.RegisterCondition(new TemplateTag { TagName = "ifc3" }, i => i.Condition);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("<item1>", "prop1_item1", 1)]
|
||||
@ -110,7 +125,7 @@ namespace NamingTemplateTests
|
||||
[DataRow("<!ifc2-><ifc1-><ifc3-><item1><item4><item3_2><-ifc3><-ifc1><-ifc2>", "prop1_item1prop2_item4prop3_item2", 3)]
|
||||
public void test(string inStr, string outStr, int numTags)
|
||||
{
|
||||
var template = NamingTemplate.Parse(inStr, new TagClass[] { props1, props2, props3, conditional1, conditional2, conditional3 });
|
||||
var template = NamingTemplate.Parse(inStr, new TagCollection[] { props1, props2, props3, conditional1, conditional2, conditional3 });
|
||||
|
||||
template.TagsInUse.Should().HaveCount(numTags);
|
||||
template.Warnings.Should().HaveCount(numTags > 0 ? 0 : 1);
|
||||
@ -132,11 +147,17 @@ namespace NamingTemplateTests
|
||||
[DataRow("<ifc2-><ifc1-><ifc3-><item1><item4><item3_2><-ifc1><-ifc2>", new string[] { "Missing <-ifc3> closing conditional.", "Missing <-ifc3> closing conditional.", "Missing <-ifc1> closing conditional.", "Missing <-ifc2> closing conditional." })]
|
||||
public void condition_error(string inStr, string[] warnings)
|
||||
{
|
||||
var template = NamingTemplate.Parse(inStr, new TagClass[] { props1, props2, props3, conditional1, conditional2, conditional3 });
|
||||
var template = NamingTemplate.Parse(inStr, new TagCollection[] { props1, props2, props3, conditional1, conditional2, conditional3 });
|
||||
|
||||
template.Errors.Should().HaveCount(0);
|
||||
template.Warnings.Should().BeEquivalentTo(warnings);
|
||||
}
|
||||
|
||||
static string GetVal(ITemplateTag templateTag, ReferenceType referenceType, string format)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[DataRow("<int1>", "55")]
|
||||
[DataRow("<int1[]>", "55")]
|
||||
@ -152,14 +173,15 @@ namespace NamingTemplateTests
|
||||
[DataRow("<item2_2_null>", "")]
|
||||
[DataRow("<item2_2_null[]>", "")]
|
||||
[DataRow("<item2_2_null[l]>", "")]
|
||||
[DataRow("<reftype[l]>", "")]
|
||||
public void formatting(string inStr, string outStr)
|
||||
{
|
||||
props1.RegisterProperty(new TemplateTag { TagName = "int1" }, i => i.Int1, formatInt);
|
||||
props3.RegisterProperty(new TemplateTag { TagName = "int2" }, i => i.Int2, formatInt);
|
||||
props3.RegisterProperty(new TemplateTag { TagName = "item3_format" }, i => i.Item3, formatString);
|
||||
props2.RegisterProperty(new TemplateTag { TagName = "item2_2_null" }, i => i.Item2, formatString);
|
||||
props1.Add(new TemplateTag { TagName = "int1" }, i => i.Int1, formatInt);
|
||||
props3.Add(new TemplateTag { TagName = "int2" }, i => i.Int2, formatInt);
|
||||
props3.Add(new TemplateTag { TagName = "item3_format" }, i => i.Item3, formatString);
|
||||
props2.Add(new TemplateTag { TagName = "item2_2_null" }, i => i.Item2, formatString);
|
||||
|
||||
var template = NamingTemplate.Parse(inStr, new TagClass[] { props1, props2, props3, conditional1, conditional2, conditional3 });
|
||||
var template = NamingTemplate.Parse(inStr, new TagCollection[] { props1, props2, props3, conditional1, conditional2, conditional3 });
|
||||
|
||||
template.Warnings.Should().HaveCount(0);
|
||||
template.Errors.Should().HaveCount(0);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user