File: StringExtensions.cs
Web Access
Project: Microsoft.DotNet.XliffTasks.csproj (Microsoft.DotNet.XliffTasks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Linq;
using System.Text.RegularExpressions;

namespace XliffTasks;

internal static class StringExtensions
{
    /// <summary>
    /// Attempts to match formatting placeholders as documented at
    /// https://docs.microsoft.com/en-us/dotnet/standard/base-types/composite-formatting.
    ///
    /// Explanation:
    ///
    /// \{
    ///    A placeholder starts with an open curly brace. Since curly braces are used in
    ///    regex syntax we escape it to be clear that we mean a literal {.
    ///
    /// (\d+)
    ///    The "index" component; one or more decimal digits. This is captured in a group
    ///    to facilitate extracting the numeric value.
    ///
    /// (\,\-?\d+)?
    ///    The optional "alignment" component. This is a comma, followed by an optional
    ///    minus sign, followed by one or more decimal digits.
    ///
    /// (\:[^\}]+)?
    ///    The optional "format string" component. This is a colon, followed by one or more
    ///    characters that aren't close curly braces.
    ///
    /// \}
    ///    The close curly brace indicates the end of the placeholder.
    /// </summary>
    private static readonly Regex s_placeholderRegex = new(@"\{(\d+)(\,\-?\d+)?(\:[^\}]+)?\}", RegexOptions.Compiled);

    /// <summary>
    /// Returns the number of replacement strings needed to properly format the given text.
    /// </summary>
    public static int GetReplacementCount(this string text)
    {
        int replacementCount = 0;

        foreach (Match placeholder in s_placeholderRegex.Matches(text))
        {
            int index = int.Parse(placeholder.Groups[1].Value);
            replacementCount = Math.Max(replacementCount, index + 1);
        }

        return replacementCount;
    }
}