File: RegistryTree.cpp

package info (click to toggle)
darkradiant 3.9.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 41,080 kB
  • sloc: cpp: 264,743; ansic: 10,659; python: 1,852; xml: 1,650; sh: 92; makefile: 21
file content (288 lines) | stat: -rw-r--r-- 7,365 bytes parent folder | download | duplicates (2)
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
#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);

	// Check if the insert point <path> exists, create it otherwise
	xml::Node insertPoint;
	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 createdNode;

	// 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;
}

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
    if (xml::NodeList nodeList = _tree.findXPath(fullKey); !nodeList.empty()) {
        // Write the content
        nodeList[0].setContent(value);
        // Remove any legacy "value" attribute
        nodeList[0].removeAttribute("value");
    }
    else {
        // If the key is still not found, something nasty has happened
        throw std::logic_error(
            "RegistryTree: created key [" + fullKey + "] but node not found"
        );
    }
}

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("-");
}

}