[Commits] (vajda) - reworked child collection merging another time

commits at osafoundation.org commits at osafoundation.org
Fri Sep 17 18:36:18 PDT 2004


Commit by: vajda
Modified files:
chandler/repository/item/Item.py 1.164 1.165
chandler/repository/persistence/RepositoryError.py 1.6 1.7
chandler/repository/persistence/RepositoryView.py 1.11 1.12
chandler/repository/persistence/XMLRefDict.py 1.11 1.12
chandler/repository/persistence/XMLRepositoryView.py 1.57 1.58
chandler/repository/tests/TestMerge.py 1.6 1.7

Log message:
   - reworked child collection merging another time
   - fixed bug 1974


ViewCVS links:
http://cvs.osafoundation.org/index.cgi/chandler/repository/item/Item.py.diff?r1=text&tr1=1.164&r2=text&tr2=1.165
http://cvs.osafoundation.org/index.cgi/chandler/repository/persistence/RepositoryError.py.diff?r1=text&tr1=1.6&r2=text&tr2=1.7
http://cvs.osafoundation.org/index.cgi/chandler/repository/persistence/RepositoryView.py.diff?r1=text&tr1=1.11&r2=text&tr2=1.12
http://cvs.osafoundation.org/index.cgi/chandler/repository/persistence/XMLRefDict.py.diff?r1=text&tr1=1.11&r2=text&tr2=1.12
http://cvs.osafoundation.org/index.cgi/chandler/repository/persistence/XMLRepositoryView.py.diff?r1=text&tr1=1.57&r2=text&tr2=1.58
http://cvs.osafoundation.org/index.cgi/chandler/repository/tests/TestMerge.py.diff?r1=text&tr1=1.6&r2=text&tr2=1.7

Index: chandler/repository/persistence/XMLRefDict.py
diff -u chandler/repository/persistence/XMLRefDict.py:1.11 chandler/repository/persistence/XMLRefDict.py:1.12
--- chandler/repository/persistence/XMLRefDict.py:1.11	Thu Sep 16 11:03:12 2004
+++ chandler/repository/persistence/XMLRefDict.py	Fri Sep 17 18:36:15 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.11 $"
-__date__      = "$Date: 2004/09/16 18:03:12 $"
+__revision__  = "$Revision: 1.12 $"
+__date__      = "$Date: 2004/09/18 01:36:15 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -358,7 +358,9 @@
 
     def _load(self, key):
 
-        if self.view._loadItem(key) is not None:
+        op, oldAlias = self._changedRefs.get(key, (0, None))
+
+        if op != 1 and self.view._loadItem(key) is not None:
             return True
 
         return False
@@ -409,15 +411,11 @@
     def _saveValues(self, version):
 
         store = self.view.repository.store
-
-        if '_mergeList' in self.__dict__:
-            children = self._mergeList
-        else:
-            children = self
-
-        for key, (op, oldAlias) in children._changedRefs.iteritems():
+        unloads = []
+        
+        for key, (op, oldAlias) in self._changedRefs.iteritems():
             try:
-                link = children._get(key, load=False)
+                link = self._get(key, load=False)
             except KeyError:
                 link = None
     
@@ -433,6 +431,9 @@
                     if alias is not None:
                         store.writeName(version, self._uuid, alias, key)
 
+                    if link._value is None:
+                        unloads.append((key, link._alias))
+
                 elif key is None:
                     self._writeRef(self._uuid, version,
                                    self._firstKey, self._lastKey, None)
@@ -445,194 +446,83 @@
             else:                     # error
                 raise ValueError, op
 
+        for key, alias in unloads:
+            self._remove(key)
+            if alias is not None:
+                del self._aliases[alias]
+
     def _clearDirties(self):
 
         self._changedRefs.clear()
-        try:
-            del self._mergeList
-        except AttributeError:
-            pass
 
     def _mergeChanges(self, oldVersion, toVersion):
 
-        self._mergeList = MergeList(self.view, self._item, oldVersion)
-        self._mergeList.collectHistory(toVersion)
-        self._mergeList.applyChanges(self, self._changedRefs)
-                        
-        self.view.logger.info('%s merged children of %s with newer versions',
-                              self.view, self._item.itsPath)
-
-
-class MergeList(LinkedMap):
-
-    def __init__(self, view, item, version):
-
-        super(MergeList, self).__init__()
-
-        self.view = view
-        self.item = item
-        self.uuid = item._uuid
-        self.version = version
-
-        self._changedRefs = {}
-        self._key = self._getRefs().prepareKey(self.uuid, self.uuid)
-        self._aliases = {}
-        
-        ref = self._getRefs().loadRef(self._key, version, self.uuid)
-        if ref is not None:
-            self._firstKey, self._lastKey, alias = ref
-
-    def _getRefs(self):
-
-        return self.view.repository.store._refs
-
-    def collectHistory(self, toVersion):
+        moves = {}
 
