Commit 03290b99 authored by Nicolas Delaby's avatar Nicolas Delaby

- Change output of xupdate:append by adding Implied attribute child

- Calculate position of node regarding his parent not by counting his position in child_list
- All equals nodes (c14n) are not compared anymore
- Fix last tested bugs
parent 5fe752bc
......@@ -52,7 +52,7 @@ class ERP5Diff:
# Declarative interfaces
zope.interface.implements(IERP5Diff,)
__version__ = 0.3
__version__ = 0.4
def __init__(self):
"""
......@@ -177,53 +177,52 @@ class ERP5Diff:
remove_element.attrib['select'] = path
root.append(remove_element)
def _xupdateInsertBefore(self, element_list, path):
"""
Insert elements before the element at 'path'.
"""
root = self._getResultRoot()
insert_element = etree.Element('{%s}insert-before' % self._ns, nsmap=root.nsmap)
insert_element.attrib['select'] = path
for element in element_list:
child_element = etree.Element('{%s}element' % self._ns, nsmap=root.nsmap)
child_element.attrib['name'] = element.tag
attr_map = element.attrib
for name, value in attr_map.items():
attr_element = etree.Element('{%s}attribute' % self._ns, nsmap=root.nsmap)
attr_element.attrib['name'] = name
attr_element.text = value
child_element.append(attr_element)
for child in element:
clone_node = deepcopy(child)
child_element.append(clone_node)
if self._hasChildren(child_element):
child_element[-1].tail = element.text
insert_element.append(child_element)
root.append(insert_element)
def _xupdateAppendElements(self, element_list, path):
"""
Append elements to the element at 'path'.
xupdate:append
xupdate:insert-before
"""
root = self._getResultRoot()
append_element = etree.Element('{%s}append' % self._ns, nsmap=root.nsmap)
append_element.attrib['select'] = path
if not element_list:
return
parent_element = element_list[0].getparent()
len_total_child_list = len(parent_element)
last_element = None
for element in element_list:
child_element = etree.Element('{%s}element' % self._ns, nsmap=root.nsmap)
if parent_element.index(element) == 0:
append_element = etree.SubElement(root, '{%s}append' % self._ns, nsmap=root.nsmap)
append_element.attrib.update({'select': path,
'child': 'first()'})
elif parent_element.index(element) == (len_total_child_list -1):
append_element = etree.SubElement(root, '{%s}append' % self._ns, nsmap=root.nsmap)
append_element.attrib.update({'select': path,
'child': 'last()'})
elif element.getprevious() in element_list:
#reuse same container as preceding
append_element = last_append_element
elif element.getnext() not in element_list:
append_element = etree.SubElement(root, '{%s}insert-before' % self._ns, nsmap=root.nsmap)
path_list = self._makeRelativePathList([element.getnext()])
next_sibling_path = self._concatPath(path, path_list[0])
append_element.attrib['select'] = next_sibling_path
else:
raise NotImplementedError
child_element = etree.SubElement(append_element, '{%s}element' % self._ns, nsmap=root.nsmap)
child_element.attrib['name'] = element.tag
attr_map = element.attrib
for name, value in attr_map.items():
attr_element = etree.Element('{%s}attribute' % self._ns, nsmap=root.nsmap)
attr_element = etree.SubElement(child_element, '{%s}attribute' % self._ns, nsmap=root.nsmap)
attr_element.attrib['name'] = name
attr_element.text = value
child_element.append(attr_element)
for child in element:
clone_node = deepcopy(child)
child_element.append(clone_node)
if self._hasChildren(child_element):
child_element[-1].tail = element.text
append_element.append(child_element)
root.append(append_element)
else:
child_element.text = element.text
last_append_element = append_element
def _testElements(self, element1, element2):
"""
......@@ -304,14 +303,6 @@ class ERP5Diff:
"""
Make a list of relative paths from a list of elements.
"""
num_map = {}
count_map = {}
for element in element_list:
if element.tag in num_map:
num_map[element.tag] += 1
else:
num_map[element.tag] = 1
count_map[element.tag] = 1
path_list = []
for element in element_list:
......@@ -328,17 +319,18 @@ class ERP5Diff:
position_predicate = ''
len_all_similar_sibling = len(element.xpath('../*[@id = "%s"]' % id_val))
if len_all_similar_sibling > 1:
position = len_all_similar_sibling - element.xpath('count(following-sibling::%s[@id = "%s"])' % (element.tag, id_val) )
position = len_all_similar_sibling - element.xpath('count(following-sibling::%s[@id = "%s"])' % (element.tag, id_val))
position_predicate = '[%i]' % position
path_list.append("%s[@id='%s']%s" % (element.tag, id_val, position_predicate,))
# Increase the count, for a case where other elements with the same tag name do not have
# 'id' attrib.
count_map[element.tag] += 1
elif num_map[element.tag] > 1:
path_list.append('%s[%d]' % (element.tag, count_map[element.tag]))
count_map[element.tag] += 1
else:
path_list.append(element.tag)
len_all_similar_sibling = len(element.findall('../%s' % element.tag))
if len_all_similar_sibling > 1:
position = len_all_similar_sibling - len(list(element.itersiblings(tag=element.tag)))
path_list.append('%s[%d]' % (element.tag, position))
else:
path_list.append(element.tag)
return path_list
......@@ -364,6 +356,33 @@ class ERP5Diff:
text += child.text
return text
def _removeStrictEqualsSubNodeList(self, old_list, new_list):
"""Remove inside list all elements which are similar
by using c14n serialisation
"""
old_candidate_list = old_list[:]
new_candidate_list = new_list[:]
for old_element in old_list:
old_tree = etree.fromstring(etree.tostring(old_element)).getroottree()
f = StringIO()
old_tree.write_c14n(f)
old_C14n = f.getvalue()
for new_element in new_list:
if new_element not in new_candidate_list:
continue
new_tree = etree.fromstring(etree.tostring(new_element)).getroottree()
f = StringIO()
new_tree.write_c14n(f)
new_C14n = f.getvalue()
if old_C14n == new_C14n:
if new_element in new_candidate_list:
new_candidate_list.remove(new_element)
if old_element in old_candidate_list:
old_candidate_list.remove(old_element)
break
return old_candidate_list, new_candidate_list
def _compareChildNodes(self, old_element, new_element, path):
"""
Compare children of two elements, and add differences into the result, if any.
......@@ -404,8 +423,9 @@ class ERP5Diff:
# The contents are elements.
self._p("Both have elements.")
old_list = self._aggregateElements(old_element)
path_list = self._makeRelativePathList(old_list)
new_list = self._aggregateElements(new_element)
old_list, new_list = self._removeStrictEqualsSubNodeList(old_list, new_list)
path_list = self._makeRelativePathList(old_list)
new_start = 0
new_len = len(new_list)
for old_node, node_path in zip(old_list, path_list):
......@@ -415,9 +435,6 @@ class ERP5Diff:
if self._testElements(old_node, new_node):
self._testAttributes(old_node, new_node, child_path)
self._compareChildNodes(old_node, new_node, child_path)
if new_current > new_start:
# There are skipped nodes in the new children.
self._xupdateInsertBefore(new_list[new_start:new_current], child_path)
new_start = new_current + 1
break
else:
......
......@@ -219,6 +219,8 @@ ERP5Diff Usage and its output example
... <object portal_type="Person" id="313731">
... <local_role type="tokens" id="tk">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_role>
... <local_permission type="tokens" id="Access contents information">&lt;?xml version="1.0"?&gt;</local_permission>
... <local_permission type="tokens" id="Add portal content">&lt;?xml version="1.0"?&gt;</local_permission>
... <local_permission type="tokens" id="View">&lt;?xml version="1.0"?&gt;</local_permission>
... </object>
... </erp5>
... """
......@@ -226,7 +228,11 @@ ERP5Diff Usage and its output example
... <erp5>
... <object portal_type="Person" id="313731">
... <local_role type="tokens" id="tatuya">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_role>
... <JohnDoe>Go to the beach</JohnDoe>
... <local_permission type="tokens" id="Access contents information">&lt;?xml version="1.0"?&gt;</local_permission>
... <local_permission type="tokens" id="Add portal content">&lt;?xml version="1.0"?&gt;</local_permission>
... <local_permission type="tokens" id="Manage portal content">&lt;?xml version="1.0"?&gt;</local_permission>
... <local_permission type="tokens" id="View">&lt;?xml version="1.0"?&gt;</local_permission>
... </object>
... </erp5>
... """
......@@ -234,8 +240,12 @@ ERP5Diff Usage and its output example
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:remove select="/object[@id='313731']/local_role[@id='tk']"/>
<xupdate:insert-before select="/object[@id='313731']/local_permission[@id='Access contents information']">
<xupdate:element name="local_role"><xupdate:attribute name="type">tokens</xupdate:attribute><xupdate:attribute name="id">tatuya</xupdate:attribute>&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</xupdate:element>
<xupdate:append select="/object[@id='313731']" child="first()">
<xupdate:element name="local_role"><xupdate:attribute name="type">tokens</xupdate:attribute><xupdate:attribute name="id">tatuya</xupdate:attribute>&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</xupdate:element>
<xupdate:element name="JohnDoe">Go to the beach</xupdate:element>
</xupdate:append>
<xupdate:insert-before select="/object[@id='313731']/local_permission[@id='View']">
<xupdate:element name="local_permission"><xupdate:attribute name="type">tokens</xupdate:attribute><xupdate:attribute name="id">Manage portal content</xupdate:attribute>&lt;?xml version="1.0"?&gt;</xupdate:element>
</xupdate:insert-before>
</xupdate:modifications>
......@@ -284,8 +294,10 @@ ERP5Diff Usage and its output example
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:remove select="/object[@id='313730']/first_name"/>
<xupdate:remove select="/object[@id='313730']/last_name"/>
<xupdate:append select="/object[@id='313730']">
<xupdate:append select="/object[@id='313730']" child="first()">
<xupdate:element name="given_name"><xupdate:attribute name="type">string</xupdate:attribute>Tatuya</xupdate:element>
</xupdate:append>
<xupdate:append select="/object[@id='313730']" child="last()">
<xupdate:element name="family_name"><xupdate:attribute name="type">string</xupdate:attribute>Kamada</xupdate:element>
</xupdate:append>
</xupdate:modifications>
......@@ -589,9 +601,9 @@ does not work as bellow example. This is a known bug.
>>> erp5diff.compare(old_xml, new_xml)
>>> erp5diff.output()
<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
<xupdate:remove select="/object[@id='313730']/workflow_action[2]"/>
<xupdate:remove select="/object[@id='313730']/workflow_action[3]"/>
<xupdate:remove select="/object[@id='313730']/workflow_action[4]"/>
<xupdate:remove select="/object[@id='313730']/workflow_action[@id='edit_workflow'][2]"/>
<xupdate:remove select="/object[@id='313730']/workflow_action[@id='edit_workflow'][3]"/>
<xupdate:remove select="/object[@id='313730']/workflow_action[@id='edit_workflow'][4]"/>
</xupdate:modifications>
21. Modify two elements that have same id
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment