[Erp5-report] r42652 nicolas - in /erp5/trunk/utils/erp5diff: ./ interfaces/ src/ src/inter...

nobody at svn.erp5.org nobody at svn.erp5.org
Tue Jan 25 14:17:13 CET 2011


Author: nicolas
Date: Tue Jan 25 14:17:13 2011
New Revision: 42652

URL: http://svn.erp5.org?rev=42652&view=rev
Log:
Update folder structure of egg
Remove zope.interface dependency
Use regular unittest

No change in API, no bug fix

Added:
    erp5/trunk/utils/erp5diff/src/ERP5Diff.py
      - copied unchanged from r36986, erp5/trunk/utils/erp5diff/ERP5Diff.py
    erp5/trunk/utils/erp5diff/src/erp5diff.1
      - copied unchanged from r36986, erp5/trunk/utils/erp5diff/erp5diff.1
    erp5/trunk/utils/erp5diff/src/interfaces/
      - copied from r36986, erp5/trunk/utils/erp5diff/interfaces/
Removed:
    erp5/trunk/utils/erp5diff/ERP5Diff.py
    erp5/trunk/utils/erp5diff/erp5diff
    erp5/trunk/utils/erp5diff/erp5diff.1
    erp5/trunk/utils/erp5diff/interfaces/
    erp5/trunk/utils/erp5diff/tests.py
Modified:
    erp5/trunk/utils/erp5diff/README
    erp5/trunk/utils/erp5diff/setup.py

