FormattedStringValueExtracter.cs 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using Abp.Collections.Extensions;
  6. using Abp.Text.Formatting;
  7. namespace Abp.Text
  8. {
  9. /// <summary>
  10. /// This class is used to extract dynamic values from a formatted string.
  11. /// It works as reverse of <see cref="string.Format(string,object)"/>
  12. /// </summary>
  13. /// <example>
  14. /// Say that str is "My name is Neo." and format is "My name is {name}.".
  15. /// Then Extract method gets "Neo" as "name".
  16. /// </example>
  17. public class FormattedStringValueExtracter
  18. {
  19. /// <summary>
  20. /// Extracts dynamic values from a formatted string.
  21. /// </summary>
  22. /// <param name="str">String including dynamic values</param>
  23. /// <param name="format">Format of the string</param>
  24. /// <param name="ignoreCase">True, to search case-insensitive.</param>
  25. /// <param name="splitformatCharacter">format is splitted using this character when provided.</param>
  26. public ExtractionResult Extract(string str, string format, bool ignoreCase = false, char? splitformatCharacter = null)
  27. {
  28. var stringComparison = ignoreCase
  29. ? StringComparison.OrdinalIgnoreCase
  30. : StringComparison.Ordinal;
  31. if (str == format)
  32. {
  33. return new ExtractionResult(true);
  34. }
  35. var formatTokens = TokenizeFormat(format, splitformatCharacter);
  36. if (formatTokens.IsNullOrEmpty())
  37. {
  38. return new ExtractionResult(str == "");
  39. }
  40. var result = new ExtractionResult(false);
  41. for (var i = 0; i < formatTokens.Count; i++)
  42. {
  43. var currentToken = formatTokens[i];
  44. var previousToken = i > 0 ? formatTokens[i - 1] : null;
  45. if (currentToken.Type == FormatStringTokenType.ConstantText)
  46. {
  47. if (i == 0)
  48. {
  49. if (str.StartsWith(currentToken.Text, stringComparison))
  50. {
  51. str = str.Substring(currentToken.Text.Length);
  52. }
  53. }
  54. else
  55. {
  56. var matchIndex = str.IndexOf(currentToken.Text, stringComparison);
  57. if (matchIndex >= 0)
  58. {
  59. Debug.Assert(previousToken != null, "previousToken can not be null since i > 0 here");
  60. result.Matches.Add(new NameValue(previousToken.Text, str.Substring(0, matchIndex)));
  61. result.IsMatch = true;
  62. str = str.Substring(matchIndex + currentToken.Text.Length);
  63. }
  64. }
  65. }
  66. }
  67. var lastToken = formatTokens.Last();
  68. if (lastToken.Type == FormatStringTokenType.DynamicValue)
  69. {
  70. result.Matches.Add(new NameValue(lastToken.Text, str));
  71. result.IsMatch = true;
  72. }
  73. return result;
  74. }
  75. private List<FormatStringToken> TokenizeFormat(string originalFormat, char? splitformatCharacter = null)
  76. {
  77. if (splitformatCharacter == null)
  78. {
  79. return new FormatStringTokenizer().Tokenize(originalFormat);
  80. }
  81. var result = new List<FormatStringToken>();
  82. var formats = originalFormat.Split(splitformatCharacter.Value);
  83. foreach (var format in formats)
  84. {
  85. result.AddRange(new FormatStringTokenizer().Tokenize(format));
  86. }
  87. return result;
  88. }
  89. /// <summary>
  90. /// Checks if given <see cref="str"/> fits to given <see cref="format"/>.
  91. /// Also gets extracted values.
  92. /// </summary>
  93. /// <param name="str">String including dynamic values</param>
  94. /// <param name="format">Format of the string</param>
  95. /// <param name="values">Array of extracted values if matched</param>
  96. /// <param name="ignoreCase">True, to search case-insensitive</param>
  97. /// <returns>True, if matched.</returns>
  98. public static bool IsMatch(string str, string format, out string[] values, bool ignoreCase = false)
  99. {
  100. var result = new FormattedStringValueExtracter().Extract(str, format, ignoreCase);
  101. if (!result.IsMatch)
  102. {
  103. values = new string[0];
  104. return false;
  105. }
  106. values = result.Matches.Select(m => m.Value).ToArray();
  107. return true;
  108. }
  109. /// <summary>
  110. /// Used as return value of <see cref="Extract"/> method.
  111. /// </summary>
  112. public class ExtractionResult
  113. {
  114. /// <summary>
  115. /// Is fully matched.
  116. /// </summary>
  117. public bool IsMatch { get; set; }
  118. /// <summary>
  119. /// List of matched dynamic values.
  120. /// </summary>
  121. public List<NameValue> Matches { get; private set; }
  122. internal ExtractionResult(bool isMatch)
  123. {
  124. IsMatch = isMatch;
  125. Matches = new List<NameValue>();
  126. }
  127. }
  128. }
  129. }