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_RELATIONAL::SetPattern( const wxString& aPattern )
163 {
164  bool matches = m_regex_search.Matches( aPattern );
165 
166  if( !matches || m_regex_search.GetMatchCount() < 5 )
167  return false;
168 
169  m_pattern = aPattern;
170  wxString key = m_regex_search.GetMatch( aPattern, 1 );
171  wxString rel = m_regex_search.GetMatch( aPattern, 2 );
172  wxString val = m_regex_search.GetMatch( aPattern, 3 );
173  wxString unit = m_regex_search.GetMatch( aPattern, 4 );
174 
175  m_key = key.Lower();
176 
177  if( rel == "<" )
178  m_relation = LT;
179  else if( rel == "<=" )
180  m_relation = LE;
181  else if( rel == "=" )
182  m_relation = EQ;
183  else if( rel == ">=" )
184  m_relation = GE;
185  else if( rel == ">" )
186  m_relation = GT;
187  else
188  return false;
189 
190  if( val == "" )
191  {
192  // Matching on empty values keeps the match list from going empty when
193  // the user types the relational operator character, which helps prevent
194  // confusion.
195  m_relation = NONE;
196  }
197  else if( !val.ToCDouble( &m_value ) )
198  return false;
199 
200  auto unit_it = m_units.find( unit.Lower() );
201 
202  if( unit_it != m_units.end() )
203  m_value *= unit_it->second;
204  else
205  return false;
206 
207  m_pattern = aPattern;
208 
209  return true;
210 }
211 
212 
214 {
215  return m_pattern;
216 }
217 
218 
219 int EDA_PATTERN_MATCH_RELATIONAL::Find( const wxString& aCandidate ) const
220 {
221  wxStringTokenizer tokenizer( aCandidate );
222  size_t lastpos = 0;
223 
224  while( tokenizer.HasMoreTokens() )
225  {
226  const wxString token = tokenizer.GetNextToken();
227  int found_delta = FindOne( token );
228 
229  if( found_delta != EDA_PATTERN_NOT_FOUND )
230  {
231  size_t found = (size_t) found_delta + lastpos;
232  return ( found > INT_MAX ) ? INT_MAX : (int) found;
233  }
234 
235  lastpos = tokenizer.GetPosition();
236  }
237 
238  return EDA_PATTERN_NOT_FOUND;
239 }
240 
241 
242 int EDA_PATTERN_MATCH_RELATIONAL::FindOne( const wxString& aCandidate ) const
243 {
244  bool matches = m_regex_description.Matches( aCandidate );
245 
246  if( !matches )
247  return EDA_PATTERN_NOT_FOUND;
248 
249  size_t start, len;
250  m_regex_description.GetMatch( &start, &len, 0 );
251  wxString key = m_regex_description.GetMatch( aCandidate, 1 );
252  wxString val = m_regex_description.GetMatch( aCandidate, 2 );
253  wxString unit = m_regex_description.GetMatch( aCandidate, 3 );
254 
255  int istart = ( start > INT_MAX ) ? INT_MAX : start;
256 
257  if( key.Lower() != m_key )
258  return EDA_PATTERN_NOT_FOUND;
259 
260  double val_parsed;
261 
262  if( !val.ToCDouble( &val_parsed ) )
263  return EDA_PATTERN_NOT_FOUND;
264 
265  auto unit_it = m_units.find( unit.Lower() );
266 
267  if( unit_it != m_units.end() )
268  val_parsed *= unit_it->second;
269 
270  switch( m_relation )
271  {
272  case LT: return val_parsed < m_value ? istart : EDA_PATTERN_NOT_FOUND;
273  case LE: return val_parsed <= m_value ? istart : EDA_PATTERN_NOT_FOUND;
274  case EQ: return val_parsed == m_value ? istart : EDA_PATTERN_NOT_FOUND;
275  case GE: return val_parsed >= m_value ? istart : EDA_PATTERN_NOT_FOUND;
276  case GT: return val_parsed > m_value ? istart : EDA_PATTERN_NOT_FOUND;
277  case NONE: return istart;
278  default: return EDA_PATTERN_NOT_FOUND;
279  }
280 }
281 
282 
284  R"((\w+)[=:]([-+]?[\d.]+)(\w*))", wxRE_ADVANCED );
286  R"(^(\w+)(<|<=|=|>=|>)([-+]?[\d.]*)(\w*)$)", wxRE_ADVANCED );
287 const std::map<wxString, double> EDA_PATTERN_MATCH_RELATIONAL::m_units = {
288  { "p", 1e-12 },
289  { "n", 1e-9 },
290  { "u", 1e-6 },
291  { "m", 1e-3 },
292  { "", 1. },
293  { "k", 1e3 },
294  { "meg",1e6 },
295  { "g", 1e9 },
296  { "t", 1e12 },
297  { "ki", 1024. },
298  { "mi", 1048576. },
299  { "gi", 1073741824. },
300  { "ti", 1099511627776. } };
301 
302 
304  : m_pattern( aPattern )
305 {
306  // Whatever syntax users prefer, it shall be matched.
307  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_REGEX>() );
308  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_WILDCARD>() );
309  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_RELATIONAL>() );
310  // If any of the above matchers couldn't be created because the pattern
311  // syntax does not match, the substring will try its best.
312  AddMatcher( aPattern, std::make_unique<EDA_PATTERN_MATCH_SUBSTR>() );
313 }
314 
315 
316 bool EDA_COMBINED_MATCHER::Find( const wxString& aTerm, int& aMatchersTriggered, int& aPosition )
317 {
318  aPosition = EDA_PATTERN_NOT_FOUND;
319  aMatchersTriggered = 0;
320 
321  for( auto const& matcher : m_matchers )
322  {
323  int local_find = matcher->Find( aTerm );
324 
325  if ( local_find != EDA_PATTERN_NOT_FOUND )
326  {
327  aMatchersTriggered += 1;
328 
329  if ( local_find < aPosition || aPosition == EDA_PATTERN_NOT_FOUND )
330  {
331  aPosition = local_find;
332  }
333  }
334  }
335 
336  return aPosition != EDA_PATTERN_NOT_FOUND;
337 }
338 
339 
340 wxString const& EDA_COMBINED_MATCHER::GetPattern() const
341 {
342  return m_pattern;
343 }
344 
345 
347  const wxString &aPattern,
348  std::unique_ptr<EDA_PATTERN_MATCH> aMatcher )
349 {
350  if ( aMatcher->SetPattern( aPattern ) )
351  {
352  m_matchers.push_back( std::move( aMatcher ) );
353  }
354 }
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 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