• Lexical
Show / Hide Table of Contents
  • Lexical.FileSystem
    • Introduction
    • Abstractions
      • IFileSystem
        • IFileSystem
        • IFileSystemBrowse
        • IFileSystemCreateDirectory
        • IFileSystemDelete
        • IFileSystemFileAttribute
        • IFileSystemMount
        • IFileSystemMove
        • IFileSystemObserve
        • IFileSystemOpen
      • IEvent
      • IEntry
      • IOption
      • IToken
    • FileSystem
    • VirtualFileSystem
    • MemoryFileSystem
    • EmbeddedFileSystem
    • HttpFileSystem
    • Decoration
    • IFileProvider
    • Utility
      • DisposeList
      • FileScanner
      • VisitTree
      • File Operation
  • Lexical.FileProvider
    • Introduction
    • Package
    • Package.Abstractions
    • Root
    • Zip
    • Dll
    • SharpCompress
    • SharpZipLib
    • FileScanner
    • Utils
  • Lexical.Localization
    • Introduction
    • Tutorial
    • Asset
      • IAsset
      • IStringAsset
    • Line
      • ILine
      • ILineFactory
      • ILineRoot
      • ILineFormat
      • ILineLogger
      • LineComparer
    • File
      • ILineReader
      • ILineWriter
      • Ini
      • Json
      • Xml
      • Resx
      • Resources
    • Miscellaneous
      • Plurality
      • ICulturePolicy
      • IStringFormat
      • Dependency Injection
    • Practices
      • Class Library
      • Class Library DI
      • Class Library DI opt.
  • Lexical.Utilities
    • Introduction
    • UnicodeString
    • FileScanner
    • Permutation
    • Tuples
    • StructList

ILineFormat

ILineFormat is root interface for ILine and String converters.
/// <summary>
/// Line format makes conversions between <see cref="ILine"/> and <see cref="String"/>.
/// 
/// Class that implements to this interface should implement one or both of the following interfaces:
/// <list type="bullet">
///     <item><see cref="ILineFormatPrinter"/></item>
///     <item><see cref="ILinePattern"/></item>
/// </list>
/// 
/// The decision on what types are instantiated is a configuration decision of the implementing class.
/// </summary>
public interface ILineFormat
{
}
ILineFormatPrinter prints ILines as Strings.
/// <summary>
/// Converts <see cref="ILine"/> to string.
/// </summary>
public interface ILineFormatPrinter : ILineFormat
{
    /// <summary>
    /// Print <paramref name="line"/> as <see cref="String"/>.
    /// 
    /// The decision on what types are instantiated is a configuration decision of the implementing class.
    /// </summary>
    /// <param name="line"></param>
    /// <returns>full name string</returns>
    string Print(ILine line);
}
ILineFormatParser parses Strings into ILine.
/// <summary>
/// Parses string into <see cref="ILine"/>.
/// </summary>
public interface ILineFormatParser : ILineFormat
{
    /// <summary>
    /// Parse string into <see cref="ILine"/>.
    /// 
    /// The decision on what types are instantiated is a configuration decision of the implementing class.
    /// </summary>
    /// <param name="str">key as string</param>
    /// <returns>Arguments that can be used for constructing or appending to a line</returns>
    /// <exception cref="LineException">If parse failed</exception>
    IEnumerable<ILineArgument> ParseArgs(string str);

    /// <summary>
    /// Parse string into key.
    /// </summary>
    /// <param name="str"></param>
    /// <param name="args">Arguments that can be used for constructing or appending to a line</param>
    /// <returns>true if parse was successful</returns>
    bool TryParseArgs(string str, out IEnumerable<ILineArgument> args);
}


Class ILineFormatPrinter ILineFormatParser
LineFormat ☑ ☑
LinePattern ☑ ☑
LineParameterPrinter ☑ ☐

LineFormat

LineFormat is an ILineFormat class that prints and parses keys into strings using the following notation.

parameterName:parameterValue:parameterName:parameterValue:...

Keys are converted to strings.

ILine key = LineAppender.NonResolving.Type("MyController").Key("Success").Culture("en");
string str = LineFormat.Parameters.Print(key);

And strings parsed to keys.

string str = @"Culture:en:Type:MyController:Key:Ok";
ILine key = LineFormat.Parameters.Parse(str, null, LineAppender.NonResolving);

