KiCad PCB EDA Suite
Loading...
Searching...
No Matches
symbol_library_adapter.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
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21#include <magic_enum.hpp>
22#include <chrono>
23#include <memory>
24
25#include <common.h>
26#include <dialog_shim.h>
27#include <ki_exception.h>
28#include <wx/log.h>
29
30#include <lib_symbol.h>
32
33#include <env_vars.h>
34#include <pgm_base.h>
35#include <project.h>
36#include <thread_pool.h>
37#include <trace_helpers.h>
39
40using namespace std::chrono_literals;
41
42
43const char* SYMBOL_LIBRARY_ADAPTER::PropPowerSymsOnly = "pwr_sym_only";
44const char* SYMBOL_LIBRARY_ADAPTER::PropNonPowerSymsOnly = "non_pwr_sym_only";
45
47
49
50
55
56
61
62
67
68
70{
71 SCH_IO* ret = dynamic_cast<SCH_IO*>( aRow->plugin.get() );
72 wxCHECK( aRow->plugin && ret, nullptr );
73 return ret;
74}
75
76
77void SYMBOL_LIBRARY_ADAPTER::enumerateLibrary( LIB_DATA* aLib, const wxString& aUri )
78{
79 wxArrayString dummyList;
80
81 std::lock_guard pluginGuard( pluginMutex( aLib->row->Nickname() ) );
82
83 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
84 schplugin( aLib )->EnumerateSymbolLib( dummyList, aUri, &options );
85}
86
87
88std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::LoadOne( LIB_DATA* aLib )
89{
91
92 std::lock_guard pluginGuard( pluginMutex( aLib->row->Nickname() ) );
93
94 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
95
96 try
97 {
98 wxArrayString dummyList;
99 schplugin( aLib )->EnumerateSymbolLib( dummyList, getUri( aLib->row ), &options );
100 wxLogTrace( traceLibraries, "Sym: %s: library enumerated %zu items", aLib->row->Nickname(), dummyList.size() );
102 aLib->status.error.reset();
103 }
104 catch( IO_ERROR& e )
105 {
107 aLib->status.error = LIBRARY_ERROR( e.Problem(), e.Where() );
108 wxLogTrace( traceLibraries, "Sym: %s: plugin threw exception: %s", aLib->row->Nickname(), e.What() );
109 }
110
111 return aLib->status;
112}
113
114
115std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::CheckLibrary( LIB_DATA* aLib )
116{
118
119 std::lock_guard pluginGuard( pluginMutex( aLib->row->Nickname() ) );
120
121 std::map<std::string, UTF8> options = aLib->row->GetOptionsMap();
122
123 try
124 {
125 schplugin( aLib )->CheckLibrary( getUri( aLib->row ), &options );
127 aLib->status.error.reset();
128 }
129 catch( IO_ERROR& e )
130 {
132 aLib->status.error = LIBRARY_ERROR( e.Problem(), e.Where() );
133 wxLogTrace( traceLibraries, "Sym: %s: library check failed: %s", aLib->row->Nickname(), e.What() );
134 }
135
136 return aLib->status;
137}
138
139
140std::optional<LIB_STATUS> SYMBOL_LIBRARY_ADAPTER::LoadOne( const wxString& nickname )
141{
143
144 if( result.has_value() )
145 return LoadOne( *result );
146
147 return LIB_STATUS { .load_status = LOAD_STATUS::LOAD_ERROR,
148 .error = LIBRARY_ERROR( { result.error() } ) };
149}
150
151
153{
154 SCH_IO_MGR::SCH_FILE_T type = SCH_IO_MGR::EnumFromStr( row->Type() );
155
156 if( type == SCH_IO_MGR::SCH_NESTED_TABLE )
157 {
158 wxString msg;
159 wxFileName fileName( m_manager.GetFullURI( row, true, &m_manager.Project() ) );
160
161 if( fileName.FileExists() )
162 return tl::unexpected( LIBRARY_TABLE_OK() );
163 else
164 msg = wxString::Format( _( "Nested table '%s' not found." ), row->URI() );
165
166 return tl::unexpected( LIBRARY_ERROR( msg ) );
167 }
168 else if( type == SCH_IO_MGR::SCH_FILE_UNKNOWN )
169 {
170 wxLogTrace( traceLibraries, "Sym: Plugin type %s is unknown!", row->Type() );
171 wxString msg = wxString::Format( _( "Unknown library type %s " ), row->Type() );
172 return tl::unexpected( LIBRARY_ERROR( msg ) );
173 }
174
175 SCH_IO* plugin = SCH_IO_MGR::FindPlugin( type );
176 wxCHECK( plugin, tl::unexpected( LIBRARY_ERROR( _( "Internal error" ) ) ) );
177
178 plugin->SetLibraryManagerAdapter( this );
179
180 wxLogTrace( traceLibraries, "Sym: Library %s (%s) plugin created", row->Nickname(),
181 magic_enum::enum_name( row->Scope() ) );
182
183 return plugin;
184}
185
186
187std::vector<LIB_SYMBOL*> SYMBOL_LIBRARY_ADAPTER::GetSymbols( const wxString& aNickname, SYMBOL_TYPE aType )
188{
189 std::vector<LIB_SYMBOL*> symbols;
190
191 std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname );
192
193 if( !maybeLib )
194 return symbols;
195
196 const LIB_DATA* lib = *maybeLib;
197
198 std::lock_guard pluginGuard( pluginMutex( aNickname ) );
199
200 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
201
202 if( aType == SYMBOL_TYPE::POWER_ONLY )
203 options[PropPowerSymsOnly] = "";
204
205 try
206 {
207 schplugin( lib )->EnumerateSymbolLib( symbols, getUri( lib->row ), &options );
208 }
209 catch( IO_ERROR& e )
210 {
211 wxLogTrace( traceLibraries, "Sym: Exception enumerating library %s: %s", lib->row->Nickname(), e.What() );
212 }
213
214 for( LIB_SYMBOL* symbol : symbols )
215 {
216 LIB_ID id = symbol->GetLibId();
217 id.SetLibNickname( lib->row->Nickname() );
218 symbol->SetLibId( id );
219 }
220
221 return symbols;
222}
223
224
225std::vector<wxString> SYMBOL_LIBRARY_ADAPTER::GetSymbolNames( const wxString& aNickname, SYMBOL_TYPE aType )
226{
227 wxArrayString namesAS;
228 std::vector<wxString> names;
229
230 if( std::optional<const LIB_DATA*> maybeLib = fetchIfLoaded( aNickname ) )
231 {
232 const LIB_DATA* lib = *maybeLib;
233
234 std::lock_guard pluginGuard( pluginMutex( aNickname ) );
235
236 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
237
238 if( aType == SYMBOL_TYPE::POWER_ONLY )
239 options[PropPowerSymsOnly] = "";
240
241 try
242 {
243 schplugin( lib )->EnumerateSymbolLib( namesAS, getUri( lib->row ), &options );
244 }
245 catch( const IO_ERROR& e )
246 {
247 wxLogTrace( traceLibraries, "Sym: Exception enumerating library %s: %s",
248 lib->row->Nickname(), e.What() );
249 }
250 }
251
252 for( const wxString& name : namesAS )
253 names.emplace_back( name );
254
255 return names;
256}
257
258
259LIB_SYMBOL* SYMBOL_LIBRARY_ADAPTER::LoadSymbol( const wxString& aNickname, const wxString& aName )
260{
261 if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aNickname ) )
262 {
263 std::lock_guard pluginGuard( pluginMutex( aNickname ) );
264
265 std::map<std::string, UTF8> options = ( *lib )->row->GetOptionsMap();
266
267 if( LIB_SYMBOL* symbol = schplugin( *lib )->LoadSymbol( getUri( ( *lib )->row ), aName, &options ) )
268 {
269 LIB_ID id = symbol->GetLibId();
270 id.SetLibNickname( ( *lib )->row->Nickname() );
271 symbol->SetLibId( id );
272 return symbol;
273 }
274 }
275 else
276 {
277 wxLogTrace( traceLibraries, "LoadSymbol: requested library %s not loaded", aNickname );
278 }
279
280 return nullptr;
281}
282
283
285 std::unique_ptr<LIB_SYMBOL> aSymbol, bool aOverwrite )
286{
287 wxCHECK( aSymbol, SAVE_SKIPPED );
288
289 LIBRARY_RESULT<LIB_DATA*> libResult = loadIfNeeded( aNickname );
290
291 if( !libResult.has_value() )
292 {
293 wxLogTrace( traceLibraries, "SaveSymbol: unable to load library %s: %s", aNickname,
294 libResult.error().message );
295 return SAVE_SKIPPED;
296 }
297
298 LIB_DATA* lib = *libResult;
299
300 if( !lib )
301 {
302 wxLogTrace( traceLibraries, "SaveSymbol: library %s not found", aNickname );
303 return SAVE_SKIPPED;
304 }
305
306 SCH_IO* plugin = schplugin( lib );
307 wxCHECK( plugin, SAVE_SKIPPED );
308
309 std::lock_guard pluginGuard( pluginMutex( aNickname ) );
310
311 const wxString symbolName = aSymbol->GetName();
312
313 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
314
315 if( !aOverwrite )
316 {
317 try
318 {
319 LIB_SYMBOL* existing = plugin->LoadSymbol( getUri( lib->row ), symbolName, &options );
320
321 if( existing )
322 return SAVE_SKIPPED;
323 }
324 catch( const IO_ERROR& e )
325 {
326 wxLogTrace( traceLibraries, "SaveSymbol: error checking for existing symbol %s:%s: %s", aNickname,
327 symbolName, e.What() );
328 return SAVE_SKIPPED;
329 }
330 }
331
332 try
333 {
334 plugin->SaveSymbol( getUri( lib->row ), std::move( aSymbol ), &options );
335 }
336 catch( const IO_ERROR& e )
337 {
338 wxLogTrace( traceLibraries, "SaveSymbol: error saving %s:%s: %s", aNickname, symbolName, e.What() );
339 return SAVE_SKIPPED;
340 }
341
342 return SAVE_OK;
343}
344
345
346void SYMBOL_LIBRARY_ADAPTER::DeleteSymbol( const wxString& aNickname, const wxString& aSymbolName )
347{
348 LIBRARY_RESULT<LIB_DATA*> libResult = loadIfNeeded( aNickname );
349
350 if( !libResult.has_value() || !*libResult )
351 {
352 wxLogTrace( traceLibraries, "DeleteSymbol: unable to load library %s", aNickname );
353 return;
354 }
355
356 LIB_DATA* lib = *libResult;
357
358 std::lock_guard lock( pluginMutex( aNickname ) );
359
360 std::map<std::string, UTF8> options = lib->row->GetOptionsMap();
361 schplugin( lib )->DeleteSymbol( getUri( lib->row ), aSymbolName, &options );
362}
363
364
366{
367 // Route through fetchIfLoaded() so LOAD_ERROR sentinel entries, which carry a null
368 // plugin, are filtered out instead of dereferenced.
369 if( std::optional<const LIB_DATA*> lib = fetchIfLoaded( aLib ) )
370 return ( *lib )->plugin->IsLibraryWritable( getUri( ( *lib )->row ) );
371
372 return false;
373}
374
375
376std::vector<wxString> SYMBOL_LIBRARY_ADAPTER::GetAvailableExtraFields( const wxString& aNickname )
377{
378 std::vector<wxString> fields;
379
380 if( std::optional<LIB_DATA*> result = fetchIfLoaded( aNickname ) )
381 {
382 LIB_DATA* rowData = *result;
383
384 int hash = schplugin( rowData )->GetModifyHash();
385
386 if( hash != rowData->modify_hash )
387 {
388 rowData->modify_hash = hash;
390 }
391
392 return rowData->available_fields_cache;
393 }
394
395 return fields;
396}
397
398
399bool SYMBOL_LIBRARY_ADAPTER::SupportsSubLibraries( const wxString& aNickname ) const
400{
401 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
402 {
403 const LIB_DATA* rowData = *result;
404 return schplugin( rowData )->SupportsSubLibraries();
405 }
406
407 return false;
408}
409
410
411std::vector<SUB_LIBRARY> SYMBOL_LIBRARY_ADAPTER::GetSubLibraries( const wxString& aNickname ) const
412{
413 std::vector<SUB_LIBRARY> ret;
414
415 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
416 {
417 const LIB_DATA* rowData = *result;
418
419 try
420 {
421 std::vector<wxString> names;
422 schplugin( rowData )->GetSubLibraryNames( names );
423
424 for( const wxString& name : names )
425 {
426 ret.emplace_back( SUB_LIBRARY {
427 .nickname = name,
428 .description = schplugin( rowData )->GetSubLibraryDescription( name ) } );
429 }
430 }
431 catch( const IO_ERROR& e )
432 {
433 wxLogTrace( traceLibraries, "Sym: Exception getting sub-libraries for %s: %s",
434 aNickname, e.What() );
435 }
436 }
437
438 return ret;
439}
440
441
442bool SYMBOL_LIBRARY_ADAPTER::SupportsConfigurationDialog( const wxString& aNickname ) const
443{
444 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
445 return ( *result )->plugin->SupportsConfigurationDialog();
446
447 return false;
448}
449
450
451int SYMBOL_LIBRARY_ADAPTER::ShowConfigurationDialog( const wxString& aNickname, wxWindow* aParent ) const
452{
453 std::optional<const LIB_DATA*> optRow = fetchIfLoaded( aNickname );
454
455 if( !optRow )
456 return wxID_CANCEL;
457
458 if( !( *optRow )->plugin->SupportsConfigurationDialog() )
459 return wxID_CANCEL;
460
461 DIALOG_SHIM* dialog = ( *optRow )->plugin->CreateConfigurationDialog( aParent );
462
463 return dialog->ShowModal();
464}
465
466
468{
469 int hash = 0;
470
471 for( const LIBRARY_TABLE_ROW* row : m_manager.Rows( Type() ) )
472 {
473 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( row->Nickname() ) )
474 {
475 const LIB_DATA* rowData = *result;
476 wxCHECK2( rowData->row, continue );
477 hash += schplugin( rowData )->GetModifyHash();
478 }
479 }
480
481 return hash;
482}
483
484
485std::optional<int> SYMBOL_LIBRARY_ADAPTER::GetLibraryModifyHash( const wxString& aNickname ) const
486{
487 if( std::optional<const LIB_DATA*> result = fetchIfLoaded( aNickname ) )
488 {
489 const LIB_DATA* rowData = *result;
490 wxCHECK( rowData->row, std::nullopt );
491 return schplugin( rowData )->GetModifyHash();
492 }
493
494 return std::nullopt;
495}
const char * name
Dialog helper object to sit in the inheritance tree between wxDialog and any class written by wxFormB...
Definition dialog_shim.h:80
int ShowModal() override
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()
virtual const wxString Problem() const
what was the problem?
virtual const wxString Where() const
where did the Problem() occur?
A wrapper for static data that should not be destroyed at program exit.
void evictOwnedGlobalEntries()
Erases this adapter's own entries (LIB_DATA::global_owner == this) from the process-wide globalLibs()...
LIBRARY_MANAGER_ADAPTER(LIBRARY_MANAGER &aManager)
Constructs a type-specific adapter into the library manager.
wxString getUri(const LIBRARY_TABLE_ROW *aRow) const
LIBRARY_RESULT< LIB_DATA * > loadIfNeeded(const wxString &aNickname)
Fetches a loaded library, triggering a load of that library if it isn't loaded yet.
static std::recursive_mutex & pluginMutex(const wxString &aNickname)
Serializes access to a library's shared plugin instance so its single mutable cache is not raced by c...
LIBRARY_MANAGER & m_manager
std::optional< const LIB_DATA * > fetchIfLoaded(const wxString &aNickname) const
LIBRARY_TABLE_SCOPE Scope() const
std::map< std::string, UTF8 > GetOptionsMap() const
const wxString & Type() const
const wxString & URI() const
const wxString & Nickname() const
A logical library item identifier and consists of various portions much like a URI.
Definition lib_id.h:45
int SetLibNickname(const UTF8 &aLibNickname)
Override the logical library name portion of the LIB_ID to aLibNickname.
Definition lib_id.cpp:113
Define a library symbol object.
Definition lib_symbol.h:114
static SCH_FILE_T EnumFromStr(const wxString &aFileType)
Return the file type from the corresponding plugin type name: "kicad", "legacy", etc.
Base class that schematic file and library loading and saving plugins should derive from.
Definition sch_io.h:60
virtual void EnumerateSymbolLib(wxArrayString &aSymbolNameList, const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr)
Populate a list of LIB_SYMBOL alias names contained within the library aLibraryPath.
Definition sch_io.cpp:90
virtual bool SupportsSubLibraries() const
Definition sch_io.h:321
virtual int GetModifyHash() const =0
Return the modification hash from the library cache.
virtual void GetAvailableSymbolFields(std::vector< wxString > &aNames)
Retrieves a list of (custom) field names that are present on symbols in this library.
Definition sch_io.h:357
virtual void DeleteSymbol(const wxString &aLibraryPath, const wxString &aSymbolName, const std::map< std::string, UTF8 > *aProperties=nullptr)
Delete the entire LIB_SYMBOL associated with aAliasName from the library aLibraryPath.
Definition sch_io.cpp:132
virtual void CheckLibrary(const wxString &aLibraryPath, const std::map< std::string, UTF8 > *aProperties=nullptr)
Validate that the library at aLibraryPath is reachable and well-formed, without necessarily loading s...
Definition sch_io.cpp:108
virtual LIB_SYMBOL * LoadSymbol(const wxString &aLibraryPath, const wxString &aPartName, const std::map< std::string, UTF8 > *aProperties=nullptr)
Load a LIB_SYMBOL object having aPartName from the aLibraryPath containing a library format that this...
Definition sch_io.cpp:116
virtual wxString GetSubLibraryDescription(const wxString &aName)
Gets a description of a sublibrary.
Definition sch_io.h:346
virtual void GetSubLibraryNames(std::vector< wxString > &aNames)
Retrieves a list of sub-libraries in this library.
Definition sch_io.h:335
bool SupportsConfigurationDialog(const wxString &aNickname) const override
std::optional< LIB_STATUS > LoadOne(LIB_DATA *aLib) override
Loads or reloads the given library, if it exists.
static SCH_IO * schplugin(const LIB_DATA *aRow)
LIB_SYMBOL * LoadSymbol(const wxString &aNickname, const wxString &aName)
Load a LIB_SYMBOL having aName from the library given by aNickname.
void DeleteSymbol(const wxString &aNickname, const wxString &aSymbolName)
Deletes the aSymbolName from the library given by aNickname.
std::optional< LIB_STATUS > CheckLibrary(LIB_DATA *aLib) override
Validates that a library is loadable. May not necessarily load the library!
SAVE_T SaveSymbol(const wxString &aNickname, std::unique_ptr< LIB_SYMBOL > aSymbol, bool aOverwrite=true)
Write aSymbol to an existing library given by aNickname.
void enumerateLibrary(LIB_DATA *aLib, const wxString &aUri) override
Override in derived class to perform library-specific enumeration.
std::vector< wxString > GetSymbolNames(const wxString &aNickname, SYMBOL_TYPE aType=SYMBOL_TYPE::ALL_SYMBOLS)
bool SupportsSubLibraries(const wxString &aNickname) const
LIBRARY_RESULT< IO_BASE * > createPlugin(const LIBRARY_TABLE_ROW *row) override
Creates a concrete plugin for the given row.
std::vector< SUB_LIBRARY > GetSubLibraries(const wxString &aNickname) const
SYMBOL_LIBRARY_ADAPTER(LIBRARY_MANAGER &aManager)
static wxString GlobalPathEnvVariableName()
static const char * PropPowerSymsOnly
LIBRARY_TABLE_TYPE Type() const override
The type of library table this adapter works with.
IO_BASE * plugin(const LIB_DATA *aRow) override
std::vector< LIB_SYMBOL * > GetSymbols(const wxString &aNickname, SYMBOL_TYPE aType=SYMBOL_TYPE::ALL_SYMBOLS)
static LEAK_AT_EXIT< std::shared_mutex > GlobalLibraryMutex
static LEAK_AT_EXIT< std::map< wxString, LIB_DATA > > GlobalLibraries
std::vector< wxString > GetAvailableExtraFields(const wxString &aNickname)
Returns a list of additional (non-mandatory) symbol fields present in the given library.
SAVE_T
The set of return values from SaveSymbol() below.
static const char * PropNonPowerSymsOnly
bool IsSymbolLibWritable(const wxString &aNickname)
Return true if the library given by aNickname is writable.
std::optional< int > GetLibraryModifyHash(const wxString &aNickname) const
Return the modify hash of a single library if it is currently loaded.
int ShowConfigurationDialog(const wxString &aNickname, wxWindow *aParent) const override
#define _(s)
Functions related to environment variables, including help functions.
const wxChar *const traceLibraries
Flag to enable library table and library manager tracing.
tl::expected< ResultType, LIBRARY_ERROR > LIBRARY_RESULT
KICOMMON_API wxString GetVersionedEnvVarName(const wxString &aBaseName)
Construct a versioned environment variable based on this KiCad major version.
Definition env_vars.cpp:78
see class PGM_BASE
Storage for an actual loaded library (including library content owned by the plugin)
std::vector< wxString > available_fields_cache
LIB_STATUS status
std::unique_ptr< IO_BASE > plugin
const LIBRARY_TABLE_ROW * row
The overall status of a loaded or loading library.
std::optional< LIBRARY_ERROR > error
LOAD_STATUS load_status
A descriptor for a sub-library (supported by database and http libraries)
wxString result
Test unit parsing edge cases and error handling.
wxLogTrace helper definitions.