From 92e4a9c09f4c15caf1b16b61b3a9ff5e87e46225 Mon Sep 17 00:00:00 2001
From: Nicolas Dumazet <nicolas.dumazet@nexedi.com>
Date: Thu, 20 Jan 2011 08:13:56 +0000
Subject: [PATCH] and finally avoid iterating twice over the interaction list

It seems that the only methods that we wanted to match in the second pass
are class methods created during the first pass:
finding the newly created candidates is easy if we keep track of class methods before and after the first pass

And then, the ony interactions than *can* augment the existing workflow definitions
are the ones using regular expressions, according to the code comments.
Queue those interactions during the first pass to reduce the space search.

Finally, the second pass is cheaper because we *know* that methods do exist
and _are_ Workflow methods already.


The overall cost of creating workflow methods should be lower thanks to those
efforts.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@42497 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5Type/Base.py | 42 +++++++++++++++++++++++++++++++---------
 1 file changed, 33 insertions(+), 9 deletions(-)

diff --git a/product/ERP5Type/Base.py b/product/ERP5Type/Base.py
index 5addc79c40..e9cf620f65 100644
--- a/product/ERP5Type/Base.py
+++ b/product/ERP5Type/Base.py
@@ -700,25 +700,29 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder,
   if not interaction_workflow_dict:
     return
 
+  class_method_list = prop_holder.getClassMethodIdList(klass)
   # only compute once this (somehow) costly list
   all_method_id_list = prop_holder.getAccessorMethodIdList() + \
                        prop_holder.getWorkflowMethodIdList() + \
-                       prop_holder.getClassMethodIdList(klass)
+                       class_method_list
 
+  interaction_queue = []
   # XXX This part is (more or less...) a copy and paste
-  # We need to run this part twice in order to handle interactions of interactions
-  # ex. an interaction workflow creates a workflow method which matches
-  # the regexp of another interaction workflow
-  for wf_id in interaction_workflow_dict.keys()*2:
-    transition_id_set, trigger_dict = interaction_workflow_dict[wf_id]
+  for wf_id, v in interaction_workflow_dict.iteritems():
+    transition_id_set, trigger_dict = v
     for tr_id, tdef in trigger_dict.iteritems():
       # XXX Prefiltering per portal type would be more efficient
       for imethod_id in tdef.method_id:
         if wildcard_interaction_method_id_match(imethod_id):
           # Interactions workflows can use regexp based wildcard methods
-          method_id_matcher = re.compile(imethod_id) # XXX What happens if exception ?
+          # XXX What happens if exception ?
+          method_id_matcher = re.compile(imethod_id).match
+
+          # queue transitions using regexps for later examination
+          interaction_queue.append((wf_id, tr_id, tdef, method_id_matcher))
+
           # XXX - class stuff is missing here
-          method_id_list = filter(method_id_matcher.match, all_method_id_list)
+          method_id_list = filter(method_id_matcher, all_method_id_list)
         else:
           # Single method
           # XXX What if the method does not exist ?
@@ -733,7 +737,7 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder,
               prop_holder.security.declareProtected(
                   Permissions.AccessContentsInformation, method_id)
             prop_holder.registerWorkflowMethod(method_id, wf_id, tr_id,
-                                                   tdef.once_per_transaction)
+                                               tdef.once_per_transaction)
             continue
 
           # Wrap method
@@ -757,6 +761,26 @@ def initializePortalTypeDynamicWorkflowMethods(self, klass, ptype, prop_holder,
           else:
             method.registerTransitionAlways(ptype, wf_id, tr_id)
 
+  if not interaction_queue:
+    return
+
+  new_method_set = set(prop_holder.getClassMethodItemList(klass))
+  added_method_set = new_method_set.difference(class_method_list)
+  # We need to run this part twice in order to handle interactions of interactions
+  # ex. an interaction workflow creates a workflow method which matches
+  # the regexp of another interaction workflow
+  for wf_id, tr_id, tdef, method_id_matcher in interaction_queue:
+    for method_id in filter(method_id_matcher, added_method_set):
+      # method must already exist and be a workflow method
+      method = getattr(klass, method_id)
+      transition_id = method.getTransitionId()
+      if transition_id in transition_id_set:
+        method.registerTransitionAlways(ptype, wf_id, transition_id)
+      if tdef.once_per_transaction:
+        method.registerTransitionOncePerTransaction(ptype, wf_id, tr_id)
+      else:
+        method.registerTransitionAlways(ptype, wf_id, tr_id)
+
 class Base( CopyContainer,
             PortalContent,
             ActiveObject,
-- 
2.30.9