A specific root can be used from which the constructed key is appended from.

string str = @"Culture:en:Type:MyController:Key:Ok";
ILine root = new StringLocalizerRoot();
ILine key = LineFormat.Parameters.Parse(str, root);

Policy uses the following escape rules.

Sequence Meaning
\: Colon
\t Tab
\r Carriage return
\n New line
\xhh Unicode 8bit
\uhhhh Unicode 16bit (surrogate)
\Uhhhhhhhh Unicode 32bit

Example of escaped key "Success\:Plural".

string str = @"Key:Success\:Plural";
ILine key = LineFormat.Parameters.Parse(str, null, LineAppender.NonResolving);
Instance Description
LineFormat.Key Prints and parses effective key of ILine.
LineFormat.Line Prints and parses whole ILine.
LineFormat.Parameters Prints and parses the parameters of ILine, excluding "String" parameter
LineFormat.ParametersInclString Prints and parses every parameter of ILine

LinePattern

ILinePattern is interface for name patterns.

/// <summary>
/// A name pattern, akin to regular expression, that can be matched against filenames and <see cref="ILine"/> instances.
/// Is a sequence of parameter and text parts.
/// 
/// Parameter parts:
///  {Culture}           - Matches to key.Culture("en")
///  {Assembly}          - Matches to key.Assembly(asm).
///  {Resource}          - Matches to key.Resource("xx").
///  {Type}              - Matches to key.Type(type)
///  {Section}           - Matches to key.Section("xx")
///  {Location}          - Matches to key.Location("xx") and a physical folder, separator is '/'.
///  {anysection}        - Matches to assembly, type and section.
///  {Key}               - Matches to key key.Key("x")
/// 
/// Before and after the part pre- and postfix separator characters can be added:
///  {/Culture.}
///  
/// Parts can be optional in curly braces {} and required in brackets [].
///  [Culture]
/// 
/// Part can be added multiple times
///  "{Location/}{Location/}{Location/}{Key}"  - Matches to, from 0 to 3 occurances of Location(), e.g. key.Location("dir").Location("dir1");
/// 
/// If parts need to be matched out of order, then occurance index can be used "_number".
///  "{Location_2/}{Location_1/}{Location_0/}{Key}"  - Matches to, from 0 to 3 occurances of Location, e.g. key.Location("dir").Location("dir1");
/// 
/// Suffix "_n" translates to five conscutive parts.
///  "[Location_n/]location.ini" translates to "[Location_0/]{Location_1/}{Location_2/}{Location_3/}{Location_4/}"
///  "[Location/]{Location_n/}location.ini" translates to "[Location_0/]{Location_1/}{Location_2/}{Location_3/}{Location_4/}{Location_5/}"
///  
/// Regular expressions can be written between &lt; and &gt; characters to specify match criteria. \ escapes \, *, +, ?, |, {, [, (,), &lt;, &gt; ^, $,., #, and white space.
///  "{Section&lt;[^:]*&gt;.}"
/// 
/// Regular expressions can be used for greedy match when matching against filenames and embedded resources.
///  "{Assembly.}{Resource&lt;.*&gt;.}{Type.}{Section.}{Key}"
/// 
/// Examples:
///   "[Assembly.]Resources.localization{-Culture}.json"
///   "[Assembly.]Resources.{Type.}localization[-Culture].json"
///   "Assets/{Type/}localization{-Culture}.ini"
///   "Assets/{Assembly/}{Type/}{Section.}localization{-Culture}.ini"
///   "{Culture.}{Type.}{Section_0.}{Section_1.}{Section_2.}[Section_n]{.Key_0}{.Key_1}{.Key_n}"
/// 
/// </summary>
public interface ILinePattern : ILineFormat
{
    /// <summary>
    /// Pattern in string format
    /// </summary>
    string Pattern { get; }

    /// <summary>
    /// All parts of the pattern
    /// </summary>
    ILinePatternPart[] AllParts { get; }

    /// <summary>
    /// All parts that capture a part of string.
    /// </summary>
    ILinePatternPart[] CaptureParts { get; }

    /// <summary>
    /// Maps parts by identifier.
    /// </summary>
    IReadOnlyDictionary<string, ILinePatternPart> PartMap { get; }