-        def collect(version, (collection, child), ref):
-            if collection == self.uuid:     # the children collection
+        def merge(version, (collection, child), ref):
+            if collection == self._uuid:     # the children collection
 
-                if child == self.uuid:      # the list head
-                    self._firstKey, self._lastKey, alias = ref
+                if child == self._uuid:      # the list head
+                    pass
 
-                elif ref is None:           # deleted child
-                    link = self._makeLink(None)
-                    self._insert(child, link)
+                elif ref is None:            # removed child
+                    op, oldAlias = self._changedRefs.get(child, (-1, None))
+                    if op == 0:
+                        self._e_1_remove(child)
+                    elif self.has_key(child):
+                        del self[child]
 
                 else:
-                    link = self._makeLink(child)
-                    link._previousKey, link._nextKey, link._alias = ref
-                    self._insert(child, link)
-                    if link._alias is not None:
-                        self._aliases[link._alias] = child
-                        
-        self._getRefs().applyHistory(collect, self.uuid,
-                                     self.version, toVersion)
-
-    def applyChanges(self, children, changes):
-
-        key = self._insertKey = self._firstKey
-        while key in self:
-            self._insertKey = key
-            key = self._get(key)._nextKey
-
-        for child in changes.keys():
-            if child in changes:
-                if child is not None:           # not the list head
-                    op, oldAlias = changes[child]
-                    if op == 0:                 # insert or change
-                        self.applyChange(children, changes, child, oldAlias)
-                    elif op == 1:               # delete
-                        raise NotImplementedError
-
-        children._firstKey = self._firstKey
-        children._lastKey = self._lastKey
-
-    def applyChange(self, children, changes, child, oldAlias):
-
-        link = children._get(child)
-        prev = prevKey = link._previousKey
-        exists = child in self
-
-        if exists:
-            alias = self._get(child)._alias
-            if oldAlias is not None:
-                if oldAlias != alias and link._alias != alias:
-                    raise MergeError, ('merging children', self.item, 'child %s renamed to %s and %s' %(oldAlias, link._alias, alias), MergeError.RENAME)
-
-        if link._alias in self._aliases:
-            alias = link._alias
-            if exists and self._aliases[alias] != child or not exists:
-                raise MergeError, ('merging children', self.item, 'child %s conflicts with other child %s, both are named %s' %(child, self._aliases[alias], alias), MergeError.RENAME)
-
-        if prev is None:
-            if exists:
-                prevKey = self._firstKey
-            else:
-                prev = prevKey = self._insertKey
-        else:
-            key = prevKey
-            while key in self:
-                prev = prevKey = key
-                key = self._get(key)._nextKey
-
-        if prevKey is not None and prevKey not in self:
-            op, oa = changes.get(prevKey, (0, None))
-            if op != 0:
-                raise ValueError, op
-            self.applyChange(children, changes, prevKey, oa)
-
-        current = self.placeChange(child, prev, link._alias)
-
-        link._previousKey = current._previousKey
-        if exists:
-            link._nextKey = current._nextKey
-
-        if oldAlias is not None:
-            self._changedRefs[child] = (0, oldAlias)
-
-        try:
-            del changes[child]
-        except KeyError:
-            pass
+                    previousKey, nextKey, alias = ref
+                    op, oldAlias = self._changedRefs.get(child, (0, None))
 
-    def placeChange(self, key, afterKey, alias):
+                    if op == 1:
+                        self._e_2_remove(child)
 
-        if key == afterKey:
-            raise ValueError, 'key == afterKey'
+                    try:
+                        link = self._get(child)
+                        if link._alias != alias:
+                            if oldAlias is not None:
+                                self._e_1_renames(oldAlias, link._alias, alias)
+                            else:
+                                key = self.resolveAlias(alias)
+                                if key is not None:
+                                    self._e_2_renames(key, alias, child)
+                                self.setAlias(child, alias)
+
+                    except KeyError:
+                        key = self.resolveAlias(alias)
+                        if key is not None:
+                            self._e_names(child, key, alias)
+                        link = self.__setitem__(child, None, alias=alias)
+
+                    if previousKey is None or self.has_key(previousKey):
+                        self.place(child, previousKey)
+                    else:
+                        moves[previousKey] = child
+                        
+        self._getRefs().applyHistory(merge, self._uuid,
+                                     self._item._version, toVersion)
 
