Commit 7a94aff6 authored by Nicolas Delaby's avatar Nicolas Delaby

- Add support of namespaces

- Support xupdate:insert-before element
- Order attributes updates
parent b57f4836
......@@ -81,8 +81,8 @@ class ERP5Diff:
"""
doc_list = []
for a in args:
if type(a) == type(''):
doc_list.append(etree.XML(a))
if isinstance(a, str):
doc_list.append(etree.fromstring(a))
else:
element_tree = etree.parse(a)
doc_list.append(element_tree.getroot())
......@@ -110,16 +110,35 @@ class ERP5Diff:
"""
return bool(len(element))
def _xupdateAppendAttributes(self, dict, path):
def _getQName(self, element, attr_name):
"""Return qualified name compatible with xpath
"""
if '{' == attr_name[0]:
#This is a Qualified attribute
index = attr_name.index('}')
local_name = attr_name[index+1:]
namespace_uri = attr_name[1:index]
prefix = [t[0] for t in element.nsmap.iteritems() if t[1] == namespace_uri][0]
return '%s:%s' % (prefix, local_name,), namespace_uri
else:
return attr_name, None
def _xupdateAppendAttributes(self, attr_dict, path):
"""
Append attrib to the element at 'path'.
"""
root = self._getResultRoot()
append_element = etree.Element('{%s}append' % self._ns, nsmap=root.nsmap)
append_element.attrib['select'] = path
for name, val in dict.iteritems():
key_list = attr_dict.keys()
key_list.sort()
for name in key_list:
val = attr_dict[name]
attr_element = etree.Element('{%s}attribute' % self._ns, nsmap=root.nsmap)
name, namespace_uri = name
attr_element.attrib['name'] = name
if namespace_uri:
attr_element.attrib['namespace'] = namespace_uri
attr_element.text = val
append_element.append(attr_element)
root.append(append_element)
......@@ -130,7 +149,7 @@ class ERP5Diff:
"""
root = self._getResultRoot()
remove_element = etree.Element('{%s}remove' % self._ns, nsmap=root.nsmap)
remove_element.attrib['select'] = self._concatPath(path, 'attribute::' + name)
remove_element.attrib['select'] = self._concatPath(path, 'attribute::' + name[0])
root.append(remove_element)
def _xupdateUpdateAttribute(self, name, val, path):
......@@ -139,7 +158,7 @@ class ERP5Diff:
"""
root = self._getResultRoot()
update_element = etree.Element('{%s}update' % self._ns, nsmap=root.nsmap)
update_element.attrib['select'] = self._concatPath(path, 'attribute::' + name)
update_element.attrib['select'] = self._concatPath(path, 'attribute::' + name[0])
update_element.text = val
root.append(update_element)
......@@ -182,38 +201,53 @@ class ERP5Diff:
Append elements to the element at 'path'.
xupdate:append
xupdate:insert-before
xupdate:insert-after
"""
root = self._getResultRoot()
if not element_list:
return
parent_element = element_list[0].getparent()
len_total_child_list = len(parent_element)
last_element = None
last_append_element = None
for element in element_list:
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:
relative_next = element.getnext()
relative_previous = element.getprevious()
if element.getprevious() in element_list:
#reuse same container as preceding
append_element = last_append_element
elif element.getnext() not in element_list:
elif relative_next is not None and relative_next not in element_list:
append_element = etree.SubElement(root, '{%s}insert-before' % self._ns, nsmap=root.nsmap)
path_list = self._makeRelativePathList([element.getnext()])
path_list = self._makeRelativePathList([relative_next])
next_sibling_path = self._concatPath(path, path_list[0])
append_element.attrib['select'] = next_sibling_path
elif relative_previous is not None and relative_previous not in element_list:
append_element = etree.SubElement(root, '{%s}insert-after' % self._ns, nsmap=root.nsmap)
path_list = self._makeRelativePathList([relative_previous])
preceding_sibling_path = self._concatPath(path, path_list[0])
append_element.attrib['select'] = preceding_sibling_path
else:
raise NotImplementedError
#xupdate:append by default
append_element = etree.SubElement(root, '{%s}append' % self._ns, nsmap=root.nsmap)
if parent_element.index(element) == 0:
child = 'first()'
elif parent_element.index(element) == (len_total_child_list -1):
child = 'last()'
else:
child = '%d' % (len_total_child_list - parent_element.index(element) + 1)
append_element.attrib.update({'select': path,
'child': child})
child_element = etree.SubElement(append_element, '{%s}element' % self._ns, nsmap=root.nsmap)
child_element.attrib['name'] = element.tag
child_element.attrib['name'] = element.xpath('name()')
namespace_uri = element.xpath('namespace-uri()')
if namespace_uri:
child_element.attrib['namespace'] = namespace_uri
attr_map = element.attrib
for name, value in attr_map.items():
attr_element = etree.SubElement(child_element, '{%s}attribute' % self._ns, nsmap=root.nsmap)
name, namespace_uri = self._getQName(element, name)
attr_element.attrib['name'] = name
if namespace_uri:
attr_element.attrib['namespace'] = namespace_uri
attr_element.text = value
for child in element:
clone_node = deepcopy(child)
......@@ -252,15 +286,20 @@ class ERP5Diff:
"""
# Make a list of dictionaries of the attributes.
dict_list = []
for attr_map in (element1.attrib, element2.attrib):
for element in (element1, element2):
d = {}
for name, value in attr_map.items():
d[name] = value
for name, value in element.attrib.items():
name, namespace_uri = self._getQName(element, name)
d[(name, namespace_uri)] = value
dict_list.append(d)
dict1, dict2 = dict_list
# Find all added or removed or changed attrib.
for name1, val1 in dict1.iteritems():
#sort key list to stick expected output
key_list1 = dict1.keys()
key_list1.sort()
for name1 in key_list1:
val1 = dict1[name1]
if name1 in dict2:
if val1 != dict2[name1]:
# The value is different.
......@@ -319,18 +358,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.xpath('name()'), id_val), namespaces=element.nsmap)
position_predicate = '[%i]' % position
path_list.append("%s[@id='%s']%s" % (element.tag, id_val, position_predicate,))
path_list.append("%s[@id='%s']%s" % (element.xpath('name()'), id_val, position_predicate,))
# Increase the count, for a case where other elements with the same tag name do not have
# 'id' attrib.
else:
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))
path_list.append('%s[%d]' % (element.xpath('name()'), position))
else:
path_list.append(element.tag)
path_list.append(element.xpath('name()'))
return path_list
......@@ -338,11 +377,7 @@ class ERP5Diff:
"""
Aggregate child elements of an element into a list.
"""
element_list = []
for child in element:
if type(child) == etree._Element:
element_list.append(child)
return element_list
return [child for child in element if type(child) == etree._Element]
def _aggregateText(self, element):
"""
......@@ -445,8 +480,8 @@ class ERP5Diff:
Otherwise, it is assumed to be a file object which contains a XML document.
"""
old_doc, new_doc = self._makeDocList(old_xml, new_xml)
old_root_element = old_doc #.getroottree() #old_doc.documentElement
new_root_element = new_doc #.getroottree() #new_doc.documentElement
old_root_element = old_doc.getroottree().getroot()
new_root_element = new_doc.getroottree().getroot()
try:
if self._result is not None:
self._result = None
......
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