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 <pavlina.chris@gmail.com>
5  * Copyright (C) 2015-2017 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 <climits>
29 #include <make_unique.h>
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 
44 int EDA_PATTERN_MATCH_SUBSTR::Find( const wxString& aCandidate ) const
45 {
46  int loc = aCandidate.Find( m_pattern );
47 
48  return ( loc == wxNOT_FOUND ) ? EDA_PATTERN_NOT_FOUND : loc;
49 }
50 
51 
56 {
57  wxLogLevel m_old_level;
58 
59 public:
60  WX_LOGLEVEL_CONTEXT( wxLogLevel level )
61  {
62  m_old_level = wxLog::GetLogLevel();
63  wxLog::SetLogLevel( level );
64  }
65 
67  {
68  wxLog::SetLogLevel( m_old_level );
69  }
70 };
71 
72 
73 bool EDA_PATTERN_MATCH_REGEX::SetPattern( const wxString& aPattern )
74 {
75  m_pattern = aPattern;
76 
77  // Evil and undocumented: wxRegEx::Compile calls wxLogError on error, even
78  // though it promises to just return false. Silence the error.
79  WX_LOGLEVEL_CONTEXT ctx( wxLOG_FatalError );
80 
81  return m_regex.Compile( aPattern, wxRE_ADVANCED );
82 }
83 
84 
85 wxString const& EDA_PATTERN_MATCH_REGEX::GetPattern() const
86 {
87  return m_pattern;
88 }
89 
90 
91 int EDA_PATTERN_MATCH_REGEX::Find( const wxString& aCandidate ) const
92 {
93  if( m_regex.IsValid() )
94  {
95  if( m_regex.Matches( aCandidate ) )
96  {
97  size_t start, len;
98  m_regex.GetMatch( &start, &len, 0 );
99  return ( start > INT_MAX ) ? INT_MAX : start;
100  }
101  else
102  {
103  return EDA_PATTERN_NOT_FOUND;
104  }
105  }
106  else
107  {
108  int loc = aCandidate.Find( m_pattern );
109  return ( loc == wxNOT_FOUND ) ? EDA_PATTERN_NOT_FOUND : loc;
110  }
111 }
112 
113 
114 bool EDA_PATTERN_MATCH_WILDCARD::SetPattern( const wxString& aPattern )
115 {
116  m_wildcard_pattern = aPattern;
117 
118  // Compile the wildcard string to a regular expression
119  wxString regex;
120  regex.Alloc( 2 * aPattern.Length() ); // no need to keep resizing, we know the size roughly
121 
122  const wxString to_replace = wxT( ".*+?^${}()|[]/\\" );
123 
124  for( wxString::const_iterator it = aPattern.begin(); it < aPattern.end(); ++it )
125  {
126  wxUniChar c = *it;
127  if( c == '?' )
128  {
129  regex += wxT( "." );
130  }
131  else if( c == '*' )
132  {
133  regex += wxT( ".*" );
134  }
135  else if( to_replace.Find( c ) != wxNOT_FOUND )
136  {
137  regex += "\\";
138  regex += c;
139  }
140  else
141  {
142  regex += c;
143  }
144  }
145 
146  return EDA_PATTERN_MATCH_REGEX::SetPattern( regex );
147 }
148 
149 
151 {
152  return m_wildcard_pattern;
153 }
154 
155 
156 int EDA_PATTERN_MATCH_WILDCARD::Find( const wxString& aCandidate ) const
157 {
158  return EDA_PATTERN_MATCH_REGEX::Find( aCandidate );
159 }
160 
161 
162 bool EDA_PATTERN_MATCH_WILDCARD_EXPLICIT::SetPattern( const wxString& aPattern )
163 {
164  m_wildcard_pattern = aPattern;
165 
166  // Compile the wildcard string to a regular expression
167  wxString regex;
168  regex.Alloc( 2 * aPattern.Length() ); // no need to keep resizing, we know the size roughly
169 
170  const wxString to_replace = wxT( ".*+?^${}()|[]/\\" );
171 
172  regex += wxT( "^" );
173  for( wxString::const_iterator it = aPattern.begin(); it < aPattern.end(); ++it )
174  {
175  wxUniChar c = *it;
176  if( c == '?' )
177  {
178  regex += wxT( "." );
179  }
180  else if( c == '*' )
181  {
182  regex += wxT( ".*" );
183  }
184  else if( to_replace.Find( c ) != wxNOT_FOUND )
185  {
186  regex += "\\";
187  regex += c;
188  }
189  else
190  {
191  regex += c;
192  }
193  }
194  regex += wxT( "$" );
195 
196  return EDA_PATTERN_MATCH_REGEX::SetPattern( regex );
197 }
198 
199 
200 bool EDA_PATTERN_MATCH_RELATIONAL::SetPattern( const wxString& aPattern )
201 {
202  bool matches = m_regex_search.Matches( aPattern );
203 
204  if( !matches || m_regex_search.GetMatchCount() < 5 )
205  return false;
206 
207  m_pattern = aPattern;
208  wxString key = m_regex_search.GetMatch( aPattern, 1 );
209  wxString rel = m_regex_search.GetMatch( aPattern, 2 );
210  wxString val = m_regex_search.GetMatch( aPattern, 3 );
211  wxString unit = m_regex_search.GetMatch( aPattern, 4 );
212 
213  m_key = key.Lower();
214 
215  if( rel == "<" )
216  m_relation = LT;
217  else if( rel == "<=" )
218  m_relation = LE;
219  else if( rel == "=" )
220  m_relation = EQ;
221  else if( rel == ">=" )
222  m_relation = GE;
223  else if( rel == ">" )
224  m_relation = GT;
225  else
226  return false;
227 
228  if( val == "" )
229  {
230  // Matching on empty values keeps the match list from going empty when
231  // the user types the relational operator character, which helps prevent
232  // confusion.
233  m_relation = NONE;
234  }
235  else if( !val.ToCDouble( &m_value ) )
236  return false;
237 
238  auto unit_it = m_units.find( unit.Lower() );
239 
240  if( unit_it != m_units.end() )
241  m_value *= unit_it->second;
242  else
243  return false;
244 
245  m_pattern = aPattern;
246 
247  return true;
248 }
249 
250 
252 {
253  return m_pattern;
254 }
255 
256 
257 int EDA_PATTERN_MATCH_RELATIONAL::Find( const wxString& aCandidate ) const
258 {
259  wxStringTokenizer tokenizer( aCandidate );
260  size_t lastpos = 0;
261 
262  while( tokenizer.HasMoreTokens() )
263  {
264  const wxString token = tokenizer.GetNextToken();
265  int found_delta = FindOne( token );
266 
267  if( found_delta != EDA_PATTERN_NOT_FOUND )
268  {
269  size_t found = (size_t) found_delta + lastpos;
270  return ( found > INT_MAX ) ? INT_MAX : (int) found;
271  }
272 
273  lastpos = tokenizer.GetPosition();
274  }
275 
276  return EDA_PATTERN_NOT_FOUND;
277 }
278 
279 
280 int EDA_PATTERN_MATCH_RELATIONAL::FindOne( const wxString& aCandidate ) const
281 {
282  bool matches = m_regex_description.Matches( aCandidate );
283 
284  if( !matches )
285  return EDA_PATTERN_NOT_FOUND;
286 
287  size_t start, len;
288  m_regex_description.GetMatch( &start, &len, 0 );
289  wxString key = m_regex_description.GetMatch( aCandidate, 1 );
290  wxString val = m_regex_description.GetMatch( aCandidate, 2 );
291  wxString unit = m_regex_description.GetMatch( aCandidate, 3 );
292 
293  int istart = ( start > INT_MAX ) ? INT_MAX : start;
294 
295  if( key.Lower() != m_key )
296  return EDA_PATTERN_NOT_FOUND;
297 
298  double val_parsed;
299 
300  if( !val.ToCDouble( &val_parsed ) )
301  return EDA_PATTERN_NOT_FOUND;
302 
303  auto unit_it = m_units.find( unit.Lower() );
304 
305  if( unit_it != m_units.end() )
306  val_parsed *= unit_it->second;
307 
308  switch( m_relation )
309  {
310  case LT: return val_parsed < m_value ? istart : EDA_PATTERN_NOT_FOUND;
311  case LE: return val_parsed <= m_value ? istart : EDA_PATTERN_NOT_FOUND;
312  case EQ: return val_parsed == m_value ? istart : EDA_PATTERN_NOT_FOUND;
313  case GE: return val_parsed >= m_value ? istart : EDA_PATTERN_NOT_FOUND;
314  case GT: return val_parsed > m_value ? istart : EDA_PATTERN_NOT_FOUND;
315  case NONE: return istart;
316  default: return EDA_PATTERN_NOT_FOUND;
317  }
318 }
319 
320 
322  R"((\w+)[=:]([-+]?[\d.]+)(\w*))", wxRE_ADVANCED );
324  R"(^(\w+)(<|<=|=|>=|>)([-+]?[\d.]*)(\w*)$)", wxRE_ADVANCED );
325 const std::map<wxString, double> EDA_PATTERN_MATCH_RELATIONAL::m_units = {
326  { "p", 1e-12 },
327  { "n", 1e-9 },
328  { "u", 1e-6 },
329  { "m", 1e-3 },
330  { "", 1. },
331  { "k", 1e3 },
332  { "meg",1e6 },
333  { "g", 1e9 },
334  { "t", 1e12 },
335  { "ki", 1024. },
336  { "mi", 1048576. },
337  { "gi", 1073741824. },
338  { "ti", 1099511627776. } };
339 
340 
342  : m_pattern( aPattern )
343 {
344  // Whatever syntax users prefer, it shall be matched.
345  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_REGEX>() );
346  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
347  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_RELATIONAL>() );
348  // If any of the above matchers couldn't be created because the pattern
349  // syntax does not match, the substring will try its best.
350  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_SUBSTR>() );
351 }
352 
353 
354 bool EDA_COMBINED_MATCHER::Find( const wxString& aTerm, int& aMatchersTriggered, int& aPosition )
355 {
356  aPosition = EDA_PATTERN_NOT_FOUND;
357  aMatchersTriggered = 0;
358 
359  for( auto const& matcher : m_matchers )
360  {
361  int local_find = matcher->Find( aTerm );
362 
363  if ( local_find != EDA_PATTERN_NOT_FOUND )
364  {
365  aMatchersTriggered += 1;
366 
367  if ( local_find < aPosition || aPosition == EDA_PATTERN_NOT_FOUND )
368  {
369  aPosition = local_find;
370  }
371  }
372  }
373 
374  return aPosition != EDA_PATTERN_NOT_FOUND;
375 }
376 
377 
378 wxString const& EDA_COMBINED_MATCHER::GetPattern() const
379 {
380  return m_pattern;
381 }
382 
383 
385  const wxString &aPattern,
386  std::unique_ptr<EDA_PATTERN_MATCH> aMatcher )
387 {
388  if ( aMatcher->SetPattern( aPattern ) )
389  {
390  m_matchers.push_back( std::move( aMatcher ) );
391  }
392 }
std::vector< std::unique_ptr< EDA_PATTERN_MATCH > > m_matchers
wxString const & GetPattern() const
virtual int Find(const wxString &aCandidate) const override
Return the location of a match iff a given candidate string matches the set pattern.
virtual wxString const & GetPattern() const override
Return the pattern passed to SetPattern().
virtual int Find(const wxString &aCandidate) const override
Return the location of a match iff a given candidate string matches the set pattern.
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 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.
int FindOne(const wxString &aCandidate) const
bool Find(const wxString &aTerm, int &aMatchersTriggered, int &aPosition)
virtual wxString const & GetPattern() const override
Return the pattern passed to SetPattern().
virtual int Find(const wxString &aCandidate) const override
Return the location of a match iff a given candidate string matches the set pattern.
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.
Implementation of std::make_unique for pre C++14 compilation environments.
virtual int Find(const wxString &aCandidate) const override
Return the location of a match iff a given candidate string matches the set pattern.
WX_LOGLEVEL_CONTEXT(wxLogLevel level)
EDA_COMBINED_MATCHER(const wxString &aPattern)
static const std::map< wxString, double > m_units