KiCad PCB EDA Suite
eda_pattern_match.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2015-2017 Chris Pavlina <[email protected]>
5  * Copyright (C) 2015-2019 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
25 #include <eda_pattern_match.h>
26 #include <wx/log.h>
27 #include <wx/tokenzr.h>
28 #include <algorithm>
29 #include <climits>
30 
31 bool EDA_PATTERN_MATCH_SUBSTR::SetPattern( const wxString& aPattern )
32 {
33  m_pattern = aPattern;
34  return true;
35 }
36 
37 
38 wxString const& EDA_PATTERN_MATCH_SUBSTR::GetPattern() const
39 {
40  return m_pattern;
41 }
42 
43 
45 {
46  int loc = aCandidate.Find( m_pattern );
47 
48  if( loc == wxNOT_FOUND )
49  return {};
50  else
51  return { loc, static_cast<int>( m_pattern.size() ) };
52 }
53 
54 
59 {
60  wxLogLevel m_old_level;
61 
62 public:
63  WX_LOGLEVEL_CONTEXT( wxLogLevel level )
64  {
65  m_old_level = wxLog::GetLogLevel();
66  wxLog::SetLogLevel( level );
67  }
68 
70  {
71  wxLog::SetLogLevel( m_old_level );
72  }
73 };
74 
75 
76 bool EDA_PATTERN_MATCH_REGEX::SetPattern( const wxString& aPattern )
77 {
78  m_pattern = aPattern;
79 
80  // Evil and undocumented: wxRegEx::Compile calls wxLogError on error, even
81  // though it promises to just return false. Silence the error.
82  WX_LOGLEVEL_CONTEXT ctx( wxLOG_FatalError );
83 
84  return m_regex.Compile( aPattern, wxRE_ADVANCED );
85 }
86 
87 
88 wxString const& EDA_PATTERN_MATCH_REGEX::GetPattern() const
89 {
90  return m_pattern;
91 }
92 
93 
95 {
96  if( m_regex.IsValid() )
97  {
98  if( m_regex.Matches( aCandidate ) )
99  {
100  size_t start, len;
101  m_regex.GetMatch( &start, &len, 0 );
102 
103  return { static_cast<int>( std::min( start, static_cast<size_t>( INT_MAX ) ) ),
104  static_cast<int>( std::min( len, static_cast<size_t>( INT_MAX ) ) ) };
105  }
106  else
107  {
108  return {};
109  }
110  }
111  else
112  {
113  int loc = aCandidate.Find( m_pattern );
114 
115  if( loc == wxNOT_FOUND )
116  return {};
117  else
118  return { loc, static_cast<int>( m_pattern.size() ) };
119  }
120 }
121 
122 
123 bool EDA_PATTERN_MATCH_WILDCARD::SetPattern( const wxString& aPattern )
124 {
125  m_wildcard_pattern = aPattern;
126 
127  // Compile the wildcard string to a regular expression
128  wxString regex;
129  regex.Alloc( 2 * aPattern.Length() ); // no need to keep resizing, we know the size roughly
130 
131  const wxString to_replace = wxT( ".*+?^${}()|[]/\\" );
132 
133  for( wxString::const_iterator it = aPattern.begin(); it < aPattern.end(); ++it )
134  {
135  wxUniChar c = *it;
136  if( c == '?' )
137  {
138  regex += wxT( "." );
139  }
140  else if( c == '*' )
141  {
142  regex += wxT( ".*" );
143  }
144  else if( to_replace.Find( c ) != wxNOT_FOUND )
145  {
146  regex += "\\";
147  regex += c;
148  }
149  else
150  {
151  regex += c;
152  }
153  }
154 
155  return EDA_PATTERN_MATCH_REGEX::SetPattern( regex );
156 }
157 
158 
160 {
161  return m_wildcard_pattern;
162 }
163 
164 
166 {
167  return EDA_PATTERN_MATCH_REGEX::Find( aCandidate );
168 }
169 
170 
171 bool EDA_PATTERN_MATCH_WILDCARD_EXPLICIT::SetPattern( const wxString& aPattern )
172 {
173  m_wildcard_pattern = aPattern;
174 
175  // Compile the wildcard string to a regular expression
176  wxString regex;
177  regex.Alloc( 2 * aPattern.Length() ); // no need to keep resizing, we know the size roughly
178 
179  const wxString to_replace = wxT( ".*+?^${}()|[]/\\" );
180 
181  regex += wxT( "^" );
182  for( wxString::const_iterator it = aPattern.begin(); it < aPattern.end(); ++it )
183  {
184  wxUniChar c = *it;
185  if( c == '?' )
186  {
187  regex += wxT( "." );
188  }
189  else if( c == '*' )
190  {
191  regex += wxT( ".*" );
192  }
193  else if( to_replace.Find( c ) != wxNOT_FOUND )
194  {
195  regex += "\\";
196  regex += c;
197  }
198  else
199  {
200  regex += c;
201  }
202  }
203  regex += wxT( "$" );
204 
205  return EDA_PATTERN_MATCH_REGEX::SetPattern( regex );
206 }
207 
208 
209 bool EDA_PATTERN_MATCH_RELATIONAL::SetPattern( const wxString& aPattern )
210 {
211  bool matches = m_regex_search.Matches( aPattern );
212 
213  if( !matches || m_regex_search.GetMatchCount() < 5 )
214  return false;
215 
216  m_pattern = aPattern;
217  wxString key = m_regex_search.GetMatch( aPattern, 1 );
218  wxString rel = m_regex_search.GetMatch( aPattern, 2 );
219  wxString val = m_regex_search.GetMatch( aPattern, 3 );
220  wxString unit = m_regex_search.GetMatch( aPattern, 4 );
221 
222  m_key = key.Lower();
223 
224  if( rel == "<" )
225  m_relation = LT;
226  else if( rel == "<=" )
227  m_relation = LE;
228  else if( rel == "=" )
229  m_relation = EQ;
230  else if( rel == ">=" )
231  m_relation = GE;
232  else if( rel == ">" )
233  m_relation = GT;
234  else
235  return false;
236 
237  if( val == "" )
238  {
239  // Matching on empty values keeps the match list from going empty when
240  // the user types the relational operator character, which helps prevent
241  // confusion.
242  m_relation = NONE;
243  }
244  else if( !val.ToCDouble( &m_value ) )
245  return false;
246 
247  auto unit_it = m_units.find( unit.Lower() );
248 
249  if( unit_it != m_units.end() )
250  m_value *= unit_it->second;
251  else
252  return false;
253 
254  m_pattern = aPattern;
255 
256  return true;
257 }
258 
259 
261 {
262  return m_pattern;
263 }
264 
265 
267  const wxString& aCandidate ) const
268 {
269  wxStringTokenizer tokenizer( aCandidate );
270  size_t lastpos = 0;
271 
272  while( tokenizer.HasMoreTokens() )
273  {
274  const wxString token = tokenizer.GetNextToken();
275  int found_delta = FindOne( token );
276 
277  if( found_delta != EDA_PATTERN_NOT_FOUND )
278  {
279  size_t found = (size_t) found_delta + lastpos;
280  return { static_cast<int>( std::min( found, static_cast<size_t>( INT_MAX ) ) ), 0 };
281  }
282 
283  lastpos = tokenizer.GetPosition();
284  }
285 
286  return {};
287 }
288 
289 
290 int EDA_PATTERN_MATCH_RELATIONAL::FindOne( const wxString& aCandidate ) const
291 {
292  bool matches = m_regex_description.Matches( aCandidate );
293 
294  if( !matches )
295  return EDA_PATTERN_NOT_FOUND;
296 
297  size_t start, len;
298  m_regex_description.GetMatch( &start, &len, 0 );
299  wxString key = m_regex_description.GetMatch( aCandidate, 1 );
300  wxString val = m_regex_description.GetMatch( aCandidate, 2 );
301  wxString unit = m_regex_description.GetMatch( aCandidate, 3 );
302 
303  int istart = ( start > INT_MAX ) ? INT_MAX : start;
304 
305  if( key.Lower() != m_key )
306  return EDA_PATTERN_NOT_FOUND;
307 
308  double val_parsed;
309 
310  if( !val.ToCDouble( &val_parsed ) )
311  return EDA_PATTERN_NOT_FOUND;
312 
313  auto unit_it = m_units.find( unit.Lower() );
314 
315  if( unit_it != m_units.end() )
316  val_parsed *= unit_it->second;
317 
318  switch( m_relation )
319  {
320  case LT: return val_parsed < m_value ? istart : EDA_PATTERN_NOT_FOUND;
321  case LE: return val_parsed <= m_value ? istart : EDA_PATTERN_NOT_FOUND;
322  case EQ: return val_parsed == m_value ? istart : EDA_PATTERN_NOT_FOUND;
323  case GE: return val_parsed >= m_value ? istart : EDA_PATTERN_NOT_FOUND;
324  case GT: return val_parsed > m_value ? istart : EDA_PATTERN_NOT_FOUND;
325  case NONE: return istart;
326  default: return EDA_PATTERN_NOT_FOUND;
327  }
328 }
329 
330 
332  R"((\w+)[=:]([-+]?[\d.]+)(\w*))", wxRE_ADVANCED );
334  R"(^(\w+)(<|<=|=|>=|>)([-+]?[\d.]*)(\w*)$)", wxRE_ADVANCED );
335 const std::map<wxString, double> EDA_PATTERN_MATCH_RELATIONAL::m_units = {
336  { "p", 1e-12 },
337  { "n", 1e-9 },
338  { "u", 1e-6 },
339  { "m", 1e-3 },
340  { "", 1. },
341  { "k", 1e3 },
342  { "meg",1e6 },
343  { "g", 1e9 },
344  { "t", 1e12 },
345  { "ki", 1024. },
346  { "mi", 1048576. },
347  { "gi", 1073741824. },
348  { "ti", 1099511627776. } };
349 
350 
352  : m_pattern( aPattern )
353 {
354  // Whatever syntax users prefer, it shall be matched.
355  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_REGEX>() );
356  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
357  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_RELATIONAL>() );
358  // If any of the above matchers couldn't be created because the pattern
359  // syntax does not match, the substring will try its best.
360  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_SUBSTR>() );
361 }
362 
363 
364 bool EDA_COMBINED_MATCHER::Find( const wxString& aTerm, int& aMatchersTriggered, int& aPosition )
365 {
366  aPosition = EDA_PATTERN_NOT_FOUND;
367  aMatchersTriggered = 0;
368 
369  for( auto const& matcher : m_matchers )
370  {
371  EDA_PATTERN_MATCH::FIND_RESULT local_find = matcher->Find( aTerm );
372 
373  if( local_find )
374  {
375  aMatchersTriggered += 1;
376 
377  if( local_find.start < aPosition || aPosition == EDA_PATTERN_NOT_FOUND )
378  {
379  aPosition = local_find.start;
380  }
381  }
382  }
383 
384  return aPosition != EDA_PATTERN_NOT_FOUND;
385 }
386 
387 
388 wxString const& EDA_COMBINED_MATCHER::GetPattern() const
389 {
390  return m_pattern;
391 }
392 
393 
395  const wxString &aPattern,
396  std::unique_ptr<EDA_PATTERN_MATCH> aMatcher )
397 {
398  if ( aMatcher->SetPattern( aPattern ) )
399  {
400  m_matchers.push_back( std::move( aMatcher ) );
401  }
402 }
std::vector< std::unique_ptr< EDA_PATTERN_MATCH > > m_matchers
virtual wxString const & GetPattern() const override
Return the pattern passed to SetPattern().
virtual FIND_RESULT Find(const wxString &aCandidate) const override
Return the location and possibly length of a match iff a given candidate string matches the set patte...
int FindOne(const wxString &aCandidate) const
const wxString & GetPattern() const
virtual wxString const & GetPattern() const override
Return the pattern passed to SetPattern().
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
void AddMatcher(const wxString &aPattern, std::unique_ptr< EDA_PATTERN_MATCH > aMatcher)
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
Abstract pattern-matching tool and implementations.
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
bool Find(const wxString &aTerm, int &aMatchersTriggered, int &aPosition)
static const int EDA_PATTERN_NOT_FOUND
virtual bool SetPattern(const wxString &aPattern) override
Set the pattern against which candidates will be matched.
virtual wxString const & GetPattern() const override
Return the pattern passed to SetPattern().
Context class to set wx loglevel for a block, and always restore it at the end.
virtual FIND_RESULT Find(const wxString &aCandidate) const override
Return the location and possibly length of a match iff a given candidate string matches the set patte...
virtual wxString const & GetPattern() const override
Return the pattern passed to SetPattern().
WX_LOGLEVEL_CONTEXT(wxLogLevel level)
virtual FIND_RESULT Find(const wxString &aCandidate) const override
Return the location and possibly length of a match iff a given candidate string matches the set patte...
virtual FIND_RESULT Find(const wxString &aCandidate) const override
Return the location and possibly length of a match iff a given candidate string matches the set patte...
EDA_COMBINED_MATCHER(const wxString &aPattern)
static const std::map< wxString, double > m_units