    //@include lib_Class.js

    /**
     * This class contains convenience methods for working with java script basic types and objects.
     *
     * @author PZ, ELO Digital Office GmbH
     * @version 1.1
     *
     * @eloall
     */
    sol.define("sol.common.ObjectUtils", {
      singleton: true,

      /**
       * @private
       * @property {Function} _toStringFunction Reference to Object.prototype.toString
       */
      _toStringFunction: Object.prototype.toString,

      /**
       * Checks, if an Object is empty.
       * @param {Object} o
       * @return {Boolean}
       */
      isEmpty: function (o) {
        if (o === null || o === undefined) {
          return true;
        }
        if (o.length > 0) {
          return false;
        }
        if (Object.getOwnPropertyNames(o).length > 0) {
          return false;
        }
        return true;
      },

      /**
       * returns a variables real type. (typeof + null, nan, array, date, javaobject, regexp,...)
       * javastrings are reported as a normal string. Best practice: Always String() values this function reports as "string"
       *
       * e.g. type(123, "number") => true
       *      type([]) => "array"
       *
       * @param {Object} val the value to typecheck
       * @param {String} should the typename which should match
       * @returns {Boolean|String} returns Boolean if `should` is defined (true if determined type equals `should`, false if not). otherwise returns the determined type as a string
       */
      type: function (val, should) {
        var t; t = (
          (val !== val && "nan")
          || ((t = typeof val) !== "object" && t)
          || (typeof java !== "undefined" && val instanceof java.lang.String && "string")
          || ((t = Object.prototype.toString.call(val)), t.substring(8, t.length - 1).toLowerCase())
        );
        return should ? t === should : t;
      },

      /**
       * Checks, if an object is a JavaScript String
       * @param {Object} o
       * @returns {Boolean}
       */
      isString: function (o) {
        return this._toStringFunction.call(o) === "[object String]";
      },

      /**
       * Checks, if an object is a JavaScript Number
       * @param {Object} o
       * @returns {Boolean}
       */
      isNumber: function (o) {
        return this._toStringFunction.call(o) === "[object Number]";
      },

      /**
       * Checks, if an object is a JavaScript Date
       * @param {Object} o
       * @returns {Boolean}
       */
      isDate: function (o) {
        return this._toStringFunction.call(o) === "[object Date]";
      },

      /**
       * Checks, if an object is a JavaScript Array
       * @param {Object} o
       * @return {Boolean}
       */
      isArray: function (o) {
        return this._toStringFunction.call(o) === "[object Array]";
      },

      /**
       * Checks, if an object is a JavaScript Function
       * @param {Object} o
       * @return {Boolean}
       */
      isFunction: function (o) {
        return this._toStringFunction.call(o) === "[object Function]";
      },

      /**
       * Checks, if an object is a JavaScript object
       * @param {Object} o
       * @return {Boolean}
       */
      isObject: function (o) {
        return this._toStringFunction.call(o) === "[object Object]";
      },

      /**
       * Checks, if an object is a Java (Rhino) object
       * @param {Object} o
       * @return {Boolean}
       */
      isJavaObject: function (o) {
        return this._toStringFunction.call(o) === "[object JavaObject]";
      },

      /**
       * Checks, if an object is a JavaScript regular expression
       * @param {Object} o
       * @return {Boolean}
       */
      isRegExp: function (o) {
        return this._toStringFunction.call(o) === "[object RegExp]";
      },

      /**
       * Checks, if an object is blank. Works for Array (only contains ) and String, all other types always return `false`.
       * @param {Object} o
       * @return {Boolean}
       */
      isBlank: function (o) {
        if (this.isArray(o)) {
          o = o.filter(function (e) {
            return (typeof e !== "undefined");
          });
        }
        if (this.isString(o)) {
          o = o.trim();
          return o.length == 0;
        }
        return this.isEmpty(o);
      },

      /**
       * Determine whether the current object `o` is truthy
       * https://developer.mozilla.org/de/docs/Glossary/Truthy
       *
       * @param {*} o
       * @returns true when the passed obj is truthy otherwise false
       */
      isTruthy: function (o) {
        return !!o;
      },

      /**
       * Returns the passed object into an array.
       * When the object is already an array the original array
       * will return
       * @param {Object|Array} o object to wrap into an array
       * @returns capsulated object within an array
      */
      toArray: function (o) {
        return sol.common.ObjectUtils.isArray(o) ? o : [o];
      },

      /**
       * Merges a list of objects.
       *
       * The `base` object is the first object in the merging chain.
       * Properties from the `mergeList` objects will be added to the base object.
       * If there is already a property in the base object, it will only be overwritten, if the types match.
       *
       *     var o1 = { a: "hello", b: "world" };
       *     var o2 = { b: "developer", c: "foobar" };
       *     var merged = sol.common.ObjectUtils.mergeObject(o1, [o2]);  // merged => { a: "hello", b: "developer", c: "foobar" }
       *
       * @param {Object} base
       * @param {Object[]} mergeList (optional)
       * @param {Boolean} [preserveCustom=false] (optional) If `true`, all objects from `mergeList` will be cloned. Otherwise the merge process will work directly on the objects and may alter them
       * @param {String} path (optional) Startpath/objectname, used for logging
       * @param {Function} assignCallback (optional) Will be called for every property assignment (if set, this function has to take care of the assignment itself)
       * @param {Function} recursionCheck (optional) This function is called for every property and decides if the property has to be merged recursively (returns `true`) or not (returns `false`)
       * @return {Object} The merged object
       */
      mergeObjects: function (base, mergeList, preserveCustom, path, assignCallback, recursionCheck) {
        var me = this,
            log = [],
            custom = [],
            idx, curr, succ, logTemp;

        if (Array.isArray(mergeList)) {
          custom = mergeList.slice();
        } else if (!!mergeList) {
          custom.push(mergeList);
        }

        if (custom.length === 0) {
          return base;
        }

        function clone(o) {
          return !!preserveCustom ? me.clone(o) : o;
        }

        idx = custom.unshift(base) - 1;

        while (idx > 0) {
          curr = clone(custom[idx]);
          succ = clone(custom[idx - 1]);

          logTemp = [];
          custom[idx - 1] = me.merge(curr, succ, logTemp, path, assignCallback, recursionCheck);
          if (logTemp.length > 0) {
            logTemp.unshift("Custom argument id " + idx + " contains logs!");
            log = log.concat(logTemp);
          }

          idx--;
        }

        if (log.length > 0) {
          custom[0]._$mergeLog$_ = log;
        }

        return custom[0];
      },

      /**
       * Clones an object.
       * @param {Object} o
       * @returns {Object}
       */
      clone: function (o) {
        return JSON.parse(JSON.stringify(o));
      },

      /**
       * Merges all properties in base into custom. Existing properties in custom will be preserved, but only
       * if they match the type of the property in base. Otherwise the property of base will be used
       * and a log will be written to array parameter log.
       *
       * This function does not support functions. It does support Array, Object and Date and creates a clone from it.
       * @param {Object} custom The object containing all merged data (object will be altered)
       * @param {Object} base The object from which will be copied to the `custom` object
       * @param {String[]} [log=[]] (optional) Logging messages will be pushed to this array
       * @param {String} [path=''] (optional) Startpath/objectname, used for logging
       * @param {Function} assignCallback (optional) Will be called for every property assignment (if set, this function has to take care of the assignment itself)
       * @param {Function} recursionCheck (optional) This function is called for every property and decides if the property has to be merged recursively (returns `true`) or not (returns `false`)
       * @returns {Object} The merged object (`custom`)
       */
      merge: function (custom, base, log, path, assignCallback, recursionCheck) {
        var me = this,
            prop;
        log = log || [];
        path = path || "";

        recursionCheck = recursionCheck || function (custom, base, prop) { // eslint-disable-line no-shadow
          return base[prop] instanceof Object && !(base[prop] instanceof Array) && !(base[prop] instanceof Date);
        };

        assignCallback = assignCallback || function (target, source, propertyName) {
          target[propertyName] = source[propertyName];
        };

        for (prop in base) {
          if (base.hasOwnProperty(prop)) {
            //check for same type (array must be checked separately) and use default property instead
            if ((typeof custom[prop] !== "undefined") && (custom[prop] !== null) && (base[prop] !== null) && ((typeof custom[prop] !== typeof base[prop]) ||
              (Array.isArray(base[prop]) !== Array.isArray(custom[prop])))) {
              log.push("Warning: The type of custom property " + path + "." + prop + " is not the same as in the target. Custom property is ignored.");
              custom[prop] = me.clone(base[prop]);
            } else if (recursionCheck(custom, base, prop)) {
              //recursion
              custom[prop] = me.merge(custom[prop] || {}, base[prop], log, path + "." + prop, assignCallback, recursionCheck); //return empty object if p does not exist in target
            } else if (custom[prop] === undefined) {
              //copy default property only if not exist in custom
              if (base[prop] instanceof Date) {
                custom[prop] = new Date(base[prop]);
              } else if (base[prop] instanceof Array) {
                custom[prop] = me.clone(base[prop]);
              } else {
                assignCallback(custom, base, prop);
              }
            }
          }
        }
        return custom;
      },

      /**
       * Converts an array of objects to an object
       * @param {Array} arr
       * @param {keyPropName} keyPropName name of the property key for the new key
       * @return {Object}
       */
      getObjectFromArray: function (arr, keyPropName) {
        var i,
            o = {},
            entry;
        if (arr && keyPropName) {
          for (i = 0; i < arr.length; i++) {
            entry = arr[i];
            o[String(entry[keyPropName])] = entry;
          }
        }
        return o;
      },

      /**
       * Returns the values of an Object
       * @param {Object} o Object
       * @return {Array} Array
       */
      getValues: function (o) {
        var key,
            arr = [];
        if (o) {
          for (key in o) {
            if (o.hasOwnProperty(key)) {
              arr.push(o[key]);
            }
          }
        }
        return arr;
      },

      /**
       * Function ´forEach´ that works in Rhino and Nashorn
       * @param {Array} arr Array
       * @param {Function} callback Callback
       * @param {Object} context Context
       */
      forEach: function (arr, callback, context) {
        if (!callback) {
          throw "Callback is missing";
        }
        var i;
        for (i = 0; i < arr.length; i++) {
          callback(arr[i], i, context);
        }
      },

      /**
       * Function ´map´ that works in Rhino and Nashorn
       * @param {Array} arr Array
       * @param {Function} callback Callback
       * @param {Object} context Context
       * @return {Array} Array
       */
      map: function (arr, callback, context) {
        var i,
            resultArr = [];
        if (!callback) {
          throw "Callback is missing";
        }
        for (i = 0; i < arr.length; i++) {
          resultArr.push(callback(arr[i], i, context));
        }
        return resultArr;
      },

      // Polyfill for Array.prototype.find (Rhino <= 1.7R5)
      arrayFind: function (arr, cb) {
        var list = arr, length = list.length, value, i;

        for (i = 0; i < length; i++) {
          value = list[i];
          if (cb(value, i, list)) {
            return value;
          }
        }
        return undefined;
      },

      /**
      * Returns the first object of an array whose property "id" has the value val.
      * @param {Array} a array containing the objects
      * @param {Any} val value to search for
      * @param {String} customProp  custom property name, if the property is not "id"
      * @param {Function} customCallback custom callback for Array.find function
      * @return {Object} found object or undefined. false if a is not an Array
      * Rhino 1.7R5 does not implement Array.prototype.find. arrayFind takes is place in this case
      */
      findObjInArray: function (a, val, customProp, customCallback) {
        var me = this,
            cb = function (ae) {
              return ae[customProp || "id"] === val;
            };
        return (
          Array.isArray(a)
          &&
          (
            (a.find && a.find(customCallback || cb))
            || (!a.find && me.arrayFind(a, (customCallback || cb)))
          )
          || undefined
        );
      },

      /**
       * Returns an object containing only specific properties of the input object.
       * @param {Object} o  input object
       * @param {Array} include  array containing all properties to include as strings. Empty array includes all properties.
       * @param {Array} exclude  array containing all properties to exclude. Empty array means exclude nothing from include. If values are defined in include and exclude, they are excluded.
       * @return {Object}
       */
      getPropsOfObj: function (o, include, exclude) {
        var name,
            result = {};

        if (typeof o === "object" && (Array.isArray(include)) && (!exclude || Array.isArray(exclude))) {
          for (name in o) {
            name = name.trim();
            if (
              name.length > 0
              && (
                (include.length === 0 && (!exclude || exclude.indexOf(name) === -1))
                || (include.indexOf(name) > -1 && (!exclude || exclude.indexOf(name) === -1))
              )
            ) {
              result[name] = o[name];
            }
          }
        }
        return result;
      },

      /**
       * gets a property from an `object` by traversing the passed `path`.
       * if the property is inside an object which is inside an array, the array must contain
       * a property called `id` containing the corresponding part of the `path`.
       * e.g. y = { x: [ {id: "test", myniceprop: 42} ] }
       * if you want to retrieve "myniceprop", you would call getProp(y, "x.test.myniceprop")
       * note: this is also a better way to get a property from an object with probably uninitialized preceding properties
       * optional: if passed a `customPropName`, getProp will use the name as the array-object-property name
       * e.g. y = { x: [ {__ID: "test", myniceprop: 42} ] }    -->  getProp(y, "x.test.myniceprop", "__ID")
       *
       * If your target prop is an array you can use the special prop `$length` to determine the array
       * length of the element. This works only in context of array and will terminate the prop request.
       *
       * e.g. y = { x : [{ id:1 }, { id:2 }] } --> getProp(y, "x.$length")
       *
       * @param {Object} object Object
       * @param {String} path Path
       * @param {String} customPropName Custom property name
       * @returns {Object}
       */
      getProp: function (object, path, customPropName) {
        var arr = (typeof path === "string") && path.split("."), curr = undefined;
        if (arr.length > 0) {
          curr = object;
          arr.forEach(function (key) {
            if (curr && (key.length > 0)) {
              if (Array.isArray(curr)) {
                if (key === "$length") {
                  // special prop to determine length of an array
                  curr = curr.length;
                } else {
                  curr = sol.common.ObjectUtils.findObjInArray(curr, key, customPropName);
                }
              } else if (typeof curr === "object") {
                curr = curr[key];
              }
            }
          });
        } else if (path === "" || path === undefined) {
          curr = object;
        }
        return curr;
      },

      /**
       * sets a property of an `object` by traversing the passed `path`.
       * the last part of the path is the target property name.
       * if a property is inside an object which is inside an array, the array must contain
       * a property called `id` containing the corresponding part of the `path`.
       * The function can then traverse the array and create an empty object if necessary.
       *
       * @param {Object} object Object
       * @param {String} path Path
       * @param {String} value Value to be set
       * @param {Boolean} overwrite Overwrite path property if it is not an object yet
       * @param {String} customPropName Custom property name
       * @returns {Object}
       */
      setProp: function (object, path, value, overwrite, customPropName) {
        var me = this, arr = (typeof path === "string") && path.split("."),
            targetProp = arr.pop(), curr = undefined;
        if (arr.length > 0) {
          curr = object;
          arr.forEach(function (key) {
            var val = curr[key],
                arrObj;
            if (me.type(curr[key], "object")) {
              curr = val;
            } else if (Array.isArray(curr[key])) {
              if (!(arrObj = sol.common.ObjectUtils.findObjInArray(val, customPropName.value, (customPropName || {}).key || key))) {
                arrObj = {};
                arrObj[(customPropName || {}).key || key || "id"] = customPropName.value;
                val.push(arrObj);
              }
              curr = arrObj;
            } else if (val == null || overwrite) {
              curr = (curr[key] = {});
            } else {
              throw "path part `" + key + "` was not an object. Pass true as 'overwrite' parameter to perform this action ...";
            }
          });
          curr[targetProp] = value;
        } else if (targetProp !== "") {
          object[targetProp] = value;
        } else if (path === "" || path === undefined) {
          throw "path must be defined in setProp";
        }

        return curr;
      },

      /**
       * Sorts a table by column
       * @param {Array} arr Array
       * @param {Number} columnIndex Column index
       */
      sortTableByColumn: function (arr, columnIndex) {

        if (!arr) {
          throw "Array is empty";
        }

        columnIndex = columnIndex || 0;

        arr.sort(function (a, b) {
          if (a[columnIndex] === b[columnIndex]) {
            return 0;
          } else {
            return (a[columnIndex] < b[columnIndex]) ? -1 : 1;
          }
        });

        return arr;
      },

      /**
       * Converts a JavaScript array to a Java array
       * @param {Array} jsArray JavaScript array
       * @param {Object} params Parameters
       * @param {String} [params.javaType=java.lang.String] Java type
       * @return {java.lang.Array} Java array
       */
      toJavaArray: function (jsArray, params) {
        var javaArray, clazz, i, elem;

        jsArray = jsArray || [];

        params = params || {};
        params.javaType = params.javaType || "java.lang.String";
        clazz = java.lang.Class.forName(params.javaType);

        javaArray = java.lang.reflect.Array.newInstance(clazz, jsArray.length);
        for (i = 0; i < jsArray.length; i++) {
          elem = jsArray[i];
          if (params.type == "java.lang.String") {
            elem = new java.lang.String(elem);
          }
          javaArray[i] = elem;
        }

        return javaArray;
      },

      /**
       * @param {Array|String} element
       * @param {*} wc
       * @param {*} ignoreCase
       * @returns {RegExp}
       */
      toRegExp: function (element, wc, ignoreCase) {
        return (typeof element === "string"
          ? sol.common.ObjectUtils.stringToRegExp
          : sol.common.ObjectUtils.arrayToRegExp
        )(element, wc, ignoreCase);
      },

      /**
       *
       * @param {*} arr
       * @param {*} wc
       * @returns {RegExp}
       */
      arrayToRegExp: function (arr, wc) {
        var compl, len = arr.length,
            addBitwiseOR = function (query, index, length) {
              return ((index + 1 < length) && (query += "|")), query;
            };

        compl = arr.reduce(function (acc, str, i) {
          if (typeof str !== "string") {
            throw "filter: only string elements are allowed for filter criteria arrays";
          }
          return acc + addBitwiseOR("^" + str + "$", i, len);
        }, "");

        return new RegExp(compl.replace(new RegExp("\\" + wc, "g"), "." + wc));
      },

      /**
       * @param {*} str
       * @param {*} wc
       * @param {*} ignoreCase
       * @returns {RegExp}
       */
      stringToRegExp: function (str, wc, ignoreCase) {
        return new RegExp("^" + str.replace(new RegExp("\\" + wc, "g"), "." + wc), (ignoreCase ? "i" : ""));
      },

      /**
       * Traverses all nodes of an object tree
       * @param {Object} obj Object
       * @param {Function} func Function
       */
      traverse: function (obj, func) {
        var me = this,
            key;

        for (key in obj) {
          if (obj[key] != null) {
            func.apply(this, [key, obj[key]]);
            if (typeof (obj[key]) == "object") {
              me.traverse(obj[key], func);
            }
          }
        }
      }
    });

    sol.define("sol.common.mixins.ObjectFilter", {
      mixin: true,

      generateFilter: function (filter) {
        if (!Array.isArray(filter)) {
          throw "filter must be an array of objects!";
        }

        function isValidCriterion(criterion) {
          if ((typeof criterion !== "object") || (typeof criterion.prop !== "string" && criterion.prop) || ((typeof criterion.value !== "string") && (!Array.isArray(criterion.value)))) {
            throw "filter criterion is no object, or prop or value not suited for filtering";
          }
          return true;
        }

        return filter
          .filter(isValidCriterion)
          // clone is important because next function mutates criterion value
          .map(sol.common.ObjectUtils.clone)
          .map(function (criterion) {
            criterion.value = sol.common.ObjectUtils.toRegExp(criterion.value, "*");
            return criterion;
          });
      },

      matchObject: function (filter, obj) {
        function testProperty(currentFilter) {
          var propertyValue = sol.common.ObjectUtils.getProp(obj, currentFilter.prop);
          return currentFilter.value.test(propertyValue);
        }

        return (filter || [])
          .reduce(function (matches, currentFilter) {
            return matches && testProperty(currentFilter);
          }, true);
      }
    });

    //# sourceURL=lib_sol.common.ObjectUtils.js