Removed: erp5/trunk/utils/erp5diff/ERP5Diff.py
URL: http://svn.erp5.org/erp5/trunk/utils/erp5diff/ERP5Diff.py?rev=42651&view=auto
==============================================================================
--- erp5/trunk/utils/erp5diff/ERP5Diff.py [utf8] (original)
+++ erp5/trunk/utils/erp5diff/ERP5Diff.py (removed)
@@ -1,742 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# Yoshinori OKUJI <yo at nexedi.com>
-#
-# Copyright (C) 2003 Nexedi SARL
-#
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ?02111-1307, USA.
-#
-##############################################################################
-
-from lxml import etree
-parser = etree.XMLParser(remove_blank_text=True)
-
-import sys
-import getopt
-import os
-try:
-  from cStringIO import StringIO
-except ImportError:
-  from StringIO import StringIO
-import re
-import codecs
-from copy import deepcopy
-from interfaces.erp5diff import IERP5Diff
-import zope.interface
-
-def isNodeEquals(old, new):
-  if old.tag != new.tag or old.attrib != new.attrib:
-    return False
-  if old.text != new.text or old.tail != new.tail:
-    return False
-  if len(old) != len(new):
-    return False
-  for old_child, new_child in zip(old, new):
-    if not isNodeEquals(old_child, new_child):
-      return False
-  return True
-
-class ERP5Diff:
-  """
-    Make a difference between two XML documents using XUpdate.
-    Use some assumptions in ERP5's data representation.
-
-    The strategy is:
-      1. Find a matching element among elements of the other XML document at the same depth.
-      2. Use the first matching element, even if there can be other better elements.
-      3. Assume that two elements are matching, if the tag names are identical. If either of
-         them has an attribute 'id', the values of the attributes 'id' also must be identical.
-      4. Don't use xupdate:rename for elements. It should be quite rare to rename tag names
-         in ERP5, and it is too complicated to support this renaming.
-      5. Ignore some types of nodes, such as EntityReference and Comment, because they are not
-         used in ERP5 XML documents.
-  """
-
-  # Declarative interfaces
-  zope.interface.implements(IERP5Diff,)
-
-  __version__ = '0.8.1'
-
-  def __init__(self):
-    """
-      Initialize itself.
-    """
-    self._verbose = 0
-    self._result = None
-    self._ns = 'http://www.xmldb.org/xupdate'
-
-  def setVerbosity(self, verbose):
-    """
-      Set the verbosity.
-    """
-    self._verbose = verbose
-
-  def _p(self, msg):
-    """
-      Print a message only if being verbose.
-    """
-    if self._verbose:
-      sys.stderr.write(str(msg) + os.linesep)
-
-  def _makeDocList(self, *args):
-    """
-      Make a list of Document objects.
-    """
-    doc_list = []
-    for a in args:
-      if isinstance(a, str):
-        doc_list.append(etree.fromstring(a, parser))
-      else:
-        element_tree = etree.parse(a, parser)
-        doc_list.append(element_tree.getroot())
-    return doc_list
-
-  def _concatPath(self, p1, p2, separator='/'):
-    """
-      Concatenate 'p1' and 'p2'. Add a separator between them,
-      only if 'p1' does not end with a separator.
-    """
-    if p1.endswith(separator):
-      return p1 + p2
-    return p1 + separator + p2
-
-  def _getResultRoot(self):
-    """
-      Return the root element of the result document.
-    """
-    return self._result
-    #return self._result.getroottree()
-
-  def _hasChildren(self, element):
-    """
-      Check whether the element has any children
-    """
-    return bool(len(element))
-
-  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]
-      if namespace_uri == 'http://www.w3.org/XML/1998/namespace':
-        prefix = 'xml'
-      else:
-        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, nsmap=None):
-    """
-      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
-    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=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)
-
-  def _xupdateRemoveAttribute(self, name, path, nsmap=None):
-    """
-      Remove an attribute from the element at 'path'.
-    """
-    root = self._getResultRoot()
-    remove_element = etree.Element('{%s}remove' % self._ns, nsmap=nsmap)
-    remove_element.attrib['select'] = self._concatPath(path, 'attribute::' + name[0])
-    root.append(remove_element)
-
-  def _xupdateUpdateAttribute(self, name, val, path, nsmap=None):
-    """
-      Update the value of an attribute of the element at 'path'.
-    """
-    root = self._getResultRoot()
-    update_element = etree.Element('{%s}update' % self._ns, nsmap=nsmap)
-    update_element.attrib['select'] = self._concatPath(path, 'attribute::' + name[0])
-    update_element.text = val
-    root.append(update_element)
-
-  def _xupdateRenameElement(self, name, path, nsmap=None):
-    """
-      Rename an existing element at 'path'.
-    """
-    root = self._getResultRoot()
-    rename_element = etree.Element('{%s}rename' % self._ns, nsmap=nsmap)
-    rename_element.attrib['select'] = path
-    rename_element.text = name
-    root.append(rename_element)
-
-  def _xupdateUpdateElement(self, element, path, nsmap=None):
-    """
-      Update the contents of an element at 'path' to that of 'element'.
-    """
-    root = self._getResultRoot()
-    update_element = etree.Element('{%s}update' % self._ns, nsmap=nsmap)
-    update_element.attrib['select'] = path 
-    if self._hasChildren(element):
-      for child in element:
-        clone_node = deepcopy(child)
-        update_element.append(clone_node)
-    else:
-      update_element.text = element.text
-    root.append(update_element)
-
-  def _xupdateRemoveElement(self, path, nsmap=None):
-    """
-      Remove an element at 'path'.
-    """
-    root = self._getResultRoot()
-    remove_element = etree.Element('{%s}remove' % self._ns, nsmap=nsmap)
-    remove_element.attrib['select'] = path
-    root.append(remove_element)
-
-  def _xupdateAppendElements(self, element_list, path):
-    """
-      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_append_element = None
-    for element in element_list:
-      # get only elements not something else (PI and comments are ignored)
-      # XXX May be support of PI and Comments should be added
-      # in this case fallback to previous code
-      # relative_next = element.getnext()
-      relative_next_list = element.xpath('following-sibling::*[1]')
-      if relative_next_list:
-        relative_next = relative_next_list[0]
-      else:
-        relative_next = None
-      relative_previous_list = element.xpath('preceding-sibling::*[1]')
-      if relative_previous_list:
-        relative_previous = relative_previous_list[0]
-      else:
-        relative_previous = None
-      if relative_previous in element_list:
-        #reuse same container as preceding
-        append_element = last_append_element
-      elif relative_next is not None and relative_next not in element_list:
-        append_element = etree.SubElement(root, '{%s}insert-before' % self._ns, nsmap=element.nsmap)
-        path_list = self._makeRelativePathList([relative_next], before=1)
-        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=element.nsmap)
-        path_list = self._makeRelativePathList([relative_previous])
-        preceding_sibling_path = self._concatPath(path, path_list[0])
-        append_element.attrib['select'] = preceding_sibling_path
-      else:
-        #xupdate:append by default
-        append_element = etree.SubElement(root, '{%s}append' % self._ns, nsmap=element.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.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=child_element.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)
-        child_element.append(clone_node)
-      if self._hasChildren(child_element) and element.text is not None:
-        child_element[-1].tail = element.text
-      else:
-        child_element.text = element.text
-      last_append_element = append_element
-
-  def _xupdateMoveElements(self, misplaced_node_dict, path, nsmap=None):
-    """
-    """
-    root = self._getResultRoot()
-    to_remove_node_list = []
-    for element_list in misplaced_node_dict.values():
-      for element_tuple in element_list:
-        to_remove_node_list.append(element_tuple[0])
-    child_path_list = self._makeRelativePathList(to_remove_node_list)
-    for child_path in child_path_list:
-      to_remove_path = self._concatPath(path, child_path)
-      self._xupdateRemoveElement(to_remove_path)
-    for previous, element_tuple_list in misplaced_node_dict.items():
-      if previous is None:
-        append_element = etree.SubElement(root, '{%s}append' % self._ns, nsmap=nsmap)
-        append_element.attrib['child'] = 'first()'
-      else:
-        append_element = etree.SubElement(root, '{%s}insert-after' % self._ns, nsmap=nsmap)
-        path_list = self._makeRelativePathList([previous])
-        preceding_sibling_path = self._concatPath(path, path_list[0])
-        append_element.attrib['select'] = preceding_sibling_path
-      for element_tuple in element_tuple_list:
-        element = element_tuple[1]
-        child_element = etree.SubElement(append_element, '{%s}element' % self._ns, nsmap=root.nsmap)
-        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=child_element.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)
-          child_element.append(clone_node)
-        if self._hasChildren(child_element) and element.text is not None:
-          child_element[-1].tail = element.text
-        else:
-          child_element.text = element.text
-
-  def _testElements(self, element1, element2):
-    """
-      Test if two given elements are matching. Matching does not mean that they are identical.
-    """
-    # Make sure that they are elements.
-    if type(element1) !=  type(element2) or type(element1) != etree._Element:
-      return False
-
-    if element1.tag != element2.tag:
-      return False
-
-    id_list = []
-    for attr_map in (element1.attrib, element2.attrib):
-      if 'id' in attr_map:
-        id_list.append(attr_map['id'])
-
-    if len(id_list) == 0:
-      return True
-    if len(id_list) == 1:
-      return False
-    return (id_list[0] == id_list[1])
-
-  def _testAttributes(self, element1, element2, path):
-    """
-      Test attrib of two given elements. Add differences, if any.
-    """
-    # Make a list of dictionaries of the attributes.
-    dict_list = []
-    for element in (element1, element2):
-      d = {}
-      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.
-    #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.
-          self._xupdateUpdateAttribute(name1, dict2[name1], path, nsmap=element.nsmap)
-        # Mark this attribute.
-        dict2[name1] = None
-      else:
-        # This attribute is removed.
-        self._xupdateRemoveAttribute(name1, path, nsmap=element.nsmap)
-    d = {}
-    for name2, val2 in dict2.iteritems():
-      if val2 is not None:
-        # This attribute is added.
-        d[name2] = val2
-    if d != {}:
-      self._xupdateAppendAttributes(d, path, nsmap=element.nsmap)
-
-  def _checkEmptiness(self, element):
-    """
-      Check if an element has Element or Text nodes
-    """
-    for child in element:
-      if type(child) == etree._Element:
-        return False
-    if element.text is not None:
-      return False
-    return True
-
-  def _checkIgnoreText(self, element):
-    """
-      Determine if text should be ignored by heuristics,
-      because ERP5 does not define any schema at the moment.
-      We ignore white-space text nodes between elements.
-      pseudo code:
-      tree = parse("
-      <node>
-         </node>")
-      tree.node.text == '\n    '
-    """
-    for child in element:
-      if type(child) == etree._Element:
-        return True
-    if element.text is None:
-      return True
-    return bool(element.text.strip()) is False or False
-
-  def _makeRelativePathList(self, element_list, before=0):
-    """
-      Make a list of relative paths from a list of elements.
-    """
-
-    path_list = []
-    for element in element_list:
-      # Check if this element has an attribute 'id'.s
-      id_val = None
-      attr_map = element.attrib
-      for name, value in attr_map.items():
-        if name in ('id', 'gid',):
-          id_val = value
-          id_of_id = name
-          break
-
-      if id_val is not None:
-        # If an attribute 'id' or 'gid' is present, uses the attribute for convenience.
-        position_predicate = ''
-        len_all_similar_sibling = len(element.xpath('../*[@%s = "%s"]' %\
-                                                           (id_of_id, id_val)))
-        if len_all_similar_sibling > 1:
-          position = len_all_similar_sibling - \
-              element.xpath('count(following-sibling::%s[@%s = "%s"])' %\
-                                   (element.xpath('name()'), id_of_id, id_val),
-                                                      namespaces=element.nsmap)
-          position_predicate = '[%i]' % position
-        path_list.append("%s[@%s='%s']%s" % (element.xpath('name()'), id_of_id,
-                                                  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.xpath('name()'), position-before))
-        else:
-          path_list.append(element.xpath('name()'))
-
-    return path_list
-
-  def _aggregateElements(self, element):
-    """
-      Aggregate child elements of an element into a list.
-    """
-    return [child for child in element if type(child) == etree._Element]
-
-  def _aggregateText(self, element):
-    """
-      Aggregate child text nodes of an element into a single string.
-    """
-    return '%s' % element.xpath('string(.)')
-
-  def _removeStrictEqualsSubNodeList(self, old_list, new_list):
-    """Remove inside list all elements which are similar
-    by using c14n serialisation
-    This script returns the same list of nodes whithout twins from other list
-    and a dictionary with nodes whose position has changed.
-    misplaced_node_dict :
-      key = anchor_node (node from which the moving node_list will be append)
-      value = list of tuple:
-                  -old_element (to remove)
-                  -new_element (to insert)
-    """
-    old_candidate_list = old_list[:]
-    new_candidate_list = new_list[:]
-    misplaced_node_dict = {}
-    misplaced_node_dict_after = {}
-    misplaced_node_dict_before = {}
-    old_new_index_mapping = {}
-    for old_index, old_element in enumerate(old_list):
-      if old_element not in old_candidate_list:
-        continue
-      for new_element in new_list:
-        new_index = new_list.index(new_element)
-        if new_element not in new_candidate_list:
-          continue
-        node_equality = isNodeEquals(old_element, new_element)
-        if node_equality:
-          index_key_on_new_tree = new_element.getparent().index(new_element)
-          old_new_index_mapping[index_key_on_new_tree] = old_element
-          new_start = new_index + 1
-          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)
-          if old_index == new_index:
-            break
-          elif old_index < new_index:
-            misplaced_node_dict = misplaced_node_dict_after
-          else:
-            misplaced_node_dict = misplaced_node_dict_before
-          previous_new_element = new_element.getprevious()
-          for key, preceding_value_list in misplaced_node_dict.items():
-            for element_tuple in preceding_value_list:
-              if previous_new_element == element_tuple[1]:
-                #reuse the same previous as much as possible
-                if key is not None:
-                  previous_new_element = previous_new_element.getparent()[key]
-                else:
-                  previous_new_element = None
-                break
-          if previous_new_element is not None:
-            index_key_on_new_tree = previous_new_element.getparent().index(previous_new_element)
-          else:
-            index_key_on_new_tree = None
-          misplaced_node_dict.setdefault(index_key_on_new_tree, []).append((old_element, new_element))
-          break
-
-    # Chosse the lighter one to minimise diff
-    after_dict_weight = sum(len(i) for i in misplaced_node_dict_after.values())
-    before_dict_weight = sum(len(i) for i in misplaced_node_dict_before.values())
-    if after_dict_weight > before_dict_weight and before_dict_weight:
-      misplaced_node_dict = misplaced_node_dict_before
-    elif after_dict_weight <= before_dict_weight and after_dict_weight:
-      misplaced_node_dict = misplaced_node_dict_after
-    else:
-      misplaced_node_dict = {}
-
-    for k, v in misplaced_node_dict.items():
-      if k in old_new_index_mapping:
-        value = misplaced_node_dict[k]
-        misplaced_node_dict[old_new_index_mapping[k]] = value
-      if k is not None:
-        #if the element which suppose to support insert-after does not exist in old_tree,
-        #its just an added node not an moving
-        #None means that the node will become first child, so keep it
-        del misplaced_node_dict[k]
-    return old_candidate_list, new_candidate_list, misplaced_node_dict
-
-
-  def _compareChildNodes(self, old_element, new_element, path):
-    """
-      Compare children of two elements, and add differences into the result, if any.
-      Call itself recursively, if these elements have grandchilden.
-    """
-    self._p("Comparing %s with %s at %s..." % (repr(old_element), repr(new_element), path))
-
-    # First, determine if they are empty.
-    old_is_empty = self._checkEmptiness(old_element)
-    new_is_empty = self._checkEmptiness(new_element)
-
-    if old_is_empty and new_is_empty:
-      # Nothing to do.
-      self._p("Both are empty.")
-      pass
-    else:
-      # Second, determine if text should be ignored.
-      old_ignore_text = self._checkIgnoreText(old_element)
-      new_ignore_text = self._checkIgnoreText(new_element)
-
-      if old_ignore_text != new_ignore_text:
-        # This means that the semantics of this element is quite different.
-        self._p("One of them has only text and the other does not, so just update all the contents.")
-        self._xupdateUpdateElement(new_element, path, nsmap=new_element.nsmap)
-      elif not old_ignore_text:
-        # The contents are only text.
-        self._p("Both have only text.")
-        old_text = self._aggregateText(old_element)
-        new_text = self._aggregateText(new_element)
-        if old_text != new_text:
-          self._p("They differ, so update the elements.")
-          self._xupdateUpdateElement(new_element, path, nsmap=new_element.nsmap)
-      else:
-        # The contents are elements.
-        self._p("Both have elements.")
-        old_list = self._aggregateElements(old_element)
-        new_list = self._aggregateElements(new_element)
-        old_list, new_list, misplaced_node_dict = self._removeStrictEqualsSubNodeList(old_list, new_list)
-        path_list = self._makeRelativePathList(old_list)
-        new_start = 0
-        new_len = len(new_list)
-        # Usefull set to detect orphan in new_list
-        new_object_left_index_set = set()
-        for old_node, node_path in zip(old_list, path_list):
-          child_path = self._concatPath(path, node_path)
-          for new_current in range(new_start, new_len):
-            new_node = new_list[new_current]
-            if self._testElements(old_node, new_node):
-              self._testAttributes(old_node, new_node, child_path)
-              self._compareChildNodes(old_node, new_node, child_path)
-              new_start = new_current + 1
-              if new_current in new_object_left_index_set:
-                new_object_left_index_set.remove(new_current)
-              break
-            else:
-              new_object_left_index_set.add(new_current)
-          else:
-            # There is no matching node. So this element must be removed.
-            self._xupdateRemoveElement(child_path, old_node.nsmap)
-        if new_len > new_start:
-          # There are remaining nodes in the new children.
-          self._xupdateAppendElements(new_list[new_start:new_len], path)
-          # if New children are allready added, clean up new_object_left_index_set
-          [new_object_left_index_set.remove(index)\
-           for index in range(new_start, new_len) if\
-           index in new_object_left_index_set]
-        if new_object_left_index_set:
-          self._xupdateAppendElements([new_list[index] for index \
-                                           in new_object_left_index_set], path)
-        if misplaced_node_dict:
-          self._xupdateMoveElements(misplaced_node_dict, path)
-
-  def compare(self, old_xml, new_xml):
-    """
-      Compare two given XML documents.
-      If an argument is a string, it is assumed to be a XML document itself.
-      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().getroot()
-    new_root_element = new_doc.getroottree().getroot()
-    try:
-      if self._result is not None:
-        self._result = None 
-      self._result = etree.Element('{%s}modifications' % self._ns, nsmap={'xupdate': self._ns})
-      self._result.set('version', '1.0')
-      if self._testElements(old_root_element, new_root_element):
-        qname = old_root_element.xpath('name()')
-        self._testAttributes(old_root_element, new_root_element, '/%s' % qname)
-        self._compareChildNodes(old_root_element, new_root_element, '/%s' % qname)
-      else:
-        # These XML documents seem to be completely different...
-        if old_root_element.tag != new_root_element.tag:
-          nsmap = old_root_element.nsmap
-          nsmap.update(new_root_element.nsmap)
-          self._xupdateRenameElement(new_root_element.xpath('name()'), '/%s' % old_root_element.xpath('name()'), nsmap)
-        qname = new_root_element.xpath('name()')
-        self._testAttributes(old_root_element, new_root_element, '/%s' % qname)
-        self._compareChildNodes(old_root_element, new_root_element, '/%s' % qname)
-    finally:
-      del old_doc
-      del new_doc
-
-  def output(self, output_file=None):
-    """
-      Output the result of parsing XML documents to 'output_file'.
-      If it is not specified, stdout is assumed.
-    """
-    if output_file is None:
-      output_file = sys.stdout
-    # stream
-    xml = etree.tostring(self._result, encoding='utf-8', pretty_print=True)
-    output_file.write(xml)
-
-  def outputString(self):
-    """
-      Return the result as a string object.
-    """
-    io = StringIO()
-    self.output(io)
-    ret = io.getvalue()
-    io.close()
-    return ret
-
-def main():
-  """
-    The main routine of ERP5Diff.
-  """
-  try:
-    opts, args = getopt.getopt(sys.argv[1:], "ho:v", ["help", "output=", "verbose"])
-  except getopt.GetoptError, msg:
-    print msg
-    print "Try ``erp5diff --help'' for more information."
-    sys.exit(2)
-  output = None
-  verbose = 0
-  for o, a in opts:
-    if o == "-v":
-      verbose = 1
-    elif o in ("-h", "--help"):
-      print '''Usage: erp5diff [OPTION]... OLD_XML NEW_XML
-Make a difference between two XML documents in XUpdate format.
-
-    -h, --help          display this message and exit
-    -o, --output=FILE   output the result to the file FILE
-    -v, --verbose       print verbose messages
-
-Report bugs to <yo at nexedi.com>.'''
-      sys.exit()
-    elif o in ("-o", "--output"):
-      output = a
-
-  if len(args) != 2:
-    if len(args) > 2:
-      print "Too many arguments."
-    else:
-      print "Too few arguments."
-    print "Try ``erp5diff --help'' for more information."
-    sys.exit(2)
-
-  d = ERP5Diff()
-  d.setVerbosity(verbose)
-
-  old_xml = open(args[0])
-  new_xml = open(args[1])
-  d.compare(old_xml, new_xml)
-  old_xml.close()
-  new_xml.close()
-
-  try:
-    if output is not None:
-      file = open(output, 'w')
-    else:
-      file = None
-    d.output(file)
-  except:
-    if output is not None:
-      file.close()
-      os.remove(output)
-    raise
-  else:
-    if file is not None:
-      file.close()
-
-  sys.exit()
-
-if __name__ == '__main__':
-  main()

Modified: erp5/trunk/utils/erp5diff/README
URL: http://svn.erp5.org/erp5/trunk/utils/erp5diff/README?rev=42652&r1=42651&r2=42652&view=diff
==============================================================================
--- erp5/trunk/utils/erp5diff/README [utf8] (original)
+++ erp5/trunk/utils/erp5diff/README [utf8] Tue Jan 25 14:17:13 2011
@@ -11,1033 +11,6 @@ Once you have installed erp5diff, you ca
 $ erp5diff old.xml new.xml
 See the manpage erp5diff(1) or "erp5diff --help" for more information.
 
-Also, you can use the module ERP5Diff from your Python script.
-Do "pydoc ERP5Diff" for more information.
-
-
-ERP5Diff Usage and its output example
-=====================================
-
-1. update the texts of the three elements
-
-  >>> from ERP5Diff import ERP5Diff
-  >>> erp5diff = ERP5Diff()
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <description type="text">description1 --- $sdfr&#231;_sdfs&#231;df_oisfsopf</description>
-  ...     <first_name type="string">Kamada</first_name>
-  ...     <last_name type="string">Kamada</last_name>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:24.700 GMT+9</time>
-  ...     </workflow_action>
-  ...     </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <description type="text">description3 &#231;sdf__sdf&#231;&#231;&#231;_df___&amp;amp;&amp;amp;&#233;]]]&#176;&#176;&#176;&#176;&#176;&#176;</description>
-  ...     <first_name type="string">Tatuya</first_name>
-  ...     <last_name type="string">Kamada</last_name>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:24.703 GMT+9</time>
-  ...     </workflow_action>
-  ...     </object>
-  ... </erp5>
-  ... """
-
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/erp5/object[@id='313730']/description">description3 çsdf__sdfççç_df___&amp;amp;&amp;amp;é]]]°°°°°°</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/first_name">Tatuya</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/workflow_action[@id='edit_workflow']/time">2009/08/28 19:12:24.703 GMT+9</xupdate:update>
-  </xupdate:modifications>
-
-2. update one element
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...      <description type="text">description2&#233;&#224;@  $*&amp;lt; &amp;lt; -----</description>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...      <description type="text">description3&#233;&#224;@  $*&amp;lt; &amp;lt; -----</description>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/erp5/object[@id='313730']/description">description3éà@  $*&amp;lt; &amp;lt; -----</xupdate:update>
-  </xupdate:modifications>
-
-3. same
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <title type="string">Tatuya Kamada</title>
-  ...     <subject_list type="lines">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;list id="i2"&gt;&lt;/list&gt;&lt;/marshal&gt;</subject_list>
-  ...     <first_name type="string">Kamada</first_name>
-  ...     <last_name type="string">Tatuya</last_name>
-  ...     <workflow_action id="edit_workflow">
-  ...       <actor type="string">tatuya</actor>
-  ...       <time type="date">2009/08/28 19:12:26.631 GMT+9</time>
-  ...     </workflow_action>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <title type="string">Tatuya Kamada</title>
-  ...     <subject_list type="lines">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;list id="i2"&gt;&lt;/list&gt;&lt;/marshal&gt;</subject_list>
-  ...     <first_name type="string">Kamada</first_name>
-  ...     <last_name type="string">Tatuya</last_name>
-  ...     <workflow_action id="edit_workflow">
-  ...       <actor type="string">tatuya</actor>
-  ...       <time type="date">2009/08/28 19:12:26.631 GMT+9</time>
-  ...     </workflow_action>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0"/>
-
-4. update the texts of the elements and remove an element
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <description type="text">description2&#233;&#224;@  $*&amp;lt; &amp;lt;&amp;lt;&amp;lt;  -----</description>
-  ...     <language type="string">en</language>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.432 GMT+9</time>
-  ...     </workflow_action>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <description type="text">description1 --- $sdfr&#231;_sdfs&#231;df_oisfsopf</description>
-  ...     <language type="None"/>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
-  ...     </workflow_action>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/erp5/object[@id='313730']/description">description1 --- $sdfrç_sdfsçdf_oisfsopf</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/language/attribute::type">None</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/language"/>
-    <xupdate:remove select="/erp5/object[@id='313730']/workflow_action[@id='edit_workflow'][2]"/>
-  </xupdate:modifications>
-
-5. update two elements includes some symbols
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <description type="text">description2&#233;&#224;@  $*&amp;lt;&amp;lt;-----&amp;gt;&amp;gt;</description>
-  ...     <language type="string">jp</language>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <description type="text">description4 sdflkmooo^^^^]]]]]{{{{{{{</description>
-  ...     <language type="string">ca</language>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/erp5/object[@id='313730']/description">description4 sdflkmooo^^^^]]]]]{{{{{{{</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/language">ca</xupdate:update>
-  </xupdate:modifications>
-
-6. update two date element which have same id
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:40.550 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:40.903 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:40.907 GMT+9</time>
-  ...     </workflow_action>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:40.550 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:40.905 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:40.910 GMT+9</time>
-  ...     </workflow_action>
-  ...    </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/erp5/object[@id='313730']/workflow_action[@id='edit_workflow'][2]/time">2009/08/28 19:12:40.905 GMT+9</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/workflow_action[@id='edit_workflow'][3]/time">2009/08/28 19:12:40.910 GMT+9</xupdate:update>
-  </xupdate:modifications>
-
-7. insert and remove elements
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <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>
-  ... """
-  >>> new_xml = """
-  ... <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>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove select="/erp5/object[@id='313731']/local_role[@id='tk']"/>
-    <xupdate:append select="/erp5/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="/erp5/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>
-
-8. update xml in xml
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313731">
-  ...     <local_permission type="tokens" id="View">&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_permission>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313731">
-  ...     <local_permission type="tokens" id="View">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Assignee&lt;/string&gt;&lt;string&gt;Assignor&lt;/string&gt;&lt;string&gt;Associate&lt;/string&gt;&lt;string&gt;Auditor&lt;/string&gt;&lt;string&gt;Author&lt;/string&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_permission>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/erp5/object[@id='313731']/local_permission[@id='View']">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Assignee&lt;/string&gt;&lt;string&gt;Assignor&lt;/string&gt;&lt;string&gt;Associate&lt;/string&gt;&lt;string&gt;Auditor&lt;/string&gt;&lt;string&gt;Author&lt;/string&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</xupdate:update>
-  </xupdate:modifications>
-
-9. rename element
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <first_name type="string">Tatuya</first_name>
-  ...     <last_name type="string">Kamada</last_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <given_name type="string">Tatuya</given_name>
-  ...     <family_name type="string">Kamada</family_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove select="/erp5/object[@id='313730']/first_name"/>
-    <xupdate:remove select="/erp5/object[@id='313730']/last_name"/>
-    <xupdate:append select="/erp5/object[@id='313730']" child="first()">
-      <xupdate:element name="given_name"><xupdate:attribute name="type">string</xupdate:attribute>Tatuya</xupdate:element>
-      <xupdate:element name="family_name"><xupdate:attribute name="type">string</xupdate:attribute>Kamada</xupdate:element>
-    </xupdate:append>
-  </xupdate:modifications>
-
-10. rename root element
-
->>> old_xml = """
-... <erp5>
-...   <object portal_type="Person" id="313730">
-...     <id type="string">313730</id>
-...     <title type="string">Tatuya Kamada</title>
-...   </object>
-... </erp5>
-... """
->>> new_xml = """
-... <erp6>
-...   <object portal_type="Person" id="313730">
-...     <id type="string">313730</id>
-...     <title type="string">Tatuya Kamada</title>
-...   </object>
-... </erp6>
-... """
->>> erp5diff.compare(old_xml, new_xml)
->>> erp5diff.output()
-<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-  <xupdate:rename select="/erp5">erp6</xupdate:rename>
-</xupdate:modifications>
-
-
-11. Update one attribute
-
->>> old_xml = """
-... <erp5>
-...   <object portal_type="Person" id="313730">
-...     <local_role type="tokens" id="fab">&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>
-...   </object>
-... </erp5>
-... """
->>> new_xml = """
-... <erp5>
-...   <object portal_type="Person" id="313730">
-...     <local_role type="ccc" id="fab">&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>
-...   </object>
-... </erp5>
-... """
->>> erp5diff.compare(old_xml, new_xml)
->>> erp5diff.output()
-<xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-  <xupdate:update select="/erp5/object[@id='313730']/local_role[@id='fab']/attribute::type">ccc</xupdate:update>
-</xupdate:modifications>
-
-12. Update two attribute
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <local_permission attr_a='aaa' type="tokens" id="View">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Assignee&lt;/string&gt;&lt;string&gt;Assignor&lt;/string&gt;&lt;string&gt;Associate&lt;/string&gt;&lt;string&gt;Auditor&lt;/string&gt;&lt;string&gt;Author&lt;/string&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_permission>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <local_permission attr_a='ccc' type="ccc" id="View">&lt;?xml version="1.0"?&gt;&lt;marshal&gt;&lt;tuple&gt;&lt;string&gt;Assignee&lt;/string&gt;&lt;string&gt;Assignor&lt;/string&gt;&lt;string&gt;Associate&lt;/string&gt;&lt;string&gt;Auditor&lt;/string&gt;&lt;string&gt;Author&lt;/string&gt;&lt;string&gt;Manager&lt;/string&gt;&lt;string&gt;Owner&lt;/string&gt;&lt;/tuple&gt;&lt;/marshal&gt;</local_permission>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/erp5/object[@id='313730']/local_permission[@id='View']/attribute::attr_a">ccc</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/local_permission[@id='View']/attribute::type">ccc</xupdate:update>
-  </xupdate:modifications>
-
-13. Update three attribute
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <title attribute_a="aaa" attribute_b="bbb" attribute_c="ccc" type="string">Tatuya Kamada</title>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <title attribute_a="nnn" attribute_b="nnn" attribute_c="nnn" type="string">Tatuya Kamada</title>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/erp5/object[@id='313730']/title/attribute::attribute_a">nnn</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/title/attribute::attribute_b">nnn</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/title/attribute::attribute_c">nnn</xupdate:update>
-  </xupdate:modifications>
-
-14. Remove one attribute
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <first_name attribute_a="aaa" attribute_b="bbb" attribute_c="ccc" type="string">Tatuya</first_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <first_name attribute_a="aaa" attribute_b="bbb" type="string">Tatuya</first_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove select="/erp5/object[@id='313730']/first_name/attribute::attribute_c"/>
-  </xupdate:modifications>
-
-15. Remove two attribute
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <first_name attribute_a="aaa" attribute_b="bbb" attribute_c="ccc" type="string">Tatuya</first_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <first_name attribute_a="aaa" type="string">Tatuya</first_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove select="/erp5/object[@id='313730']/first_name/attribute::attribute_b"/>
-    <xupdate:remove select="/erp5/object[@id='313730']/first_name/attribute::attribute_c"/>
-  </xupdate:modifications>
-
-
-16. Remove three attribute
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <first_name attribute_a="aaa" attribute_b="bbb" attribute_c="ccc" type="string">Tatuya</first_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <first_name type="string">Tatuya</first_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove select="/erp5/object[@id='313730']/first_name/attribute::attribute_a"/>
-    <xupdate:remove select="/erp5/object[@id='313730']/first_name/attribute::attribute_b"/>
-    <xupdate:remove select="/erp5/object[@id='313730']/first_name/attribute::attribute_c"/>
-  </xupdate:modifications>
-
-17. Append one attribute
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <last_name type="string">Kamada</last_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <last_name attribute_a="aaa" type="string">Kamada</last_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:append select="/erp5/object[@id='313730']/last_name">
-      <xupdate:attribute name="attribute_a">aaa</xupdate:attribute>
-    </xupdate:append>
-  </xupdate:modifications>
-
-
-18. Append two attribute
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <last_name type="string">Kamada</last_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <last_name attribute_a="aaa" attribute_b="bbb" type="string">Kamada</last_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:append select="/erp5/object[@id='313730']/last_name">
-      <xupdate:attribute name="attribute_a">aaa</xupdate:attribute>
-      <xupdate:attribute name="attribute_b">bbb</xupdate:attribute>
-    </xupdate:append>
-  </xupdate:modifications>
-
-19. Append three attribute
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <last_name type="string">Kamada</last_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <last_name attribute_a="aaa" attribute_b="bbb" attribute_c="ccc" type="string">Kamada</last_name>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:append select="/erp5/object[@id='313730']/last_name">
-      <xupdate:attribute name="attribute_a">aaa</xupdate:attribute>
-      <xupdate:attribute name="attribute_b">bbb</xupdate:attribute>
-      <xupdate:attribute name="attribute_c">ccc</xupdate:attribute>
-    </xupdate:append>
-  </xupdate:modifications>
-
-
-20. Remove some elements that have same id
-
-This is an unexpected case for current ERP5Diff alogrithm. So current ERP5Diff
-does not work as bellow example. This is a known bug.
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.432 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.434 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.432 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.430 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.428 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.426 GMT+9</time>
-  ...     </workflow_action>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.430 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.428 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.426 GMT+9</time>
-  ...     </workflow_action>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove select="/erp5/object[@id='313730']/workflow_action[@id='edit_workflow'][2]"/>
-    <xupdate:remove select="/erp5/object[@id='313730']/workflow_action[@id='edit_workflow'][3]"/>
-    <xupdate:remove select="/erp5/object[@id='313730']/workflow_action[@id='edit_workflow'][4]"/>
-  </xupdate:modifications>
-
-21. Modify two elements that have same id
-
- As well as No.20. This a known bug, too.
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.432 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.434 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.436 GMT+9</time>
-  ...     </workflow_action>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Person" id="313730">
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/28 19:12:34.424 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/29 19:12:34.432 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/30 19:12:34.434 GMT+9</time>
-  ...     </workflow_action>
-  ...     <workflow_action id="edit_workflow">
-  ...       <time type="date">2009/08/31 19:12:34.436 GMT+9</time>
-  ...     </workflow_action>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/erp5/object[@id='313730']/workflow_action[@id='edit_workflow'][2]/time">2009/08/29 19:12:34.432 GMT+9</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/workflow_action[@id='edit_workflow'][3]/time">2009/08/30 19:12:34.434 GMT+9</xupdate:update>
-    <xupdate:update select="/erp5/object[@id='313730']/workflow_action[@id='edit_workflow'][4]/time">2009/08/31 19:12:34.436 GMT+9</xupdate:update>
-  </xupdate:modifications>
-
-22. Modify attributes of sequencial objects
-
- ERP5Diff creates target index from 0 as a XPath string, but according to the
- definition of the XPath specification <http://www.w3.org/TR/xpath>, it is wrong.
- It should be start from 1. This is a known problem.
-
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Test">
-  ...     <title>A</title>
-  ...   </object>
-  ...   <object portal_type="Test">
-  ...     <title>A</title>
-  ...   </object>
-  ...   <object portal_type="Test">
-  ...     <title>A</title>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Test">
-  ...     <title>A</title>
-  ...   </object>
-  ...   <object portal_type="Test">
-  ...     <title>B</title>
-  ...   </object>
-  ...   <object portal_type="Test">
-  ...     <title>C</title>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/erp5/object[2]/title">B</xupdate:update>
-    <xupdate:update select="/erp5/object[3]/title">C</xupdate:update>
-  </xupdate:modifications>
-
-23. Modify nodes with Qualified Names
-  ERP5Diff should create xpath valid expression with correct prefix
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Test">
-  ...     <prefix:title xmlns:prefix="http://any_uri">A</prefix:title>
-  ...   </object>
-  ...   <object portal_type="Test">
-  ...     <prefixbis:title xmlns:prefixbis="http://any_uri_bis">A</prefixbis:title>
-  ...   </object>
-  ...   <object portal_type="Test">
-  ...     <againanotherprefix:title xmlns:againanotherprefix="http://any_uri">A</againanotherprefix:title>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Test">
-  ...     <anotherprefix:title xmlns:anotherprefix="http://any_uri">A</anotherprefix:title>
-  ...   </object>
-  ...   <object portal_type="Test">
-  ...     <prefix:title xmlns:prefix="http://any_uri" prefix:myattr="anyvalue">B</prefix:title>
-  ...   </object>
-  ...   <object portal_type="Test">
-  ...     <title>A</title>
-  ...   </object>
-  ...   <erp5:object portal_type="Test" xmlns:erp5="http://www.erp5.org/namespaces/erp5_object">
-  ...     <title>B</title>
-  ...   </erp5:object>
-  ...   <object portal_type="Test">
-  ...     <prefix:title xmlns:prefix="http://any_uri">C</prefix:title>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove xmlns:prefixbis="http://any_uri_bis" select="/erp5/object[2]/prefixbis:title"/>
-    <xupdate:append xmlns:prefix="http://any_uri" select="/erp5/object[2]" child="first()">
-      <xupdate:element name="prefix:title" namespace="http://any_uri"><xupdate:attribute name="prefix:myattr" namespace="http://any_uri">anyvalue</xupdate:attribute>B</xupdate:element>
-    </xupdate:append>
-    <xupdate:remove xmlns:againanotherprefix="http://any_uri" select="/erp5/object[3]/againanotherprefix:title"/>
-    <xupdate:append select="/erp5/object[3]" child="first()">
-      <xupdate:element name="title">A</xupdate:element>
-    </xupdate:append>
-    <xupdate:insert-after xmlns:erp5="http://www.erp5.org/namespaces/erp5_object" select="/erp5/object[3]">
-      <xupdate:element name="erp5:object" namespace="http://www.erp5.org/namespaces/erp5_object">
-        <xupdate:attribute name="portal_type">Test</xupdate:attribute>
-        <title>B</title>
-      </xupdate:element>
-      <xupdate:element name="object">
-        <xupdate:attribute name="portal_type">Test</xupdate:attribute>
-        <prefix:title xmlns:prefix="http://any_uri">C</prefix:title>
-      </xupdate:element>
-    </xupdate:insert-after>
-  </xupdate:modifications>
-
-24. Modify nodes with Qualified Names
-  Work on Attributes specially
-  >>> old_xml = """
-  ... <erp5>
-  ...   <object portal_type="Test">
-  ...     <title xmlns:prefix="http://any_uri" prefix:attr="A">A</title>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> new_xml = """
-  ... <erp5>
-  ...   <object portal_type="Test">
-  ...     <title xmlns:prefix="http://any_uri" prefix:attr="B">A</title>
-  ...   </object>
-  ... </erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update xmlns:prefix="http://any_uri" select="/erp5/object/title/attribute::prefix:attr">B</xupdate:update>
-  </xupdate:modifications>
-
-25. Modify nodes with Qualified Names at root level
-  Work on Attributes specially
-  >>> old_xml = """
-  ... <erp5:erp5 xmlns:erp5="http://www.erp5.org/namspaces/erp5_object" a="aaa" b="bbb">
-  ...   <object portal_type="Test">
-  ...     <title xmlns:prefix="http://any_uri" prefix:attr="A">A</title>
-  ...   </object>
-  ... </erp5:erp5>
-  ... """
-  >>> new_xml = """
-  ... <aaa:erp5 xmlns:aaa="http://www.erp5.org/namspaces/aaa" b="bbb" >
-  ...   <object portal_type="Test">
-  ...     <title xmlns:prefix="http://any_uri" prefix:attr="B">A</title>
-  ...   </object>
-  ... </aaa:erp5>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:rename xmlns:aaa="http://www.erp5.org/namspaces/aaa" xmlns:erp5="http://www.erp5.org/namspaces/erp5_object" select="/erp5:erp5">aaa:erp5</xupdate:rename>
-    <xupdate:remove xmlns:aaa="http://www.erp5.org/namspaces/aaa" select="/aaa:erp5/attribute::a"/>
-    <xupdate:update xmlns:prefix="http://any_uri" xmlns:aaa="http://www.erp5.org/namspaces/aaa" select="/aaa:erp5/object/title/attribute::prefix:attr">B</xupdate:update>
-  </xupdate:modifications>
-
-26. Reorder some nodes to the end of list
-  >>> old_xml = """
-  ... <ul>
-  ...   <li>1</li>
-  ...   <li>2</li>
-  ...   <li>3</li>
-  ...   <li>4</li>
-  ...   <li>5</li>
-  ...   <li>6</li>
-  ...   <li>7</li>
-  ...   <li>8</li>
-  ...   <li>9</li>
-  ... </ul>
-  ... """
-  >>> new_xml = """
-  ... <ul>
-  ...   <li>1</li>
-  ...   <li>2</li>
-  ...   <li>5</li>
-  ...   <li>6</li>
-  ...   <li>7</li>
-  ...   <li>3</li>
-  ...   <li>4</li>
-  ...   <li>8</li>
-  ...   <li>9</li>
-  ... </ul>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove select="/ul/li[3]"/>
-    <xupdate:remove select="/ul/li[4]"/>
-    <xupdate:insert-after select="/ul/li[7]">
-      <xupdate:element name="li">3</xupdate:element>
-      <xupdate:element name="li">4</xupdate:element>
-    </xupdate:insert-after>
-  </xupdate:modifications>
-
-26. Reorder some nodes from the end of list
-  >>> old_xml = """
-  ... <ul>
-  ...   <li>1</li>
-  ...   <li>2</li>
-  ...   <li>3</li>
-  ...   <li>4</li>
-  ...   <li>5</li>
-  ...   <li>6</li>
-  ...   <li>7</li>
-  ...   <li>8</li>
-  ...   <li>9</li>
-  ... </ul>
-  ... """
-  >>> new_xml = """
-  ... <ul>
-  ...   <li>1</li>
-  ...   <li>2</li>
-  ...   <li>7</li>
-  ...   <li>8</li>
-  ...   <li>3</li>
-  ...   <li>4</li>
-  ...   <li>5</li>
-  ...   <li>6</li>
-  ...   <li>9</li>
-  ... </ul>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove select="/ul/li[7]"/>
-    <xupdate:remove select="/ul/li[8]"/>
-    <xupdate:insert-after select="/ul/li[2]">
-      <xupdate:element name="li">7</xupdate:element>
-      <xupdate:element name="li">8</xupdate:element>
-    </xupdate:insert-after>
-  </xupdate:modifications>
-
-27. Reorder some nodes at start
-  >>> old_xml = """
-  ... <ul>
-  ...   <li>1</li>
-  ...   <li>2</li>
-  ...   <li>3</li>
-  ...   <li>4</li>
-  ...   <li>5</li>
-  ...   <li>6</li>
-  ...   <li>7</li>
-  ...   <li>8</li>
-  ...   <li>9</li>
-  ... </ul>
-  ... """
-  >>> new_xml = """
-  ... <ul>
-  ...   <li>5</li>
-  ...   <li>6</li>
-  ...   <li>1</li>
-  ...   <li>2</li>
-  ...   <li>3</li>
-  ...   <li>4</li>
-  ...   <li>7</li>
-  ...   <li>8</li>
-  ...   <li>9</li>
-  ... </ul>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove select="/ul/li[5]"/>
-    <xupdate:remove select="/ul/li[6]"/>
-    <xupdate:append child="first()">
-      <xupdate:element name="li">5</xupdate:element>
-      <xupdate:element name="li">6</xupdate:element>
-    </xupdate:append>
-  </xupdate:modifications>
-
-28. Reorder some nodes at the end
-  >>> old_xml = """
-  ... <ul>
-  ...   <li>1</li>
-  ...   <li>2</li>
-  ...   <li>3</li>
-  ...   <li>4</li>
-  ...   <li>5</li>
-  ...   <li>6</li>
-  ...   <li>7</li>
-  ...   <li>8</li>
-  ...   <li>9</li>
-  ... </ul>
-  ... """
-  >>> new_xml = """
-  ... <ul>
-  ...   <li>1</li>
-  ...   <li>4</li>
-  ...   <li>5</li>
-  ...   <li>6</li>
-  ...   <li>7</li>
-  ...   <li>8</li>
-  ...   <li>9</li>
-  ...   <li>2</li>
-  ...   <li>3</li>
-  ... </ul>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove select="/ul/li[2]"/>
-    <xupdate:remove select="/ul/li[3]"/>
-    <xupdate:insert-after select="/ul/li[9]">
-      <xupdate:element name="li">2</xupdate:element>
-      <xupdate:element name="li">3</xupdate:element>
-    </xupdate:insert-after>
-  </xupdate:modifications>
-
-29. Delete children with white-space as text nodes
-  >>> old_xml = """
-  ... <object>
-  ...   <local_permission type="tokens" id="View">
-  ...      <marshal:marshal xmlns:marshal="http://www.erp5.org/namespaces/marshaller">
-  ...        <marshal:tuple>
-  ...          <marshal:string>Assignee</marshal:string>
-  ...          <marshal:string>Assignor</marshal:string>
-  ...        </marshal:tuple>
-  ...      </marshal:marshal>
-  ...    </local_permission>
-  ...  </object>
-  ... """
-  >>> new_xml = """
-  ... <object>
-  ...   <local_permission type="tokens" id="View">
-  ...      <marshal:marshal xmlns:marshal="http://www.erp5.org/namespaces/marshaller">
-  ...        <marshal:tuple>
-  ...        </marshal:tuple>
-  ...      </marshal:marshal>
-  ...    </local_permission>
-  ...  </object>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove xmlns:marshal="http://www.erp5.org/namespaces/marshaller" select="/object/local_permission[@id='View']/marshal:marshal/marshal:tuple/marshal:string[1]"/>
-    <xupdate:remove xmlns:marshal="http://www.erp5.org/namespaces/marshaller" select="/object/local_permission[@id='View']/marshal:marshal/marshal:tuple/marshal:string[2]"/>
-  </xupdate:modifications>
-
-29Bis. Delete childrens with auto-closing nodes
-  >>> old_xml = """
-  ... <object>
-  ...   <local_permission type="tokens" id="View">
-  ...      <marshal:marshal xmlns:marshal="http://www.erp5.org/namespaces/marshaller">
-  ...        <marshal:tuple>
-  ...          <marshal:string>Assignee</marshal:string>
-  ...          <marshal:string>Assignor</marshal:string>
-  ...        </marshal:tuple>
-  ...      </marshal:marshal>
-  ...    </local_permission>
-  ...  </object>
-  ... """
-  >>> new_xml = """
-  ... <object>
-  ...   <local_permission type="tokens" id="View">
-  ...      <marshal:marshal xmlns:marshal="http://www.erp5.org/namespaces/marshaller">
-  ...        <marshal:tuple/>
-  ...      </marshal:marshal>
-  ...    </local_permission>
-  ...  </object>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:remove xmlns:marshal="http://www.erp5.org/namespaces/marshaller" select="/object/local_permission[@id='View']/marshal:marshal/marshal:tuple/marshal:string[1]"/>
-    <xupdate:remove xmlns:marshal="http://www.erp5.org/namespaces/marshaller" select="/object/local_permission[@id='View']/marshal:marshal/marshal:tuple/marshal:string[2]"/>
-  </xupdate:modifications>
-
-30. Replace a node by another one following by a modification
-  >>> old_xml = """
-  ... <resource reference="Product Ballon de Plage a5962z">
-  ...   <title>Ballon de Plage</title>
-  ...   <reference>a5962z</reference>
-  ...   <sale_price>200.250000</sale_price>
-  ...   <purchase_price>100.250000</purchase_price>
-  ...   <category>ball_size/s4</category>
-  ...   <category>ball_size/s5</category>
-  ...   <category>colour/black</category>
-  ...   <category>colour/white</category>
-  ...   <category>type/product</category>
-  ... </resource>
-  ... """
-  >>> new_xml = """
-  ... <resource reference="Product Ballon de Plage a5962z">
-  ...   <title>Ballon de Plage</title>
-  ...   <reference>a5962z</reference>
-  ...   <sale_price>120.000000</sale_price>
-  ...   <ean13>1357913579130</ean13><!--replace purchase_price -->
-  ...   <category>ball_size/s4</category>
-  ...   <category>ball_size/s6</category><!--first modification to trig the bug -->
-  ...   <category>colour/red</category>
-  ...   <category>colour/white</category>
-  ...   <category>type/product</category>
-  ... </resource>
-  ... """
-  >>> erp5diff.compare(old_xml, new_xml)
-  >>> erp5diff.output()
-  <xupdate:modifications xmlns:xupdate="http://www.xmldb.org/xupdate" version="1.0">
-    <xupdate:update select="/resource/sale_price">120.000000</xupdate:update>
-    <xupdate:remove select="/resource/purchase_price"/>
-    <xupdate:update select="/resource/category[2]">ball_size/s6</xupdate:update>
-    <xupdate:update select="/resource/category[3]">colour/red</xupdate:update>
-    <xupdate:insert-before select="/resource/category[0]">
-      <xupdate:element name="ean13">1357913579130</xupdate:element>
-    </xupdate:insert-before>
-  </xupdate:modifications>
 
 - 2003-12-04, Yoshinori OKUJI <yo at nexedi.com>
 - 2009-09-15, Tatuya Kamada <tatuya at nexedi.com>

Removed: erp5/trunk/utils/erp5diff/erp5diff
URL: http://svn.erp5.org/erp5/trunk/utils/erp5diff/erp5diff?rev=42651&view=auto
==============================================================================
--- erp5/trunk/utils/erp5diff/erp5diff [utf8] (original)
+++ erp5/trunk/utils/erp5diff/erp5diff (removed)
@@ -1,27 +0,0 @@
-#! /usr/bin/python
-
-##############################################################################
-#
-# Yoshinori OKUJI <yo at nexedi.com>
-#
-# Copyright (C) 2003 Nexedi SARL
-#
-# This program is Free Software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ?See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA ?02111-1307, USA.
-#
-##############################################################################
-
-from ERP5Diff import main
-
-main()
\ No newline at end of file

Removed: erp5/trunk/utils/erp5diff/erp5diff.1
URL: http://svn.erp5.org/erp5/trunk/utils/erp5diff/erp5diff.1?rev=42651&view=auto
==============================================================================
--- erp5/trunk/utils/erp5diff/erp5diff.1 [utf8] (original)
+++ erp5/trunk/utils/erp5diff/erp5diff.1 (removed)
@@ -1,32 +0,0 @@
-.TH ERP5DIFF 1 "4 Dec 2003" "ERP5DIFF version 0.1" Nexedi
-.SH NAME
-erp5diff \- find differences between two XML documents for ERP5
-.SH SYNOPSIS
-.B erp5diff
-[\fIoptions\fR]...
-.LP
-.SH DESCRIPTION
-ERP5Diff is a XUpdate Generator for ERP5. It takes two XML files
-as input data, and generates differences between these two XML
-documents in XUpdate language.
-.LP
-ERP5Diff depends on more or less ERP5's XML data format. So this tool
-cannot be used for general purpose, but might work if your XML files
-are similar to ERP5's.
-.SH OPTIONS
-.TP
-\fB\-o\fR, \fB\-\-output\fR=\fIFILE\fR
-Specify the output file. The standard output is used by default.
-.TP
-\fB\-h\fR, \fB\-\-help\fR
-Display the usage and exit.
-.TP
-\fB\-v\fR, \fB\-\-verbose\fR
-Print verbose messages. Only useful for debugging.
-.SH AUTHOR
-Yoshinori OKUJI <yo at nexedi.com>
-.SH "SEE ALSO"
-\fIhttp://www.xmldb.org/xupdate/index.html\fR,
-\fIhttp://www.w3.org/TR/xpath\fR,
-\fIhttp://www.w3.org/TR/REC-xml\fR,
-\fIhttp://erp5.org\fR

Modified: erp5/trunk/utils/erp5diff/setup.py
URL: http://svn.erp5.org/erp5/trunk/utils/erp5diff/setup.py?rev=42652&r1=42651&r2=42652&view=diff
==============================================================================
--- erp5/trunk/utils/erp5diff/setup.py [utf8] (original)
+++ erp5/trunk/utils/erp5diff/setup.py [utf8] Tue Jan 25 14:17:13 2011
@@ -5,27 +5,28 @@ from setuptools import setup, find_packa
 import re
 
 api_version = re.search(r'\s*__version__\s*=\s*(\S+)',
-                        open('ERP5Diff.py').read()).group(1).strip()
+                        open('src/ERP5Diff.py').read()).group(1).strip()
 revision = 0
 version = '%s.%s' % (api_version.replace("'", ''), revision)
 
+
 setup(name="erp5diff",
       version=version,
       description="XUpdate Generator for ERP5",
       author="Yoshinori OKUJI",
       author_email="yo at nexedi.com",
       url="http://www.erp5.org/",
-      download_url="http://www.nexedi.org/static/packages/source/erp5diff-%s.tar.gz" % version,
       license="GPL",
-      packages=find_packages(),
-      py_modules=["ERP5Diff"],
-      scripts=["erp5diff"],
-      data_files=[('share/man/man1', ['erp5diff.1'])],
-      install_requires=['zope.interface', 'lxml'],
+      packages=find_packages('src'),
+      package_dir={'': 'src'},
+      entry_points={'console_scripts': ["erp5diff = ERP5Diff:main"]},
+      data_files=[('share/man/man1', ['src/erp5diff.1'])],
+      install_requires=['lxml'],
       classifiers=['License :: OSI Approved :: GNU General Public License (GPL)',
                   'Operating System :: OS Independent',
                   'Topic :: Text Processing :: Markup :: XML',
                   'Topic :: Utilities'],
       include_package_data=True,
       zip_safe=False,
+      test_suite='tests',
      )

Removed: erp5/trunk/utils/erp5diff/tests.py
URL: http://svn.erp5.org/erp5/trunk/utils/erp5diff/tests.py?rev=42651&view=auto
==============================================================================
--- erp5/trunk/utils/erp5diff/tests.py [utf8] (original)
+++ erp5/trunk/utils/erp5diff/tests.py (removed)
@@ -1,23 +0,0 @@
-from zope import interface
-
-import zope.testing
-import unittest
-
-OPTIONFLAGS = (zope.testing.doctest.ELLIPSIS |
-               zope.testing.doctest.NORMALIZE_WHITESPACE)
-
-
-def test_suite():
-    doctests = ('README',)
-
-    globs = dict(interface=interface)
-  
-    return unittest.TestSuite((
-        zope.testing.doctest.DocFileSuite(doctest,
-                                          optionflags=OPTIONFLAGS,
-                                          globs=globs,
-                                          ) for doctest in doctests
-        ))
-
-if __name__ == '__main__':
-    unittest.main(defaultTest='test_suite')



More information about the Erp5-report mailing list