    //@include lib_Class.js
    //@include lib_sol.common.Config.js
    //@include lib_sol.common.TranslateTerms.js
    //@include lib_sol.common.ObjectUtils.js
    //@include lib_sol.common.SordUtils.js
    //@include lib_sol.common.Template.js
    //@include lib_sol.common.WfUtils.js

    /*
     * @author ESt, ELO Digital Office GmbH
     * @version 1.03.000
     *
     * @elojc
     * @eloas
     * @eloix
     *
     * Injection Mixin
     *
     * You can use this mixin in business solution classes to load configurations
     * and apply templating to them using sord- or other data
     *
     * As this mixin calls the including classes' inject method, you have to define an inject
     * method in the including class.
     *
     * Usually, this mixin is accompanied by a solution-specific `configuration`-mixin, which
     * is responsible for describing the relationship between a config-name and the config repository path.
     * (see sol.hr.mixins.Configuration for an example configuration-description file)
     *
     * A typical usage of this mixin would be e.g. an indexserver-service.
     * ## Possible Injections
     * Injections have to be defined in the classes `inject`-property.
     *
     * ### Config Injection
     *
     * You can inject configs or a property of a config by using the `config` and `prop` keywords:
     *
     *     { config: "hr", prop: "entities.file.valueA", template: true }
     *
     * This would initialize the hr.config, read the property `valueA` and apply templating to it.
     * The `template` keyword is optional. You can enable it, if the config property contains a handlebars template to render it.
     * You may also define `emptyNonRendered: true`, which will set the handlebars template to an empty string, if it could not be rendered.
     * (Otherwise the handlebars template will still be the value of the injected string)
     *
     * Every injected config property injection which results in an object or array will be deep-copied.
     *
     * ### Sord Injection
     *
     * Injecting sords is possible by using the `sordId` and `flowId` keywords:
     *
     *     { sordId: "12345", flowId: "442381" }
     *
     * Usually the objId and flowId are values one does not know at compile time and must therefore be determined dynamically.
     * You can use
     *
     *     { sordIdFromProp: "objId", flowIdFromProp: "flowId" }
     *
     * You can use the optional setting to skip injecting a sord if no objId was found:
     *
     *     { sordIdFromProp: "objId", flowIdFromProp: "flowId", optional: true }
     *
     * to access a property stored in `me`/`this` (see extensive example below)
     *
     * If you define the property `includeBlobs`, the sord's FormBlobs will also we read.
     *
     * All sords will automatically be added to the data used for handlebars templating.
     * If you don't want to add it to the templating data, you can define the property `forTemplating` as false.
     *
     * ### JSON Injection
     *
     * You can inject a json-string, which will then be parsed, by using the `json` property.
     *
     *     { json: '{ "myProp": 12345123 }' }
     *
     * As with sordIdFromProp, you can use `jsonFromProp` to read the JSON from a variable.
     *
     * All parsed JSON will be added to the data used for handlebars templating.
     * If you don't want to add it to templating, set the `forTemplating` property to false.
     *
     * ### Property Injection (Inject into templating data)
     *
     * You can add any data local to the class (`me`/`this`) to the handlebars templating by defining
     *
     *     { prop: "params" }
     *
     * Usually, the injection mechanism would overwrite the original params if you define
     *
     *     params: { prop: "params" }
     *
     * ### Add to templating but don't inject
     *
     * You can disable injection by defining the property `dontInject` as true.
     * This way, the property will be added to templating but won't be injected.
     * This is only possible in Sord-, JSON- and Property-Injection.
     *
     * ### Disable logging of an injected value
     *
     * You can disable the logging of specific injections by defining the property
     * `log` as false. This may be useful for sensitive data. Instead of the value,
     * `N/A` will be shown in the logs.
     * Always doublecheck if the actual logging outputs reflect your wishes.
     *
     * ### Usage of `sol.common.mixins.Injector.SordToken`
     *
     * Use this token mixin to inject automatically sord injectorId by objId or source prop.
     * The mixin function will append either `{sord: { "sordIdFromProp": "objId"}}` or `{sord: { "prop": "source"}}`
     * to your `inject: {}` variable. It depends which params you have defined in your function context. me.objId will always
     * win when it is set. Otherwise it will use me.source to create the sordToken.
     * The token will only append when token (injectorId) is not already set.
     *
     *  sol.define("sol.hr.ix.actions.GetMyServiceResult", {
     *      extend: "sol.common.ix.ServiceBase",
     *
     *      mixins: ["sol.hr.mixins.Configuration",  "sol.common.mixins.Injector.SordToken", "sol.common.mixins.Injector.SordToken"],
     *  }
     *
     *
     * It is important that `sol.common.mixins.Injector.SordToken` is included first before `sol.common.mixins.Injector.SordToken` mechanismus is triggered!
     * It does not matter if the inject mechanism is called manually.
     *
     * ## Example
     *
     * This simple example reads a parameter from the config-file, applies a sord, a parameter and a translation to the template
     * and returns the value. Note: sords are retrieved with a normal user connection. If you need access to a sord via ixConnectAdmin,
     * you must checkout the sord manually in the initialize function and add it to the templating using the `prop`-option
     *
     *    /hr/Configuration/hr.config:
     *    {
     *      entities: { file: { valueA: "{{translate 'sol.hr.descr'}}{{PERSONNELFILE.objKeys.LASTNAME}}, {{params.name}}" } }
     *    }
     *
     *    /hr/All Rhino/lib_sol.hr.mixins.Configuration:
     *      //include lib_Class.js
     *
     *      sol.define("sol.hr.mixins.Configuration", {
     *        mixin: true,
     *
     *        $configRelation: {
     *          hr: "/hr/Configuration/hr.config",
     *          myOtherConfig: "/hr/Configuration/something.config",
     *          // load recruiting.config. if it is not found, load recruiting.fallback.config
     *          recruiting_in_hr: ["/recruiting/Configuration/recruiting.config", "/hr/Configuration/recruiting.fallback.config"]
     *        }
     *      });
     *
     *    note: this example implies that the parameters
     *      { objId: "12345", flowId: "441234", name: "Vertragsanpassung" }
     *    are passed to the service.
     *    //include lib_sol.hr.mixins.Configuration.js
     *    //include lib_sol.common.Injection.js
     *
     *    sol.define("sol.hr.ix.actions.GetMyServiceResult", {
     *      extend: "sol.common.ix.ServiceBase",
     *
     *      mixins: ["sol.hr.mixins.Configuration", "sol.common.mixins.Inject"],
     *
     *      inject: {
     *        myConfigValue: { config: "hr", prop: "entities.file.valueA", template: true }, // ""
     *        // templating data
     *        PERSONNELFILE: { sordIdFromProp: "objId", flowIdFromProp: "flowId" },
     *        params: { prop: "params" }
     *        // mySpecialSord: { prop: "myAdminAccessSord" }
     *      },
     *
     *      initialize: function (params) {
     *        var me = this;
     *        this.$super("sol.common.ix.ActionBase", "initialize", [params]);
     *        me.params = params;
     *        // me.myAdminAccessSord = me.getMySordViaAdminConnection();
     *      },
     *
     *      process: function () {
     *        var me = this;
     *        return me.myConfigValue;  // "Titel: Mayer, Vertragsanpassung"
     *      }
     *
     *  ## initializing config before initialize of calling class
     *
     *  If you need access to config values during initialize(), you can omit "sol.common.mixins.Inject" from the mixins array
     *  and call its constructor manually:
     *
     *      mixins: ["sol.hr.mixins.Configuration"],
     *      initialize: function (params) {
     *        var me = this;
     *        sol.create("sol.common.Injection").inject(me);  // sets up config including templating ...
     *
     *        me.tableTitle = me.dynkwl.tableTitle; // this is a config value we need before calling $super.initialize();
     *        me.$super("sol.hr.ix.dynkwl.MyIterator", "initialize", [config]);
     *      }
     *
     */
    sol.define("sol.common.mixins.Inject", {
      mixin: true,

      initialize: function () {
        var me = this;
        me.logger && me.logger.debug(["Initializing injection mixin."]);
        sol.create("sol.common.Injection").inject(me);
      }
    });

    sol.define("sol.common.mixins.Injection.SordToken", {
      mixin: true,
      initialize: function (config) {
        var me = this, sordInjector;

        sordInjector = config.objId
          ? { sord: { sordIdFromProp: "objId" } }
          : { sord: { prop: "source" } };

        // We can pass source as templateSord directly to the function
        // in this case we dont want to checkout the sord again
        // so we provide the templateSord object as sord to handlebar.
        // That means we can use either objId or templateSord object to inject the
        // sord object to the handlebar context and resolve templates in configs
        sol.common.InjectionUtils.addInjections(me, sordInjector);
      }
    });

    sol.define("sol.common.Injection", {
      /**
       * @private
       * used in logging messages if a string if masked with ifLog() (injection.log===false)
       */
      noLogTxt: "N/A",

      /**
       * returns `any` if `log` is not false. Otherwise returns `me.noLogTxt`.
       */
      ifLog: function (log, any) {
        var me = this;
        return (log !== false) ? any : me.noLogTxt;
      },

       /**
       * retrieves template sord of objId/guid & flowId
       * make sure the objId/guid exists first!
       * @param {String} objId GUID/ObjId of sord to perform checkout on
       * @param {String} flowId used by getTemplateSord to retrieve wfMapKeys and formblobs
       * @return {TemplateSord} returns the requested templateSord
       */
      getSordData: function (objId, flowId, asAdmin, formBlobs) {
        var me = this;
        me.logger.debug(["Retrieving sord-data"]);
        return sol.common.WfUtils.getTemplateSord(
          ((asAdmin && typeof ixConnectAdmin !== "undefined") ? ixConnectAdmin : ixConnect).ix().checkoutSord(objId, SordC.mbAllIndex, LockC.NO),
          flowId || me.flowId,
          { asAdmin: asAdmin, formBlobs: formBlobs }
        ).sord;
      },

      initConfig: function (configName, classContext) {
        var me = this, configPaths, configLoaded;
        me.logger.debug(["Reading configuration because injection requires access to configuration `{0}`. See further log for the actual config-property injection.", configName]);
        if (me.typeOf(classContext.$configRelation, "object")) {
          configPaths = classContext.$configRelation[configName];
          if (configPaths && (Array.isArray(configPaths) || (configPaths = [configPaths]))) {
              configLoaded = configPaths.some(function (path) {
                try {
                  classContext.$configs[configName] = (sol.create("sol.common.Config", { compose: path, copy: true })).config;
                } catch (_) {}
                return !!classContext.$configs[configName];
              });
              if (!configLoaded) {
                throw "Config at path " + configPaths[0] + "was not found!";
              }
            me.logger.debug(["Configuration `{0}` loaded.", configName]);
          } else {
            throw "$configRelation of configuration-mixin did not contain a configuration-path for config `" + configName + "`.";
          }
        } else {
          throw "$configRelation is not defined or not a javascript object. Please make sure to include a configuration mixin in the calling class.";
        }
      },

      injectConfig: function (config, injectId, classContext) {
        var me = this, configName = String(config.config);
        me.logger.debug(["Setup configuration `{0}` for injection `{1}`.", configName, injectId]);
        classContext.$configs = classContext.$configs || {};
        classContext.$configs[configName] || me.initConfig(configName, classContext);
        me.logger.debug("Adding config-property injection object to backlog for subsequent injection", me.ifLog(config.log, config));
        me.$configInjections.push(config);
      },

      injectSordById: function (sordConfig, injectId, classContext) {
        var me = this, sordData,
            sordId = sordConfig.sordId || sol.common.ObjectUtils.getProp(classContext, sordConfig.sordIdFromProp),
            flowId = sordConfig.flowId || sol.common.ObjectUtils.getProp(classContext, sordConfig.flowIdFromProp);

        if (!sordId && sordConfig.optional) {
          me.logger.debug("Optional sord was not found and therefore not injected. Continuing ...");
          return;
        }
        me.logger.debug(["Injecting sord-data of injection `{0}` by id `{1}` using flowId `{2}`.", injectId, me.ifLog(sordConfig.log, sordId), me.ifLog(sordConfig.log, flowId || me.flowId)]);
        sordData = me.getSordData(sordId, flowId, false, (sordConfig.includeBlobs === true));
        if (sordConfig.forTemplating !== false) {  // is added to templating as default
          me.logger.debug("Adding retrieved sord-data to templating-data.");
          classContext.$templatingData = classContext.$templatingData || {};
          classContext.$templatingData[injectId] = sordData;  // also add sord to templating
        } else {
          me.logger.debug("Sord-data has been retrieved but has not been added to templating-data.");
        }
        return sordConfig.dontInject ? undefined : sordData;
      },

      injectJSON: function (json, injectId, classContext) {
        var me = this, result;
        me.logger.debug(["Parsing JSON of injection `{0}`.", injectId]);
        result = JSON.parse(me.typeOf(json.json, "string") ? String(json.json) : sol.common.ObjectUtils.getProp(classContext, json.jsonFromProp));
        if (json.forTemplating !== false) {  // is added to templating as default
          me.logger.debug("Adding parsed JSON to templating-data.");
          classContext.$templatingData = classContext.$templatingData || {};
          classContext.$templatingData[injectId] = result;  // also add data to templating
        } else {
          me.logger.debug("JSON has been parsed but has not been added to templating-data.");
        }
        return json.dontInject ? undefined : result;
      },

      injectFromThis: function (prop, injectId, classContext) {
        var me = this, result, propType;
        me.logger.debug(["Reading property of class-context as defined in injection `{0}`.", injectId]);
        result = sol.common.ObjectUtils.getProp(classContext, String(prop.prop));
        propType = me.typeOf(result);
        me.logger.debug("Property value read", me.ifLog(prop.log, result));
        if ((propType === "object") || (propType === "array")) {
          me.logger.debug("The value is an object or an array. It will be cloned to minimize sideeffects");
          result = me.copyConfig(result);
        }
        if (prop.forTemplating !== false) {  // is added to templating as default
          me.logger.debug("Adding value to templating-data");
          classContext.$templatingData = classContext.$templatingData || {};
          classContext.$templatingData[injectId] = result;  // also add data to templating
        } else {
          me.logger.debug("Property has been read but has not been added to templating-data.");
        }
        return prop.dontInject ? undefined : result;
      },

      injectConfigProperty: function (config, classContext) {
        var me = this, prop, propType;
        me.logger.debug(["Reading config-property `{0}` from configuration `{1}`.", config.prop, config.config]);
        prop = sol.common.ObjectUtils.getProp(classContext.$configs[config.config], config.prop);
        propType = me.typeOf(prop);
        me.logger.debug("Configuration value read", me.ifLog(config.log, prop));
        if ((propType === "object") || (propType === "array")) {
          me.logger.debug("The value is an object or an array. It will be cloned to minimize sideeffects");
          prop = me.copyConfig(prop);
        }
        return prop;
      },

      copyConfig: function (obj) {
        return JSON.parse(JSON.stringify(obj, function (_, val) {
          return (val && val.getClass) ? String(val) : val;
        }));
      },

      performInjection: function (injection, injectionId, classContext) {
        var me = this, injectionFct;
        me.logger.debug(["Deciding on injection mechanism for injection `{0}`", injectionId]);
        if ((me.typeOf(injection.config, "string") && String(injection.config)) && (me.typeOf(injection.prop, "string"))) {
          injectionFct = me.injectConfig; // setup config
        } else if ((injection.sordId || injection.sordIdFromProp) && (injection.sordId + "")) {
          injectionFct = me.injectSordById; // injects a sord by passed Id
        } else if (me.typeOf(injection.json, "string") || (me.typeOf(injection.jsonFromProp, "string") && String(injection.jsonFromProp))) {
          injectionFct = me.injectJSON; // injects data parsed from JSON
        } else if (me.typeOf(injection.prop, "string") && String(injection.prop)) {
          injectionFct = me.injectFromThis; // injects data from this to this (basically only used to expose data to templating)
        } else {
          throw "Injection `" + injectionId + "` did not match any injection mechanism or  the mechanism configuration is incomplete. (possible mechanisms: config, sordId, json, prop, ...)";
        }

        injection.injectId = injectionId;
        injection.template && me.$injectionsToTemplate.push(injection);

        return injectionFct && injectionFct.call(me, injection, injectionId, classContext);
      },

      /**
       * injects data into a class `classContext`
       * @param {InitializedClassContext} classContext me/this of the class to inject data into.
       */
      inject: function (classContext) {
        var me = this, injections = classContext.inject, injectionResult;
        me.$configInjections = [];
        me.$injectionsToTemplate = [];
        me.typeOf = sol.common.ObjectUtils.type;
        me.logger.enter("sol.common.Injection.inject");
        me.logger.debug("Running inject on class-context");
        if (me.typeOf(injections, "object")) {
          me.logger.debug("Analyzing injections", injections);
          Object.keys(injections).forEach(function (injectionId) {
            var injection = injections[injectionId];
            me.logger.debug(["Testing injection `{0}`", injectionId]);
            if (me.typeOf(injection, "object")) {
              me.logger.debug(["Acting on injection `{0}`", injectionId]);
              injectionResult = me.performInjection(injection, injectionId, classContext);
              // every function will return its result which will then be injected. only config properties don't return & will be injected later
              if (injectionResult !== undefined) {
                classContext[injectionId] = injectionResult;
                me.logger.debug(["Injection `{0}` has been injected into the class context. Value", injectionId], me.ifLog(injection.log, injectionResult));
              } else {
                me.logger.debug(["Injection value of injection `{0}` is undefined. This may be ok, if configuration was read or you defined `dontInject`", injectionId]);
              }
            } else {
              throw "Injecting `" + injectionId + "` failed. The property value is not a javascript object.";
            }
          });

          me.logger.debug("Inject config properties from backlog");
          me.$configInjections.forEach(function (injection) {
            classContext[injection.injectId] = me.injectConfigProperty(injection, classContext);
          });

          me.$injectionsToTemplate.forEach(function (injection) {
            if (injection.template) {
              me.logger.debug("Applying template to injected value. Value before templating", me.ifLog(injection.log, classContext[injection.injectId]));
              classContext[injection.injectId] = sol.common.TemplateUtils.render(classContext[injection.injectId], classContext.$templatingData, { emptyNonRendered: !!injection.emptyNonRendered });
              me.logger.debug("Value after templating", me.ifLog(injection.log, classContext[injection.injectId]));
            }
          });
          me.logger.debug("All injections have been performed.");
        } else {
          throw "No injections defined. `inject` property value must be a javascript object. Type was: `" + me.typeOf(injections) + "`";
        }
        me.logger.exit("sol.common.Injection.inject");
      }
    });

    sol.define("sol.common.InjectionUtils", {
      singleton: true,

      /**
       * Append all injection keys to `context.inject` when the key does not already exist
       * @param {*} context
       * @param {*} injections
       */
      addInjections: function (context, injections) {
        Object.keys(injections)
        .filter(function (key) {
          return !context.inject[key];
        })
        .forEach(function (key) {
          context.inject[key] = injections[key];
        });
      }

    });