-        try:
-            current = self._get(key, False)
-            exists = True
-        except KeyError:
-            current = self._makeLink(key)
-            current._alias = alias
-            self._insert(key, current)
-            exists = False
-
-        if exists:
-            if current._previousKey == afterKey:
-                return current
-            if current._previousKey is not None:
-                previous = self._get(current._previousKey)
-            else:
-                previous = None
-            if current._nextKey is not None:
-                next = self._get(current._nextKey)
-            else:
-                next = None
-
-        if afterKey is None:
-            after = None
-            afterNextKey = self._firstKey
-        else:
-            after = self._get(afterKey)
-            afterNextKey = after._nextKey
+        for previousKey, child in moves.iteritems():
+            self.place(child, previousKey)
+                        
+        self.view.logger.info('%s merged children of %s with newer versions',
+                              self.view, self._item.itsPath)
 
-        if exists:
-            if previous is not None:
-                previous._setNext(current._nextKey, current._previousKey, self)
-            if next is not None:
-                next._setPrevious(current._previousKey, current._nextKey, self)
-
-        current._setNext(afterNextKey, key, self)
-        if afterNextKey is not None:
-            self._get(afterNextKey)._setPrevious(key, afterNextKey, self)
-        if after is not None:
-            after._setNext(key, afterKey, self)
 
-        current._setPrevious(afterKey, key, self)
+    def _e_1_remove(self, *args):
+        raise MergeError, ('merging children', self._item, 'modified child %s was removed in other view' %(args), MergeError.MOVE)
 
-        return current
-            
-    def linkChanged(self, link, key):
+    def _e_2_remove(self, *args):
+        raise MergeError, ('merging children', self._item, 'removed child %s was modified in other view' %(args), MergeError.MOVE)
 
-        op, alias = self._changedRefs.get(key, (1, link._alias))
-        if op != 0:
-            self._changedRefs[key] = (0, alias)
+    def _e_1_renames(self, *args):
+        raise MergeError, ('merging children', self._item, 'child %s renamed to %s and %s' %(args), MergeError.RENAME)
 
-    def _load(self, key):
+    def _e_2_renames(self, *args):
+        raise MergeError, ('merging children', self._item, 'child %s named %s conflicts with child %s of same name' %(args), MergeError.NAME)
 
-        raise MergeError, ('merging children', self.item,
-                           'of a bug: _load should not be called.',
-                           MergeError.BUG)
+    def _e_names(self, *args):
+        raise MergeError, ('merging children', self._item, 'child %s conflicts with other child %s, both are named %s' %(args), MergeError.NAME)
 

Index: chandler/repository/persistence/RepositoryError.py
diff -u chandler/repository/persistence/RepositoryError.py:1.6 chandler/repository/persistence/RepositoryError.py:1.7
--- chandler/repository/persistence/RepositoryError.py:1.6	Wed Sep 15 15:43:14 2004
+++ chandler/repository/persistence/RepositoryError.py	Fri Sep 17 18:36:15 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.6 $"
-__date__      = "$Date: 2004/09/15 22:43:14 $"
+__revision__  = "$Revision: 1.7 $"
+__date__      = "$Date: 2004/09/18 01:36:15 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -47,5 +47,9 @@
     BUG    = 0
     RENAME = 1
     MOVE   = 2
+    NAME   = 3
 
-    codeNames = { BUG: 'BUG', RENAME: 'RENAME', 'MOVE': MOVE }
+    codeNames = { BUG: 'BUG',
+                  RENAME: 'RENAME',
+                  MOVE: 'MOVE',
+                  NAME: 'NAME' }

Index: chandler/repository/persistence/RepositoryView.py
diff -u chandler/repository/persistence/RepositoryView.py:1.11 chandler/repository/persistence/RepositoryView.py:1.12
--- chandler/repository/persistence/RepositoryView.py:1.11	Tue Sep 14 11:08:16 2004
+++ chandler/repository/persistence/RepositoryView.py	Fri Sep 17 18:36:15 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.11 $"
-__date__      = "$Date: 2004/09/14 18:08:16 $"
+__revision__  = "$Revision: 1.12 $"
+__date__      = "$Date: 2004/09/18 01:36:15 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -461,7 +461,7 @@
                 self.logger.debug('loading item %s', string[index+6:index+28])
             else:
                 self.logger.debug('loading item %s', string)
