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