From 41d58c162cbc1cfd1fe3616374372d258c5576c3 Mon Sep 17 00:00:00 2001
From: Julien Muchembled <jm@nexedi.com>
Date: Wed, 3 Nov 2010 18:43:30 +0000
Subject: [PATCH] Simplify RoundingProxy

git-svn-id: https://svn.erp5.org/repos/public/erp5/sandbox/amount_generator@39856 20353a03-c40f-0410-a6d1-a30d3c3de9de
---
 product/ERP5/Document/RoundingModel.py | 131 +++++++++++--------------
 1 file changed, 55 insertions(+), 76 deletions(-)

diff --git a/product/ERP5/Document/RoundingModel.py b/product/ERP5/Document/RoundingModel.py
index a6d4033369..f4a0390c51 100644
--- a/product/ERP5/Document/RoundingModel.py
+++ b/product/ERP5/Document/RoundingModel.py
@@ -98,84 +98,63 @@ class RoundingModel(Predicate):
     Return a rounding proxy object which getter methods returns rounded
     value by following the rounding model definition.
     """
-    rounding_model = self
-    rounded_property_getter_method_name_list = []
-    rounded_property_special_property_name_list = []
+    if not isinstance(document, RoundingProxy):
+      document = RoundingProxy(document.asContext())
+    document._applyRoundingModel(self)
+    return document
 
-    if isinstance(document, RoundingProxy):
-      temp_document = document._getOriginalDocument()
-      original_document = document
-    else:
-      temp_document = document.asContext()
-      original_document = temp_document
-
-    for property_id in rounding_model.getRoundedPropertyIdList():
-      getter_name = 'get%s' % UpperCase(property_id)
-      getter = getattr(temp_document, getter_name, None)
-      setter_name = 'set%s' % UpperCase(property_id)
-      setter = getattr(temp_document, setter_name, None)
-
-      if getter is not None and setter is not None:
-        # round the property value itself
-        setter(self.roundValue(getter()))
-      else:
-        # cannot round the property value so that the return value of getter
-        # will be rounded
-        rounded_property_getter_method_name_list.append(getter_name)
-        if getter is not None and setter is None:
-          rounded_property_special_property_name_list.append(property_id)
-
-    class _RoundingProxy(RoundingProxy):
-
-      def _getOriginalDocument(self):
-        if isinstance(original_document, RoundingProxy):
-          return original_document._getOriginalDocument()
-        else:
-          return original_document
-
-      def getRoundingModelPrecision(self, property_id):
-        """
-        Return precision value of rounding model. This is useful for
-        float field.
-        """
-        if property_id in rounding_model.getRoundedPropertyIdList():
-          return rounding_model.getPrecision()
-        elif isinstance(original_document, RoundingProxy):
-          return original_document.getRoundingModelPrecision(property_id)
-        else:
-          return None
-
-      def getProperty(self, key, *args, **kw):
-        result = original_document.getProperty(key, *args, **kw)
-        if key in rounded_property_special_property_name_list:
-          return rounding_model.roundValue(result)
-        else:
-          return result
-
-      # XXX not sure why but getObject may return original_document
-      # unless it is overridden here.
-      def getObject(self, *args, **kw):
-        return self
-
-      def __getattr__(self, name):
-        attribute = getattr(original_document, name)
-        if getattr(attribute, 'DUMMY_ROUNDING_METHOD_MARK', None) is DUMMY_ROUNDING_METHOD_MARK:
-          return attribute
-        if name in rounded_property_getter_method_name_list:
-          def dummyMethod(*args, **kw):
-            return rounding_model.roundValue(attribute(*args, **kw))
-          dummyMethod.DUMMY_ROUNDING_METHOD_MARK = DUMMY_ROUNDING_METHOD_MARK
-          return dummyMethod
-        else:
-          return attribute
-    return _RoundingProxy()
-
-DUMMY_ROUNDING_METHOD_MARK = object()
 
 # Use the Extension Class only because it is necessary to allow an instance
 # of this class to be wrapped in an acquisition wrapper.
 class RoundingProxy(ExtensionClass.Base):
-  """Super class of _RoundingProxy class defined above. Use this class for
-  isinstance method to check if object is a real instance or a rounding proxy
-  instance.
-  """
+
+  def __init__(self, ob):
+    super(RoundingProxy, self).__init__()
+    self._ob = ob
+    self._rounded_value_set = set()
+    self._rounding_model_dict = {}
+
+  def __getattr__(self, name):
+    # do not do 'self._ob' to avoid acquisition wrapping
+    return getattr(self.__dict__['_ob'], name)
+
+  def _applyRoundingModel(self, rounding_model):
+    document = self.__dict__['_ob']
+    roundValue = rounding_model.roundValue
+    def decorate(original_getter):
+      return lambda *args, **kw: roundValue(original_getter(*args, **kw))
+    for property_id in rounding_model.getRoundedPropertyIdList():
+      assert property_id not in self._rounding_model_dict
+      self._rounding_model_dict[property_id] = rounding_model
+      getter_name = 'get%s' % UpperCase(property_id)
+      getter = getattr(document, getter_name, None)
+      setter_name = '_set%s' % UpperCase(property_id)
+      setter = getattr(document, setter_name, None)
+      if setter is not None:
+        # round the property value itself
+        setter(roundValue(getter()))
+        # tell getProperty not to round it again
+        self._rounded_value_set.add(property_id)
+      elif getter is not None:
+        # cannot round the property value now so do it dynamically
+        setattr(self, getter_name, decorate(getter))
+
+  def getRoundingModelPrecision(self, property_id):
+    """
+    Return precision value of rounding model. This is useful for
+    float field.
+    """
+    rounding_model = self._rounding_model_dict.get(property_id)
+    if rounding_model is not None:
+      return rounding_model.getPrecision()
+
+  def getProperty(self, key, *args, **kw):
+    result = self.__dict__['_ob'].getProperty(key, *args, **kw)
+    if key not in self._rounded_value_set:
+      rounding_model = self._rounding_model_dict.get(key)
+      if rounding_model is not None:
+        return rounding_model.roundValue(result)
+    return result
+
+  def getObject(self, *args, **kw):
+    return self
-- 
2.30.9