-            
+
         handler = ItemHandler(self, parent or self, afterLoadHooks, instance)
         parser.parseDoc(doc, handler)
 

Index: chandler/repository/item/Item.py
diff -u chandler/repository/item/Item.py:1.164 chandler/repository/item/Item.py:1.165
--- chandler/repository/item/Item.py:1.164	Thu Sep 16 11:03:12 2004
+++ chandler/repository/item/Item.py	Fri Sep 17 18:36:15 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.164 $"
-__date__      = "$Date: 2004/09/16 18:03:12 $"
+__revision__  = "$Revision: 1.165 $"
+__date__      = "$Date: 2004/09/18 01:36:15 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -733,8 +733,8 @@
 
         if not load:
             if self._children is not None:
-                for child in self._children._itervalues():
-                    yield child._value
+                for link in self._children._itervalues():
+                    yield link._value
 
         elif self._children is not None:
             for child in self._children:
@@ -2470,9 +2470,10 @@
 
     def _unloadChild(self, child):
 
-        self._remove(child._uuid)
-        if child._name is not None:
-            del self._aliases[child._name]
+        if child._uuid in self:
+            self._remove(child._uuid)
+            if child._name is not None:
+                del self._aliases[child._name]
     
     def __repr__(self):
 
@@ -2498,13 +2499,6 @@
             if buffer is not None:
                 buffer.close()
 
-    def _load(self, key):
-
-        if self._item.getRepositoryView()._loadItem(key) is not None:
-            return True
-
-        return False
-
     def _saveValues(self, version):
 
         pass

Index: chandler/repository/persistence/XMLRepositoryView.py
diff -u chandler/repository/persistence/XMLRepositoryView.py:1.57 chandler/repository/persistence/XMLRepositoryView.py:1.58
--- chandler/repository/persistence/XMLRepositoryView.py:1.57	Fri Sep 17 14:42:51 2004
+++ chandler/repository/persistence/XMLRepositoryView.py	Fri Sep 17 18:36:15 2004
@@ -1,6 +1,6 @@
 
-__revision__  = "$Revision: 1.57 $"
-__date__      = "$Date: 2004/09/17 21:42:51 $"
+__revision__  = "$Revision: 1.58 $"
+__date__      = "$Date: 2004/09/18 01:36:15 $"
 __copyright__ = "Copyright (c) 2002 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -362,11 +362,9 @@
                 self._mergeNDIRTY(item, parentId, oldVersion, toVersion)
                 oldDirty &= ~Item.NDIRTY
 
-            # @@@ Per Andi, commenting these out to prevent infinite recursion
-            # Instead, we'll get a VersionConflictError
-            # if newDirty & oldDirty & Item.CDIRTY:
-            #     # item._children._mergeChanges(oldVersion, toVersion)
-            #    oldDirty &= ~Item.CDIRTY
+            if newDirty & oldDirty & Item.CDIRTY:
+                item._children._mergeChanges(oldVersion, toVersion)
+                oldDirty &= ~Item.CDIRTY
 
             if newDirty and oldDirty:
                 raise VersionConflictError, (item, newDirty, oldDirty)

