ALib C++ Library
Library Version: 2402 R1
Documentation generated by doxygen
Loading...
Searching...
No Matches
wildcardmatcher.cpp
1// #################################################################################################
2// ALib C++ Library
3//
4// Copyright 2013-2024 A-Worx GmbH, Germany
5// Published under 'Boost Software License' (a free software license, see LICENSE.txt)
6// #################################################################################################
8
9#if !defined(ALIB_DOX)
10#if !defined (HPP_ALIB_STRINGS_UTIL_WILDCARDMATCHER)
12#endif
13
14#if !defined (HPP_ALIB_STRINGS_SUBSTRING)
16#endif
17#endif // !defined(ALIB_DOX)
18
19namespace alib { namespace strings { namespace util {
20
21#define STRING -1
22#define ASTERISK -2
23
24template<typename TChar>
26{
27 // Note: The following invariants will be true in the command vector:
28 // - A String-entry will be a non-empty string
29 // - No two * follow each other"
30 // - A ? will not follow an asterisk (combinations of ? and ' will be sorted to "?(n) *"
31
32 commands.clear();
33
34 TSubstring<TChar> parser= pattern;
35 while( parser.IsNotEmpty() )
36 {
37 // *
38 if(parser.CharAtStart() == '*' )
39 {
40 // add if empty or last is not * already.
41 if( commands.size() == 0
42 || commands.back().first != ASTERISK ) commands.emplace_back( ASTERISK, nullptr );
43
44 parser.ConsumeChar();
45 continue;
46 }
47
48 // ?
49 int qtyQ= 0;
50 while( parser.ConsumeChar('?') )
51 ++qtyQ;
52
53 if( qtyQ )
54 {
55 // previous is "*" ?
56 if( commands.size() > 1
57 && (commands.end()-1)->first == ASTERISK )
58 {
59
60 // before * we already have Q? -> add to the Q
61 if( commands.size() >= 2
62 && (commands.end()-2)->first > 0 )
63 {
64 (commands.end()-2)->first+= qtyQ;
65 continue;
66 }
67
68 commands.emplace( commands.end() - 1, qtyQ, nullptr );
69 continue;
70 }
71
72 // Just add it
73 commands.emplace_back( qtyQ, nullptr );
74 continue;
75 }
76
77 // strings
78 integer idx= 1;
79 while ( idx < parser.Length() && parser[idx] != '*' && parser[idx] != '?' )
80 ++idx;
81
82 commands.emplace_back( STRING, TString<TChar>(parser.Buffer(), idx ) );
83 parser.ConsumeChars( idx );
84 continue;
85 }
86
87}
88
89template<typename TChar>
90bool TWildcardMatcher<TChar>::Match( const TString<TChar>& pHaystack, lang::Case sensitivity )
91{
92 if( commands.size() == 0 )
93 return true;
94
95 if( pHaystack.IsNull() )
96 return false;
97
98 TSubstring<TChar> haystack= pHaystack;
99 size_t actCmd = 0;
100 size_t lastCmd= static_cast<size_t>( commands.size() - 1 );
101 bool lastWasAsterisk= false;
102
103 for( actCmd= 0; actCmd <= lastCmd ; ++actCmd )
104 {
105 auto& cmd= commands[actCmd];
106
107 // ?
108 if( cmd.first > 0 )
109 {
110 if ( haystack.Length() < cmd.first )
111 return false;
112 haystack.template ConsumeChars<false>( cmd.first );
113 continue;
114 }
115
116 // *
117 if( cmd.first == ASTERISK )
118 {
119 if ( actCmd == lastCmd )
120 return true;
121
122 lastWasAsterisk= true;
123 continue;
124 }
125
126 // string
127 if( cmd.second.Length() > haystack.Length() )
128 return false;
129
130 if( lastWasAsterisk )
131 {
132 integer idx= sensitivity==lang::Case::Sensitive ? haystack.template IndexOf<false, lang::Case::Sensitive>( cmd.second )
133 : haystack.template IndexOf<false, lang::Case::Ignore >( cmd.second );
134 if( idx < 0 )
135 return false;
136 haystack.ConsumeChars( idx + cmd.second.Length() );
137 lastWasAsterisk= false;
138 continue;
139 }
140
141 if( sensitivity==lang::Case::Sensitive ? (!haystack.template StartsWith<false, lang::Case::Sensitive>( cmd.second ) )
142 : (!haystack.template StartsWith<false, lang::Case::Ignore >( cmd.second ) ) )
143 return false;
144
145 haystack.template ConsumeChars<false>( cmd.second.Length() );
146 }
147
148 return haystack.IsEmpty() || lastWasAsterisk;
149}
150
151template void TWildcardMatcher<nchar>::Compile( const TString<nchar>& pattern );
152template bool TWildcardMatcher<nchar>::Match ( const TString<nchar>& haystack, lang::Case sensitivity );
153template void TWildcardMatcher<wchar>::Compile( const TString<wchar>& pattern );
154template bool TWildcardMatcher<wchar>::Match ( const TString<wchar>& haystack, lang::Case sensitivity );
155
156#undef STRING
157#undef ASTERISK
158}}} // namespace [alib::strings::util]
constexpr bool IsNull() const
Definition string.hpp:395
constexpr bool IsEmpty() const
Definition string.hpp:414
constexpr bool IsNotEmpty() const
Definition string.hpp:420
constexpr integer Length() const
Definition string.hpp:357
TChar CharAtStart() const
Definition string.hpp:459
constexpr const TChar * Buffer() const
Definition string.hpp:350
integer ConsumeChars(integer regionLength, TSubstring *target=nullptr)
ALIB_API bool Match(const TString< TChar > &haystack, lang::Case sensitivity=lang::Case::Sensitive)
ALIB_API void Compile(const TString< TChar > &pattern)
Definition alib.cpp:57
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:286