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