[Zope-Checkins] CVS: Zope3/lib/python/Zope/ObjectHub - IObjectHub.py:1.2 IRuidObjectEvent.py:1.2 ObjectHub.py:1.2 RuidObjectEvent.py:1.2 __init__.py:1.2
Jim Fulton
jim@zope.com
Mon, 10 Jun 2002 19:30:00 -0400
Update of /cvs-repository/Zope3/lib/python/Zope/ObjectHub
In directory cvs.zope.org:/tmp/cvs-serv20468/lib/python/Zope/ObjectHub
Added Files:
IObjectHub.py IRuidObjectEvent.py ObjectHub.py
RuidObjectEvent.py __init__.py
Log Message:
Merged Zope-3x-branch into newly forked Zope3 CVS Tree.
=== Zope3/lib/python/Zope/ObjectHub/IObjectHub.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+
+from Zope.Event.IEventChannel import IEventChannel
+
+class IObjectHub(IEventChannel):
+ """ObjectHub.
+
+ Receives Object Modify Events from the Event Service, and
+ changes these into RUID Object Modify Events, then passes
+ these on to its PlugIns (possibly via some other EventChannels).
+
+ To map Object Modify Events onto RUID Object Modify Events, take
+ the location from the Object Modify Event, look up the RUID for this
+ location, and create an equivalent RUID Object Modify Event using this
+ RUID.
+
+ Note that we are concerned with locations and not with Objects.
+ An object may have more than one location. That doesn't concern
+ us here.
+
+ Table of decisions about maintaining the location<->ruid lookup:
+
+ Register
+
+ if location already in lookup:
+ raise ObjectHubError, as this is implies bad state somewhere
+ generate new ruid
+ place ruid<->location into lookup, to say that we have an
+ interesting object
+
+ send out ruid object register event to plug-ins, via event channels
+
+ Unregister
+
+ if location not in lookup:
+ raise ObjectHubError, as this is implies bad state somewhere
+ remove location<->ruid from lookup
+
+ send out ruid unregister event to plug-ins, via event channels
+
+ Add (specialises Register)
+
+ as Register, except at the end send out ruid object add event
+ instead
+
+ Modify
+
+ if location not in lookup:
+ ignore this event, as we're not interested in this object
+ else:
+ look up ruid for the location
+ send out ruid object modify event to plug-ins,
+ via event channels
+
+ Move
+
+ if old_location not in lookup:
+ ignore this event, as we're not interested in this object
+ elif new_location is in lookup:
+ raise ObjectHubError
+ else:
+ look up ruid for old_location
+ change lookup:
+ remove ruid<->old_location
+ add ruid<->new_location
+ send out ruid object context-change event to plug-ins,
+ via event channels
+
+ Remove (specialises Unregister)
+
+ if old_location not in lookup:
+ ignore this event, as we're not interested in this object
+ else:
+ look up ruid for old_location
+ change lookup:
+ remove ruid<->old_location
+ send out ruid object remove event to plug-ins,
+ via event channels
+
+ # XXX: Possibly add Link to EventChannel.
+ # This implies multiple locations for a ruid.
+ # We'll refactor later if needed.
+
+ # Possibly also add registerObject and unregisterObject methods
+ # unless these are better handled by events, or unless we don't
+ # need them.
+ """
+
+
+ def lookupRuid(location):
+ """Returns the ruid int that is mapped to the given location.
+
+ Location is either a string, or a sequence of strings.
+ It must be absolute, so if it is a string it must start with a '/',
+ and if it is a sequence, it must start with an empty string.
+
+ ('','whatever','whatever2')
+ '/whatever/whatever2'
+
+ If there is no ruid, raise Zope.Exceptions.NotFoundError.
+
+ """
+
+ def lookupLocation(ruid):
+ """Returns a location as a string.
+
+ If there is no location, raise Zope.Exceptions.NotFoundError.
+
+ """
+
+ def getObject(ruid):
+ """Returns an object for the given ruid.
+
+ If there is no such ruid, raise Zope.Exceptions.NotFoundError.
+ If there is no such object, passes through whatever error
+ the traversal service raises.
+ """
=== Zope3/lib/python/Zope/ObjectHub/IRuidObjectEvent.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+from Zope.Event.IEvent import IEvent
+
+class IRuidObjectEvent(IEvent):
+ """Something has happened to an object for which there is an ruid.
+ An ruid is a way or refering to an object independent of location.
+ """
+
+ def getRuid():
+ """Returns the object's ruid."""
+
+ def getLocation():
+ """Returns the object's current location."""
+
+ def getObject():
+ """Returns the object."""
+
+
+
+class IRuidObjectRegisteredEvent(IRuidObjectEvent):
+ """An ruid has been freshly created and mapped against an object."""
+
+
+class IRuidObjectUnregisteredEvent(IRuidObjectEvent):
+ """We are no longer interested in this object."""
+
+
+class IRuidObjectAddedEvent(IRuidObjectRegisteredEvent):
+ """An ruid has been freshly created and mapped against an object.
+ Also, implies the object has been newly added."""
+
+
+class IRuidObjectModifiedEvent(IRuidObjectEvent):
+ """An object with an ruid has been modified."""
+
+
+class IRuidObjectContextChangedEvent(IRuidObjectEvent):
+ """An object with an ruid has had its context changed. Typically, this
+ means that it has been moved."""
+
+
+class IRuidObjectRemovedEvent(IRuidObjectUnregisteredEvent):
+ """An object with an ruid has been removed."""
+
+ def getLocation():
+ """Returns the object's location before it was removed."""
+
+ def getObject():
+ """Returns the object, or None if the object is unavailable."""
=== Zope3/lib/python/Zope/ObjectHub/ObjectHub.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+
+from IObjectHub import IObjectHub
+from Zope.Event.IObjectEvent import IObjectAddedEvent, IObjectModifiedEvent
+from Zope.Event.IObjectEvent import IObjectRemovedEvent, IObjectMovedEvent
+from Zope.Event.IEvent import IEvent
+from Zope.Event.EventChannel import EventChannel
+from RuidObjectEvent import RuidObjectRegisteredEvent
+from RuidObjectEvent import RuidObjectUnregisteredEvent
+from RuidObjectEvent import RuidObjectAddedEvent
+from RuidObjectEvent import RuidObjectModifiedEvent
+from RuidObjectEvent import RuidObjectContextChangedEvent
+from RuidObjectEvent import RuidObjectRemovedEvent
+
+from Zope.Exceptions import NotFoundError
+from Persistence import Persistent
+from Interface.Implements import objectImplements
+from types import StringTypes
+from Persistence.BTrees.IOBTree import IOBTree
+from Persistence.BTrees.OIBTree import OIBTree
+
+from Zope.App.Traversing.ITraverser import ITraverser
+from Zope.ComponentArchitecture import getAdapter
+
+import random
+def randid():
+ # Return a random number between -2*10**9 and 2*10**9, but not 0.
+ abs = random.randrange(1, 2000000001)
+ if random.random() < 0.5:
+ return -abs
+ else:
+ return abs
+
+class ObjectHubError(Exception):
+ pass
+
+class ObjectHub(Persistent):
+
+ __implements__ = IObjectHub
+
+ def __init__(self):
+ self.__ruid_to_location = IOBTree()
+ self.__location_to_ruid = OIBTree()
+ self.__eventchannel = EventChannel()
+
+ _clear = __init__
+
+
+ ############################################################
+ # Implementation methods for interface
+ # Zope.ObjectHub.IObjectHub
+
+ def subscribe(self, subscriber, event_type=IEvent, filter=None):
+ '''See interface ISubscribable'''
+ self.__eventchannel.subscribe(subscriber, event_type, filter)
+
+ def unsubscribe(self, subscriber, event_type=None, filter=None):
+ '''See interface ISubscribable'''
+ self.__eventchannel.unsubscribe(subscriber, event_type, filter)
+
+ def listSubscriptions(self, subscriber, event_type=None):
+ "See interface ISubscribable"
+ return self.__eventchannel.listSubscriptions(subscriber, event_type)
+
+ def notify(self, event):
+ '''See interface ISubscriber'''
+ if IObjectAddedEvent.isImplementedBy(event):
+ self._objectAdded(event.getLocation())
+
+ elif IObjectModifiedEvent.isImplementedBy(event):
+ self._objectModified(event.getLocation())
+
+ elif IObjectMovedEvent.isImplementedBy(event):
+ self._objectMoved(event.getFromLocation(),
+ event.getLocation())
+
+ elif IObjectRemovedEvent.isImplementedBy(event):
+ self._objectRemoved(event.getLocation(), event.getObject())
+
+ # otherwise, ignore the event
+
+ def lookupRuid(self, location):
+ '''See interface IObjectHub'''
+ try:
+ return self.__location_to_ruid[self._canonical(location)]
+ except KeyError:
+ raise NotFoundError, self._canonical(location)
+
+ def lookupLocation(self, ruid):
+ '''See interface IObjectHub'''
+ try:
+ return self.__ruid_to_location[ruid]
+ except KeyError:
+ raise NotFoundError, ruid
+
+ def getObject(self, ruid):
+ '''See interface IObjectHub'''
+ location = self.lookupLocation(ruid)
+ adapter = getAdapter(object, ITraverser)
+ return adapter.traverse(location)
+
+ #
+ ############################################################
+
+ def _generateRuid(self, location):
+ index=getattr(self, '_v_nextid', 0)
+ if index%4000 == 0: index = randid()
+ ruid_to_location=self.__ruid_to_location
+ while not ruid_to_location.insert(index, location):
+ index=randid()
+ self._v_nextid=index+1
+ return index
+
+ def _canonical(location):
+ if not isinstance(location, StringTypes):
+ location='/'.join(location)
+ # URIs are ascii, right?
+ return str(location)
+
+ _canonical=staticmethod(_canonical)
+
+ def _objectAdded(self, location):
+ canonical_location = self._canonical(location)
+
+ location_to_ruid = self.__location_to_ruid
+
+ if location_to_ruid.has_key(canonical_location):
+ raise ObjectHubError, 'location %s already in object hub' % \
+ canonical_location
+ ruid = self._generateRuid(canonical_location)
+ location_to_ruid[canonical_location] = ruid
+
+ # send out to plugins IRuidObjectAddedEvent
+ event = RuidObjectAddedEvent(
+ self,
+ ruid,
+ canonical_location)
+ self.__eventchannel.notify(event)
+
+
+ def _objectModified(self, location):
+ location_to_ruid = self.__location_to_ruid
+ canonical_location = self._canonical(location)
+ if not location_to_ruid.has_key(canonical_location):
+ # we're not interested in this event
+ return
+
+ # send out to plugins IRuidObjectModifiedEvent
+ event = RuidObjectModifiedEvent(
+ self,
+ location_to_ruid[canonical_location],
+ canonical_location)
+ self.__eventchannel.notify(event)
+
+
+ def _objectMoved(self, old_location, new_location):
+ location_to_ruid = self.__location_to_ruid
+ canonical_location = self._canonical(old_location)
+ canonical_new_location = self._canonical(new_location)
+ if location_to_ruid.has_key(canonical_new_location):
+ raise ObjectHubError(
+ 'Cannot move to location %s, '
+ 'as there is already something there'
+ % canonical_new_location)
+ if not location_to_ruid.has_key(canonical_location):
+ # we're not interested in this event
+ return
+
+ ruid = location_to_ruid[canonical_location]
+ del location_to_ruid[canonical_location]
+ location_to_ruid[canonical_new_location] = ruid
+ self.__ruid_to_location[ruid] = canonical_new_location
+
+ # send out to plugins IRuidObjectContextChangedEvent
+ event = RuidObjectContextChangedEvent(
+ self,
+ ruid,
+ canonical_new_location)
+ self.__eventchannel.notify(event)
+
+
+ def _objectRemoved(self, location, obj):
+ location_to_ruid = self.__location_to_ruid
+ ruid_to_location = self.__ruid_to_location
+ canonical_location = self._canonical(location)
+ try:
+ ruid = location_to_ruid[canonical_location]
+ except KeyError:
+ # we don't know about this location, so we
+ # can ignore this event
+ return
+
+ del ruid_to_location[ruid]
+ del location_to_ruid[canonical_location]
+
+ # send out to plugins IRuidObjectRemovedEvent
+ event = RuidObjectRemovedEvent(
+ obj,
+ ruid,
+ canonical_location)
+ self.__eventchannel.notify(event)
=== Zope3/lib/python/Zope/ObjectHub/RuidObjectEvent.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+
+from IRuidObjectEvent import IRuidObjectRegisteredEvent
+from IRuidObjectEvent import IRuidObjectAddedEvent
+from IRuidObjectEvent import IRuidObjectUnregisteredEvent
+from IRuidObjectEvent import IRuidObjectModifiedEvent
+from IRuidObjectEvent import IRuidObjectContextChangedEvent
+from IRuidObjectEvent import IRuidObjectRemovedEvent
+
+class RuidObjectEvent:
+ """Convenient mix-in for RuidObjectEvents"""
+
+ def __init__(self, objecthub, ruid, location):
+ # we keep all three, to avoid unnecessary lookups
+ # and to give the objecthub an opportunity to do
+ # caching of objects
+ self.__objecthub = objecthub
+ self.__ruid = ruid
+ self.__location = location
+
+ def getRuid(self):
+ return self.__ruid
+
+ def getLocation(self):
+ return self.__location
+
+ def getObject(self):
+ if hasattr(self, '_v_object'):
+ return self._v_object
+ obj = self._v_object = self.__objecthub.getObject(self.__ruid)
+ return obj
+
+
+class RuidObjectRegisteredEvent(RuidObjectEvent):
+ """An ruid has been freshly created and mapped against an object."""
+
+ __implements__ = IRuidObjectRegisteredEvent
+
+
+class RuidObjectUnregisteredEvent(RuidObjectEvent):
+ """We are no longer interested in this object."""
+
+ __implements__ = IRuidObjectUnregisteredEvent
+
+
+class RuidObjectAddedEvent(RuidObjectEvent):
+ """An ruid has been freshly created and mapped against an object.
+ Also, implies the object has been newly added."""
+
+ __implements__ = IRuidObjectAddedEvent
+
+
+class RuidObjectModifiedEvent(RuidObjectEvent):
+ """An object with an ruid has been modified."""
+
+ __implements__ = IRuidObjectModifiedEvent
+
+
+class RuidObjectContextChangedEvent(RuidObjectEvent):
+ """An object with an ruid has had its context changed. Typically, this
+ means that it has been moved."""
+
+ __implements__ = IRuidObjectContextChangedEvent
+
+
+class RuidObjectRemovedEvent(RuidObjectEvent):
+ """An object with an ruid has been removed."""
+
+ __implements__ = IRuidObjectRemovedEvent
+
+ def __init__(self, obj, ruid, location):
+ self.__object = obj
+ self.__ruid = ruid
+ self.__location = location
+
+ def getRuid(self):
+ return self.__ruid
+
+ def getLocation(self):
+ return self.__location
+
+ def getObject(self):
+ return self.__object
=== Zope3/lib/python/Zope/ObjectHub/__init__.py 1.1 => 1.2 ===
+#
+# Copyright (c) 2001, 2002 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""
+
+Revision information:
+$Id$
+"""
+