[Zope3-checkins] CVS: Zope3/src/zope/app/container - configure.zcml:1.11 zopecontainer.py:1.19

Steve Alexander steve@cat-box.net
Wed, 4 Jun 2003 10:58:27 -0400


Update of /cvs-repository/Zope3/src/zope/app/container
In directory cvs.zope.org:/tmp/cvs-serv19568/src/zope/app/container

Modified Files:
	configure.zcml zopecontainer.py 
Log Message:
Installed and registered ZopeContainerDecorator.
For new code, it is now unnecessary to adapt a context-wrapped container
to ZopeContainerAdapter. The context-wrapper will do all the correct
wrapping and unwrapping, and will send the correct events.

Existing calls to getAdapter(container, IZopeContainer) are harmless, as
a context-wrapped container provides IZopeContainer. So, the getAdapter
call will simply return the already-wrapped container.
I'll be changing existing code to avoid using ZopeContainerAdapter.



=== Zope3/src/zope/app/container/configure.zcml 1.10 => 1.11 ===
--- Zope3/src/zope/app/container/configure.zcml:1.10	Mon Mar 31 09:48:40 2003
+++ Zope3/src/zope/app/container/configure.zcml	Wed Jun  4 10:57:56 2003
@@ -5,79 +5,88 @@
    xmlns:event="http://namespaces.zope.org/event"
 >
 
-  <adapter 
+  <adapter
      provides="zope.app.interfaces.container.find.IFind"
      for="zope.app.interfaces.container.IReadContainer"
-     permission="zope.ManageContent" 
-     factory="zope.app.container.find.FindAdapter" 
+     permission="zope.ManageContent"
+     factory="zope.app.container.find.FindAdapter"
      />
 
-  <adapter 
+  <adapter
       for="zope.app.interfaces.container.IReadContainer"
       provides="zope.app.interfaces.file.IReadDirectory"
       factory=".directory.noop"
       />
 
-  <adapter 
+  <adapter
       for="zope.app.interfaces.container.IWriteContainer"
       provides="zope.app.interfaces.file.IWriteDirectory"
       factory=".directory.noop"
       />
 
-  <adapter factory="zope.app.container.traversal.ContainerTraversable"
-           provides="zope.app.interfaces.traversing.ITraversable"
-           for="zope.app.interfaces.container.IReadContainer" 
-           />
-
-  <adapter factory="zope.app.container.zopecontainer.ZopeContainerAdapter"
-           provides="zope.app.interfaces.container.IZopeContainer"
-           for="zope.app.interfaces.container.IContainer" 
-           />
-
-  <adapter factory="zope.app.container.size.ContainerSized"
-           provides="zope.app.interfaces.size.ISized"
-           for="zope.app.interfaces.container.IContainer" 
-           />
+  <adapter
+      factory="zope.app.container.traversal.ContainerTraversable"
+      provides="zope.app.interfaces.traversing.ITraversable"
+      for="zope.app.interfaces.container.IReadContainer"
+      />
 
-  <adapter 
+  <adapter
+      factory="zope.app.container.zopecontainer.ZopeContainerAdapter"
+      provides="zope.app.interfaces.container.IZopeContainer"
+      for="zope.app.interfaces.container.IContainer"
+      />
+
+  <adapter
+      factory="zope.app.container.zopecontainer.ZopeContainerDecorator"
+      provides="zope.app.interfaces.context.IZopeContextWrapper"
+      for="zope.app.interfaces.container.IContainer"
+      />
+
+  <adapter
+      factory="zope.app.container.size.ContainerSized"
+      provides="zope.app.interfaces.size.ISized"
+      for="zope.app.interfaces.container.IContainer"
+      />
+
+  <adapter
      provides="zope.app.interfaces.container.ICopySource"
      for="zope.app.interfaces.content.folder.IFolder"
-     permission="zope.ManageContent" 
-     factory="zope.app.container.copypastemove.CopySource" 
+     permission="zope.ManageContent"
+     factory="zope.app.container.copypastemove.CopySource"
      />
 
