[Dev] Code review of ItemCollection changes

John Anderson john at osafoundation.org
Mon Aug 30 17:04:40 PDT 2004


Hi Stuart & Ted:

As I discussed with each of you, I'd like to get you to code review my 
ItemCollection changes.

I've attached only the main changes: ItemCollection.py, a snipped of 
parcel XML for ItemCollection and diffs for some minor changes to Ted's 
Query.

I'm going to be out tommorrow morning, but will try to get in touch with 
you in the afternoon to see if we can do a code review.

John
-------------- next part --------------
__date__ = "$Date: 2004/08/24 01:48:52 $"
__copyright__ = "Copyright (c) 2003-2004 Open Source Applications Foundation"
__license__ = "http://osafoundation.org/Chandler_0.1_license_terms.htm"

import application.Globals as Globals
import repository.item.Item as Item
import repository.query.Query as RepositoryQuery

class ItemCollection2(Item.Item):

    def subscribe (self, callbackItem=None, callbackMethodName=None):
        """
          Subscribed ItemCollections will automatically results up to date. Optionally,
        you can specify a method on an item to be called when the results change. There
        may be more than one subscriber.
        """
        if not self.isPinned():
            self.setPinned()
            query = self.createRepositoryQuery()
            query.subscribe (self, "onItemCollectionChanged")
            self.notifyOfChanges ("multiple changes")
        if callbackItem is not None:
            self._callbacks [callbackItem.itsUUID] = callbackMethodName

    def createRepositoryQuery (self):
        self._callbacks = {} # transient
        self._query = RepositoryQuery.Query (Globals.repository) # transient
        self._updateCount = 0 # transient
        self.queryStringStale = True
        return self._query

    def unsubscribe (self, callbackItem=None):
        """
          If you don't specify a callbackItemUUID, all subscriptions will be removed.

          When an ItemCollections is unsubcribed, resultsStale may be inaccurate and
        the results will not be updated automatically. To update results on an unsubscribed
        ItemCollection, call updateResults.        
        """
        if callbackItem is None:
            self._callbacks = {}
        else:
            del self._callbacks [callbackItem.itsUUID]

        if len (self._callbacks) == 0:
            remainingSubscribers = self._query.unsubscribe (self)
            if remainingSubscribers == 0:
                del self._query
                del self._callbacks
                self.setPinned (False)
    
    def onItemCollectionChanged (self, action):
        self.resultsStale = True
        self.notifyOfChanges (action)

    def notifyOfChanges (self, action):
        if self.isPinned() and self._updateCount == 0:
            for callbackUUID in self._callbacks.keys():
                item = Globals.repository.find (callbackUUID)
                method = getattr (type(item), self._callbacks [callbackUUID])
                method (item, action)

    def add (self, item):
        """
          Add an item to the inclusions
        """
        if item not in self.inclusions:
            self.inclusions.append (item)
            if item in self.exclusions:
                self.exclusions.remove (item)
            if item not in self._results:
                self._results.append (item)
            self.notifyOfChanges ("entered")
            self.queryStringStale = True

    def remove (self, item):
        """
          Remove an item from the exclusions
        """
        if item not in self.exclusions:
            self.exclusions.append (item)
            if item in self.inclusions:
                self.inclusions.remove (item)
            if item in self._results:
                self._results.remove (item)
            self.notifyOfChanges ("exited")
            self.queryStringStale = True

    def addFilterKind (self, item):
        """
          Add an kind to the list of kinds to filter
        """
        if item not in self.filterKinds:
            self.filterKinds.append (item)
            self.notifyOfChanges ("multiple changes")
            self.queryStringStale = True

    def removeFilterKind (self, item):
        """
          Remove an kind to the list of kinds to filter
        """
        del self.filterKinds [item]
        self.notifyOfChanges ("multiple changes")
        self.queryStringStale = True

    def beginUpdate (self):
        """
          When making lots of modifications to inclusions, exclusion, rule or filterKinds
        surround the changes with beginUpdate and endUpdate to avoid causing each change
        to send a separate notification as in:

          itemCollection.beginUpdate()
          try:
              for item in list:
                  itemCollection.add (item)
          finally:
              itemCollection.endUpdate()

          Don't call beginUpdate unless the ItemCollection is subscribed.
        """
        self._updateCount += 1

    def endUpdate (self):
        """
          See endUpdate.
        """
        self._updateCount -= 1
        if self._updateCount == 0:
            self.notifyOfChanges (self, "multiple changes")

    def getRule (self):
        return self._rule

    def setRule (self, value):
        """
          When setting the rule, make sure we set resultsStale and queryStringStale
        """
        self.resultsStale = True
        self.queryStringStale = True
        self._rule = value

    rule = property (getRule, setRule)

    def getResults (self):
        """
          Override getting results to make sure it isn't stale
        """
        if self.resultsStale or self.queryStringStale:
            self.updateResults()
        return self._results

    results = property (getResults)

    def updateResults (self):
        """
         Refresh the cached query results by executing the repository query if necessary
        """
        try:
            query = self._query
        except AttributeError:
            query = self.createRepositoryQuery()
            
        if self.queryStringStale:
            query.queryString, query.args = self.calculateQueryStringAndArgs()
            self.queryStringStale = False
            self.resultsStale = True
            query.execute ()
        if self.resultsStale:
            self.resultsStale = False
            self._results = [index for index in query]

    def calculateQueryStringAndArgs (self):
        args = {}
        rule = self._rule
        if len (self.inclusions):
            if rule:
                rule = "union (" + rule + ",for i in $0 where True)"
            else:
                rule = "for i in $0 where True"
            args ["$0"] = (self.itsUUID, "inclusions")
        if rule:
            if len (self.exclusions):
                rule = "difference (" + rule + ",for i in $1 where True)"
                args ["$1"] = (self.isUUID, "exclusions")
            if len (self.filterKinds) != 0:
                for kind in self.filterKinds:
                    filterKinds += ", " + kind.itsPath
                rule = "union (" + rule + filterKinds + ")"
        return (rule, args)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: parcel.xml
