-
Notifications
You must be signed in to change notification settings - Fork 47
/
RegistryTree.cpp
312 lines (249 loc) · 7.76 KB
/
RegistryTree.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#include "RegistryTree.h"
#include "itextstream.h"
#include "string/split.h"
#include "string/encoding.h"
namespace registry
{
RegistryTree::RegistryTree() :
_topLevelNode(TOPLEVEL_NODE_NAME),
_defaultImportNode(std::string("/") + _topLevelNode),
_tree(xml::Document::create())
{
// Create the base XML structure with the <darkradiant> top-level tag
_tree.addTopLevelNode(_topLevelNode);
}
RegistryTree::RegistryTree(const RegistryTree& other) :
_topLevelNode(other._topLevelNode),
_defaultImportNode(other._defaultImportNode),
_tree(xml::Document::clone(other._tree)) // copy-construct
{
}
std::string RegistryTree::prepareKey(const std::string& key)
{
if (key.empty())
{
// no string passed, return to sender
return key;
}
else if (key[0]=='/')
{
// this is a path relative to root, don't alter it
return key;
}
else
{
// add the prefix <darkradiant> and return
return std::string("/") + _topLevelNode + std::string("/") + key;
}
}
xml::NodeList RegistryTree::findXPath(const std::string& xPath)
{
return _tree.findXPath(prepareKey(xPath));
}
bool RegistryTree::keyExists(const std::string& key)
{
std::string fullKey = prepareKey(key);
xml::NodeList result = _tree.findXPath(fullKey);
return !result.empty();
}
std::size_t RegistryTree::deleteXPath(const std::string& path)
{
// Add the toplevel node to the path if required
std::string fullPath = prepareKey(path);
xml::NodeList nodeList = _tree.findXPath(fullPath);
for (xml::Node& node : nodeList)
{
// unlink and delete the node
node.erase();
}
return nodeList.size();
}
xml::Node RegistryTree::createKeyWithName(const std::string& path,
const std::string& key,
const std::string& name)
{
// Add the toplevel node to the path if required
std::string fullPath = prepareKey(path);
xml::Node insertPoint(nullptr, nullptr);
// Check if the insert point <path> exists, create it otherwise
if (!keyExists(fullPath))
{
insertPoint = createKey(fullPath);
}
else
{
xml::NodeList nodeList = _tree.findXPath(fullPath);
insertPoint = nodeList[0];
}
// Add the <key> to the insert point <path>
xml::Node createdNode = insertPoint.createChild(key);
// Set the "name" attribute and return
createdNode.setAttributeValue("name", name);
return createdNode;
}
xml::Node RegistryTree::createKey(const std::string& key)
{
// Add the toplevel node to the path if required
std::string fullKey = prepareKey(key);
std::vector<std::string> parts;
string::split(parts, fullKey, "/");
// Are there any slashes in the path at all? If not, exit, we've no use for this
if (parts.empty())
{
rMessage() << "XMLRegistry: Cannot insert key/path without slashes." << std::endl;
return xml::Node(nullptr, nullptr);
}
xml::Node createdNode(nullptr, nullptr);
// The temporary path variable for walking through the hierarchy
std::string path("");
// Start at the root node
xml::Node insertPoint = _tree.getTopLevelNode();
for (const std::string& part : parts)
{
if (part.empty()) continue;
// Construct the new path to be searched for
path += "/" + part;
// Check if the path exists
xml::NodeList nodeList = _tree.findXPath(path);
if (!nodeList.empty())
{
// node exists, set the insertPoint to this node and continue
insertPoint = nodeList[0];
// Set the createdNode to this point, in case this is the node to be created
createdNode = insertPoint;
}
else
{
// Node not found, insert it and store the newly created node as new insertPoint
createdNode = insertPoint.createChild(part);
insertPoint = createdNode;
createdNode.addText(" ");
}
}
// return the pointer to the deepest, newly created node
return createdNode;
}
std::string RegistryTree::get(const std::string& key)
{
// Add the toplevel node to the path if required
std::string fullKey = prepareKey(key);
// Try to load the node, return an empty string if nothing is found
xml::NodeList nodeList = _tree.findXPath(fullKey);
// Does it even exist?
// There is the theoretical case that this returns two nodes that match the key criteria
// This function always uses the first one, but this may be changed if this turns out to be problematic
if (!nodeList.empty())
{
// Get and convert the value
return string::utf8_to_mb(nodeList[0].getAttributeValue("value"));
}
return std::string();
}
void RegistryTree::set(const std::string& key, const std::string& value)
{
// Add the toplevel node to the path if required
std::string fullKey = prepareKey(key);
// If the key doesn't exist, we have to create an empty one
if (!keyExists(fullKey))
{
createKey(fullKey);
}
// Try to find the node
xml::NodeList nodeList = _tree.findXPath(fullKey);
if (!nodeList.empty())
{
// Set the value
nodeList[0].setAttributeValue("value", value);
}
else
{
// If the key is still not found, something nasty has happened
rMessage() << "XMLRegistry: Critical: Key " << fullKey << " not found (it really should be there)!" << std::endl;
}
}
void RegistryTree::setAttribute(const std::string& path,
const std::string& attrName, const std::string& attrValue)
{
// Add the toplevel node to the path if required
std::string fullKey = prepareKey(path);
// If the key doesn't exist, we have to create an empty one
if (!keyExists(fullKey))
{
createKey(fullKey);
}
// Try to find the node
xml::NodeList nodeList = _tree.findXPath(fullKey);
if (!nodeList.empty())
{
// Set the value
nodeList[0].setAttributeValue(attrName, attrValue);
}
else
{
// If the key is still not found, something nasty has happened
rMessage() << "XMLRegistry: Critical: Key " << fullKey << " not found (it really should be there)!" << std::endl;
}
}
void RegistryTree::importFromFile(const std::string& importFilePath,
const std::string& parentKey)
{
std::string importKey = parentKey;
// If an empty parentKey was passed, set it to the default import node
if (importKey.empty())
{
importKey = _defaultImportNode;
}
// Check if the importKey exists - if not: create it
std::string fullImportKey = prepareKey(importKey);
if (!keyExists(fullImportKey))
{
createKey(fullImportKey);
}
// Lookup the mount point by using findXPath(), it must exist by now
xml::NodeList importNodeList = _tree.findXPath(fullImportKey);
if (importNodeList.empty())
{
rMessage() << "XMLRegistry: Critical: ImportNode could not be found." << std::endl;
return;
}
rMessage() << "XMLRegistry: Importing XML file: " << importFilePath << std::endl;
// Load the file
xml::Document importDoc(importFilePath);
if (!importDoc.isValid())
{
// Throw the XMLImportException
throw std::runtime_error("Unable to load file: " + importFilePath);
}
// Import the document into our XML tree
_tree.importDocument(importDoc, importNodeList[0]);
}
void RegistryTree::exportToFile(const std::string& key, const std::string& filename)
{
if (key.empty()) return;
// Add the toplevel node to the key if required
std::string fullKey = prepareKey(key);
// Try to find the specified node
xml::NodeList result = _tree.findXPath(fullKey);
if (result.empty())
{
rMessage() << "XMLRegistry: Failed to save path " << fullKey << std::endl;
return;
}
// Create a new xml::Document
xml::Document targetDoc = xml::Document::create();
std::string keyName = fullKey.substr(fullKey.rfind("/") + 1);
// Add an empty toplevel node with the given key (leaf) name
targetDoc.addTopLevelNode(keyName);
// Select all the child nodes of the export key
xml::NodeList children = _tree.findXPath(fullKey + "/*");
// Copy the child nodes into this document
targetDoc.copyNodes(children);
// Save the whole document to the specified filename
targetDoc.saveToFile(filename);
// rMessage() << "XMLRegistry: Saved " << key << " to " << filename << std::endl;
}
void RegistryTree::dump() const
{
_tree.saveToFile("-");
}
}