KiCad PCB EDA Suite
Loading...
Searching...
No Matches
library_table.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 The KiCad Developers, see AUTHORS.txt for contributors.
5 * @author Jon Evans <[email protected]>
6 *
7 * This program is free software: you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation, either version 3 of the License, or (at your
10 * option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include <boost/lexical_cast.hpp>
22
25#include <richio.h>
26#include <string_utils.h>
27#include <trace_helpers.h>
28#include <wx_filename.h>
29#include <xnode.h>
31
32
33const wxString LIBRARY_TABLE_ROW::TABLE_TYPE_NAME = wxT( "Table" );
34
35
37{
38 return m_scope == aOther.m_scope
39 && m_nickname == aOther.m_nickname
40 && m_uri == aOther.m_uri
41 && m_type == aOther.m_type
42 && m_options == aOther.m_options
43 && m_description == aOther.m_description
44 && m_disabled == aOther.m_disabled
45 && m_hidden == aOther.m_hidden;
46}
47
48
49std::map<std::string, UTF8> LIBRARY_TABLE_ROW::GetOptionsMap() const
50{
52}
53
54
55LIBRARY_TABLE::LIBRARY_TABLE( const wxFileName &aPath, LIBRARY_TABLE_SCOPE aScope ) :
56 m_scope( aScope )
57{
59
60 wxFileName file( aPath );
62 m_path = file.GetAbsolutePath();
63
64 if( !file.FileExists() )
65 {
66 m_ok = false;
67 m_errorDescription = wxString::Format( _( "The library table path '%s' does not exist" ),
68 file.GetFullPath() );
69 return;
70 }
71
72 tl::expected<LIBRARY_TABLE_IR, LIBRARY_PARSE_ERROR> ir = parser.Parse( m_path.ToStdString() );
73
74 if( ir.has_value() )
75 {
76 m_ok = initFromIR( *ir );
77 }
78 else
79 {
80 m_ok = false;
81 m_errorDescription = ir.error().description;
82 }
83}
84
85
86LIBRARY_TABLE::LIBRARY_TABLE( const wxString &aBuffer, LIBRARY_TABLE_SCOPE aScope ) :
87 m_path( wxEmptyString ),
88 m_scope( aScope )
89{
91
92 tl::expected<LIBRARY_TABLE_IR, LIBRARY_PARSE_ERROR> ir =
93 parser.ParseBuffer( aBuffer.ToStdString() );
94
95 if( ir.has_value() )
96 {
97 m_ok = initFromIR( *ir );
98 }
99 else
100 {
101 m_ok = false;
102 m_errorDescription = ir.error().description;
103 }
104}
105
106
107bool LIBRARY_TABLE::operator==( const LIBRARY_TABLE& aOther ) const
108{
109 return m_path == aOther.m_path
110 && m_scope == aOther.m_scope
111 && m_type == aOther.m_type
112 && m_version == aOther.m_version
113 && m_rows == aOther.m_rows;
114}
115
116
118{
119 m_type = aIR.type;
120
121 try
122 {
123 m_version = boost::lexical_cast<int>( aIR.version );
124 }
125 catch( const boost::bad_lexical_cast & )
126 {
127 m_version = std::nullopt;
128 }
129
130 for( const LIBRARY_TABLE_ROW_IR& row : aIR.rows )
131 addRowFromIR( row );
132
133 return true;
134}
135
136
138{
140
141 row.m_nickname = wxString::FromUTF8( aIR.nickname );
142 row.m_uri = wxString::FromUTF8( aIR.uri );
143 row.m_type = wxString::FromUTF8( aIR.type );
144 row.m_options = wxString::FromUTF8( aIR.options );
145 row.m_description = wxString::FromUTF8( aIR.description );
146 row.m_hidden = aIR.hidden;
147 row.m_disabled = aIR.disabled;
148 row.m_ok = true;
149 row.m_scope = m_scope;
150
151 m_rows.emplace_back( row );
152 return true;
153}
154
155
157{
158 static const std::map<LIBRARY_TABLE_TYPE, wxString> types = {
159 { LIBRARY_TABLE_TYPE::SYMBOL, "sym_lib_table" },
160 { LIBRARY_TABLE_TYPE::FOOTPRINT, "fp_lib_table" },
161 { LIBRARY_TABLE_TYPE::DESIGN_BLOCK, "design_block_lib_table" }
162 };
163
164 wxCHECK( types.contains( Type() ), /* void */ );
165
166 XNODE self( wxXML_ELEMENT_NODE, types.at( Type() ) );
167
168 // TODO(JE) library tables - version management?
169 self.AddAttribute( "version", 7 );
170
171 for( const LIBRARY_TABLE_ROW& row : Rows() )
172 {
173 wxString uri = row.URI();
174 uri.Replace( '\\', '/' );
175
176 XNODE* rowNode = new XNODE( wxXML_ELEMENT_NODE, "lib" );
177 rowNode->AddAttribute( "name", row.Nickname() );
178 rowNode->AddAttribute( "type", row.Type() );
179 rowNode->AddAttribute( "uri", uri );
180 rowNode->AddAttribute( "options", row.Options() );
181 rowNode->AddAttribute( "descr", row.Description() );
182
183 if( row.Disabled() )
184 rowNode->AddChild( new XNODE( wxXML_ELEMENT_NODE, "disabled" ) );
185
186 if( row.Hidden() )
187 rowNode->AddChild( new XNODE( wxXML_ELEMENT_NODE, "hidden" ) );
188
189 self.AddChild( rowNode );
190 }
191
192 self.Format( aOutput );
193}
194
195
197{
198 LIBRARY_TABLE_ROW row = {};
199
200 row.SetScope( m_scope );
201 row.SetOk();
202
203 return row;
204}
205
206
208{
209 return Rows().emplace_back( MakeRow() );
210}
211
212
213bool LIBRARY_TABLE::HasRow( const wxString& aNickname ) const
214{
215 for( const LIBRARY_TABLE_ROW& row : m_rows )
216 {
217 if( row.Nickname() == aNickname )
218 return true;
219 }
220
221 return false;
222}
223
224
225bool LIBRARY_TABLE::HasRowWithURI( const wxString& aUri, const PROJECT& aProject,
226 bool aSubstituted ) const
227{
228 for( const LIBRARY_TABLE_ROW& row : m_rows )
229 {
230 if( !aSubstituted && row.URI() == aUri )
231 return true;
232
233 if( aSubstituted && LIBRARY_MANAGER::ExpandURI( row.URI(), aProject ) == aUri )
234 return true;
235 }
236
237 return false;
238}
239
240
241std::optional<LIBRARY_TABLE_ROW*> LIBRARY_TABLE::Row( const wxString& aNickname )
242{
243 for( LIBRARY_TABLE_ROW& row : m_rows )
244 {
245 if( row.Nickname() == aNickname )
246 return &row;
247 }
248
249 return std::nullopt;
250}
251
252
253std::optional<const LIBRARY_TABLE_ROW*> LIBRARY_TABLE::Row( const wxString& aNickname ) const
254{
255 for( const LIBRARY_TABLE_ROW& row : m_rows )
256 {
257 if( row.Nickname() == aNickname )
258 return &row;
259 }
260
261 return std::nullopt;
262}
263
264
266{
267 wxLogTrace( traceLibraries, "Saving %s", Path() );
268 wxFileName fn( Path() );
269 // This should already be normalized, but just in case...
270 fn.Normalize( FN_NORMALIZE_FLAGS | wxPATH_NORM_ENV_VARS );
271
272 try
273 {
274 PRETTIFIED_FILE_OUTPUTFORMATTER formatter( fn.GetFullPath(), KICAD_FORMAT::FORMAT_MODE::LIBRARY_TABLE );
275 Format( &formatter );
276 }
277 catch( IO_ERROR& e )
278 {
279 wxLogTrace( traceLibraries, "Exception while saving: %s", e.What() );
280 return tl::unexpected( LIBRARY_ERROR( e.What() ) );
281 }
282
283 return LIBRARY_RESULT<void>();
284}
285
286
287#define OPT_SEP '|'
288
289std::map<std::string, UTF8> LIBRARY_TABLE::ParseOptions( const std::string& aOptionsList )
290{
291 std::map<std::string, UTF8> props;
292
293 if( aOptionsList.size() )
294 {
295 const char* cp = &aOptionsList[0];
296 const char* end = cp + aOptionsList.size();
297
298 std::string pair;
299
300 // Parse all name=value pairs
301 while( cp < end )
302 {
303 pair.clear();
304
305 // Skip leading white space.
306 while( cp < end && isspace( *cp ) )
307 ++cp;
308
309 // Find the end of pair/field
310 while( cp < end )
311 {
312 if( *cp == '\\' && cp + 1 < end && cp[1] == OPT_SEP )
313 {
314 ++cp; // skip the escape
315 pair += *cp++; // add the separator
316 }
317 else if( *cp == OPT_SEP )
318 {
319 ++cp; // skip the separator
320 break; // process the pair
321 }
322 else
323 {
324 pair += *cp++;
325 }
326 }
327
328 // stash the pair
329 if( pair.size() )
330 {
331 // first equals sign separates 'name' and 'value'.
332 size_t eqNdx = pair.find( '=' );
333
334 if( eqNdx != pair.npos )
335 {
336 std::string name = pair.substr( 0, eqNdx );
337 std::string value = pair.substr( eqNdx + 1 );
338 props[name] = value;
339 }
340 else
341 {
342 props[pair] = ""; // property is present, but with no value.
343 }
344 }
345 }
346 }
347
348 return props;
349}
350
351
352UTF8 LIBRARY_TABLE::FormatOptions( const std::map<std::string, UTF8>* aProperties )
353{
354 UTF8 ret;
355
356 if( aProperties )
357 {
358 for( std::map<std::string, UTF8>::const_iterator it = aProperties->begin();
359 it != aProperties->end(); ++it )
360 {
361 const std::string& name = it->first;
362
363 const UTF8& value = it->second;
364
365 if( ret.size() )
366 ret += OPT_SEP;
367
368 ret += name;
369
370 // the separation between name and value is '='
371 if( value.size() )
372 {
373 ret += '=';
374
375 for( std::string::const_iterator si = value.begin(); si != value.end(); ++si )
376 {
377 // escape any separator in the value.
378 if( *si == OPT_SEP )
379 ret += '\\';
380
381 ret += *si;
382 }
383 }
384 }
385 }
386
387 return ret;
388}
const char * name
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
virtual const wxString What() const
A composite of Problem() and Where()
static wxString ExpandURI(const wxString &aShortURI, const PROJECT &aProject)
tl::expected< LIBRARY_TABLE_IR, LIBRARY_PARSE_ERROR > ParseBuffer(const std::string &aBuffer)
tl::expected< LIBRARY_TABLE_IR, LIBRARY_PARSE_ERROR > Parse(const std::filesystem::path &aPath)
LIBRARY_TABLE_ROW()=default
void SetOk(bool aOk=true)
bool operator==(const LIBRARY_TABLE_ROW &aOther) const
std::map< std::string, UTF8 > GetOptionsMap() const
static const wxString TABLE_TYPE_NAME
void SetScope(LIBRARY_TABLE_SCOPE aScope)
LIBRARY_TABLE_SCOPE m_scope
LIBRARY_RESULT< void > Save()
bool operator==(const LIBRARY_TABLE &aOther) const
void Format(OUTPUTFORMATTER *aOutput) const
static std::map< std::string, UTF8 > ParseOptions(const std::string &aOptionsList)
LIBRARY_TABLE_TYPE Type() const
LIBRARY_TABLE_TYPE m_type
What type of content this table contains (footprint, symbol, design block, etc)
std::optional< LIBRARY_TABLE_ROW * > Row(const wxString &aNickname)
std::vector< LIBRARY_TABLE_ROW > m_rows
LIBRARY_TABLE_ROW & InsertRow()
Builds a new row and inserts it at the end of the table; returning a reference to the row.
static UTF8 FormatOptions(const std::map< std::string, UTF8 > *aProperties)
const wxString & Path() const
wxString m_path
The full path to the file this table was parsed from, if any.
wxString m_errorDescription
LIBRARY_TABLE_ROW MakeRow() const
Builds a new row that is suitable for this table (does not insert it)
bool HasRow(const wxString &aNickname) const
LIBRARY_TABLE(const wxFileName &aPath, LIBRARY_TABLE_SCOPE aScope)
Creates a library table from a file on disk.
bool HasRowWithURI(const wxString &aUri, const PROJECT &aProject, bool aSubstituted=false) const
Returns true if the given (fully-expanded) URI exists as a library in this table.
std::optional< int > m_version
The format version, if present in the parsed file.
const std::vector< LIBRARY_TABLE_ROW > & Rows() const
bool addRowFromIR(const LIBRARY_TABLE_ROW_IR &aIR)
LIBRARY_TABLE_SCOPE m_scope
bool initFromIR(const LIBRARY_TABLE_IR &aIR)
An interface used to output 8 bit text in a convenient way.
Definition richio.h:323
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition utf8.h:72
std::string::const_iterator begin() const
Definition utf8.h:220
std::string::size_type size() const
Definition utf8.h:117
std::string::const_iterator end() const
Definition utf8.h:221
static void ResolvePossibleSymlinks(wxFileName &aFilename)
An extension of wxXmlNode that can format its contents as KiCad-style s-expressions.
Definition xnode.h:71
void Format(OUTPUTFORMATTER *out) const
Write this object as UTF8 out to an OUTPUTFORMATTER as an S-expression.
Definition xnode.cpp:113
void AddAttribute(const wxString &aName, const wxString &aValue) override
Definition xnode.cpp:92
#define _(s)
const wxChar *const traceLibraries
Flag to enable library table and library manager tracing.
#define OPT_SEP
options separator character
tl::expected< ResultType, LIBRARY_ERROR > LIBRARY_RESULT
LIBRARY_TABLE_SCOPE
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
The intermediate representation that a library table is parsed into.
std::vector< LIBRARY_TABLE_ROW_IR > rows
LIBRARY_TABLE_TYPE type
VECTOR2I end
wxLogTrace helper definitions.
#define FN_NORMALIZE_FLAGS
Default flags to pass to wxFileName::Normalize().
Definition wx_filename.h:39