PatternMatcher.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/Serialization/SerializeContext.h>
  9. #include <AzCore/Serialization/EditContext.h>
  10. #include <AzCore/std/string/regex.h>
  11. #include <AzFramework/StringFunc/StringFunc.h>
  12. #include <SceneAPI/SceneCore/Utilities/Reporting.h>
  13. #include <SceneAPI/SceneCore/Utilities/PatternMatcher.h>
  14. namespace AZ
  15. {
  16. namespace SceneAPI
  17. {
  18. namespace SceneCore
  19. {
  20. PatternMatcher::PatternMatcher(const char* pattern, MatchApproach matcher)
  21. : m_patterns({ AZStd::string(pattern) })
  22. , m_matcher(matcher)
  23. {
  24. }
  25. PatternMatcher::PatternMatcher(const AZStd::string& pattern, MatchApproach matcher)
  26. : m_patterns({ pattern })
  27. , m_matcher(matcher)
  28. {
  29. }
  30. PatternMatcher::PatternMatcher(AZStd::string&& pattern, MatchApproach matcher)
  31. : m_patterns({ AZStd::move(pattern) })
  32. , m_matcher(matcher)
  33. {
  34. }
  35. PatternMatcher::PatternMatcher(const AZStd::span<const AZStd::string_view> patterns, MatchApproach matcher)
  36. : m_matcher(matcher)
  37. {
  38. m_patterns.reserve(patterns.size());
  39. for (auto& pattern : patterns)
  40. {
  41. m_patterns.emplace_back(pattern);
  42. }
  43. }
  44. PatternMatcher::PatternMatcher(const PatternMatcher& rhs)
  45. : m_patterns(rhs.m_patterns)
  46. , m_matcher(rhs.m_matcher)
  47. {
  48. }
  49. PatternMatcher::PatternMatcher(PatternMatcher&& rhs)
  50. : m_patterns(AZStd::move(rhs.m_patterns))
  51. , m_matcher(rhs.m_matcher)
  52. {
  53. }
  54. PatternMatcher& PatternMatcher::operator=(const PatternMatcher& rhs)
  55. {
  56. m_patterns = rhs.m_patterns;
  57. m_matcher = rhs.m_matcher;
  58. m_regexMatchers.clear();
  59. return *this;
  60. }
  61. PatternMatcher& PatternMatcher::operator=(PatternMatcher&& rhs)
  62. {
  63. m_patterns = AZStd::move(rhs.m_patterns);
  64. m_matcher = rhs.m_matcher;
  65. m_regexMatchers.clear();
  66. return *this;
  67. }
  68. bool PatternMatcher::LoadFromJson(rapidjson::Document::ConstMemberIterator member)
  69. {
  70. if (!member->value.HasMember("PatternMatcher"))
  71. {
  72. AZ_TracePrintf(Utilities::WarningWindow, "Missing element 'PatternMatcher'.");
  73. return false;
  74. }
  75. if (!member->value.HasMember("Pattern"))
  76. {
  77. AZ_TracePrintf(Utilities::WarningWindow, "Missing element 'Pattern'.");
  78. return false;
  79. }
  80. const auto& patternMatcherValue = member->value["PatternMatcher"];
  81. if (!patternMatcherValue.IsString())
  82. {
  83. AZ_TracePrintf(Utilities::WarningWindow, "Element 'PatternMatcher' is not a string.");
  84. return false;
  85. }
  86. const auto& patternValue = member->value["Pattern"];
  87. if (!patternValue.IsString())
  88. {
  89. AZ_TracePrintf(Utilities::WarningWindow, "Element 'Pattern' is not a string.");
  90. return false;
  91. }
  92. if (AzFramework::StringFunc::Equal(patternMatcherValue.GetString(), "postfix"))
  93. {
  94. m_matcher = MatchApproach::PostFix;
  95. }
  96. else if (AzFramework::StringFunc::Equal(patternMatcherValue.GetString(), "prefix"))
  97. {
  98. m_matcher = MatchApproach::PreFix;
  99. }
  100. else if (AzFramework::StringFunc::Equal(patternMatcherValue.GetString(), "regex"))
  101. {
  102. m_matcher = MatchApproach::Regex;
  103. }
  104. else
  105. {
  106. AZ_TracePrintf(Utilities::WarningWindow,
  107. "Element 'PatternMatcher' is no one of the available options postfix, prefix or regex.");
  108. return false;
  109. }
  110. m_patterns = { patternValue.GetString() };
  111. return true;
  112. }
  113. bool PatternMatcher::MatchesPattern(const char* name, size_t nameLength) const
  114. {
  115. return MatchesPattern(AZStd::string_view(name, nameLength));
  116. }
  117. bool PatternMatcher::MatchesPattern(AZStd::string_view name) const
  118. {
  119. constexpr bool caseSensitive = false;
  120. switch (m_matcher)
  121. {
  122. case MatchApproach::PreFix:
  123. for (const auto& pattern : m_patterns)
  124. {
  125. // Perform a case-insensitive prefix match.
  126. if (AZ::StringFunc::StartsWith(name, pattern, caseSensitive))
  127. {
  128. return true;
  129. }
  130. }
  131. return false;
  132. case MatchApproach::PostFix:
  133. for (const auto& pattern : m_patterns)
  134. {
  135. // Perform a case-insensitive postfix match.
  136. if (AZ::StringFunc::EndsWith(name, pattern, caseSensitive))
  137. {
  138. return true;
  139. }
  140. }
  141. return false;
  142. case MatchApproach::Regex:
  143. {
  144. for (size_t index = 0; index < m_patterns.size(); index++)
  145. {
  146. // Because PatternMatcher can get default constructed and serialized into directly, there's no good place
  147. // to initialize the regex matchers on construction, so we'll lazily initialize it here on first use.
  148. if (m_regexMatchers.empty())
  149. {
  150. m_regexMatchers.resize(m_patterns.size());
  151. }
  152. if (m_regexMatchers[index] == nullptr)
  153. {
  154. m_regexMatchers[index] = AZStd::make_unique<AZStd::regex>(m_patterns[index], AZStd::regex::extended);
  155. }
  156. AZStd::cmatch match;
  157. if (AZStd::regex_match(name.begin(), name.end(), match, *(m_regexMatchers[index])))
  158. {
  159. return true;
  160. }
  161. }
  162. return false;
  163. }
  164. default:
  165. AZ_Assert(false, "Unknown option '%i' for pattern matcher.", m_matcher);
  166. return false;
  167. }
  168. }
  169. const AZStd::string& PatternMatcher::GetPattern() const
  170. {
  171. const static AZStd::string emptyString{};
  172. return !m_patterns.empty() ? m_patterns.front() : emptyString;
  173. }
  174. PatternMatcher::MatchApproach PatternMatcher::GetMatchApproach() const
  175. {
  176. return m_matcher;
  177. }
  178. void PatternMatcher::Reflect(ReflectContext* context)
  179. {
  180. SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
  181. if (serializeContext)
  182. {
  183. serializeContext->Class<PatternMatcher>()
  184. ->Version(2)
  185. ->Field("patterns", &PatternMatcher::m_patterns)
  186. ->Field("matcher", &PatternMatcher::m_matcher);
  187. EditContext* editContext = serializeContext->GetEditContext();
  188. if (editContext)
  189. {
  190. editContext->Class<PatternMatcher>("Pattern matcher", "")
  191. ->ClassElement(Edit::ClassElements::EditorData, "")
  192. ->Attribute(Edit::Attributes::AutoExpand, true)
  193. ->DataElement(Edit::UIHandlers::Default, &PatternMatcher::m_patterns, "Patterns", "The patterns the matcher will check against.")
  194. ->DataElement(Edit::UIHandlers::ComboBox, &PatternMatcher::m_matcher, "Matcher", "The used approach for matching.")
  195. ->EnumAttribute(MatchApproach::PreFix, "PreFix")
  196. ->EnumAttribute(MatchApproach::PostFix, "PostFix")
  197. ->EnumAttribute(MatchApproach::Regex, "Regex");
  198. }
  199. }
  200. }
  201. } // SceneCore
  202. } // SceneAPI
  203. } // AZ