KiCad PCB EDA Suite
json_settings.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) 2020 Jon Evans <jon@craftyjon.com>
5  * Copyright (C) 2020-2021 KiCad Developers, see AUTHORS.txt for contributors.
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 <algorithm>
22 #include <fstream>
23 #include <iomanip>
24 #include <utility>
25 #include <sstream>
26 
27 #include <locale_io.h>
28 #include <gal/color4d.h>
29 #include <settings/json_settings.h>
32 #include <settings/parameters.h>
33 #include <wx/config.h>
34 #include <wx/debug.h>
35 #include <wx/fileconf.h>
36 #include <wx/filename.h>
37 #include <wx/log.h>
38 #include <wx/wfstream.h>
39 
40 const wxChar* const traceSettings = wxT( "KICAD_SETTINGS" );
41 
42 
43 nlohmann::json::json_pointer JSON_SETTINGS_INTERNALS::PointerFromString( std::string aPath )
44 {
45  std::replace( aPath.begin(), aPath.end(), '.', '/' );
46  aPath.insert( 0, "/" );
47 
48  nlohmann::json::json_pointer p;
49 
50  try
51  {
52  p = nlohmann::json::json_pointer( aPath );
53  }
54  catch( ... )
55  {
56  wxASSERT_MSG( false, wxT( "Invalid pointer path in PointerFromString!" ) );
57  }
58 
59  return p;
60 }
61 
62 
63 JSON_SETTINGS::JSON_SETTINGS( const wxString& aFilename, SETTINGS_LOC aLocation,
64  int aSchemaVersion, bool aCreateIfMissing, bool aCreateIfDefault,
65  bool aWriteFile ) :
66  m_filename( aFilename ),
67  m_legacy_filename( "" ),
68  m_location( aLocation ),
69  m_createIfMissing( aCreateIfMissing ),
70  m_createIfDefault( aCreateIfDefault ),
71  m_writeFile( aWriteFile ),
72  m_deleteLegacyAfterMigration( true ),
73  m_resetParamsIfMissing( true ),
74  m_schemaVersion( aSchemaVersion ),
75  m_manager( nullptr )
76 {
77  m_internals = std::make_unique<JSON_SETTINGS_INTERNALS>();
78 
79  try
80  {
81  m_internals->SetFromString( "meta.filename", GetFullFilename() );
82  }
83  catch( ... )
84  {
85  wxLogTrace( traceSettings, "Error: Could not create filename field for %s",
86  GetFullFilename() );
87  }
88 
89 
90  m_params.emplace_back(
91  new PARAM<int>( "meta.version", &m_schemaVersion, m_schemaVersion, true ) );
92 }
93 
94 
96 {
97  for( auto param: m_params )
98  delete param;
99 
100  m_params.clear();
101 }
102 
103 
105 {
106  return wxString( m_filename + "." + getFileExt() );
107 }
108 
109 
110 nlohmann::json& JSON_SETTINGS::At( const std::string& aPath )
111 {
112  return m_internals->At( aPath );
113 }
114 
115 
116 bool JSON_SETTINGS::Contains( const std::string& aPath ) const
117 {
118  return m_internals->contains( JSON_SETTINGS_INTERNALS::PointerFromString( aPath ) );
119 }
120 
121 
122 size_t JSON_SETTINGS::Count( const std::string& aPath ) const
123 {
125 }
126 
127 
129 {
130  return m_internals.get();
131 }
132 
133 
135 {
136  for( auto param : m_params )
137  {
138  try
139  {
140  param->Load( this, m_resetParamsIfMissing );
141  }
142  catch( ... )
143  {
144  // Skip unreadable parameters in file
145  wxLogTrace( traceSettings, "param '%s' load err", param->GetJsonPath().c_str() );
146  }
147  }
148 }
149 
150 
151 bool JSON_SETTINGS::LoadFromFile( const wxString& aDirectory )
152 {
153  // First, load all params to default values
154  m_internals->clear();
155  Load();
156 
157  bool success = true;
158  bool migrated = false;
159  bool legacy_migrated = false;
160 
161  LOCALE_IO locale;
162 
163  auto migrateFromLegacy = [&] ( wxFileName& aPath ) {
164  // Backup and restore during migration so that the original can be mutated if convenient
165  bool backed_up = false;
166  wxFileName temp;
167 
168  if( aPath.IsDirWritable() )
169  {
170  temp.AssignTempFileName( aPath.GetFullPath() );
171 
172  if( !wxCopyFile( aPath.GetFullPath(), temp.GetFullPath() ) )
173  {
174  wxLogTrace( traceSettings, "%s: could not create temp file for migration",
175  GetFullFilename() );
176  }
177  else
178  backed_up = true;
179  }
180 
181  // Silence popups if legacy file is read-only
182  wxLogNull doNotLog;
183 
184  wxConfigBase::DontCreateOnDemand();
185  auto cfg = std::make_unique<wxFileConfig>( wxT( "" ), wxT( "" ), aPath.GetFullPath() );
186 
187  // If migrate fails or is not implemented, fall back to built-in defaults that were
188  // already loaded above
189  if( !MigrateFromLegacy( cfg.get() ) )
190  {
191  wxLogTrace( traceSettings,
192  "%s: migrated; not all settings were found in legacy file",
193  GetFullFilename() );
194  }
195  else
196  {
197  wxLogTrace( traceSettings, "%s: migrated from legacy format", GetFullFilename() );
198  }
199 
200  if( backed_up )
201  {
202  cfg.reset();
203 
204  if( !wxCopyFile( temp.GetFullPath(), aPath.GetFullPath() ) )
205  {
206  wxLogTrace( traceSettings,
207  "migrate; copy temp file %s to %s failed",
208  temp.GetFullPath(), aPath.GetFullPath() );
209  }
210 
211  if( !wxRemoveFile( temp.GetFullPath() ) )
212  {
213  wxLogTrace( traceSettings,
214  "migrate; failed to remove temp file %s",
215  temp.GetFullPath() );
216  }
217  }
218 
219  // Either way, we want to clean up the old file afterwards
220  legacy_migrated = true;
221  };
222 
223  wxFileName path;
224 
225  if( aDirectory.empty() )
226  {
227  path.Assign( m_filename );
228  path.SetExt( getFileExt() );
229  }
230  else
231  {
232  wxString dir( aDirectory );
233  path.Assign( dir, m_filename, getFileExt() );
234  }
235 
236  if( !path.Exists() )
237  {
238  // Case 1: legacy migration, no .json extension yet
239  path.SetExt( getLegacyFileExt() );
240 
241  if( path.Exists() )
242  {
243  migrateFromLegacy( path );
244  }
245  // Case 2: legacy filename is different from new one
246  else if( !m_legacy_filename.empty() )
247  {
248  path.SetName( m_legacy_filename );
249 
250  if( path.Exists() )
251  migrateFromLegacy( path );
252  }
253  else
254  {
255  success = false;
256  }
257  }
258  else
259  {
260  if( !path.IsFileWritable() )
261  m_writeFile = false;
262 
263  try
264  {
265  FILE* fp = wxFopen( path.GetFullPath(), wxT( "rt" ) );
266 
267  if( fp )
268  {
269  *static_cast<nlohmann::json*>( m_internals.get() ) =
270  nlohmann::json::parse( fp, nullptr,
271  /* allow_exceptions = */ true,
272  /* ignore_comments = */ true );
273 
274  // If parse succeeds, check if schema migration is required
275  int filever = -1;
276 
277  try
278  {
279  filever = m_internals->Get<int>( "meta.version" );
280  }
281  catch( ... )
282  {
283  wxLogTrace( traceSettings, "%s: file version could not be read!",
284  GetFullFilename() );
285  success = false;
286  }
287 
288  if( filever >= 0 && filever < m_schemaVersion )
289  {
290  wxLogTrace( traceSettings, "%s: attempting migration from version %d to %d",
291  GetFullFilename(), filever, m_schemaVersion );
292 
293  if( Migrate() )
294  {
295  migrated = true;
296  }
297  else
298  {
299  wxLogTrace( traceSettings, "%s: migration failed!", GetFullFilename() );
300  }
301  }
302  else if( filever > m_schemaVersion )
303  {
304  wxLogTrace( traceSettings,
305  "%s: warning: file version %d is newer than latest (%d)",
306  GetFullFilename(), filever, m_schemaVersion );
307  }
308  }
309  else
310  {
311  wxLogTrace( traceSettings, "%s exists but can't be opened for read",
312  GetFullFilename() );
313  }
314  }
315  catch( nlohmann::json::parse_error& error )
316  {
317  wxLogTrace( traceSettings, "Json parse error reading %s: %s",
318  path.GetFullPath(), error.what() );
319  wxLogTrace( traceSettings, "Attempting migration in case file is in legacy format" );
320  migrateFromLegacy( path );
321  }
322  }
323 
324  // Now that we have new data in the JSON structure, load the params again
325  Load();
326 
327  // And finally load any nested settings
328  for( auto settings : m_nested_settings )
329  settings->LoadFromFile();
330 
331  wxLogTrace( traceSettings, "Loaded <%s> with schema %d", GetFullFilename(), m_schemaVersion );
332 
333  // If we migrated, clean up the legacy file (with no extension)
334  if( legacy_migrated || migrated )
335  {
336  if( legacy_migrated && m_deleteLegacyAfterMigration && !wxRemoveFile( path.GetFullPath() ) )
337  {
338  wxLogTrace( traceSettings, "Warning: could not remove legacy file %s",
339  path.GetFullPath() );
340  }
341 
342  // And write-out immediately so that we don't lose data if the program later crashes.
344  SaveToFile( aDirectory, true );
345  }
346 
347  return success;
348 }
349 
350 
352 {
353  bool modified = false;
354 
355  for( auto param : m_params )
356  {
357  modified |= !param->MatchesFile( this );
358  param->Store( this );
359  }
360 
361  return modified;
362 }
363 
364 
366 {
367  for( auto param : m_params )
368  param->SetDefault();
369 }
370 
371 
372 bool JSON_SETTINGS::IsDefault( const std::string& aParamName )
373 {
374  for( const PARAM_BASE* param : m_params )
375  {
376  if( param->GetJsonPath() == aParamName )
377  return param->IsDefault();
378  }
379 
380  return false;
381 }
382 
383 
384 bool JSON_SETTINGS::SaveToFile( const wxString& aDirectory, bool aForce )
385 {
386  if( !m_writeFile )
387  return false;
388 
389  // Default PROJECT won't have a filename set
390  if( m_filename.IsEmpty() )
391  return false;
392 
393  wxFileName path;
394 
395  if( aDirectory.empty() )
396  {
397  path.Assign( m_filename );
398  path.SetExt( getFileExt() );
399  }
400  else
401  {
402  wxString dir( aDirectory );
403  path.Assign( dir, m_filename, getFileExt() );
404  }
405 
406  if( !m_createIfMissing && !path.FileExists() )
407  {
408  wxLogTrace( traceSettings,
409  "File for %s doesn't exist and m_createIfMissing == false; not saving",
410  GetFullFilename() );
411  return false;
412  }
413 
414  // Ensure the path exists, and create it if not.
415  if( !path.DirExists() && !path.Mkdir() )
416  {
417  wxLogTrace( traceSettings, "Warning: could not create path %s, can't save %s",
418  path.GetPath(), GetFullFilename() );
419  return false;
420  }
421 
422  if( ( path.FileExists() && !path.IsFileWritable() ) ||
423  ( !path.FileExists() && !path.IsDirWritable() ) )
424  {
425  wxLogTrace( traceSettings, "File for %s is read-only; not saving", GetFullFilename() );
426  return false;
427  }
428 
429  bool modified = false;
430 
431  for( auto settings : m_nested_settings )
432  modified |= settings->SaveToFile();
433 
434  modified |= Store();
435 
436  if( !modified && !aForce && path.FileExists() )
437  {
438  wxLogTrace( traceSettings, "%s contents not modified, skipping save", GetFullFilename() );
439  return false;
440  }
441  else if( !modified && !aForce && !m_createIfDefault )
442  {
443  wxLogTrace( traceSettings,
444  "%s contents still default and m_createIfDefault == false; not saving",
445  GetFullFilename() );
446  return false;
447  }
448 
449  wxLogTrace( traceSettings, "Saving %s", GetFullFilename() );
450 
452  bool success = true;
453 
454  try
455  {
456  std::stringstream buffer;
457  buffer << std::setw( 2 ) << *m_internals << std::endl;
458 
459  wxFFileOutputStream fileStream( path.GetFullPath(), "wb" );
460 
461  if( !fileStream.IsOk()
462  || !fileStream.WriteAll( buffer.str().c_str(), buffer.str().size() ) )
463  {
464  wxLogTrace( traceSettings, "Warning: could not save %s", GetFullFilename() );
465  success = false;
466  }
467  }
468  catch( nlohmann::json::exception& error )
469  {
470  wxLogTrace( traceSettings, "Catch error: could not save %s. Json error %s",
471  GetFullFilename(), error.what() );
472  success = false;
473  }
474  catch( ... )
475  {
476  wxLogTrace( traceSettings, "Error: could not save %s." );
477  success = false;
478  }
479 
480  return success;
481 }
482 
483 
484 OPT<nlohmann::json> JSON_SETTINGS::GetJson( const std::string& aPath ) const
485 {
486  nlohmann::json::json_pointer ptr = m_internals->PointerFromString( aPath );
487 
488  if( m_internals->contains( ptr ) )
489  {
490  try
491  {
492  return OPT<nlohmann::json>{ m_internals->at( ptr ) };
493  }
494  catch( ... )
495  {
496  }
497  }
498 
499  return OPT<nlohmann::json>{};
500 }
501 
502 
503 template<typename ValueType>
504 OPT<ValueType> JSON_SETTINGS::Get( const std::string& aPath ) const
505 {
506  if( OPT<nlohmann::json> ret = GetJson( aPath ) )
507  {
508  try
509  {
510  return ret->get<ValueType>();
511  }
512  catch( ... )
513  {
514  }
515  }
516 
517  return NULLOPT;
518 }
519 
520 
521 // Instantiate all required templates here to allow reducing scope of json.hpp
522 template OPT<bool> JSON_SETTINGS::Get<bool>( const std::string& aPath ) const;
523 template OPT<double> JSON_SETTINGS::Get<double>( const std::string& aPath ) const;
524 template OPT<float> JSON_SETTINGS::Get<float>( const std::string& aPath ) const;
525 template OPT<int> JSON_SETTINGS::Get<int>( const std::string& aPath ) const;
526 template OPT<unsigned int> JSON_SETTINGS::Get<unsigned int>( const std::string& aPath ) const;
527 template OPT<unsigned long long> JSON_SETTINGS::Get<unsigned long long>( const std::string& aPath ) const;
528 template OPT<std::string> JSON_SETTINGS::Get<std::string>( const std::string& aPath ) const;
529 template OPT<nlohmann::json> JSON_SETTINGS::Get<nlohmann::json>( const std::string& aPath ) const;
530 template OPT<KIGFX::COLOR4D> JSON_SETTINGS::Get<KIGFX::COLOR4D>( const std::string& aPath ) const;
531 
532 
533 template<typename ValueType>
534 void JSON_SETTINGS::Set( const std::string& aPath, ValueType aVal )
535 {
536  m_internals->SetFromString( aPath, aVal );
537 }
538 
539 
540 // Instantiate all required templates here to allow reducing scope of json.hpp
541 template void JSON_SETTINGS::Set<bool>( const std::string& aPath, bool aValue );
542 template void JSON_SETTINGS::Set<double>( const std::string& aPath, double aValue );
543 template void JSON_SETTINGS::Set<float>( const std::string& aPath, float aValue );
544 template void JSON_SETTINGS::Set<int>( const std::string& aPath, int aValue );
545 template void JSON_SETTINGS::Set<unsigned int>( const std::string& aPath, unsigned int aValue );
546 template void JSON_SETTINGS::Set<unsigned long long>( const std::string& aPath, unsigned long long aValue );
547 template void JSON_SETTINGS::Set<const char*>( const std::string& aPath, const char* aValue );
548 template void JSON_SETTINGS::Set<std::string>( const std::string& aPath, std::string aValue );
549 template void JSON_SETTINGS::Set<nlohmann::json>( const std::string& aPath, nlohmann::json aValue );
550 template void JSON_SETTINGS::Set<KIGFX::COLOR4D>( const std::string& aPath, KIGFX::COLOR4D aValue );
551 
552 
553 void JSON_SETTINGS::registerMigration( int aOldSchemaVersion, int aNewSchemaVersion,
554  std::function<bool()> aMigrator )
555 {
556  wxASSERT( aNewSchemaVersion > aOldSchemaVersion );
557  wxASSERT( aNewSchemaVersion <= m_schemaVersion );
558  m_migrators[aOldSchemaVersion] = std::make_pair( aNewSchemaVersion, aMigrator );
559 }
560 
561 
563 {
564  int filever = m_internals->Get<int>( "meta.version" );
565 
566  while( filever < m_schemaVersion )
567  {
568  if( !m_migrators.count( filever ) )
569  {
570  wxLogTrace( traceSettings, "Migrator missing for %s version %d!",
571  typeid( *this ).name(), filever );
572  return false;
573  }
574 
575  std::pair<int, std::function<bool()>> pair = m_migrators.at( filever );
576 
577  if( pair.second() )
578  {
579  wxLogTrace( traceSettings, "Migrated %s from %d to %d", typeid( *this ).name(),
580  filever, pair.first );
581  filever = pair.first;
582  m_internals->At( "meta.version" ) = filever;
583  }
584  else
585  {
586  wxLogTrace( traceSettings, "Migration failed for %s from %d to %d",
587  typeid( *this ).name(), filever, pair.first );
588  return false;
589  }
590  }
591 
592  return true;
593 }
594 
595 
596 bool JSON_SETTINGS::MigrateFromLegacy( wxConfigBase* aLegacyConfig )
597 {
598  wxLogTrace( traceSettings,
599  "MigrateFromLegacy() not implemented for %s", typeid( *this ).name() );
600  return false;
601 }
602 
603 
604 bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath,
605  wxString& aTarget )
606 {
607  nlohmann::json::json_pointer ptr = JSON_SETTINGS_INTERNALS::PointerFromString( aPath );
608 
609  if( aObj.contains( ptr ) && aObj.at( ptr ).is_string() )
610  {
611  aTarget = aObj.at( ptr ).get<wxString>();
612  return true;
613  }
614 
615  return false;
616 }
617 
618 
619 bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath,
620  bool& aTarget )
621 {
622  nlohmann::json::json_pointer ptr = JSON_SETTINGS_INTERNALS::PointerFromString( aPath );
623 
624  if( aObj.contains( ptr ) && aObj.at( ptr ).is_boolean() )
625  {
626  aTarget = aObj.at( ptr ).get<bool>();
627  return true;
628  }
629 
630  return false;
631 }
632 
633 
634 bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath,
635  int& aTarget )
636 {
637  nlohmann::json::json_pointer ptr = JSON_SETTINGS_INTERNALS::PointerFromString( aPath );
638 
639  if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_integer() )
640  {
641  aTarget = aObj.at( ptr ).get<int>();
642  return true;
643  }
644 
645  return false;
646 }
647 
648 
649 bool JSON_SETTINGS::SetIfPresent( const nlohmann::json& aObj, const std::string& aPath,
650  unsigned int& aTarget )
651 {
652  nlohmann::json::json_pointer ptr = JSON_SETTINGS_INTERNALS::PointerFromString( aPath );
653 
654  if( aObj.contains( ptr ) && aObj.at( ptr ).is_number_unsigned() )
655  {
656  aTarget = aObj.at( ptr ).get<unsigned int>();
657  return true;
658  }
659 
660  return false;
661 }
662 
663 
664 template<typename ValueType>
665 bool JSON_SETTINGS::fromLegacy( wxConfigBase* aConfig, const std::string& aKey,
666  const std::string& aDest )
667 {
668  ValueType val;
669 
670  if( aConfig->Read( aKey, &val ) )
671  {
672  try
673  {
674  ( *m_internals )[aDest] = val;
675  }
676  catch( ... )
677  {
678  wxASSERT_MSG( false, wxT( "Could not write value in fromLegacy!" ) );
679  return false;
680  }
681 
682  return true;
683  }
684 
685  return false;
686 }
687 
688 
689 // Explicitly declare these because we only support a few types anyway, and it means we can keep
690 // wxConfig detail out of the header file
691 template bool JSON_SETTINGS::fromLegacy<int>( wxConfigBase*, const std::string&,
692  const std::string& );
693 
694 template bool JSON_SETTINGS::fromLegacy<double>( wxConfigBase*, const std::string&,
695  const std::string& );
696 
697 template bool JSON_SETTINGS::fromLegacy<bool>( wxConfigBase*, const std::string&,
698  const std::string& );
699 
700 
701 bool JSON_SETTINGS::fromLegacyString( wxConfigBase* aConfig, const std::string& aKey,
702  const std::string& aDest )
703 {
704  wxString str;
705 
706  if( aConfig->Read( aKey, &str ) )
707  {
708  try
709  {
710  ( *m_internals )[aDest] = str.ToUTF8();
711  }
712  catch( ... )
713  {
714  wxASSERT_MSG( false, wxT( "Could not write value in fromLegacyString!" ) );
715  return false;
716  }
717 
718  return true;
719  }
720 
721  return false;
722 }
723 
724 
725 bool JSON_SETTINGS::fromLegacyColor( wxConfigBase* aConfig, const std::string& aKey,
726  const std::string& aDest )
727 {
728  wxString str;
729 
730  if( aConfig->Read( aKey, &str ) )
731  {
733  color.SetFromWxString( str );
734 
735  try
736  {
737  nlohmann::json js = nlohmann::json::array( { color.r, color.g, color.b, color.a } );
738  ( *m_internals )[aDest] = js;
739  }
740  catch( ... )
741  {
742  wxASSERT_MSG( false, wxT( "Could not write value in fromLegacyColor!" ) );
743  return false;
744  }
745 
746  return true;
747  }
748 
749  return false;
750 }
751 
752 
754 {
755  wxLogTrace( traceSettings, "AddNestedSettings %s", aSettings->GetFilename() );
756  m_nested_settings.push_back( aSettings );
757 }
758 
759 
761 {
762  if( !aSettings )
763  return;
764 
765  auto it = std::find_if( m_nested_settings.begin(), m_nested_settings.end(),
766  [&aSettings]( const JSON_SETTINGS* aPtr ) {
767  return aPtr == aSettings;
768  } );
769 
770  if( it != m_nested_settings.end() )
771  {
772  wxLogTrace( traceSettings, "Flush and release %s", ( *it )->GetFilename() );
773  ( *it )->SaveToFile();
774  m_nested_settings.erase( it );
775  }
776 
777  aSettings->SetParent( nullptr );
778 }
779 
780 
781 // Specializations to allow conversion between wxString and std::string via JSON_SETTINGS API
782 
783 template<> OPT<wxString> JSON_SETTINGS::Get( const std::string& aPath ) const
784 {
785  if( OPT<nlohmann::json> opt_json = GetJson( aPath ) )
786  return wxString( opt_json->get<std::string>().c_str(), wxConvUTF8 );
787 
788  return NULLOPT;
789 }
790 
791 
792 template<> void JSON_SETTINGS::Set<wxString>( const std::string& aPath, wxString aVal )
793 {
794  ( *m_internals )[aPath] = aVal.ToUTF8();
795 }
796 
797 // Specializations to allow directly reading/writing wxStrings from JSON
798 
799 void to_json( nlohmann::json& aJson, const wxString& aString )
800 {
801  aJson = aString.ToUTF8();
802 }
803 
804 
805 void from_json( const nlohmann::json& aJson, wxString& aString )
806 {
807  aString = wxString( aJson.get<std::string>().c_str(), wxConvUTF8 );
808 }
void SetParent(JSON_SETTINGS *aParent, bool aLoadFromFile=true)
void ResetToDefaults()
Resets all parameters to default values.
void Set(const std::string &aPath, ValueType aVal)
Stores a value into the JSON document Will throw an exception if ValueType isn't something that the l...
virtual bool Store()
Stores the current parameters into the JSON document represented by this object Note: this doesn't do...
JSON_SETTINGS_INTERNALS * Internals()
std::vector< PARAM_BASE * > m_params
The list of parameters (owned by this object)
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:40
bool m_createIfMissing
Whether or not the backing store file should be created it if doesn't exist.
wxString GetFilename() const
Definition: json_settings.h:71
int color
Definition: DXF_plotter.cpp:60
virtual wxString getLegacyFileExt() const
bool parse(std::istream &aStream, bool aVerbose)
Parse a PCB or footprint file from the given input stream.
SETTINGS_LOC
Definition: json_settings.h:46
virtual bool LoadFromFile(const wxString &aDirectory="")
Loads the backing file from disk and then calls Load()
OPT< nlohmann::json > GetJson(const std::string &aPath) const
Fetches a JSON object that is a subset of this JSON_SETTINGS object, using a path of the form "key1....
wxString GetFullFilename() const
static nlohmann::json::json_pointer PointerFromString(std::string aPath)
Builds a JSON pointer based on a given string.
nlohmann::json json
Definition: gerbview.cpp:41
nlohmann::json & At(const std::string &aPath)
Wrappers for the underlying JSON API so that most consumers don't need json.hpp All of these function...
void from_json(const nlohmann::json &aJson, wxString &aString)
void AddNestedSettings(NESTED_SETTINGS *aSettings)
Transfers ownership of a given NESTED_SETTINGS to this object.
bool m_deleteLegacyAfterMigration
Whether or not to delete legacy file after migration.
std::map< int, std::pair< int, std::function< bool()> > > m_migrators
A map of starting schema version to a pair of <ending version, migrator function>
static LIB_SYMBOL * dummy()
Used to draw a dummy shape when a LIB_SYMBOL is not found in library.
Definition: sch_symbol.cpp:71
wxString m_filename
The filename (not including path) of this settings file (inicode)
bool Migrate()
Migrates the schema of this settings from the version in the file to the latest version.
NESTED_SETTINGS is a JSON_SETTINGS that lives inside a JSON_SETTINGS.
virtual bool SaveToFile(const wxString &aDirectory="", bool aForce=false)
wxString m_legacy_filename
The filename of the wxConfig legacy file (if different from m_filename)
const auto NULLOPT
Definition: optional.h:9
void to_json(nlohmann::json &aJson, const wxString &aString)
std::unique_ptr< JSON_SETTINGS_INTERNALS > m_internals
static bool SetIfPresent(const nlohmann::json &aObj, const std::string &aPath, wxString &aTarget)
Sets the given string if the given key/path is present.
std::vector< NESTED_SETTINGS * > m_nested_settings
Nested settings files that live inside this one, if any.
JSON_SETTINGS(const wxString &aFilename, SETTINGS_LOC aLocation, int aSchemaVersion)
Definition: json_settings.h:63
bool IsDefault(const std::string &aParamName)
Checks if the current state of a parameter matches its default value.
bool fromLegacyString(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig string value to a given JSON pointer value.
bool m_resetParamsIfMissing
Whether or not to set parameters to their default value if missing from JSON on Load()
bool fromLegacyColor(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy COLOR4D stored in a wxConfig string to a given JSON pointer value.
void registerMigration(int aOldSchemaVersion, int aNewSchemaVersion, std::function< bool(void)> aMigrator)
Registers a migration from one schema version to another.
OPT< ValueType > Get(const std::string &aPath) const
Fetches a value from within the JSON document.
boost::optional< T > OPT
Definition: optional.h:7
bool fromLegacy(wxConfigBase *aConfig, const std::string &aKey, const std::string &aDest)
Translates a legacy wxConfig value to a given JSON pointer value.
bool Contains(const std::string &aPath) const
virtual ~JSON_SETTINGS()
const wxChar *const traceSettings
Flag to enable debug output of settings operations and management.
bool m_createIfDefault
Whether or not the backing store file should be created if all parameters are still at their default ...
void ReleaseNestedSettings(NESTED_SETTINGS *aSettings)
Saves and frees a nested settings object, if it exists within this one.
virtual wxString getFileExt() const
virtual bool MigrateFromLegacy(wxConfigBase *aLegacyConfig)
Migrates from wxConfig to JSON-based configuration.
int m_schemaVersion
Version of this settings schema.
virtual void Load()
Updates the parameters of this object based on the current JSON document contents.
size_t Count(const std::string &aPath) const
bool m_writeFile
Whether or not the backing store file should be written.
A color representation with 4 components: red, green, blue, alpha.
Definition: color4d.h:103