/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #if !defined( INCLUDED_PREFERENCEDICTIONARY_H ) #define INCLUDED_PREFERENCEDICTIONARY_H #include "preferencesystem.h" #include "xml/ixml.h" #include "stream/stringstream.h" #include "generic/callback.h" #include "versionlib.h" #include class PreferenceDictionary : public PreferenceSystem { class PreferenceEntry { StringImportCallback m_importer; StringExportCallback m_exporter; public: PreferenceEntry( const StringImportCallback& importer, const StringExportCallback& exporter ) : m_importer( importer ), m_exporter( exporter ){ } void importString( const char* string ){ m_importer( string ); } void exportString( const StringImportCallback& importer ){ m_exporter( importer ); } }; typedef std::map PreferenceEntries; PreferenceEntries m_preferences; typedef std::map PreferenceCache; PreferenceCache m_cache; public: typedef PreferenceEntries::iterator iterator; iterator begin(){ return m_preferences.begin(); } iterator end(){ return m_preferences.end(); } iterator find( const char* name ){ return m_preferences.find( name ); } void registerPreference( const char* name, const StringImportCallback& importer, const StringExportCallback& exporter ){ m_preferences.insert( PreferenceEntries::value_type( name, PreferenceEntry( importer, exporter ) ) ); PreferenceCache::iterator i = m_cache.find( name ); if ( i != m_cache.end() ) { importer( ( *i ).second.c_str() ); m_cache.erase( i ); } } void importPref( const char* name, const char* value ){ PreferenceEntries::iterator i = m_preferences.find( name ); if ( i != m_preferences.end() ) { ( *i ).second.importString( value ); } else { m_cache.erase( name ); m_cache.insert( PreferenceCache::value_type( name, value ) ); } } }; inline void XMLPreference_importString( XMLImporter& importer, const char* value ){ importer.write( value, string_length( value ) ); } typedef ReferenceCaller1 XMLPreferenceImportStringCaller; class XMLPreferenceDictionaryExporter : public XMLExporter { class XMLQPrefElement : public XMLElement { const char* m_version; public: XMLQPrefElement( const char* version ) : m_version( version ){ } const char* name() const { return "qpref"; } const char* attribute( const char* name ) const { if ( string_equal( name, "version" ) ) { return m_version; } return ""; } void forEachAttribute( XMLAttrVisitor& visitor ) const { visitor.visit( "version", m_version ); } }; class XMLPreferenceElement : public XMLElement { const char* m_name; public: XMLPreferenceElement( const char* name ) : m_name( name ){ } const char* name() const { return "epair"; } const char* attribute( const char* name ) const { if ( string_equal( name, "name" ) ) { return m_name; } return ""; } void forEachAttribute( XMLAttrVisitor& visitor ) const { visitor.visit( "name", m_name ); } }; typedef PreferenceDictionary PreferenceEntries; PreferenceEntries& m_preferences; const char* m_version; public: XMLPreferenceDictionaryExporter( PreferenceDictionary& preferences, const char* version ) : m_preferences( preferences ), m_version( version ){ } void exportXML( XMLImporter& importer ){ importer.write( "\n", 1 ); XMLQPrefElement qpref_element( m_version ); importer.pushElement( qpref_element ); importer.write( "\n", 1 ); for ( PreferenceEntries::iterator i = m_preferences.begin(); i != m_preferences.end(); ++i ) { XMLPreferenceElement epair_element( ( *i ).first.c_str() ); importer.pushElement( epair_element ); ( *i ).second.exportString( XMLPreferenceImportStringCaller( importer ) ); importer.popElement( epair_element.name() ); importer.write( "\n", 1 ); } importer.popElement( qpref_element.name() ); importer.write( "\n", 1 ); } }; class XMLPreferenceDictionaryImporter : public XMLImporter { struct xml_state_t { enum ETag { tag_qpref, tag_qpref_ignore, tag_epair, tag_epair_ignore }; xml_state_t( ETag tag ) : m_tag( tag ){ } ETag m_tag; CopiedString m_name; StringOutputStream m_ostream; }; typedef std::vector xml_stack_t; xml_stack_t m_xml_stack; typedef PreferenceDictionary PreferenceEntries; PreferenceEntries& m_preferences; Version m_version; public: XMLPreferenceDictionaryImporter( PreferenceDictionary& preferences, const char* version ) : m_preferences( preferences ), m_version( version_parse( version ) ){ } void pushElement( const XMLElement& element ){ if ( m_xml_stack.empty() ) { if ( string_equal( element.name(), "qpref" ) ) { Version dataVersion( version_parse( element.attribute( "version" ) ) ); if ( !version_compatible( m_version, dataVersion ) ) { globalOutputStream() << "qpref import: data version " << dataVersion << " is not compatible with code version " << m_version << "\n"; m_xml_stack.push_back( xml_state_t::tag_qpref_ignore ); } else { globalOutputStream() << "qpref import: data version " << dataVersion << " is compatible with code version " << m_version << "\n"; m_xml_stack.push_back( xml_state_t::tag_qpref ); } } else { // not valid } } else { switch ( m_xml_stack.back().m_tag ) { case xml_state_t::tag_qpref: if ( string_equal( element.name(), "epair" ) ) { m_xml_stack.push_back( xml_state_t::tag_epair ); m_xml_stack.back().m_name = element.attribute( "name" ); } else { // not valid } break; case xml_state_t::tag_qpref_ignore: if ( string_equal( element.name(), "epair" ) ) { m_xml_stack.push_back( xml_state_t::tag_epair_ignore ); } else { // not valid } break; case xml_state_t::tag_epair: case xml_state_t::tag_epair_ignore: // not valid break; } } } void popElement( const char* name ){ if ( m_xml_stack.back().m_tag == xml_state_t::tag_epair ) { m_preferences.importPref( m_xml_stack.back().m_name.c_str(), m_xml_stack.back().m_ostream.c_str() ); } m_xml_stack.pop_back(); } std::size_t write( const char* buffer, std::size_t length ){ return m_xml_stack.back().m_ostream.write( buffer, length ); } }; #endif