Type: text/xml
Size: 3458 bytes
Desc: not available
Url : http://lists.osafoundation.org/pipermail/dev/attachments/20040830/fc7bfe67/parcel.xml
-------------- next part --------------
Index: D:/John/osaf/chandler/repository/query/Query.py
===================================================================
RCS file: /usr/local/cvsrep/chandler/repository/query/Query.py,v
retrieving revision 1.8
diff -b -u -2 -r1.8 Query.py
--- D:/John/osaf/chandler/repository/query/Query.py	30 Aug 2004 19:14:18 -0000	1.8
+++ D:/John/osaf/chandler/repository/query/Query.py	31 Aug 2004 00:01:24 -0000
@@ -30,9 +30,10 @@
         self.__rep = repo
         self.queryString = queryString
-        self.args = []
+        self.args = {}
         self._kind = None
         self._logical_plan = None
         self._predicate = None
         self.recursive = True
+        self._callbacks = {}
 
     def execute(self):
@@ -63,16 +64,23 @@
         log.debug("execute: %s:%f" % (self.queryString,time.time()-start))
 
-    def subscribe(self):
+    def subscribe(self, callbackItem, callbackMethodName):
         """
         This query should subscribe to repository changes
         """
+        self._callbacks [callbackItem.itsUUID] = callbackMethodName
         log.debug("RepoQuery<>.subscribe(): %s" % (self.queryString))
         self.__rep.addNotificationCallback(self.queryCallback)
         
-    def unsubscribe(self):
+    def unsubscribe(self, callbackItem=None):
         """
-        This query should stop subscribing to repository changes
+        This query should stop subscribing to repository changes. If you don't specify a
+        callbackItemUUID, all subscriptions will be removed.
         """
+        if callbackItem is None:
+            self._callbacks = {}
+        else:
+            del self._callbacks [callbackItem.itsUUID]
         self.__rep.removeNotificationCallback(self.queryCallback)
+        return len (self._callbacks)
     
     def queryCallback(self, view, changes, notification, **kwds):
@@ -115,4 +123,10 @@
         if changed:
             log.debug("RepoQuery.queryCallback: %s %s query result" % (uuid, action))
+            if len (self._callbacks):
+                for callbackUUID in self._callbacks.keys():
+                    item = view.find (callbackUUID)
+                    method = getattr (type(item), self._callbacks [callbackUUID])
+                    method (item, action)
+            else:
             view.findPath('//parcels/osaf/framework/query_changed').Post( {'query' : i.itsUUID, 'action': action} )
         log.debug("queryCallback: %s:%f" % (self.queryString, time.time()-start))
@@ -153,5 +167,7 @@
                 return ('kind', kind)
             if name.startswith('$'): # variable argument
-                return ('arg',self.args[int(name[1:])-1])
+                itemUUID, attribute = self.args[name]
+                item = self.__rep.find (itemUUID)
+                return ('arg',item.getAttributeValue(attribute))
             assert False, "lookup_source couldn't handle %s" % name
 


More information about the Dev mailing list