9 namespace GdomeSmartDOMExt
13 Diff::diff(const Document& dest, const Document& source, flatNodeEq flatEq)
19 return diff(dest.get_documentElement(), source.get_documentElement(), flatEq);
23 Diff::diff(const Element& dest, const Element& source, flatNodeEq flatEq)
30 Document doc = di.createDocument(DDIFF_NS_URI, "diff:doc", DocumentType());
31 Element root = doc.get_documentElement();
32 root.setAttributeNS(XMLNS_NS_URI, "xmlns:diff", DDIFF_NS_URI);
34 Diff diff(dest, doc, flatEq);
35 if (Node d = diff.diffNodes(dest, source)) root.appendChild(d);
36 else root.appendChild(doc.createElementNS(DDIFF_NS_URI, "diff:same"));
41 struct NodeEqPredicate : std::binary_function<Node,Node,bool>
43 NodeEqPredicate(Diff::flatNodeEq e) : eq(e) { };
44 bool operator()(const Node& n1, const Node& n2) const { return eq(n1, n2); };
51 collectProperAttributes(const Node& n)
54 NamedNodeMap map = n.get_attributes();
55 unsigned len = map.get_length();
57 std::vector<Node> res;
59 for (unsigned i = 0; i < len; i++)
61 Node attr = map.item(i);
63 if (attr.get_nodeName() != "xmlns" && attr.get_prefix() != "xmlns") res.push_back(attr);
70 Diff::defaultFlatNodeEq(const Node& n1, const Node& n2)
75 unsigned nodeType = n1.get_nodeType();
76 if (nodeType != n2.get_nodeType()) return false;
78 GdomeString ns1 = n1.get_namespaceURI();
79 GdomeString ns2 = n2.get_namespaceURI();
80 if (ns1 != ns2) return false;
84 case Node::ATTRIBUTE_NODE:
88 if (n1.get_localName() != n2.get_localName()) return false;
93 if (n1.get_nodeName() != n2.get_nodeName()) return false;
95 // WARNING: fallback for checking node value
97 case Node::CDATA_SECTION_NODE:
98 if (n1.get_nodeValue() != n2.get_nodeValue()) return false;
100 case Node::ELEMENT_NODE:
102 //cout << "comparing: " << n1.get_nodeName() << " ? " << n2.get_nodeName() << endl;
106 if (n1.get_localName() != n2.get_localName()) return false;
111 if (n1.get_nodeName() != n2.get_nodeName()) return false;
114 std::vector<Node> m1 = collectProperAttributes(n1);
115 std::vector<Node> m2 = collectProperAttributes(n2);
116 if (m1.size() != m2.size()) return false;
118 for (unsigned i = 0; i < m1.size(); i++)
120 std::vector<Node>::iterator p2 = std::find_if(m2.begin(), m2.end(), std::bind2nd(NodeEqPredicate(defaultFlatNodeEq), m1[i]));
121 if (p2 == m2.end()) return false;
133 Diff::sameChunk(const Node& res, unsigned long n) const
136 Element s = doc.createElementNS(DDIFF_NS_URI, "diff:same");
139 std::ostringstream os;
141 s.setAttribute("count", os.str());
147 Diff::diffNodes(const Node& p1, const Node& p2) const
151 Element m = doc.createElementNS(DDIFF_NS_URI, "diff:merge");
152 if (diffChildren(p1, p2, m)) return m;
157 Element r = doc.createElementNS(DDIFF_NS_URI, "diff:replace");
158 r.appendChild(doc.importNode(p2, true));
164 Diff::diffChildren(const Node& n1, const Node& n2, const Node& res) const
170 Node p1 = n1.get_firstChild();
171 Node p2 = n2.get_firstChild();
176 if (Node d = diffNodes(p1, p2))
181 sameChunk(res, nSame);
189 p1 = p1.get_nextSibling();
190 p2 = p2.get_nextSibling();
198 sameChunk(res, nSame);
202 unsigned nRemoved = 0;
206 p1 = p1.get_nextSibling();
211 Element r = doc.createElementNS(DDIFF_NS_URI, "diff:remove");
214 std::ostringstream os;
216 r.setAttribute("count", os.str());
227 sameChunk(res, nSame);
231 Element i = doc.createElementNS(DDIFF_NS_URI, "diff:insert");
234 i.appendChild(doc.importNode(p2, true));
235 p2 = p2.get_nextSibling();
244 getFirstElement(const Node& n)
246 Node p = n.get_firstChild();
247 while (p && p.get_nodeType() != Node::ELEMENT_NODE)
248 p = p.get_nextSibling();
253 getNextElement(const Node& n)
255 Node p = n.get_nextSibling();
256 while (p && p.get_nodeType() != Node::ELEMENT_NODE)
257 p = p.get_nextSibling();
262 Diff::patchRootNode(const Node& node, const Element& elem) const
264 GdomeString name = elem.get_localName();
267 if (elem.hasAttribute("count"))
270 istringstream is(elem.getAttribute("count"));
275 else if (name == "replace")
277 Document d1 = node.get_ownerDocument();
278 Node parent = node.get_parentNode();
281 /* the following patch is because of gdome2 bug that prevents from
282 * replacing the root element of a document.
284 assert(!node.get_previousSibling());
285 assert(!node.get_nextSibling());
286 parent.removeChild(node);
287 parent.appendChild(d1.importNode(getFirstElement(elem), true));
289 parent.replaceChild(d1.importNode(getFirstElement(elem), true), node);
291 else if (name == "merge")
292 patchChildren(node, elem);
298 Diff::patchChildren(const Node& n1, const Element& e2) const
300 Node p1 = n1.get_firstChild();
301 Element p2 = getFirstElement(e2);
304 GdomeString name = p2.get_localName();
308 if (p2.hasAttribute("count"))
310 istringstream is(p2.getAttribute("count"));
315 if (!p1) throw BADDiff("too few nodes in original document (same)");
316 p1 = p1.get_nextSibling();
319 else if (name == "replace")
321 Document d1 = n1.get_ownerDocument();
322 if (!p1) throw BADDiff("no node to replace in original document");
323 Node next = p1.get_nextSibling();
324 n1.replaceChild(d1.importNode(p2.get_firstChild(), true), p1);
327 else if (name == "insert")
329 Document d1 = n1.get_ownerDocument();
330 for (Node i = p2.get_firstChild(); i; i = i.get_nextSibling())
331 n1.insertBefore(d1.importNode(i, true), p1);
333 else if (name == "merge")
335 if (!p1) throw BADDiff("no node to merge in original document");
336 patchChildren(p1, p2);
337 p1 = p1.get_nextSibling();
339 else if (name == "remove")
342 if (p2.hasAttribute("count"))
344 istringstream is(p2.getAttribute("count"));
349 if (!p1) throw BADDiff("too few nodes in original document (remove)");
350 Node next = p1.get_nextSibling();
358 p2 = getNextElement(p2);
365 patchRootNode(dest, getFirstElement(doc.get_documentElement()));