Index: chandler/repository/tests/TestMerge.py
diff -u chandler/repository/tests/TestMerge.py:1.6 chandler/repository/tests/TestMerge.py:1.7
--- chandler/repository/tests/TestMerge.py:1.6	Fri Sep 17 16:05:37 2004
+++ chandler/repository/tests/TestMerge.py	Fri Sep 17 18:36:16 2004
@@ -2,8 +2,8 @@
 Test merging of items
 """
 
-__revision__  = "$Revision: 1.6 $"
-__date__      = "$Date: 2004/09/17 23:05:37 $"
+__revision__  = "$Revision: 1.7 $"
+__date__      = "$Date: 2004/09/18 01:36:16 $"
 __copyright__ = "Copyright (c) 2003 Open Source Applications Foundation"
 __license__   = "http://osafoundation.org/Chandler_0.1_license_terms.htm"
 
@@ -148,6 +148,7 @@
         try:
             self.rename('foo', 'bar')
         except MergeError, e:
+            #print e
             self.assert_(e.getReasonCode() == MergeError.RENAME)
 
     def testMoveSame(self):
@@ -157,6 +158,7 @@
         try:
             self.move('foo', 'bar')
         except MergeError, e:
+            #print e
             self.assert_(e.getReasonCode() == MergeError.MOVE)
         else:
             self.assert_(False)
@@ -177,7 +179,8 @@
         try:
             main.commit()
         except MergeError, e:
-            self.assert_(e.getReasonCode() == MergeError.RENAME)
+            #print e
+            self.assert_(e.getReasonCode() == MergeError.NAME)
         else:
             self.assert_(False)
 
@@ -201,15 +204,128 @@
         try:
             main.commit()
         except MergeError, e:
-            self.assert_(e.getReasonCode() == MergeError.RENAME)
+            #print e
+            self.assert_(e.getReasonCode() == MergeError.NAME)
+        else:
+            self.assert_(False)
+
+    def testMoveFirst(self):
+        pm = self.rep['p']
+        km = self.rep.findPath('//Schema/Core/Item')
+        km.newItem('foo', pm)
+        km.newItem('bar', pm)
+        km.newItem('i1', pm)
+        km.newItem('i2', pm)
+        self.rep.commit()
+        
+        view = self.rep.createView('view')
+        main = self.rep.setCurrentView(view)
+        po = self.rep['p']
+        po.placeChild(po['i1'], None)
+        view.commit()
+        
+        view = self.rep.setCurrentView(main)
+        pm = self.rep['p']
+        pm.placeChild(pm['i2'], None)
+        main.commit()
+        
+        names = [c.itsName for c in pm.iterChildren()]
+        self.assert_(names[0] == 'i1')
+        self.assert_(len(names) == 4)
+
+    def testChange1Remove1(self):
+
+        pm = self.rep['p']
+        km = self.rep.findPath('//Schema/Core/Item')
+        km.newItem('q', self.rep)
+        km.newItem('foo', pm)
+        km.newItem('bar', pm)
+        km.newItem('i1', pm)
+        km.newItem('i2', pm)
+        self.rep.commit()
+        
+        view = self.rep.createView('view')
+        main = self.rep.setCurrentView(view)
+        po = self.rep['p']
+        po.placeChild(po['i1'], None)
+        view.commit()
+        
+        view = self.rep.setCurrentView(main)
+        pm = self.rep['p']
+        qm = self.rep['q']
+
+        pm['i1'].move(qm)
+
+        try:
+            main.commit()
+        except MergeError, e:
+            #print e
+            self.assert_(e.getReasonCode() == MergeError.MOVE)
         else:
             self.assert_(False)
 
+    def testRemove1Change1(self):
+
+        pm = self.rep['p']
+        km = self.rep.findPath('//Schema/Core/Item')
+        km.newItem('q', self.rep)
+        km.newItem('foo', pm)
+        km.newItem('bar', pm)
+        km.newItem('i1', pm)
+        km.newItem('i2', pm)
+        self.rep.commit()
+        
+        view = self.rep.createView('view')
+        main = self.rep.setCurrentView(view)
+        po = self.rep['p']
+        qo = self.rep['q']
+        po['i1'].move(qo)
+        view.commit()
+        
+        view = self.rep.setCurrentView(main)
+        pm = self.rep['p']
+
+        pm.placeChild(pm['i1'], None)
+
+        try:
+            main.commit()
+        except MergeError, e:
+            #print e
+            self.assert_(e.getReasonCode() == MergeError.MOVE)
+        else:
+            self.assert_(False)
+
+    def testRemove1ChangeOther(self):
+
+        pm = self.rep['p']
+        km = self.rep.findPath('//Schema/Core/Item')
+        km.newItem('q', self.rep)
+        km.newItem('foo', pm)
+        km.newItem('bar', pm)
+        km.newItem('i1', pm)
+        km.newItem('i2', pm)
+        self.rep.commit()
+        
+        view = self.rep.createView('view')
+        main = self.rep.setCurrentView(view)
+        po = self.rep['p']
+        qo = self.rep['q']
+        po['i1'].move(qo)
+        view.commit()
+        
+        view = self.rep.setCurrentView(main)
+        pm = self.rep['p']
+        pm['foo'].rename('baz')
+        main.commit()
+
+        names = [c.itsName for c in pm.iterChildren()]
+        self.assert_(names[0] == 'baz')
+        self.assert_(len(names) == 3)
+
 
 if __name__ == "__main__":
 #    import hotshot
 #    profiler = hotshot.Profile('/tmp/TestItems.hotshot')
 #    profiler.run('unittest.main()')
 #    profiler.close()
-    # unittest.main()
-    pass
+    unittest.main()



More information about the Commits mailing list