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>
33 namespace GdomeSmartDOMExt
37 Diff::diff(const Document& dest, const Document& source, flatNodeEq flatEq)
43 return diff(dest.get_documentElement(), source.get_documentElement(), flatEq);
47 Diff::diff(const Element& dest, const Element& source, flatNodeEq flatEq)
54 Document doc = di.createDocument(DDIFF_NS_URI, "diff:doc", DocumentType());
55 Element root = doc.get_documentElement();
56 root.setAttributeNS(XMLNS_NS_URI, "xmlns:diff", DDIFF_NS_URI);
58 Diff diff(dest, doc, flatEq);
59 if (Node d = diff.diffNodes(dest, source)) root.appendChild(d);
60 else root.appendChild(doc.createElementNS(DDIFF_NS_URI, "diff:same"));
65 struct NodeEqPredicate : std::binary_function<Node,Node,bool>
67 NodeEqPredicate(Diff::flatNodeEq e) : eq(e) { };
68 bool operator()(const Node& n1, const Node& n2) const { return eq(n1, n2); };
75 collectProperAttributes(const Node& n)
78 NamedNodeMap map = n.get_attributes();
79 unsigned len = map.get_length();
81 std::vector<Node> res;
83 for (unsigned i = 0; i < len; i++)
85 Node attr = map.item(i);
87 if (attr.get_nodeName() != "xmlns" && attr.get_prefix() != "xmlns") res.push_back(attr);
94 Diff::defaultFlatNodeEq(const Node& n1, const Node& n2)
99 unsigned nodeType = n1.get_nodeType();
100 if (nodeType != n2.get_nodeType()) return false;
102 GdomeString ns1 = n1.get_namespaceURI();
103 GdomeString ns2 = n2.get_namespaceURI();
104 if (ns1 != ns2) return false;
108 case Node::ATTRIBUTE_NODE:
112 if (n1.get_localName() != n2.get_localName()) return false;
117 if (n1.get_nodeName() != n2.get_nodeName()) return false;
119 // WARNING: fallback for checking node value
120 case Node::TEXT_NODE:
121 case Node::CDATA_SECTION_NODE:
122 if (n1.get_nodeValue() != n2.get_nodeValue()) return false;
124 case Node::ELEMENT_NODE:
126 //cout << "comparing: " << n1.get_nodeName() << " ? " << n2.get_nodeName() << endl;
130 if (n1.get_localName() != n2.get_localName()) return false;
135 if (n1.get_nodeName() != n2.get_nodeName()) return false;
138 std::vector<Node> m1 = collectProperAttributes(n1);
139 std::vector<Node> m2 = collectProperAttributes(n2);
140 if (m1.size() != m2.size()) return false;
142 for (unsigned i = 0; i < m1.size(); i++)
144 std::vector<Node>::iterator p2 = std::find_if(m2.begin(), m2.end(), std::bind2nd(NodeEqPredicate(defaultFlatNodeEq), m1[i]));
145 if (p2 == m2.end()) return false;
157 Diff::sameChunk(const Node& res, unsigned long n) const
160 Element s = doc.createElementNS(DDIFF_NS_URI, "diff:same");
163 std::ostringstream os;
165 s.setAttribute("count", os.str());
171 Diff::diffNodes(const Node& p1, const Node& p2) const
175 Element m = doc.createElementNS(DDIFF_NS_URI, "diff:merge");
176 if (diffChildren(p1, p2, m)) return m;
181 Element r = doc.createElementNS(DDIFF_NS_URI, "diff:replace");
182 r.appendChild(doc.importNode(p2, true));
188 Diff::diffChildren(const Node& n1, const Node& n2, const Node& res) const
194 Node p1 = n1.get_firstChild();
195 Node p2 = n2.get_firstChild();
200 if (Node d = diffNodes(p1, p2))
205 sameChunk(res, nSame);
213 p1 = p1.get_nextSibling();
214 p2 = p2.get_nextSibling();
222 sameChunk(res, nSame);
226 unsigned nRemoved = 0;
230 p1 = p1.get_nextSibling();
235 Element r = doc.createElementNS(DDIFF_NS_URI, "diff:remove");
238 std::ostringstream os;
240 r.setAttribute("count", os.str());
251 sameChunk(res, nSame);
255 Element i = doc.createElementNS(DDIFF_NS_URI, "diff:insert");
258 i.appendChild(doc.importNode(p2, true));
259 p2 = p2.get_nextSibling();
268 getFirstElement(const Node& n)
270 Node p = n.get_firstChild();
271 while (p && p.get_nodeType() != Node::ELEMENT_NODE)
272 p = p.get_nextSibling();
277 getNextElement(const Node& n)
279 Node p = n.get_nextSibling();
280 while (p && p.get_nodeType() != Node::ELEMENT_NODE)
281 p = p.get_nextSibling();
286 Diff::patchRootNode(const Node& node, const Element& elem) const
288 GdomeString name = elem.get_localName();
291 if (elem.hasAttribute("count"))
294 istringstream is(elem.getAttribute("count"));
299 else if (name == "replace")
301 Document d1 = node.get_ownerDocument();
302 Node parent = node.get_parentNode();
305 /* the following patch is because of gdome2 bug that prevents from
306 * replacing the root element of a document.
308 assert(!node.get_previousSibling());
309 assert(!node.get_nextSibling());
310 parent.removeChild(node);
311 parent.appendChild(d1.importNode(getFirstElement(elem), true));
313 parent.replaceChild(d1.importNode(getFirstElement(elem), true), node);
315 else if (name == "merge")
316 patchChildren(node, elem);
322 Diff::patchChildren(const Node& n1, const Element& e2) const
324 Node p1 = n1.get_firstChild();
325 Element p2 = getFirstElement(e2);
328 GdomeString name = p2.get_localName();
332 if (p2.hasAttribute("count"))
334 istringstream is(p2.getAttribute("count"));
339 if (!p1) throw BADDiff("too few nodes in original document (same)");
340 p1 = p1.get_nextSibling();
343 else if (name == "replace")
345 Document d1 = n1.get_ownerDocument();
346 if (!p1) throw BADDiff("no node to replace in original document");
347 Node next = p1.get_nextSibling();
348 n1.replaceChild(d1.importNode(p2.get_firstChild(), true), p1);
351 else if (name == "insert")
353 Document d1 = n1.get_ownerDocument();
354 for (Node i = p2.get_firstChild(); i; i = i.get_nextSibling())
355 n1.insertBefore(d1.importNode(i, true), p1);
357 else if (name == "merge")
359 if (!p1) throw BADDiff("no node to merge in original document");
360 patchChildren(p1, p2);
361 p1 = p1.get_nextSibling();
363 else if (name == "remove")
366 if (p2.hasAttribute("count"))
368 istringstream is(p2.getAttribute("count"));
373 if (!p1) throw BADDiff("too few nodes in original document (remove)");
374 Node next = p1.get_nextSibling();
382 p2 = getNextElement(p2);
389 patchRootNode(dest, getFirstElement(doc.get_documentElement()));