35a281ad42fc8a7a386a613ad94b1c2b0527928a
[xonotic/netradiant.git] / radiant / preferencedictionary.h
1 /*
2 Copyright (C) 2001-2006, William Joseph.
3 All Rights Reserved.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #if !defined(INCLUDED_PREFERENCEDICTIONARY_H)
23 #define INCLUDED_PREFERENCEDICTIONARY_H
24
25 #include "preferencesystem.h"
26 #include "xml/ixml.h"
27 #include "stream/stringstream.h"
28 #include "generic/callback.h"
29 #include "versionlib.h"
30 #include <map>
31
32 class PreferenceDictionary : public PreferenceSystem
33 {
34   class PreferenceEntry
35   {
36     StringImportCallback m_importer;
37     StringExportCallback m_exporter;
38   public:
39     PreferenceEntry(const StringImportCallback& importer, const StringExportCallback& exporter)
40       : m_importer(importer), m_exporter(exporter)
41     {
42     }
43     void importString(const char* string)
44     {
45       m_importer(string);
46     }
47     void exportString(const StringImportCallback& importer)
48     {
49       m_exporter(importer);
50     }
51   };
52
53   typedef std::map<CopiedString, PreferenceEntry> PreferenceEntries;
54   PreferenceEntries m_preferences;
55
56   typedef std::map<CopiedString, CopiedString> PreferenceCache;
57   PreferenceCache m_cache;
58
59 public:
60   typedef PreferenceEntries::iterator iterator;
61
62   iterator begin()
63   {
64     return m_preferences.begin();
65   }
66   iterator end()
67   {
68     return m_preferences.end();
69   }
70   iterator find(const char* name)
71   {
72     return m_preferences.find(name);
73   }
74
75   void registerPreference(const char* name, const StringImportCallback& importer, const StringExportCallback& exporter)
76   {
77     m_preferences.insert(PreferenceEntries::value_type(name, PreferenceEntry(importer, exporter)));
78     PreferenceCache::iterator i = m_cache.find(name);
79     if(i != m_cache.end())
80     {
81       importer((*i).second.c_str());
82       m_cache.erase(i);
83     }
84   }
85
86   void importPref(const char* name, const char* value)
87   {
88     PreferenceEntries::iterator i = m_preferences.find(name);
89     if(i != m_preferences.end())
90     {
91       (*i).second.importString(value);
92     }
93     else
94     {
95       m_cache.insert(PreferenceCache::value_type(name, value));
96     }
97   }
98 };
99
100 inline void XMLPreference_importString(XMLImporter& importer, const char* value)
101 {
102   importer.write(value, string_length(value));
103 }
104 typedef ReferenceCaller1<XMLImporter, const char*, XMLPreference_importString> XMLPreferenceImportStringCaller;
105
106 class XMLPreferenceDictionaryExporter : public XMLExporter
107 {
108   class XMLQPrefElement : public XMLElement
109   {
110     const char* m_version;
111   public:
112     XMLQPrefElement(const char* version) : m_version(version)
113     {
114     }
115     const char* name() const
116     {
117       return "qpref";
118     }
119     const char* attribute(const char* name) const
120     {
121       if(string_equal(name, "version"))
122       {
123         return m_version;
124       }
125       return "";
126     }
127     void forEachAttribute(XMLAttrVisitor& visitor) const
128     {
129       visitor.visit("version", m_version);
130     }
131   };
132     
133   class XMLPreferenceElement : public XMLElement
134   {
135     const char* m_name;
136   public:
137     XMLPreferenceElement(const char* name)
138       : m_name(name)
139     {
140     }
141     const char* name() const
142     {
143       return "epair";
144     }
145     const char* attribute(const char* name) const
146     {
147       if(string_equal(name, "name"))
148         return m_name;
149       return "";
150     }
151     void forEachAttribute(XMLAttrVisitor& visitor) const
152     {
153       visitor.visit("name", m_name);
154     }
155   };
156   
157   typedef PreferenceDictionary PreferenceEntries;
158   PreferenceEntries& m_preferences;
159   const char* m_version;
160 public:
161   XMLPreferenceDictionaryExporter(PreferenceDictionary& preferences, const char* version)
162     : m_preferences(preferences), m_version(version)
163   {
164   }
165
166   void exportXML(XMLImporter& importer)
167   {
168     importer.write("\n", 1);
169
170     XMLQPrefElement qpref_element(m_version);
171     importer.pushElement(qpref_element);
172     importer.write("\n", 1);
173
174     for(PreferenceEntries::iterator i = m_preferences.begin(); i != m_preferences.end(); ++i)
175     {
176       XMLPreferenceElement epair_element((*i).first.c_str());
177
178       importer.pushElement(epair_element);
179
180       (*i).second.exportString(XMLPreferenceImportStringCaller(importer));
181
182       importer.popElement(epair_element.name());
183       importer.write("\n", 1);
184     }
185
186     importer.popElement(qpref_element.name());
187     importer.write("\n", 1);
188   }
189 };
190
191 class XMLPreferenceDictionaryImporter : public XMLImporter
192 {
193   struct xml_state_t
194   {
195     enum ETag
196     {
197       tag_qpref,
198       tag_qpref_ignore,
199       tag_epair,
200       tag_epair_ignore
201     };
202
203     xml_state_t(ETag tag)
204       : m_tag(tag)
205     {
206     }
207
208     ETag m_tag;
209     CopiedString m_name;
210     StringOutputStream m_ostream;
211   };
212
213   typedef std::vector<xml_state_t> xml_stack_t;
214   xml_stack_t m_xml_stack;
215
216   typedef PreferenceDictionary PreferenceEntries;
217   PreferenceEntries& m_preferences;
218   Version m_version;
219 public:
220   XMLPreferenceDictionaryImporter(PreferenceDictionary& preferences, const char* version)
221     : m_preferences(preferences), m_version(version_parse(version))
222   {
223   }
224
225   void pushElement(const XMLElement& element)
226   {
227     if(m_xml_stack.empty())
228     {
229       if(string_equal(element.name(), "qpref"))
230       {
231         Version dataVersion(version_parse(element.attribute("version")));
232         if(!version_compatible(m_version, dataVersion))
233         {
234           globalOutputStream() << "qpref import: data version " << dataVersion << " is not compatible with code version " << m_version << "\n";
235           m_xml_stack.push_back(xml_state_t::tag_qpref_ignore);
236         }
237         else
238         {
239           globalOutputStream() << "qpref import: data version " << dataVersion << " is compatible with code version " << m_version << "\n";
240           m_xml_stack.push_back(xml_state_t::tag_qpref);
241         }
242       }
243       else
244       {
245         // not valid
246       }
247     }
248     else
249     {
250       switch(m_xml_stack.back().m_tag)
251       {
252       case xml_state_t::tag_qpref:
253         if(string_equal(element.name(), "epair"))
254         {
255           m_xml_stack.push_back(xml_state_t::tag_epair);
256           m_xml_stack.back().m_name = element.attribute("name");
257         }
258         else
259         {
260           // not valid
261         }
262         break;
263       case xml_state_t::tag_qpref_ignore:
264         if(string_equal(element.name(), "epair"))
265         {
266           m_xml_stack.push_back(xml_state_t::tag_epair_ignore);
267         }
268         else
269         {
270           // not valid
271         }
272         break;
273       case xml_state_t::tag_epair:
274       case xml_state_t::tag_epair_ignore:
275         // not valid
276         break;
277       }
278     }
279
280   }
281   void popElement(const char* name)
282   {
283     if(m_xml_stack.back().m_tag == xml_state_t::tag_epair)
284     {
285       m_preferences.importPref(m_xml_stack.back().m_name.c_str(), m_xml_stack.back().m_ostream.c_str());
286     }
287     m_xml_stack.pop_back();
288   }
289   std::size_t write(const char* buffer, std::size_t length)
290   {
291     return m_xml_stack.back().m_ostream.write(buffer, length);
292   }
293 };
294
295 #endif