-  <adapter 
+  <adapter
      provides="zope.app.interfaces.container.INoChildrenCopySource"
      for="zope.app.interfaces.content.folder.IFolder"
-     permission="zope.ManageContent" 
-     factory="zope.app.container.copypastemove.NoChildrenCopySource" 
+     permission="zope.ManageContent"
+     factory="zope.app.container.copypastemove.NoChildrenCopySource"
      />
 
-  <adapter 
+  <adapter
      provides="zope.app.interfaces.container.IMoveSource"
      for="zope.app.interfaces.content.folder.IFolder"
-     permission="zope.ManageContent" 
-     factory="zope.app.container.copypastemove.MoveSource" 
+     permission="zope.ManageContent"
+     factory="zope.app.container.copypastemove.MoveSource"
      />
 
-  <adapter 
+  <adapter
      provides="zope.app.interfaces.container.IPasteTarget"
      for="zope.app.interfaces.content.folder.IFolder"
-     permission="zope.ManageContent" 
-     factory="zope.app.container.copypastemove.PasteTarget" 
+     permission="zope.ManageContent"
+     factory="zope.app.container.copypastemove.PasteTarget"
      />
-           
-  <adapter 
+
+  <adapter
      provides="zope.app.interfaces.container.IPasteNamesChooser"
      for="zope.app.interfaces.content.folder.IFolder"
-     permission="zope.ManageContent" 
-     factory="zope.app.container.copypastemove.PasteNamesChooser" 
+     permission="zope.ManageContent"
+     factory="zope.app.container.copypastemove.PasteNamesChooser"
      />
 
 
-  <event:subscribe 
-      subscriber = ".dependency.CheckDependency"
-      event_types = "zope.app.interfaces.event.IObjectRemovedEvent"
+  <event:subscribe
+      subscriber=".dependency.CheckDependency"
+      event_types="zope.app.interfaces.event.IObjectRemovedEvent"
       />
 
 </zopeConfigure>


=== Zope3/src/zope/app/container/zopecontainer.py 1.18 => 1.19 ===
--- Zope3/src/zope/app/container/zopecontainer.py:1.18	Sun Jun  1 11:59:29 2003
+++ Zope3/src/zope/app/container/zopecontainer.py	Wed Jun  4 10:57:56 2003
@@ -21,22 +21,23 @@
 from zope.app.interfaces.container import IOptionalNamesContainer
 from zope.app.interfaces.container import IContainerNamesContainer
 from zope.component import queryAdapter, getAdapter
-from zope.app.context import ContextWrapper
+from zope.app.context import ContextWrapper, Wrapper
 from zope.app.event import publish
 from zope.app.interfaces.container import IAddNotifiable
 from zope.app.interfaces.container import IDeleteNotifiable
 from zope.app.interfaces.copypastemove import IObjectMover
 from types import StringTypes
-from zope.proxy import removeAllProxies
+from zope.proxy import removeAllProxies, getProxiedObject
 from zope.exceptions import NotFoundError, DuplicationError
-from zope.app.event.objectevent \
-     import ObjectRemovedEvent, ObjectModifiedEvent, ObjectAddedEvent
+from zope.app.event.objectevent import ObjectRemovedEvent
+from zope.app.event.objectevent import ObjectModifiedEvent, ObjectAddedEvent
+from zope.interface import implements
 
 _marker = object()
 
 class ZopeContainerAdapter:
 
-    __implements__ = IZopeContainer
+    implements(IZopeContainer)
 
     def __init__(self, container):
         self.context = container
@@ -178,6 +179,135 @@
         target = self.context
 
         if target.__contains__(newKey):
