From 7b8811c80bf9fec37c5eb3377eb4f40b6ad0533d Mon Sep 17 00:00:00 2001
From: Julien Muchembled <jm@nexedi.com>
Date: Wed, 2 Jun 2010 14:49:05 +0000
Subject: [PATCH] Revert [35537] and speed up contentValues by optimizing
 TypesTool.listContentTypes

[35537] was committed for performance reason, but it changed the behaviour of
Folder.contentValues

git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@35908 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5Type/Core/Folder.py    |  5 ++-
 product/ERP5Type/Tool/TypesTool.py | 65 +++++++++++++++++++++++++++---
 2 files changed, 63 insertions(+), 7 deletions(-)

diff --git a/product/ERP5Type/Core/Folder.py b/product/ERP5Type/Core/Folder.py
index 2c1caa6faf..beac777940 100644
--- a/product/ERP5Type/Core/Folder.py
+++ b/product/ERP5Type/Core/Folder.py
@@ -1431,16 +1431,17 @@ class Folder(CopyContainer, CMFBTreeFolder, CMFHBTreeFolder, Base, FolderMixIn,
   def contentValues(self, *args, **kw):
     # Returns a list of documents contained in this folder.
     # ( no docstring to prevent publishing )
+    portal_type_id_list = self._getTypesTool().listContentTypes()
     filter_kw = kw.pop('filter', None) or {}
     portal_type = kw.pop('portal_type', None)
     if 'portal_type' in filter_kw:
       portal_type = filter_kw.pop('portal_type')
     if portal_type is None:
-      kw['portal_type'] = self._getTypesTool().listContentTypes()
+      kw['portal_type'] = portal_type_id_list
     else:
       if isinstance(portal_type, str):
         portal_type = portal_type,
-      kw['portal_type'] = portal_type
+      kw['portal_type'] = [x for x in portal_type if x in portal_type_id_list]
     object_list = self.objectValues(*args, **kw)
     if filter_kw:
       object_list = filter(ContentFilter(**filter_kw), object_list)
diff --git a/product/ERP5Type/Tool/TypesTool.py b/product/ERP5Type/Tool/TypesTool.py
index 554996754a..9f2c2798a9 100644
--- a/product/ERP5Type/Tool/TypesTool.py
+++ b/product/ERP5Type/Tool/TypesTool.py
@@ -16,6 +16,7 @@
 ##############################################################################
 
 import imp, sys, warnings
+from itertools import chain
 import zope.interface
 from Acquisition import aq_base
 from AccessControl import ClassSecurityInfo
@@ -30,6 +31,47 @@ from zLOG import LOG, WARNING, PANIC
 from Products.ERP5Type.interfaces import ITypeProvider, ITypesTool
 
 
+class ComposedObjectIds(object):
+  """Sequence type used to iterate efficiently over a union of folders
+
+  Returned values are ids of contained objects.
+  This type should used instead of building a simple list from the concatenation
+  of multiple calls on objectIds.
+
+  Note this class even implements '__contains__', which makes:
+    'some_id' in ComposedObjectIds([container])
+  faster than:
+    'some_id' in container.objectIds()
+
+  XXX Is it only useful for TypesTool ?
+      If not, this should it be moved in another place, like ERP5Type.Utils
+  """
+  __allow_access_to_unprotected_subobjects__ = 1
+
+  def __init__(self, container_list):
+    self._container_list = container_list
+
+  def __contains__(self, item):
+    for container in self._container_list:
+      if container.has_key(item):
+        return True
+    return False
+
+  def __iter__(self):
+    return chain(*[container.objectIds() for container in self._container_list])
+
+  def __len__(self):
+    return sum(map(len, self._container_list))
+
+  def __getitem__(self, item):
+    for container in self._container_list:
+      count = len(container)
+      if item < count:
+        return container.objectIds()[item]
+      item -= count
+    raise IndexError
+
+
 CMFCore_TypesTool = CMFCore_TypesToolModule.TypesTool
 
 class TypeProvider(BaseTool, CMFCore_TypesTool):
@@ -54,19 +96,32 @@ class TypesTool(TypeProvider):
   security = ClassSecurityInfo()
   security.declareObjectProtected(Permissions.AccessContentsInformation)
 
+  def listContentTypes(self, container=None):
+    """List content types from all providers
+    """
+    if container is not None:
+      # XXX Slow legacy implementation. Is 'container_list' parameter useful ?
+      return CMFCore_TypesTool.listContentTypes(self, container)
+    object_list = [self]
+    _getOb = self.getPortalObject()._getOb
+    for provider in self.type_provider_list:
+      provider_value = _getOb(provider, None)
+      if provider_value:
+        object_list.append(provider_value)
+    return ComposedObjectIds(object_list)
+
   def listTypeInfo(self, container=None):
     """List type information from all providers
     """
     listTypeInfo = CMFCore_TypesTool.listTypeInfo
     type_info_list = listTypeInfo(self, container=container)
-    portal = self.getPortalObject()
+    _getOb = self.getPortalObject()._getOb
     for provider in self.type_provider_list:
-      provider_value = portal._getOb(provider, None)
+      provider_value = _getOb(provider, None)
       if provider_value is not None:
-        type_info_list.extend(
-            listTypeInfo(provider_value, container=container))
+        type_info_list += listTypeInfo(provider_value, container=container)
     return type_info_list
-  
+
   def _aq_dynamic(self, id):
     """Get a type information from a provider
     """
-- 
2.30.9