From fcdadc8bd486c32bf7362794f56aca4398e3e887 Mon Sep 17 00:00:00 2001
From: Sebastian Listl <s.listl@adito.de>
Date: Fri, 30 Oct 2020 11:36:53 +0100
Subject: [PATCH] Utils.clone support for Map, Set and Symbol properties

---
 process/Sql_lib/process.js  | 27 +++++------------------
 process/Util_lib/process.js | 43 ++++++++++++++++++++++++++++++-------
 2 files changed, 40 insertions(+), 30 deletions(-)

diff --git a/process/Sql_lib/process.js b/process/Sql_lib/process.js
index d0c827e77c..fc73644c52 100644
--- a/process/Sql_lib/process.js
+++ b/process/Sql_lib/process.js
@@ -801,8 +801,6 @@ function SqlBuilder (pAlias)
     
     this._where = {};
     this._initWhere();
-    
-    SqlBuilder.defineCanBuildSql(this);
 }
 
 /**
@@ -823,6 +821,8 @@ SqlBuilder.checkCanBuildSql = function (pObject)
     return pObject[SqlBuilder.getCanBuildSqlSymbol()];
 }
 
+SqlBuilder.defineCanBuildSql(SqlBuilder.prototype);
+
 /**
  * Deep copies the SqlBuilder object and returns a new one.<br/>
  * Use this if you want to add for example add additional parameters without modifying the current builder.
@@ -830,25 +830,7 @@ SqlBuilder.checkCanBuildSql = function (pObject)
  */
 SqlBuilder.prototype.copy = function()
 {
-    var newBuilder = _deepCopyByJson(this, new SqlBuilder());
-    return newBuilder;
-    
-    // NOTE: this works only with simple data types. 
-    // Here we only use strings, arrays, booleans and null, so this should work
-    function _deepCopyByJson(pObject, pNewObject)
-    {
-        // deep copy by using json
-        var deepCopied = JSON.parse(JSON.stringify(pObject));
-        
-        // set the props of the new object to the deepCopied ones.
-        // without this all functions would be lost
-        for (let prop in deepCopied)
-        {
-            pNewObject[prop] = deepCopied[prop]
-        }
-        
-        return pNewObject;
-    }
+    return Utils.clone(this);
 }
 
 // errors which are thrown by the SqlBuilder
@@ -3117,9 +3099,10 @@ SqlBuilder._CaseStatement = function ()
     this._whenThens = [];
     this._elseValue = null;
     this._afterWhenMask = new SqlBuilder._CaseWhen(this);
-    SqlBuilder.defineCanBuildSql(this);
 }
 
+SqlBuilder.defineCanBuildSql(SqlBuilder._CaseStatement.prototype);
+
 /**
  * @param {String|String[]|SqlBuilder|PreparedSqlArray} [pFieldOrCond] If this is the only parameter, it is used as Subselect <br/>
  *                                                                     else it is used as Field. <br/>
diff --git a/process/Util_lib/process.js b/process/Util_lib/process.js
index 97545be173..778e4ae46c 100644
--- a/process/Util_lib/process.js
+++ b/process/Util_lib/process.js
@@ -53,7 +53,7 @@ Utils.isNullOrEmpty = function (pObject)
 }
 
 /**
- * Creates a deep copy of the given object. Also works with arrays.
+ * Creates a deep copy of the given object. Also works with arrays, maps and sets.
  * 
  * @param {Object} pObject the object to create a copy of
  * @return {Object} the cloned object
@@ -69,7 +69,7 @@ Utils.isNullOrEmpty = function (pObject)
  * 
  * logging.log(original.name != copy.name);    //true
  * logging.log(copy instanceof MyObject());    //true, prototypes are set correctly
- * logging.log(copy.obj1 === copy.obj2);       //true, relative object references are kept
+ * logging.log(copy.obj1 === copy.obj2);       //true, relative object references are preserved
  */
 Utils.clone = function (pObject)
 {
@@ -84,20 +84,47 @@ Utils.clone = function (pObject)
         if (referenceMap.has(pObject))
             return referenceMap.get(pObject);
         
-        var clonedObject = Array.isArray(pObject) 
-            ? [] 
-            : Object.create(Object.getPrototypeOf(pObject)); //set the prototype of the given object
+        var clonedObject;
+        if (Array.isArray(pObject))
+            clonedObject = [];
+        else if (pObject instanceof Map)
+            clonedObject = new Map();
+        else if (pObject instanceof Set)
+            clonedObject = new Set();
+        else
+            clonedObject = Object.create(Object.getPrototypeOf(pObject)); //set the prototype of the given object
         
         /* keeps track of all encountered objects and maps the original to the copy, this makes it possible to:
            - have the same relative references in the copy as in the original
            - copy cyclic references without error */
         referenceMap.set(pObject, clonedObject);
         
-        for (let key in pObject) 
+        if (pObject instanceof Map)
+        {
+            pObject.forEach(function (value, key)
+            {
+                clonedObject.set(_clone(key), _clone(value));
+            });
+        }
+        else if (pObject instanceof Set)
         {
-            var value = pObject[key];
-            clonedObject[key] = _clone(value); //Recursively (deep) copy for nested objects, including arrays
+            pObject.forEach(function (value)
+            {
+                clonedObject.add(_clone(value));
+            });
         }
+        else
+        {
+            for (let key in pObject) 
+            {
+                clonedObject[key] = _clone(pObject[key]); //Recursively (deep) copy for nested objects, including arrays
+            }
+        }
+        
+        Object.getOwnPropertySymbols(pObject).forEach(function (sym)
+        {
+            clonedObject[sym] = _clone(pObject[sym]);
+        });
         
         return clonedObject;
     }
-- 
GitLab