ALib C++ Library
Library Version: 2412 R0
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 !DOXYGEN
12#endif // !DOXYGEN
13
14namespace alib { namespace strings { namespace util {
15
16#define STRING -1
17#define ASTERISK -2
18
19template<typename TChar>
21{
22 // Note: The following invariants will be true in the command vector:
23 // - A String-entry will be a non-empty string
24 // - No two * follow each other"
25 // - A ? will not follow an asterisk (combinations of ? and ' will be sorted to "?(n) *"
26
27 commands.clear();
28
29 TSubstring<TChar> parser= pattern;
30 while( parser.IsNotEmpty() )
31 {
32 // *
33 if(parser.CharAtStart() == '*' )
34 {
35 // add if empty or last is not * already.
36 if( commands.size() == 0
37 || commands.back().first != ASTERISK ) commands.emplace_back( ASTERISK, nullptr );
38
39 parser.ConsumeChar();
40 continue;
41 }
42
43 // ?
44 int qtyQ= 0;
45 while( parser.ConsumeChar('?') )
46 ++qtyQ;
47
48 if( qtyQ )
49 {
50 // previous is "*" ?
51 if( commands.size() > 1
52 && (commands.end()-1)->first == ASTERISK )
53 {
54
55 // before * we already have Q? -> add to the Q
56 if( commands.size() >= 2
57 && (commands.end()-2)->first > 0 )
58 {
59 (commands.end()-2)->first+= qtyQ;
60 continue;
61 }
62
63 commands.emplace( commands.end() - 1, qtyQ, nullptr );
64 continue;
65 }
66
67 // Just add it
68 commands.emplace_back( qtyQ, nullptr );
69 continue;
70 }
71
72 // strings
73 integer idx= 1;
74 while ( idx < parser.Length() && parser[idx] != '*' && parser[idx] != '?' )
75 ++idx;
76
77 commands.emplace_back( STRING, TString<TChar>(parser.Buffer(), idx ) );
78 parser.ConsumeChars( idx );
79 continue;
80 }
81
82}
83
84template<typename TChar>
85bool TWildcardMatcher<TChar>::Match( const TString<TChar>& pHaystack, lang::Case sensitivity )
86{
87 if( commands.size() == 0 )
88 return true;
89
90 if( pHaystack.IsNull() )
91 return false;
92
93 TSubstring<TChar> haystack= pHaystack;
94 size_t actCmd = 0;
95 size_t lastCmd= size_t( commands.size() - 1 );
96 bool lastWasAsterisk= false;
97
98 for( actCmd= 0; actCmd <= lastCmd ; ++actCmd )
99 {
100 auto& cmd= commands[actCmd];
101
102 // ?
103 if( cmd.first > 0 )
104 {
105 if ( haystack.Length() < cmd.first )
106 return false;
107 haystack.template ConsumeChars<NC>( cmd.first );
108 continue;
109 }
110
111 // *
112 if( cmd.first == ASTERISK )
113 {
114 if ( actCmd == lastCmd )
115 return true;
116
117 lastWasAsterisk= true;
118 continue;
119 }
120
121 // string
122 if( cmd.second.Length() > haystack.Length() )
123 return false;
124
125 if( lastWasAsterisk )
126 {
127 integer idx= sensitivity==lang::Case::Sensitive ? haystack.template IndexOf<NC, lang::Case::Sensitive>( cmd.second, 0, haystack.Length() - cmd.second.Length() + 1 )
128 : haystack.template IndexOf<NC, lang::Case::Ignore >( cmd.second, 0, haystack.Length() - cmd.second.Length() + 1 );
129 if( idx < 0 )
130 return false;
131 haystack.ConsumeChars( idx + cmd.second.Length() );
132 lastWasAsterisk= false;
133 continue;
134 }
135
136 if( sensitivity==lang::Case::Sensitive ? (!haystack.template StartsWith<NC, lang::Case::Sensitive>( cmd.second ) )
137 : (!haystack.template StartsWith<NC, lang::Case::Ignore >( cmd.second ) ) )
138 return false;
139
140 haystack.template ConsumeChars<NC>( cmd.second.Length() );
141 }
142
143 return haystack.IsEmpty() || lastWasAsterisk;
144}
145
146template void TWildcardMatcher<nchar>::Compile( const TString<nchar>& pattern );
147template bool TWildcardMatcher<nchar>::Match ( const TString<nchar>& haystack, lang::Case sensitivity );
148template void TWildcardMatcher<wchar>::Compile( const TString<wchar>& pattern );
149template bool TWildcardMatcher<wchar>::Match ( const TString<wchar>& haystack, lang::Case sensitivity );
150
151#undef STRING
152#undef ASTERISK
153}}} // namespace [alib::strings::util]
154
constexpr bool IsNull() const
Definition string.hpp:364
constexpr bool IsEmpty() const
Definition string.hpp:383
constexpr bool IsNotEmpty() const
Definition string.hpp:389
constexpr integer Length() const
Definition string.hpp:326
TChar CharAtStart() const
Definition string.hpp:466
constexpr const TChar * Buffer() const
Definition string.hpp:319
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)
Case
Denotes upper and lower case character treatment.
Definition alib.cpp:69
lang::integer integer
Type alias in namespace alib.
Definition integers.hpp:273