using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Abp.Collections.Extensions;
using Abp.Text.Formatting;
namespace Abp.Text
{
///
/// This class is used to extract dynamic values from a formatted string.
/// It works as reverse of
///
///
/// Say that str is "My name is Neo." and format is "My name is {name}.".
/// Then Extract method gets "Neo" as "name".
///
public class FormattedStringValueExtracter
{
///
/// Extracts dynamic values from a formatted string.
///
/// String including dynamic values
/// Format of the string
/// True, to search case-insensitive.
/// format is splitted using this character when provided.
public ExtractionResult Extract(string str, string format, bool ignoreCase = false, char? splitformatCharacter = null)
{
var stringComparison = ignoreCase
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;
if (str == format)
{
return new ExtractionResult(true);
}
var formatTokens = TokenizeFormat(format, splitformatCharacter);
if (formatTokens.IsNullOrEmpty())
{
return new ExtractionResult(str == "");
}
var result = new ExtractionResult(false);
for (var i = 0; i < formatTokens.Count; i++)
{
var currentToken = formatTokens[i];
var previousToken = i > 0 ? formatTokens[i - 1] : null;
if (currentToken.Type == FormatStringTokenType.ConstantText)
{
if (i == 0)
{
if (str.StartsWith(currentToken.Text, stringComparison))
{
str = str.Substring(currentToken.Text.Length);
}
}
else
{
var matchIndex = str.IndexOf(currentToken.Text, stringComparison);
if (matchIndex >= 0)
{
Debug.Assert(previousToken != null, "previousToken can not be null since i > 0 here");
result.Matches.Add(new NameValue(previousToken.Text, str.Substring(0, matchIndex)));
result.IsMatch = true;
str = str.Substring(matchIndex + currentToken.Text.Length);
}
}
}
}
var lastToken = formatTokens.Last();
if (lastToken.Type == FormatStringTokenType.DynamicValue)
{
result.Matches.Add(new NameValue(lastToken.Text, str));
result.IsMatch = true;
}
return result;
}
private List TokenizeFormat(string originalFormat, char? splitformatCharacter = null)
{
if (splitformatCharacter == null)
{
return new FormatStringTokenizer().Tokenize(originalFormat);
}
var result = new List();
var formats = originalFormat.Split(splitformatCharacter.Value);
foreach (var format in formats)
{
result.AddRange(new FormatStringTokenizer().Tokenize(format));
}
return result;
}
///
/// Checks if given fits to given .
/// Also gets extracted values.
///
/// String including dynamic values
/// Format of the string
/// Array of extracted values if matched
/// True, to search case-insensitive
/// True, if matched.
public static bool IsMatch(string str, string format, out string[] values, bool ignoreCase = false)
{
var result = new FormattedStringValueExtracter().Extract(str, format, ignoreCase);
if (!result.IsMatch)
{
values = new string[0];
return false;
}
values = result.Matches.Select(m => m.Value).ToArray();
return true;
}
///
/// Used as return value of method.
///
public class ExtractionResult
{
///
/// Is fully matched.
///
public bool IsMatch { get; set; }
///
/// List of matched dynamic values.
///
public List Matches { get; private set; }
internal ExtractionResult(bool isMatch)
{
IsMatch = isMatch;
Matches = new List();
}
}
}
}