1 /* This file is part of EdiTeX, an editor of mathematical
2 * expressions based on TeX syntax.
4 * Copyright (C) 2002-2003 Luca Padovani <lpadovan@cs.unibo.it>,
5 * 2003 Paolo Marinelli <pmarinel@cs.unibo.it>.
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 * For more information, please visit the project's home page
22 * http://helm.cs.unibo.it/editex/
23 * or send an email to <lpadovan@cs.unibo.it>
34 namespace GdomeSmartDOMExt
38 Diff::diff(const Document& dest, const Document& source, flatNodeEq flatEq)
44 return diff(dest.get_documentElement(), source.get_documentElement(), flatEq);
48 Diff::diff(const Element& dest, const Element& source, flatNodeEq flatEq)
55 Document doc = di.createDocument(DDIFF_NS_URI, "diff:doc", DocumentType());
56 Element root = doc.get_documentElement();
57 root.setAttributeNS(XMLNS_NS_URI, "xmlns:diff", DDIFF_NS_URI);
59 Diff diff(dest, doc, flatEq);
60 if (Node d = diff.diffNodes(dest, source)) root.appendChild(d);
61 else root.appendChild(doc.createElementNS(DDIFF_NS_URI, "diff:same"));
66 struct NodeEqPredicate : std::binary_function<Node,Node,bool>
68 NodeEqPredicate(Diff::flatNodeEq e) : eq(e) { };
69 bool operator()(const Node& n1, const Node& n2) const { return eq(n1, n2); };
76 collectProperAttributes(const Node& n)
79 NamedNodeMap map = n.get_attributes();
80 unsigned len = map.get_length();
82 std::vector<Node> res;
84 for (unsigned i = 0; i < len; i++)
86 Node attr = map.item(i);
88 if (attr.get_nodeName() != "xmlns" && attr.get_prefix() != "xmlns") res.push_back(attr);
95 Diff::defaultFlatNodeEq(const Node& n1, const Node& n2)
100 unsigned nodeType = n1.get_nodeType();
101 if (nodeType != n2.get_nodeType()) return false;
103 GdomeString ns1 = n1.get_namespaceURI();
104 GdomeString ns2 = n2.get_namespaceURI();
105 if (ns1 != ns2) return false;
109 case Node::ATTRIBUTE_NODE:
113 if (n1.get_localName() != n2.get_localName()) return false;
118 if (n1.get_nodeName() != n2.get_nodeName()) return false;
120 // WARNING: fallback for checking node value
121 case Node::TEXT_NODE:
122 case Node::CDATA_SECTION_NODE:
123 if (n1.get_nodeValue() != n2.get_nodeValue()) return false;
125 case Node::ELEMENT_NODE:
127 //cout << "comparing: " << n1.get_nodeName() << " ? " << n2.get_nodeName() << endl;
131 if (n1.get_localName() != n2.get_localName()) return false;
136 if (n1.get_nodeName() != n2.get_nodeName()) return false;
139 std::vector<Node> m1 = collectProperAttributes(n1);
140 std::vector<Node> m2 = collectProperAttributes(n2);
141 if (m1.size() != m2.size()) return false;
143 for (unsigned i = 0; i < m1.size(); i++)
145 std::vector<Node>::iterator p2 = std::find_if(m2.begin(), m2.end(), std::bind2nd(NodeEqPredicate(defaultFlatNodeEq), m1[i]));
146 if (p2 == m2.end()) return false;
158 Diff::sameChunk(const Node& res, unsigned long n) const
161 Element s = doc.createElementNS(DDIFF_NS_URI, "diff:same");
164 std::ostringstream os;
166 s.setAttribute("count", os.str());
172 Diff::diffNodes(const Node& p1, const Node& p2) const
176 Element m = doc.createElementNS(DDIFF_NS_URI, "diff:merge");
177 if (diffChildren(p1, p2, m)) return m;
182 Element r = doc.createElementNS(DDIFF_NS_URI, "diff:replace");
183 r.appendChild(doc.importNode(p2, true));
189 Diff::diffChildren(const Node& n1, const Node& n2, const Node& res) const
195 Node p1 = n1.get_firstChild();
196 Node p2 = n2.get_firstChild();
201 if (Node d = diffNodes(p1, p2))
206 sameChunk(res, nSame);
214 p1 = p1.get_nextSibling();
215 p2 = p2.get_nextSibling();
223 sameChunk(res, nSame);
227 unsigned nRemoved = 0;
231 p1 = p1.get_nextSibling();
236 Element r = doc.createElementNS(DDIFF_NS_URI, "diff:remove");
239 std::ostringstream os;
241 r.setAttribute("count", os.str());
252 sameChunk(res, nSame);
256 Element i = doc.createElementNS(DDIFF_NS_URI, "diff:insert");
259 i.appendChild(doc.importNode(p2, true));
260 p2 = p2.get_nextSibling();
269 getFirstElement(const Node& n)
271 Node p = n.get_firstChild();
272 while (p && p.get_nodeType() != Node::ELEMENT_NODE)
273 p = p.get_nextSibling();
278 getNextElement(const Node& n)
280 Node p = n.get_nextSibling();
281 while (p && p.get_nodeType() != Node::ELEMENT_NODE)
282 p = p.get_nextSibling();
287 Diff::patchRootNode(const Node& node, const Element& elem) const
289 GdomeString name = elem.get_localName();
292 if (elem.hasAttribute("count"))
295 std::istringstream is(elem.getAttribute("count"));
300 else if (name == "replace")
302 Document d1 = node.get_ownerDocument();
303 Node parent = node.get_parentNode();
306 /* the following patch is because of gdome2 bug that prevents from
307 * replacing the root element of a document.
309 assert(!node.get_previousSibling());
310 assert(!node.get_nextSibling());
311 parent.removeChild(node);
312 parent.appendChild(d1.importNode(getFirstElement(elem), true));
314 parent.replaceChild(d1.importNode(getFirstElement(elem), true), node);
316 else if (name == "merge")
317 patchChildren(node, elem);
323 Diff::patchChildren(const Node& n1, const Element& e2) const
325 Node p1 = n1.get_firstChild();
326 Element p2 = getFirstElement(e2);
329 GdomeString name = p2.get_localName();
333 if (p2.hasAttribute("count"))
335 std::istringstream is(p2.getAttribute("count"));
340 if (!p1) throw BADDiff("too few nodes in original document (same)");
341 p1 = p1.get_nextSibling();
344 else if (name == "replace")
346 Document d1 = n1.get_ownerDocument();
347 if (!p1) throw BADDiff("no node to replace in original document");
348 Node next = p1.get_nextSibling();
349 n1.replaceChild(d1.importNode(p2.get_firstChild(), true), p1);
352 else if (name == "insert")
354 Document d1 = n1.get_ownerDocument();
355 for (Node i = p2.get_firstChild(); i; i = i.get_nextSibling())
356 n1.insertBefore(d1.importNode(i, true), p1);
358 else if (name == "merge")
360 if (!p1) throw BADDiff("no node to merge in original document");
361 patchChildren(p1, p2);
362 p1 = p1.get_nextSibling();
364 else if (name == "remove")
367 if (p2.hasAttribute("count"))
369 std::istringstream is(p2.getAttribute("count"));
374 if (!p1) throw BADDiff("too few nodes in original document (remove)");
375 Node next = p1.get_nextSibling();
383 p2 = getNextElement(p2);
390 patchRootNode(dest, getFirstElement(doc.get_documentElement()));