KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sch_io_mgr.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) 2016 CERN
5 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * @author Wayne Stambaugh <[email protected]>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 2
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 */
22
23#include <memory>
24
25#include <wx/filename.h>
26#include <wx/uri.h>
27
28#include <sch_io/sch_io_mgr.h>
32
46#include <common.h> // for ExpandEnvVarSubstitutions
47#include <ki_exception.h>
48
50#include <kiway_player.h>
51#include <string_utils.h>
53
54#define FMT_UNIMPLEMENTED _( "Plugin '%s' does not implement the '%s' function." )
55#define FMT_NOTFOUND _( "Plugin type '%s' is not found." )
56
57
58
59// Some day plugins might be in separate DLL/DSOs, simply because of numbers of them
60// and code size. Until then, use the simplest method:
61
62// This implementation is one of two which could be done.
63// The other one would cater to DLL/DSO's. But since it would be nearly
64// impossible to link a KICAD type DLL/DSO right now without pulling in all
65// ::Draw() functions, I forgo that option temporarily.
66
67// Some day it may be possible to have some built in AND some DLL/DSO
68// plugins coexisting.
69
70
71SCH_IO* SCH_IO_MGR::FindPlugin( SCH_FILE_T aFileType )
72{
73 // This implementation is subject to change, any magic is allowed here.
74 // The public SCH_IO_MGR API is the only pertinent public information.
75
76 switch( aFileType )
77 {
78 case SCH_KICAD: return new SCH_IO_KICAD_SEXPR();
79 case SCH_LEGACY: return new SCH_IO_KICAD_LEGACY();
80 case SCH_ALTIUM: return new SCH_IO_ALTIUM();
81 case SCH_CADSTAR_ARCHIVE: return new SCH_IO_CADSTAR_ARCHIVE();
82 case SCH_DATABASE: return new SCH_IO_DATABASE();
83 case SCH_EAGLE: return new SCH_IO_EAGLE();
84 case SCH_EASYEDA: return new SCH_IO_EASYEDA();
85 case SCH_EASYEDAPRO: return new SCH_IO_EASYEDAPRO();
86 case SCH_EASYEDAPRO_V3: return new SCH_IO_EASYEDAPRO_V3();
87 case SCH_GEDA: return new SCH_IO_GEDA();
88 case SCH_LTSPICE: return new SCH_IO_LTSPICE();
89 case SCH_HTTP: return new SCH_IO_HTTP_LIB();
90 case SCH_PADS: return new SCH_IO_PADS();
91 case SCH_DIPTRACE: return new SCH_IO_DIPTRACE();
92 case SCH_PCAD: return new SCH_IO_PCAD();
93 case SCH_ORCAD: return new SCH_IO_ORCAD();
94 default: return nullptr;
95 }
96}
97
98
99const wxString SCH_IO_MGR::ShowType( SCH_FILE_T aType )
100{
101 // keep this function in sync with EnumFromStr() relative to the
102 // text spellings. If you change the spellings, you will obsolete
103 // library tables, so don't do change, only additions are ok.
104
105 switch( aType )
106 {
107 case SCH_KICAD: return wxString( wxT( "KiCad" ) );
108 case SCH_LEGACY: return wxString( wxT( "Legacy" ) );
109 case SCH_ALTIUM: return wxString( wxT( "Altium" ) );
110 case SCH_CADSTAR_ARCHIVE: return wxString( wxT( "CADSTAR Schematic Archive" ) );
111 case SCH_DATABASE: return wxString( wxT( "Database" ) );
112 case SCH_EAGLE: return wxString( wxT( "EAGLE" ) );
113 case SCH_EASYEDA: return wxString( wxT( "EasyEDA (JLCEDA) Std" ) );
114 case SCH_EASYEDAPRO: return wxString( wxT( "EasyEDA (JLCEDA) Pro" ) );
115 case SCH_EASYEDAPRO_V3: return wxString( wxT( "EasyEDA (JLCEDA) Pro v3" ) );
116 case SCH_GEDA: return wxString( wxT( "gEDA / Lepton EDA" ) );
117 case SCH_LTSPICE: return wxString( wxT( "LTspice" ) );
118 case SCH_HTTP: return wxString( wxT( "HTTP" ) );
119 case SCH_PADS: return wxString( wxT( "PADS Logic" ) );
120 case SCH_DIPTRACE: return wxString( wxT( "DipTrace" ) );
121 case SCH_PCAD: return wxString( wxT( "P-CAD" ) );
122 case SCH_ORCAD: return wxString( wxT( "OrCAD" ) );
123 case SCH_NESTED_TABLE: return LIBRARY_TABLE_ROW::TABLE_TYPE_NAME;
124 default: return wxString::Format( _( "Unknown SCH_FILE_T value: %d" ), aType );
125 }
126}
127
128
129SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::EnumFromStr( const wxString& aType )
130{
131 // keep this function in sync with ShowType() relative to the
132 // text spellings. If you change the spellings, you will obsolete
133 // library tables, so don't do change, only additions are ok.
134
135 if( aType == wxT( "KiCad" ) )
136 return SCH_KICAD;
137 else if( aType == wxT( "Legacy" ) )
138 return SCH_LEGACY;
139 else if( aType == wxT( "Altium" ) )
140 return SCH_ALTIUM;
141 else if( aType == wxT( "CADSTAR Schematic Archive" ) )
142 return SCH_CADSTAR_ARCHIVE;
143 else if( aType == wxT( "Database" ) )
144 return SCH_DATABASE;
145 else if( aType == wxT( "EAGLE" ) )
146 return SCH_EAGLE;
147 else if( aType == wxT( "EasyEDA (JLCEDA) Std" ) )
148 return SCH_EASYEDA;
149 else if( aType == wxT( "EasyEDA (JLCEDA) Pro" ) )
150 return SCH_EASYEDAPRO;
151 else if( aType == wxT( "EasyEDA (JLCEDA) Pro v3" ) )
152 return SCH_EASYEDAPRO_V3;
153 else if( aType == wxT( "gEDA / Lepton EDA" ) )
154 return SCH_GEDA;
155 else if( aType == wxT( "LTspice" ) )
156 return SCH_LTSPICE;
157 else if( aType == wxT( "HTTP" ) )
158 return SCH_HTTP;
159 else if( aType == wxT( "PADS Logic" ) )
160 return SCH_PADS;
161 else if( aType == wxT( "DipTrace" ) )
162 return SCH_DIPTRACE;
163 else if( aType == wxT( "P-CAD" ) )
164 return SCH_PCAD;
165 else if( aType == wxT( "OrCAD" ) )
166 return SCH_ORCAD;
167 else if( aType == LIBRARY_TABLE_ROW::TABLE_TYPE_NAME )
168 return SCH_NESTED_TABLE;
169
170 return SCH_FILE_UNKNOWN;
171}
172
173
174SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::GuessPluginTypeFromLibPath( const wxString& aLibPath, int aCtl )
175{
177
178 if( parser.Parse( aLibPath.ToStdString() ).has_value() )
179 return SCH_NESTED_TABLE;
180
181 for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector )
182 {
183 bool isKiCad = fileType == SCH_IO_MGR::SCH_KICAD || fileType == SCH_IO_MGR::SCH_LEGACY;
184
185 if( ( aCtl & KICTL_KICAD_ONLY ) && !isKiCad )
186 continue;
187
188 if( ( aCtl & KICTL_NONKICAD_ONLY ) && isKiCad )
189 continue;
190
191 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( fileType ) );
192
193 if( !pi )
194 continue;
195
196 // For SCH_IO_MGR::SCH_KICAD and KICTL_CREATE option is set, use SCH_IO::CanReadLibrary()
197 // here instead of SCH_IO_KICAD_SEXPR::CanReadLibrary because aLibPath perhaps
198 // does notexist, and we need to use the version that does not test the existence
199 // of the file, just know if aLibPath file type can be handled.
200 if( fileType == SCH_IO_MGR::SCH_KICAD && ( aCtl & KICTL_CREATE ) )
201 {
202 if( pi->SCH_IO::CanReadLibrary( aLibPath ) ) // Test only the file ext
203 return fileType;
204 }
205 else
206 {
207 // Other lib types must be tested using the specific CanReadLibrary() and
208 // in some cases need to read the file
209 if( pi->CanReadLibrary( aLibPath ) )
210 return fileType;
211 }
212 }
213
214 return SCH_IO_MGR::SCH_FILE_UNKNOWN;
215}
216
217
218SCH_IO_MGR::SCH_FILE_T SCH_IO_MGR::GuessPluginTypeFromSchPath( const wxString& aSchematicPath,
219 int aCtl )
220{
221 for( const SCH_IO_MGR::SCH_FILE_T& fileType : SCH_IO_MGR::SCH_FILE_T_vector )
222 {
223 bool isKiCad = fileType == SCH_IO_MGR::SCH_KICAD || fileType == SCH_IO_MGR::SCH_LEGACY;
224
225 if( ( aCtl & KICTL_KICAD_ONLY ) && !isKiCad )
226 continue;
227
228 if( ( aCtl & KICTL_NONKICAD_ONLY ) && isKiCad )
229 continue;
230
231 IO_RELEASER<SCH_IO> pi( SCH_IO_MGR::FindPlugin( fileType ) );
232
233 if( !pi )
234 continue;
235
236 if( pi->CanReadSchematicFile( aSchematicPath ) )
237 return fileType;
238 }
239
240 return SCH_IO_MGR::SCH_FILE_UNKNOWN;
241}
242
243
244bool SCH_IO_MGR::ConvertLibrary( std::map<std::string, UTF8>* aOldFileProps, const wxString& aOldFilePath,
245 const wxString& aNewFilepath, REPORTER* aReporter )
246{
247 auto report = [&]( const wxString& aMessage, SEVERITY aSeverity )
248 {
249 if( aReporter )
250 aReporter->Report( aMessage, aSeverity );
251 };
252
253 SCH_IO_MGR::SCH_FILE_T oldFileType = SCH_IO_MGR::GuessPluginTypeFromLibPath( aOldFilePath );
254
255 if( oldFileType == SCH_IO_MGR::SCH_FILE_UNKNOWN )
256 {
257 report( _( "Unrecognized library type" ), RPT_SEVERITY_ERROR );
258 return false;
259 }
260
261 // A nested library table has no plugin to enumerate it; reject it before the null-plugin
262 // path below.
263 if( oldFileType == SCH_IO_MGR::SCH_NESTED_TABLE )
264 return false;
265
266 // Live backends are not convertible to a static .kicad_sym snapshot. HTTP requires a
267 // library manager adapter that ConvertLibrary cannot provide, and DATABASE has the same
268 // semantic mismatch. Without this guard the call silently produces an empty library.
269 if( oldFileType == SCH_IO_MGR::SCH_HTTP || oldFileType == SCH_IO_MGR::SCH_DATABASE )
270 return false;
271
272 IO_RELEASER<SCH_IO> oldFilePI( SCH_IO_MGR::FindPlugin( oldFileType ) );
273 IO_RELEASER<SCH_IO> kicadPI( SCH_IO_MGR::FindPlugin( SCH_IO_MGR::SCH_KICAD ) );
274
275 if( !oldFilePI || !kicadPI )
276 return false;
277
278 if( aReporter )
279 oldFilePI->SetReporter( aReporter );
280
281 std::vector<LIB_SYMBOL*> symbols;
282 std::vector<std::unique_ptr<LIB_SYMBOL>> newSymbols;
283 std::map<LIB_SYMBOL*, LIB_SYMBOL*> symbolMap;
284
285 report( wxString::Format( _( "Loading symbol library '%s'" ), aOldFilePath ), RPT_SEVERITY_ACTION );
286
287 try
288 {
289 oldFilePI->EnumerateSymbolLib( symbols, aOldFilePath, aOldFileProps );
290
291 // Copy non-derived symbols first, so we can build a map from symbols to newSymbols
292 for( LIB_SYMBOL* symbol : symbols )
293 {
294 if( symbol->IsDerived() )
295 continue;
296
297 symbol->SetName( EscapeString( symbol->GetName(), CTX_LIBID ) );
298
299 newSymbols.push_back( std::make_unique<LIB_SYMBOL>( *symbol ) );
300 symbolMap[symbol] = newSymbols.back().get();
301 }
302
303 // Now do the derived symbols using the map to hook them up to their newSymbol parents
304 for( LIB_SYMBOL* symbol : symbols )
305 {
306 if( !symbol->IsDerived() )
307 continue;
308
309 symbol->SetName( EscapeString( symbol->GetName(), CTX_LIBID ) );
310
311 newSymbols.push_back( std::make_unique<LIB_SYMBOL>( *symbol ) );
312 newSymbols.back()->SetParent( symbolMap[ symbol->GetParent().lock().get() ] );
313 }
314
315 // Create a blank library
316 kicadPI->SaveLibrary( aNewFilepath );
317 }
318 catch( const IO_ERROR& io_err )
319 {
320 report( wxString::Format( _( "Library '%s' Convert err: \"%s\"" ), aOldFilePath, io_err.What() ),
322 return false;
323 }
324 catch( const std::exception& e )
325 {
326 report( wxString::Format( _( "Error loading library '%s': %s" ), aOldFilePath, e.what() ), RPT_SEVERITY_ERROR );
327 return false;
328 }
329 catch( ... )
330 {
331 report( wxString::Format( _( "Error loading library '%s': unknown error" ), aOldFilePath ),
333 return false;
334 }
335
336 bool ok = true;
337
338 for( std::unique_ptr<LIB_SYMBOL>& symbol : newSymbols )
339 {
340 const wxString symbolName = symbol->GetName();
341
342 try
343 {
344 kicadPI->SaveSymbol( aNewFilepath, std::move( symbol ) );
345 }
346 catch( const IO_ERROR& io_err )
347 {
348 report( wxString::Format( _( "Error saving symbol '%s': %s" ), symbolName, io_err.What() ),
350 ok = false;
351 }
352 catch( const std::exception& e )
353 {
354 report( wxString::Format( _( "Error saving symbol '%s': %s" ), symbolName, e.what() ),
356 ok = false;
357 }
358 catch( ... )
359 {
360 report( wxString::Format( _( "Error saving symbol '%s': unknown error" ), symbolName ),
362 ok = false;
363 }
364 }
365
366 return ok;
367}
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()
tl::expected< LIBRARY_TABLE_IR, LIBRARY_PARSE_ERROR > Parse(const std::filesystem::path &aPath)
static const wxString TABLE_TYPE_NAME
Define a library symbol object.
Definition lib_symbol.h:119
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:102
A KiCad database library provides both symbol and footprint metadata, so there are "shim" plugins on ...
A SCH_IO derivation for loading DipTrace schematic files (.dch).
A SCH_IO derivation for loading Eagle schematic files.
A SCH_IO derivation for loading gEDA/gschem schematic files (.sch).
Definition sch_io_geda.h:74
A KiCad HTTP library provides both symbol and footprint metadata, so there are "shim" plugins on both...
A SCH_IO derivation for loading schematic files created before the new s-expression file format.
A SCH_IO derivation for loading schematic files using the new s-expression file format.
static bool ConvertLibrary(std::map< std::string, UTF8 > *aOldFileProps, const wxString &aOldFilePath, const wxString &aNewFilepath, REPORTER *aReporter=nullptr)
Convert a schematic symbol library to the latest KiCad format.
static SCH_FILE_T EnumFromStr(const wxString &aFileType)
Return the #SCH_FILE_T from the corresponding plugin type name: "kicad", "legacy",...
static const wxString ShowType(SCH_FILE_T aFileType)
Return a brief name for a plugin, given aFileType enum.
static SCH_FILE_T GuessPluginTypeFromSchPath(const wxString &aSchematicPath, int aCtl=0)
Return a plugin type given a schematic using the file extension of aSchematicPath.
static SCH_FILE_T GuessPluginTypeFromLibPath(const wxString &aLibPath, int aCtl=0)
Return a plugin type given a symbol library using the file extension of aLibPath.
A SCH_IO derivation for loading PADS Logic schematic files.
Definition sch_io_pads.h:39
A SCH_IO derivation for loading P-CAD 2006 ASCII schematic files (.SCH).
Definition sch_io_pcad.h:49
Base class that schematic file and library loading and saving plugins should derive from.
Definition sch_io.h:60
#define _(s)
std::unique_ptr< T > IO_RELEASER
Helper to hold and release an IO_BASE object when exceptions are thrown.
Definition io_mgr.h:33
#define KICTL_CREATE
caller thinks requested project files may not exist.
#define KICTL_KICAD_ONLY
chosen file is from KiCad according to user
#define KICTL_NONKICAD_ONLY
chosen file is non-KiCad according to user
SEVERITY
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_ACTION
MODEL3D_FORMAT_TYPE fileType(const char *aFileName)
wxString EscapeString(const wxString &aSource, ESCAPE_CONTEXT aContext)
The Escape/Unescape routines use HTML-entity-reference-style encoding to handle characters which are:...
@ CTX_LIBID
Definition of file extensions used in Kicad.