    //@include lib_Class.js

    /**
     * @class sol.common.IxUtils
     * @extends sol.Base
     * @singleton
     *
     * This class provides basic functionality for ix operations.
     *
     * @author NM, ELO Digital Office GmbH
     * @version 1.03.006
     *
     * @eloall
     */
    sol.define("sol.common.IxUtils", {
      singleton: true,

      CONST: {
        EVENT_TYPES: {
          ACTION: "ACTION",
          REFRESH: "REFRESH",
          GOTO: "GOTO",
          SELECT: "SELECT",
          VIEW: "VIEW",
          DIALOG: "DIALOG",
          ERROR: "ERROR",
          FEEDBACK: "FEEDBACK"
        },
        COMPONENTS: {
          IX: "IX",
          AS: "AS"
        }
      },

      /**
       * Executes an ix registered function thanks to its name.
       * Handles the transformation of ix Any-Objects.
       *
       * Please note that executing Index Server calls in Web Applications must be done asynchronously doe to performance
       * reasons.
       *
       * Example for Rhino engine scripts (sync) (IX, AS, Java Client)
       *
       *     var result = sol.common.IxUtils.execute('RF_FunctionName', {
       *       objId: '1'
       *     });
       *
       * Example for ELO Web Client & ELO Apps (async)
       *
       *     var result = sol.common.IxUtils.execute('RF_FunctionName', {
       *       objId: '1'
       *     }, function (result) {
       *       // success
       *     }, function () {
       *       // error
       *     };
       *
       * # Performance optimizatios in ELO Apps (since ELOwf 10.01.002)
       *
       * ELO Apps allow establishing IX connections without loading the IXClient.js file.
       * This is handled by the module elo.module.session.light` and will lead to a performance
       * boost at startup time. Keep in mind that only Registered Function calls are supported.
       *
       * # Use custom IndexServer connection
       *
       * You can use your own ix connection object
       *
       *    var result = sol.common.IxUtils.execute("RF_FunctionName",
       *        {
       *          objId: 1
       *        },
       *        null,
       *        null,
       *        {
       *          connection: customConnection
       *        }
       *      )
       *
       * Hint: Using custom connection object is only supported in rhino
       *
       * @param {String} funcName
       * @param {Object} paramObj
       * @param {Function} successFct success handler. required if executing scripts in the Web Client
       * @param {Function} failureFct error handler. required if executing scripts in the Web Client
       * @param {Object} proxyCfg proxy configuration
       * @param {Object} proxyCfg.connection Function should be called with these connection instead of default connection.
       *
       *
       * @return {Object} jsonResult only returned if call is sync.
       */
      execute: function (funcName, paramObj, successFct, failureFct, proxyCfg) {
        var proxy, result;

        proxy = sol.create("sol.common.IxUtils.Proxy", {
          fctName: funcName,
          params: paramObj,
          successFct: successFct,
          failureFct: failureFct,
          connection: (proxyCfg || {}).connection
        });
        result = proxy.execute();

        return result;
      },

      /**
       * @protected
       * Wraps ixUtils.execute for optimized execution
       * The RF must implement an optimization mechanism which returns an optimization id if called with `optimize:true`
       * and uses the optimization, also returning the id, if passed said optimization id as `optimize:<optimizationId>`.
       * (Worst case, if the RF does not implement the mechanism the RF is executed as usual)
       * An optimization id can be any number/string, but it should be unique. An example for such an RF is sol.common.service.SordProvider
       * @param {String} rf
       * @param {Object} config params for RF
       * @param {Object} cacheObject should be a persistent javascript object in your calling class. Will hold optimization ids.
       * @param {String} optimization (optional) give an optimization a name. This must be used, if optimizedExecute is called
       * @param {String[]} excludeFromConfig (optional) define multiple config properties which will be set to undefined if optimization is available
       * @return {Object} whatever the RF returns
       */
      optimizedExecute: function (rf, config, cacheObject, optimization, excludeFromConfig) {
        var result;
        try {
          config = config ? JSON.parse((sol.common.JsonUtils ? sol.common.JsonUtils.stringifyQuick : JSON.stringify)(config)) : {};
        } catch (_e) {
          throw "optimizedExecute of `" + rf + "`: config is not valid JSON. (Hint: Make sure the config-object does contain any java-strings.)";
        }
        try { // get optimization from cache
          (config.optimize = cacheObject[optimization || (optimization = "_")])
            && excludeFromConfig.forEach(function (excl) { // exclude unnecessary properties from config
              config[excl] = undefined;
            });
        } catch (_e) {}
        config.optimize || (config.optimize = true); // add optimize === true if optimization run for first time
        return (cacheObject[optimization] = (result = sol.common.IxUtils.execute(rf, config)).optimization), result; // store optimization ID in cache
      },

      /**
       * @private
       * @since 1.04.000
       * @param {String} currentVersionString
       * @param {String} requiredVersionString
       * @return {Boolean}
       */
      checkVersion: function (currentVersionString, requiredVersionString) {
        var result = true,
            currentRegex, requiredRegex, currentVersionMatch, requiredVersionMatch, currentPart, requiredPart;

        currentRegex = /([0-9]+(\\.[0-9]+)*)/g;
        requiredRegex = /([0-9]+(\\.[0-9]+)*)/g;
        currentVersionMatch = currentRegex.exec(currentVersionString);
        requiredVersionMatch = requiredRegex.exec(requiredVersionString);

        while (requiredVersionMatch !== null) {
          currentPart = (currentVersionMatch) ? parseInt(currentVersionMatch[0], 10) : 0;
          requiredPart = parseInt(requiredVersionMatch[0], 10);
          if (requiredPart > currentPart) {
            result = false;
            break;
          } else if (requiredPart < currentPart) {
            result = true;
            break;
          }
          currentVersionMatch = currentRegex.exec(currentVersionString);
          requiredVersionMatch = requiredRegex.exec(requiredVersionString);
        }

        return result;
      }

    });

    /**
     * @private
     * @since 1.04.000
     */
    sol.define("sol.common.IxUtils.Proxy", {

      MIN_VERSION_WF_SESSION_LIGHT: "10.02.000",

      initialize: function (config) {
        var me = this;

        me.fctName = config.fctName;

        me.connection = config.connection;

        me.successFct = config.successFct;
        me.failureFct = config.failureFct;

        me.params = config.params || {};
        if ((typeof ixConnectAdmin != "undefined") && ixConnectAdmin.loginResult && ixConnectAdmin.loginResult.clientInfo) {
          // pass admin ticket if called in rhino environments that have administration privileges.
          me.params.adminTicket = String(ixConnectAdmin.loginResult.clientInfo.ticket);
        }

        me.isWebClient = me.isWebClient();
        me.isEloApp = me.isEloApp();
        me.isEloWf = me.isEloWf();
        me.isWebApp = (me.isWebClient || me.isEloApp || me.isEloWf);
        me.isRhino = me.isRhino();

        if (!me.isRhino && me.connection) {
          throw Error("Custom connection is supported only in rhino engine");
        }

        if (me.isEloApp) {
          me.isSessionLight = !api.IX;
        } else if (me.isEloWf) {
          // only use session light in forms if called asynchronous to ensure backwards compatibiliy
          me.isSessionLight = me.successFct && me.wfSupportsSessionLight();
        }
      },

      isWebClient: function () {
        return (typeof api !== "undefined") && api.Webclient && api.Webclient.getIXConnection;
      },

      isEloApp: function () {
        return (typeof api !== "undefined") && api.webapps && api.webapps.WebApp;
      },

      isEloWf: function () {
        return (typeof elo !== "undefined") && elo.appInfo && elo.appInfo.application && elo.appInfo.application.name === "ELOwf";
      },

      isRhino: function () {
        var me = this;
        return (typeof ixConnect !== "undefined") && !(me.isWebClient || me.isEloApp || me.isEloWf);
      },

      wfSupportsSessionLight: function () {
        var me = this;
        return (typeof ELOWF_VERSION !== "undefined") ? sol.common.IxUtils.checkVersion(ELOWF_VERSION, me.MIN_VERSION_WF_SESSION_LIGHT) : false;
      },

      getIXConnection: function () {
        var me = this;
        if (me.isWebClient) {
          return api.Webclient.getIXConnection();
        }
        if (me.isEloApp) {
          return api.IX;
        }
        if (me.isEloWf) {
          return elo.IX;
        }
        if (me.isRhino) {
          // use passed ix connection instead of default ixConnect object
          return me.connection || ixConnect;
        }
      },

      getAny: function () {
        var me = this,
            any = me.params;
        if (!me.isSessionLight) {
          any = (typeof Any !== "undefined") ? new Any() : new de.elo.ix.client.Any();

          //TODO correct, as soon as ELOas gets CONST variable
          any.type = (typeof CONST !== "undefined") ? CONST.ANY.TYPE_STRING : ((typeof ixConnect !== "undefined") ? ixConnect.CONST.ANY.TYPE_STRING : elo.CONST.ANY.TYPE_STRING);
          any.stringValue = sol.common.JsonUtils ? sol.common.JsonUtils.stringifyAll(me.params) : JSON.stringify(me.params);
        }
        return any;
      },

      execute: function () {
        var me = this;

        if (!me.fctName) {
          throw "IllegalStateException: no function name set";
        }

        if (!me.params) {
          throw "IllegalStateException: no parameters set";
        }

        if (me.isWebApp && me.isSessionLight) {
          // webapp with session light mode (currently ELOwf and ELOapp)
          if (!me.successFct) {
            throw me.$className + ": synchronous ix functions calls are unsupported of running web apps and forms in session light mode.";
          }
          me.callAsyncSessionLight();
        } else if (me.isWebApp) {
          // webapp without session light mode
          if (!me.successFct) {
            console.warn(me.$className + ": synchronous ix functions calls should be used with care due to performance reasons. It is recommended to use function handlers instead.");
            return me.callSync();
          }
          me.callAsync();
        } else {
          // Rhino
          if (me.successFct) {
            throw "IllegalStateException: " + me.$className + ": async ix functions calls are currently not supported on the Rhino engine.";
          }
          return me.callSync();
        }

      },

      callSync: function () {
        var me = this,
            conn = me.getIXConnection(),
            anyResult, jsonResult;

        if (!conn) {
          if (me.isEloWf) {
            throw "IllegalStateException: " + me.$className + ": IX session not available. Use sol.common.forms.Utils.initializeIxSession.";
          } else {
            throw "IllegalStateException: IX connection not initialized";
          }
        }

        anyResult = conn.ix().executeRegisteredFunction(me.fctName, me.getAny());
        jsonResult = (anyResult && anyResult.stringValue) ? String(anyResult.stringValue) : "{}";

        return JSON.parse(jsonResult);
      },

      callAsync: function () {
        var me = this,
            conn = me.getIXConnection();

        if (!conn) {
          if (me.isEloWf) {
            // check if 'sol.common.forms.Utils' is included and initialize IX connection
            if (sol.common.forms && sol.common.forms.Utils && sol.common.forms.Utils.initializeIxSession) {
              sol.common.forms.Utils.initializeIxSession(function () {
                me.callAsync();
              });
              return;
            } else {
              throw "IllegalStateException: " + me.$className + ": can not initialize IX session. Include sol.common.forms.Utils.initializeIxSession.";
            }
          } else {
            throw "IllegalStateException: IX connection not initialized";
          }
        }

        conn.ix().executeRegisteredFunction(me.fctName, me.getAny(), new de.elo.ix.client.AsyncCallback(function (successAnyResult) {
          // sucess
          var successJsonResult = (successAnyResult && successAnyResult.stringValue) ? String(successAnyResult.stringValue) : "{}";
          me.successFct.call(me, JSON.parse(successJsonResult));
        }, function (ex) {
          // failure
          if (me.failureFct) {
            me.failureFct.call(me, ex);
          }
        }));
      },

      callAsyncSessionLight: function () {
        var me = this;

        if (me.isEloWf) {
          if (sol.common.forms && sol.common.forms.Utils && sol.common.forms.Utils.callRegisteredFunction) {
            sol.common.forms.Utils.callRegisteredFunction(me.fctName, me.params, me.successFct, function (errorObj) {
              // failure
              var ex = {},
                  error;
              if (!!errorObj) {
                if (errorObj.constructor === String) {
                  try {
                    error = JSON.parse(errorObj);
                    ex.msg = error.error.message;
                    ex.code = error.error.code;
                  } catch (e) {
                    ex.msg = errorObj;
                  }
                } else {
                  ex.msg = errorObj.error.message;
                  ex.code = errorObj.error.code;
                }
              }
              if (me.failureFct) {
                me.failureFct.call(me, ex);
              }
            });
          } else {
            throw "'sol.common.forms.Utils' not included or do not support session light for ELOwf";
          }
        } else if (me.isEloApp) {
          api.rest.RestUtils.callRegisteredFunction(me.fctName, me.params, me.successFct, function (errorObj) {
            // failure
            var ex = {},
                error;
            if (!!errorObj) {
              if (errorObj.constructor === String) {
                try {
                  error = JSON.parse(errorObj);
                  ex.msg = error.error.message;
                  ex.code = error.error.code;
                } catch (e) {
                  ex.msg = errorObj;
                }
              } else {
                ex.msg = errorObj.error.message;
                ex.code = errorObj.error.code;
              }
            }
            if (me.failureFct) {
              me.failureFct.call(me, ex);
            }
          });
        } else {
          throw "Session light only supported in ELOwf and ELOapps";
        }
      }

    });

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