+            raise DuplicationError("name, %s, is already in use" % newKey)
+
+        if mover.moveable() and mover.moveableTo(target, newKey):
+            # the mover will call beforeDeleteHook hook for us
+            mover.moveTo(target, newKey)
+            # the mover will call the afterAddHook hook for us
+            # the mover will publish an ObjectMovedEvent for us
+
+class ZopeContainerDecorator(Wrapper):
+    implements(IZopeContainer)
+
+    def __getitem__(self, key):
+        "See IZopeItemContainer"
+        container = getProxiedObject(self)
+        value = container[key]
+        return ContextWrapper(value, self, name=key)
+
+    def get(self, key, default=None):
+        "See IZopeSimpleReadContainer"
+        container = getProxiedObject(self)
+        value = container.get(key, _marker)
+        if value is not _marker:
+            return ContextWrapper(value, self, name=key)
+        else:
+            return default
+
+    def values(self):
+        "See IZopeReadContainer"
+        container = getProxiedObject(self)
+        result = []
+        for key, value in container.items():
+            result.append(ContextWrapper(value, self, name=key))
+        return result
+
+    def items(self):
+        "See IZopeReadContainer"
+        container = getProxiedObject(self)
+        result = []
+        for key, value in container.items():
+            result.append((key, ContextWrapper(value, self, name=key)))
+        return result
+
+    def setObject(self, key, object):
+        "See IZopeWriteContainer"
+        if not isinstance(key, StringTypes):
+            raise TypeError("Item name is not a string.")
+
+        container = getProxiedObject(self)
+
+        if not key:
+            if not (IOptionalNamesContainer.isImplementedBy(container)
+                    or IContainerNamesContainer.isImplementedBy(container)):
+                raise ValueError("Empty names are not allowed")
+
+        # We remove the proxies from the object before adding it to
+        # the container, because we can't store proxies.
+        object = removeAllProxies(object)
+
+        # Add the object
+        key = container.setObject(key, object)
+
+        # Publish an added event
+        # We explicitly get the object back from the container with
+        # container[key], because some kinds of container may choose
+        # to store a different object than the exact one we added.
+        object = self[key]
+        publish(self, ObjectAddedEvent(object))
+
+        # Call the after add hook, if necessary
+        adapter = queryAdapter(object, IAddNotifiable)
+        if adapter is not None:
+            adapter.afterAddHook(object, self)
+
+        publish(self, ObjectModifiedEvent(self))
+        return key
+
+    def __delitem__(self, key):
+        "See IZopeWriteContainer"
+        container = getProxiedObject(self)
+
+        object = ContextWrapper(container[key], self, name=key)
+
+        # Call the before delete hook, if necessary
+        adapter = queryAdapter(object, IDeleteNotifiable)
+        if adapter is not None:
+            adapter.beforeDeleteHook(object, self)
+        elif hasattr(object, 'beforeDeleteHook'):
+            # XXX: Ideally, only do this in debug mode.
+            from warnings import warn
+            warn('Class %s has beforeDeleteHook but is not'
+                 ' IDeleteNotifiable' % object.__class__)
+
+        del container[key]
+
+        publish(self, ObjectRemovedEvent(object))
+        publish(self, ObjectModifiedEvent(self))
+
+        return key
+
+    def rename(self, currentKey, newKey):
+        """Put the object found at 'currentKey' under 'newKey' instead.
+
+        The container can choose different or modified 'newKey'. The
+        'newKey' that was used is returned.
+
+        If the object at 'currentKey' is IMoveNotifiable, its
+        beforeDeleteHook method is called, with a movingTo
+        argument of the container's path plus the 'newKey'.
+        Otherwise, if the object at 'currentKey' is IDeleteNotifiable,
+        its beforeDeleteHook method is called.
+
+        Then, the object is removed from the container using the
+        container's __del__ method.
+
+        Then, If the object is IMoveNotifiable, its afterAddHook
+        method is called, with a movedFrom argument of the container's
+        path plus the 'currentKey'.
+        Otherwise, if the object is IAddNotifiable, its afterAddHook
+        method is called.
+
+        Then, an IObjectMovedEvent is published.
+        """
+        object = self.get(currentKey)
+        if object is None:
+            raise NotFoundError(self, currentKey)
+        mover = getAdapter(object, IObjectMover)
+        target = self
+
+        if newKey in target:
             raise DuplicationError("name, %s, is already in use" % newKey)
 
         if mover.moveable() and mover.moveableTo(target, newKey):