+ xmlNodeSetContentLen(node, reinterpret_cast<const xmlChar *>(str.data()), str.size());
+ if (clean) {
+ clean_node(node);
+ }
+}
+
+string Replace::get_contents() { return str; }
+
+Clone::Clone(const vector<Directive *> &subdirectives)
+ : subdirectives(subdirectives) {}
+
+Clone::Clone(const vector<Substitute *> &subdirectives_subs)
+{
+ for (auto it : subdirectives_subs) {
+ subdirectives.push_back(static_cast<Directive *>(it));
+ }
+}
+
+Clone::Clone(initializer_list<Directive *> subdirectives)
+ : subdirectives(subdirectives) {}
+
+Clone::~Clone()
+{
+ for (auto it : subdirectives) {
+ delete it;
+ }
+}
+
+void Clone::process(xmlNode *node, bool clean)
+{
+ // We can't use xmlNewDocFragment, since xmlDOMWrapCloneNode only knows
+ // how to clone elements.
+ vector<xmlNode *> new_nodes;
+
+ for (auto it : subdirectives) {
+ xmlNode *new_node;
+ xmlDOMWrapCloneNode(NULL, node->doc, node, &new_node, node->doc, NULL, 1, 0);
+ it->process(new_node, clean);
+ while (new_node->children != NULL) {
+ xmlNode *child = new_node->children;
+ xmlUnlinkNode(child);
+ new_nodes.push_back(child);
+ }
+ xmlFreeNode(new_node);
+ }
+
+ xmlFreeNodeList(node->children);
+ node->content = NULL;
+ node->children = node->last = NULL;
+
+ for (auto child : new_nodes) {
+ xmlAddChild(node, child);
+ }
+ if (clean) {
+ clean_node(node);
+ }
+}
+
+Alternate::Alternate(const string &attribute,
+ const vector<Substitute *> &subdirectives_subs,
+ const vector<string> &alternatives)
+ : Clone(subdirectives_subs)
+{
+ for (unsigned ix = 0; ix < subdirectives_subs.size(); ++ix) {
+ string value = alternatives[ix % alternatives.size()];
+ subdirectives_subs[ix]->substitution_map.insert(make_pair(
+ attribute,
+ new Replace { value }));
+ }