    /// <summary>
    /// List of all parameter names
    /// </summary>
    string[] ParameterNames { get; }

    /// <summary>
    /// Maps parts by parameter identifier.
    /// </summary>
    IReadOnlyDictionary<string, ILinePatternPart[]> ParameterMap { get; }

    /// <summary>
    /// Match parameters from an object.
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    ILinePatternMatch Match(ILine key);

    /// <summary>
    /// A regular expression pattern that captures same parts from a filename string.
    /// </summary>
    Regex Regex { get; }
}

/// <summary>
/// Part of a pattern.
/// </summary>
public interface ILinePatternPart
{
    /// <summary>
    /// Text that represents this part in pattern.
    /// for "_n" part, the first part has "_n" in PatternText, and the rest have "".
    /// </summary>
    string PatternText { get; }

    /// <summary>
    /// Part identifier, unique in context of Pattern.CaptureParts.
    /// The first occurance is the "ParameterName" as is, and succeeding have underscore and index "ParameterName_#" starting with index '1'.
    /// </summary>
    string Identifier { get; }

    /// <summary>
    /// Separator
    /// </summary>
    string PrefixSeparator { get; }

    /// <summary>
    /// Separator
    /// </summary>
    string PostfixSeparator { get; }

    /// <summary>
    /// Parameter identifier. Does not include occurance index, e.g. "_1".
    /// </summary>
    string ParameterName { get; }

    /// <summary>
    /// If set, then is non-matchable Text part.
    /// </summary>
    string Text { get; }

    /// <summary>
    /// Is part mandatory
    /// </summary>
    bool Required { get; }

    /// <summary>
    /// Index in <see cref="ILinePattern.AllParts"/>.
    /// </summary>
    int Index { get; }

    /// <summary>
    /// Index in <see cref="ILinePattern.CaptureParts"/>.
    /// </summary>
    int CaptureIndex { get; }

    /// <summary>
    /// The order of occurance to capture against.
    /// 
    /// As special case Int32.MaxValue means the last occurance "{.Section}"
    /// 
    /// For example "{.Section_0}" captures first occurance, and the part's OccuranceIndex = 0.
    ///             "{.Section}" captures the last occurance overriding possible ordered occurance if there is only one match.
    /// </summary>
    int OccuranceIndex { get; }

    /// <summary>
    /// Regex pattern for this part.
    /// </summary>
    Regex Regex { get; }

    /// <summary>
    /// Tests if text is match.
    /// </summary>
    /// <param name="text"></param>
    /// <returns></returns>
    bool IsMatch(string text);
}

/// <summary>
/// Match result.
/// </summary>
public interface ILinePatternMatch : IReadOnlyDictionary<string, string>
{
    /// <summary>
    /// Associated patern.
    /// </summary>
    ILinePattern Pattern { get; }

    /// <summary>
    /// Resolved part values. Corresponds to <see cref="ILinePattern.CaptureParts"/>.
    /// </summary>
    string[] PartValues { get; }

    /// <summary>
    /// Part values by part index in <see cref="ILinePatternPart.CaptureIndex"/>.
    /// </summary>
    /// <param name="ix"></param>
    /// <returns></returns>
    string this[int ix] { get; }

    /// <summary>
    /// Get part value by part identifier.
    /// </summary>
    /// <param name="identifier">identifier, e.g. "Culture", "Type"</param>
    /// <returns>value or null</returns>
    new string this[string identifier] { get; }

    /// <summary>
    /// Where all required parts found.
    /// </summary>
    bool Success { get; }
}


LinePattern is a regular-expression like pattern to print and extract parameters from keys and strings.

// Let's create an example key
ILine key = LineAppender.NonResolving
    .Location("Patches")
    .Type("MyController")
    .Section("Errors")
    .Key("InvalidState")
    .Culture("en");
// Create pattern
ILineFormat myPolicy = new LinePattern("{Culture/}{Location/}{Type/}{Section/}[Key].txt");
// "en/Patches/MyController/Errors/InvalidState.txt"
string str = myPolicy.Print(key);

Name pattern consists of parameters. They are written in format of "{prefix ParameterName suffix}".
Braces "{parameter/}" make parameter optional, and brackets "[parameter/]" mandatory.

// Create pattern
ILineFormat myPolicy = new LinePattern("Patches/{Section}[-Key]{-Culture}.png");

Parameter can be added multiple times.

// Create pattern
ILineFormat myPolicy = new LinePattern("{Location/}{Location/}{Location/}{Section}{-Key}{-Culture}.png");
// Create key
ILine key2 = LineAppender.NonResolving
    .Location("Patches").Location("20181130").Section("icons").Key("ok").Culture("de");
// Converts to "Patches/20181130/icons-ok-de.png"
string str = myPolicy.Print(key2);

A shorter way to add consecutive parameters is use suffix "_n". It translates to the five following occurances. If part is required, e.g. "[parametername_n]", then only first part is required and others optional.

// "[Location_n/]" translates to "[Location_0/]{Location_1/}{Location_2/}{Location_3/}{Location_4/}"
ILineFormat myPolicy = new LinePattern("[Location_n/]{Section}{-Key}{-Culture}.png");
// Create key
ILine key2 = LineAppender.NonResolving
    .Location("Patches").Location("20181130").Section("icons").Key("ok").Culture("de");
// Converts to "Patches/20181130/icons-ok-de.png"
string str = myPolicy.Print(key2);

Parameters need to be added in non-consecutive order, then "_#" can be used to represent the occurance index.

// Create pattern
ILineFormat myPolicy = new LinePattern("{Location_3}{Location_2/}{Location_1/}{Location/}{Section}{-Key}{-Culture}.png");
// Create key
ILine key2 = LineAppender.NonResolving
    .Location("Patches").Location("20181130").Section("icons").Key("ok").Culture("de");
// Converts to "20181130/Patches/icons-ok-de.png"
string str = myPolicy.Print(key2);

Regular expression can be written inside angle brackets "{parameter<regexp>/}", which gives more control over matching.

// Create pattern with regular expression detail
ILinePattern myPolicy = new LinePattern("{Location<[^/]+>/}{Section}{-Key}{-Culture}.png");
// Use its regular expression
Match match = myPolicy.Regex.Match("patches/icons-ok-de.png");

LineParameterPrinter

LineParameterPrinter is a generic class that prints key parts into strings by applying configured rules.

Let's create an example key.

// Let's create an example key
ILine key = LineAppender.NonResolving
        .Location("Patches")
        .Section("Controllers")
        .Type("MyController")
        .Section("Errors")
        .Key("InvalidState")
        .Culture("en");

And now, let's try out different policies to see how they look.

// "en:Patches:Controllers:MyController:Errors:InvalidState"
string str1 = LineParameterPrinter.Default.Print(key);
// "en.Patches.Controllers.MyController.Errors.InvalidState"
string str2 = LineParameterPrinter.Dot_Dot_Dot.Print(key);
// "Patches:Controllers:MyController:Errors:InvalidState"
string str3 = LineParameterPrinter.None_Colon_Colon.Print(key);
// "en:Patches.Controllers.MyController.Errors.InvalidState"
string str4 = LineParameterPrinter.Colon_Dot_Dot.Print(key);
// "en:Patches:Controllers:MyController:Errors.InvalidState"
string str5 = LineParameterPrinter.Colon_Colon_Dot.Print(key);

Policy is created by adding rules to LineParameterPrinter.

// Create a custom policy 
ILineFormat myPolicy = new LineParameterPrinter()
    // Enable non-canonical "Culture" parameter with "/" separator
    .Rule("Culture", true, postfixSeparator: "/", order: ParameterInfos.Default.GetValue("Culture").Order)
    // Disable other non-canonical parts
    .NonCanonicalRule(false)
    // Enable canonical all parts with "/" separator
    .CanonicalRule(true, prefixSeparator: "/")
    // Set "Key" parameter's prefix to "/"
    .Rule("Key", true, prefixSeparator: "/", order: ParameterInfos.Default.GetValue("Key").Order);

// "en/Patches/MyController/Errors/InvalidState"
string str = myPolicy.Print(key);

Links

  • ILineFormat is the root interface for classes that formulate ILine into identity string.
  • ILineFormatPrinter is a subinterface where Build() can be implemented directly.
  • ILinePattern is a subinterface that formulates parametrization with a template string.
  • LineParameterPrinter is implementation of IAssetNameProvider.
  • LinePattern is the default implementation of ILinePattern.
  • LineFormat is context-free string format.
Back to top Copyright © 2015-2020 Toni Kalajainen