    importPackage(Packages.de.elo.ix.client.feed);

    //@include lib_Class.js

    /**
     * This class helps in creating simplified instances of de.elo.ix.client.*-objects like `Sord`. Simplified objects are
     * mostly used by sol.common.Template or sol.common.ix.functions.JsonDataCollector. Formatters are used by solutions if large
     * amounts of data are loaded from ELO or if properties needs to be accessible in templating environments.
     *
     * There are several formatter implementations for formatting objects e.g. `sol.common.ObjectFormatter.TemplateSord`.
     * Core formatting operations like objKey filtering, etc. is done by a base class
     * e.g. `sol.common.ObjectFormatter.BaseSord` which is the superclass of `TemplateSord` implementation.
     *
     *     var formattedObject = sol.common.ObjectFormatter.format({
     *       sord: {
     *         formatter: 'sol.common.ObjectFormatter.{FORMATTER CLASS NAME}',
     *         // original object returned by ix
     *         data: {IX OBJECT},
     *         config: {
     *           // formatter configuration as defined by the formatters base class
     *         }
     *     });
     *
     * Following list of Base classes can be used in order to process `Sords`, `Tasks` and keyword lists.
     *
     * - sol.common.ObjectFormatter.BaseSord
     * - sol.common.ObjectFormatter.BaseTask
     *
     * # Example usage by templates
     *
     * Templates can't access objKeys stored in array values in an easy way.
     *
     *     var data = sol.common.ObjectFormatter.format({
     *       sord: {
     *         formatter: 'sol.common.ObjectFormatter.TemplateSord',
     *         // instance of de.elo.ix.client.Sord
     *         data: sord
     *       }
     *     });
     *
     * The transformed object results as followed.
     *
     *     data = {
     *       "id": "7572",
     *       "maskName": "Incoming invoice",
     *       "name": "Invoice 0000",
     *       "IDateIso": "20150720142400",
     *       "XDateIso": "",
     *       "objKeys": {
     *         "VENDOR_NAME": "Weiler KG",
     *         "INVOICE_DATE": "20150601162415",
     *         "INVOICE_CASH_DISCOUNT_AMOUNT": 554
     *       }
     *     }
     *
     * # Example usage for statistical processing
     *
     * If working with large amounts of data it is mandatory to define the properties required for the data analys in order
     * to reduce the amount of data loaded from the client. IndexServer Sord-Objects contain a lot of information that might
     * not be required for the analysis. sol.common.ObjectFormatter.StatisticSord allows defining the sord keys, objKeys,
     * ... information that is required.
     *
     * Please note that using map keys is more expensive than storing values in objKeys since accessing these values requires
     * additional ix checkout calls. Furthermore objKeys can be converted to numeric values automatically.
     *
     * This functionality is used by services while collecting data for dashboard apps
     * developed in angular js.
     *
     *     var data = sol.common.ObjectFormatter.format({
     *       sord: {
     *         formatter: 'sol.common.ObjectFormatter.StatisticSord',
     *         // instance of de.elo.ix.client.Sord
     *         data: sord,
     *         config: {
     *           sordKeys: ['id', 'maskName', 'name', 'IDateIso', 'XDateIso'],
     *           objKeys: ['VENDOR_NAME', 'INVOICE_DATE', 'INVOICE_CASH_DISCOUNT_AMOUNT']
     *         }
     *     });
     *
     * The transformed object results as followed.
     *
     *     data = {
     *       "id": "7572",
     *       "maskName": "Incoming invoice",
     *       "name": "Invoice 0000",
     *       "IDateIso": "20150720142400",
     *       "XDateIso": "",
     *       "O_VENDOR_NAME": "Weiler KG",
     *       "O_INVOICE_DATE": "20150601162415",
     *       "O_INVOICE_CASH_DISCOUNT_AMOUNT": 554,
     *     }
     *
     *
     *
     * @author Michael Weiler, ELO Digital Office GmbH
     * @version 1.0
     *
     * @eloix
     * @elojc
     * @eloas
     */
    sol.define("sol.common.ObjectFormatter", {

      singleton: true,

      format: function (config) {
        var me = this, result = {},
            partName;
        config = config || {};
        for (partName in config) {
          me.formatPart(partName, config[partName], result);
        }
        return result;
      },

      /**
       * @private
       * @param {String} partName
       * @param {Object} configPart
       * @param {Object} result
       */
      formatPart: function (partName, configPart, result) {
        var formatter,
            data = configPart.data;

        if (data.getClass && data.getClass().isArray()) {
          data = Array.prototype.slice.call(data);
        }

        if (Array.isArray(data)) {
          result[partName] = [];
          data.forEach(function (dataPart) {
            formatter = sol.create(configPart.formatter, { data: dataPart, config: configPart.config });
            result[partName].push(formatter.build());
          });
        } else {
          formatter = sol.create(configPart.formatter, { data: configPart.data, config: configPart.config });
          result[partName] = formatter.build();
        }
      }
    });

    /**
     * Represents limited data of an de.elo.ix.client.Sord object.
     *
     * This class implements limiting objKeys, sord keys and map keys. Please refer to sub classes for more information on the
     * usage.
     *
     * @author Michael Weiler, ELO Digital Office GmbH
     * @author Nils Mosbach, ELO Digital Office GmbH
     * @version 1.0
     *
     * @eloix
     * @elojc
     * @eloas
     */
    sol.define("sol.common.ObjectFormatter.BaseSord", {

      /**
       * @cfg {Array} sordKeys
       * Names of the properties that should be included.
       * The ID and the mask name are included by default.
       */
      sordKeys: ["guid", "id", "maskName"],

      /**
       * @cfg {Number} descMaxLen
       * Maximum length of the sord description
       */
      descMaxLen: undefined,

      /**
       * @cfg {Array} objKeys
       * Key names of the object keys that should be included.
       */
      objKeys: [],

      /**
       * @cfg {Boolean} hiddenObjKeys
       * Key names of the hidden object keys that should be included.
       */
      hiddenObjKeys: undefined,

      /**
       * @cfg {Array} mapKeys
       * Map field names of the map fields that should be included.
       */
      mapKeys: undefined,

      /**
       * @cfg {Array} wfMapKeys
       * Map field names of the workflow map fields that should be included.
       */
      wfMapKeys: undefined,

      /**
       * @cfg {Array} formBlobs
       * Form-Blob field names of the Form-Blob-fields that should be included.
       */
      formBlobs: undefined,

      /**
       * @cfg {Boolean} allObjKeys
       * Set to true if all objKeys should be included. objKeys[] configuration is ignored in this case.
       */
      allObjKeys: false,

      /**
       * @cfg {Boolean} allMapFields
       * Set to true if all map fields should be included. mapKeys[] configuration is ignored in this case.
       */
      allMapFields: false,

      /**
       * @cfg {Boolean} allFormBlobFields
       * Set to true if all Form-Blob fields should be included. formBlobs[] configuration is ignored in this case.
       */
      allFormBlobFields: false,

      /**
       * @cfg {String} objKeyPrefix
       * Defines a prefix for objKey field names.
       */
      objKeyPrefix: "",

      /**
       * @cfg {String} hiddenObjKeyPrefix
       * Defines a prefix for hidden objKey field names.
       */
      hiddenObjKeyPrefix: "",

      /**
       * @cfg {String} objKeyPrefix
       * Defines a prefix for mapKey field names.
       */
      mapKeyPrefix: "",

      /**
       * @cfg {String} formBlobPrefix
       * Defines a prefix for formBlob field names.
       */
      formBlobPrefix: "",

      /**
       * @private
       * @cfg {String} feedActions
       * This is an experimental feature. Don't rely on its existance.
       * Set to true if feed actions of this sord should be collected as well.
       */
      feedActions: false,

      /**
       * @private
       * @cfg {de.elo.ix.client.feed.EActionType[]} list of feed action types
       * This is an experimental feature. Don't rely on its existance.
       * Define a list of Feed Action types that should be returned.
       */
      feedActionTypes: [],

      /**
       * @private
       * @cfg {Number} maximum amount of actions that are read
       * This is an experimental feature. Don't rely on its existance.
       * The amount of feed actions that is returned by FindFirstActions.
       */
      feedActionsMax: 500,

      /**
       * @property {Boolean} flatObjStructure
       * Use a flat object structure. If true, objKeyPrefix and mapKeyPrefix must be set in order to prevent overriding sord keys.
       *
       * Default. flatObjStructure = false, objKeyPrefix ""
       *
       *     {
       *       name: "my invoice",
       *       objKeys: {
       *         INVOICE_NUMBER: "1234#2"
       *       }
       *     }
       *
       * Flat structure with objKeyPrefix "O_"
       *
       *     {
       *       name: "my invoice",
       *       O_INVOICE_NUMBER: "1234#2"
       *     }
       */
      flatObjStructure: false,

      pilcrow: "\u00b6",

      actionTypeMappings: {
        AutoComment: EActionType.AutoComment,
        Released: EActionType.Released,
        SordCreated: EActionType.SordCreated,
        UserComment: EActionType.UserComment,
        VersionCreated: EActionType.VersionCreated,
        WorkVersionCreated: EActionType.WorkVersionCreated,
        WorkVersionSwitched: EActionType.WorkVersionSwitched
      },

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

        me._cacheMaskObjKeys = {};

        me.$super("sol.Base", "initialize", [params.config]);

        if (me.sordKeys.length === 0) {
          me.sordKeys.push("id");
        }

        if (!me.objKeys) {
          me.objKeys = [];
        }

        me.sord = params.data;

        me.feedActionTypes = me.normalizeFeedActionTypes(me.feedActionTypes);
      },

      /**
       * Builds the base representation of the ELO object. This function is called by sol.common.ObjectFormatter#format
       * @param {de.elo.ix.client.Sord} originalSord
       * @return {Object} formattedSord
       */
      build: function (originalSord) {
        var me = this,
            sord, maskName, formattedSord;
        me.logger.enter("build", arguments);
        sord = originalSord || me.sord;
        maskName = String(sord.maskName);
        formattedSord = me.initialiseFormattedSord();

        // prepare cache for mask that allows fast iterations over required objKeys.
        me.buildCacheMaskObjKeys(maskName, sord.objKeys);

        me.applySordKeys(formattedSord, sord);
        me.applyObjKeys(formattedSord, maskName, sord.objKeys);
        me.applyMapData(formattedSord, sord);
        me.flowId && me.applyWfMapData(formattedSord, sord, me.flowId);
        me.applyFormBlobData(formattedSord, sord.id);
        me.applyFeedActions(formattedSord, sord);

        me.logger.exit("build", formattedSord);
        return formattedSord;
      },

      /**
       * @private
       * @param {Object} toObj
       * @param {de.elo.ix.client.Sord} sord
       */
      applySordKeys: function (toObj, sord) {
        var me = this,
            key, value, i, length;

        for (i = 0, length = me.sordKeys.length; i < length; i++) {
          key = me.sordKeys[i];
          value = String(sord[key]);
          if ((key == "desc") && me.descMaxLen && value && (value.length > me.descMaxLen)) {
            value = value.substr(0, me.descMaxLen - 3) + "...";
          }
          toObj[key] = value;
        }
      },

      /**
       * @private
       * @param {Object} toObj
       * @param {String} maskName
       * @param {de.elo.ix.client.ObjKey[]} objKeys
       */
      applyObjKeys: function (toObj, maskName, objKeys) {
        var me = this,
            objKeyCache = me._cacheMaskObjKeys[maskName],
            objKey, i, length, dataArr,
            cachePos, objKeyName;

        for (i = 0, length = objKeyCache.length; i < length; i++) {
          cachePos = objKeyCache[i];
          if (cachePos && cachePos.arrPos < objKeys.length
              && objKeys[cachePos.arrPos].id === cachePos.id) {
            objKey = objKeys[cachePos.arrPos];
          } else {
            continue;
          }

          if (objKey) {
            dataArr = Array.prototype.slice.call(objKey.data);
            me.applyObjKey(toObj, me.objKeyPrefix, objKey.name, String(dataArr.join(me.pilcrow)));
          }
        }

        if (me.hiddenObjKeys) {
          for (i = objKeys.length - 1; i >= 0; i--) {
            objKey = objKeys[i];
            if (objKey.id < DocMaskLineC.MIN_ID_HIDDEN_VALUE) {
              break;
            }
            objKeyName = objKey.name + "";
            if (me.hiddenObjKeys.indexOf(objKeyName) > -1) {
              me.applyObjKey(toObj, me.hiddenObjKeyPrefix, objKey.name, String(dataArr.join(me.pilcrow)));
            }
          }
        }
      },

      /**
       * @private
       * @param {Object} toObj
       * @param {String} prefix
       * @param {String} key
       * @param {String} value
       */
      applyObjKey: function (toObj, prefix, key, value) {
        var me = this;
        if (me.flatObjStructure) {
          toObj[prefix + key] = value;
        } else {
          toObj.objKeys[prefix + key] = value;
        }
      },

      /**
       * @private
       * @param {Object} toObj
       * @param {de.elo.ix.client.Sord} sord
       */
      applyMapData: function (toObj, sord) {
        var me = this,
            keys = me.allMapFields ? null : me.mapKeys,
            mapItems, i, mapItem;

        if (me.allMapFields || me.mapKeys) {
          mapItems = ((me.asAdmin && typeof ixConnectAdmin !== "undefined" && ixConnectAdmin) || ixConnect).ix().checkoutMap(MapDomainC.DOMAIN_SORD, sord.id, keys, LockC.NO).items;
          for (i = 0; i < mapItems.length; i++) {
            mapItem = mapItems[i];
            me.applyMapKey(toObj, String(mapItem.key), String(mapItem.value));
          }
        }
      },

      /**
       * @private
       * @param {Object} toObj
       * @param {de.elo.ix.client.Sord} sord
       * @param {String} flowId
       */
      applyWfMapData: function (toObj, sord, flowId) {
        var me = this,
            keys = me.allMapFields ? null : me.mapKeys,
            mapItems, i, mapItem;

        if (me.allMapFields || me.mapKeys) {
          mapItems = ((me.asAdmin && typeof ixConnectAdmin !== "undefined" && ixConnectAdmin) || ixConnect).ix().checkoutMap(MapDomainC.DOMAIN_WORKFLOW_ACTIVE, flowId, keys, LockC.NO).items;
          for (i = 0; i < mapItems.length; i++) {
            mapItem = mapItems[i];
            me.applyWfMapKey(toObj, String(mapItem.key), String(mapItem.value));
          }
        }
      },

      /**
       * @private
       * @param {Object} toObj
       * @param {String} objId
       * @param {String} flowId
       */
      applyFormBlobData: function (toObj, objId) {
        var me = this,
            keys = me.allFormBlobFields ? null : me.formBlobs,
            blobItems, i, blobItem, binaryStream, str;

        if (me.allFormBlobFields || me.formBlobs) {
          blobItems = ((me.asAdmin && typeof ixConnectAdmin !== "undefined" && ixConnectAdmin) || ixConnect).ix().checkoutMap("formdata", objId, keys, LockC.NO).items;
          for (i = 0; i < blobItems.length; i++) {
            blobItem = blobItems[i];
            binaryStream = blobItem.getBlobValue().getStream();
            str = Packages.org.apache.commons.io.IOUtils.toString(binaryStream, java.nio.charset.StandardCharsets.UTF_8);
            binaryStream.close();
            me.applyFormBlobKey(toObj, String(blobItem.key), String(str));
          }
        }
      },

      /**
       * @private
       * @param {Object} toObj
       * @param {de.elo.ix.client.Sord} sord
       */
      applyFeedActions: function (toObj, sord) {
        var me = this;
        if (me.feedActions) {
          toObj.feedActions = JSON.parse(me.getFeedActionsJson(sord.guid));
        }
      },

      /**
       * @private
       * @param {Object} toObj
       * @param {String} key
       * @param {String} value
       */
      applyMapKey: function (toObj, key, value) {
        var me = this;
        if (me.flatObjStructure) {
          toObj[me.mapKeyPrefix + key] = value;
        } else {
          toObj.mapKeys[me.mapKeyPrefix + key] = value;
        }
      },

      /**
       * @private
       * @param {Object} toObj
       * @param {String} key
       * @param {String} value
       */
      applyWfMapKey: function (toObj, key, value) {
        var me = this;
        if (me.flatObjStructure) {
          toObj[me.mapKeyPrefix + key] = value;
        } else {
          toObj.wfMapKeys[me.mapKeyPrefix + key] = value;
        }
      },

      /**
       * @private
       * @param {Object} toObj
       * @param {String} key
       * @param {String} value
       */
      applyFormBlobKey: function (toObj, key, value) {
        var me = this;
        if (me.flatObjStructure) {
          toObj[me.mapKeyPrefix + key] = value;
        } else {
          toObj.formBlobs[me.mapKeyPrefix + key] = value;
        }
      },

      /**
       * Builds a cache for required objkeys. That allows reducing iteration times if thousands of sords are formatted.
       * @private
       * @param {String} maskName
       * @param {de.elo.ix.client.ObjKey[]} objKeys
       */
      buildCacheMaskObjKeys: function (maskName, objKeys) {
        var me = this,
            cache, i, objKey;
        if (!me._cacheMaskObjKeys.hasOwnProperty(maskName)) {
          cache = [];
          // iterate over all objKeys from the current sord
          for (i = 0; i < objKeys.length; i++) {
            objKey = objKeys[i];
            if (me.isValidObjKey(objKey)) {
              cache.push({
                id: objKey.id,
                arrPos: i
              });
            }
          }
          me._cacheMaskObjKeys[maskName] = cache;
        }
      },

      /**
       * checks if objKey should be applied to the formatted object.
       * @private
       * @param {de.elo.ix.client.ObjKey} sordObjKey
       * @return {Boolean} result
       */
      isValidObjKey: function (sordObjKey) {
        var me = this,
            i, length;

        if (sordObjKey.id < 0) {
          return false;
        }

        if (!me.allObjKeys) {
          for (i = 0, length = me.objKeys.length; i < length; i++) {
            if (sordObjKey.name == me.objKeys[i]) {
              return true;
            }
          }
        } else {
          return true;
        }
      },

      /**
       * @private
       * @return {Object} initial formattedSord
       */
      initialiseFormattedSord: function () {
        var me = this;

        return me.flatObjStructure ? {} : {
          objKeys: {},
          mapKeys: {},
          wfMapKeys: {},
          formBlobs: {}
        };
      },

      /**
       * Build JSON Data Structure. This function should only be used by the ELO Business Solutions team,
       * since API calls might change due to performance improvements!
       *
       * This function was designed to reduce processing times if huge amounts of data are collected by services.
       * Creation of objects and properties is more than 50 times slower than building the json result with a string buffer.
       *
       * Due to performance reasons, no map data is collected.
       *
       * @param {de.elo.ix.client.Sord} originalSord Index Server Sord object
       * @param {Object} mask (optional) Mask object created by e.g. Children data collector. Pass if
       * @param {Object} params (optional)
       * @param {String[]} params.dataAsString (optional) Contains the names of the fields, for which all values (not just the first) should be returned as pilcrow separated string
       * @param {String[]} params.dataAsArray (optional) Contains the names of the fields, for which all values (not just the first) should be returned as an array
       * @param {boolean} params.addSordTypeKind (optional) if set to true adds sord type kind (e.g. "REPOSITORY", "FOLDER", "DOCUMENT") to formattedSord
       * @return {Object}
       */
      buildJson: function (originalSord, mask, params) {
        var me = this,
            sord = originalSord || me.sord,
            maskName = sord.maskName,
            // generate json as string array
            formattedSord = [],
            key, i, length, lengthO, objKey, objKeyName,
            objKeyCache, addedObjKeys, value, cachePos,
            _result,
            alreadyContainsStartElement;

        // prepare cache for mask that allows fast iterations over required objKeys.
        me.buildCacheMaskObjKeys(maskName, sord.objKeys);

        formattedSord.push("{");

        if (me.shouldAddSordTypeKind(params)) {
          me.addSordTypeKind(formattedSord, sord);
          alreadyContainsStartElement = true;
        }

        if (me.sordKeys.length > 0 && alreadyContainsStartElement) {
          formattedSord.push(",");
        }

        // apply sordKeys
        for (i = 0, length = me.sordKeys.length; i < length; i++) {
          key = me.sordKeys[i];
          formattedSord.push('"');
          formattedSord.push(key);
          formattedSord.push('":');
          value = String(sord[key]);
          if ((key == "desc") && me.descMaxLen && value && (value.length > me.descMaxLen)) {
            value = value.substr(0, me.descMaxLen);
          }
          formattedSord.push(
            // set undefined to null
            sord[key] === undefined ? "null"
              : (
              // else: escape strings
                JSON.stringify(value)
              ));
          if (i != length - 1) {
            formattedSord.push(",");
          }
        }
        // < apply sordKeys

        // apply ObjKeys
        if (me.allObjKeys || (me.objKeys && me.objKeys.length > 0)) {

          objKeyCache = me._cacheMaskObjKeys[maskName];
          addedObjKeys = false;
          for (i = 0, lengthO = objKeyCache.length; i < lengthO; i++) {
            cachePos = objKeyCache[i];
            if (cachePos && cachePos.arrPos < sord.objKeys.length
                && sord.objKeys[cachePos.arrPos].id === cachePos.id) {
              objKey = sord.objKeys[cachePos.arrPos];
              me.addObjKeyJson(mask, params, formattedSord, addedObjKeys, me.objKeyPrefix, objKey);
              addedObjKeys = true;
            } else {
              continue;
            }
          }
        }

        if (me.hiddenObjKeys) {
          for (i = sord.objKeys.length - 1; i >= 0; i--) {
            objKey = sord.objKeys[i];
            if (objKey.id < DocMaskLineC.MIN_ID_HIDDEN_VALUE) {
              break;
            }
            objKeyName = objKey.name + "";
            if (me.hiddenObjKeys.indexOf(objKeyName) > -1) {
              me.addObjKeyJson(mask, params, formattedSord, addedObjKeys, me.hiddenObjKeyPrefix, objKey);
              addedObjKeys = true;
            }
          }
        }

        if (!me.flatObjStructure && addedObjKeys) {
          formattedSord.push("}");
        }

        // < apply sordKeys
        if (me.feedActions) {
          formattedSord.push(', "feedActions": ');
          formattedSord.push(me.getFeedActionsJson(sord.guid));
        }

        formattedSord.push("}");
        _result = formattedSord.join("");
        return _result;
      },

      addObjKeyJson: function (mask, params, formattedSord, addedObjKeys, objKeyPrefix, objKey) {
        var me = this,
            val, adjustValueFunction;

        if (objKey) {

          adjustValueFunction = function (value) {
            var field = mask ? mask.fields[objKey.name] : null;
            if (value === undefined || value === "" || value === null) {
              value = "null";
            } else if (field && field.type && (field.type.indexOf("NUMBER") === 0)) {
              if (value === "") {
                value = "null";
              }
              value = parseFloat(value.replace(",", ".")) || "null";
            } else {
              value = JSON.stringify(String(value));
            }
            return value;
          };

          if (addedObjKeys) {
            // add separators between objKeys
            formattedSord.push(",");
          } else {
          // add separators for objKey block
            formattedSord.push(",");
            if (!me.flatObjStructure) {
              formattedSord.push('"objKeys": {');
            }
          }
          formattedSord.push('"');
          formattedSord.push(objKeyPrefix + objKey.name);
          formattedSord.push('":');

          // adjust val
          if (params && params.dataAsString && (params.dataAsString.length > 0) && (params.dataAsString.indexOf(String(objKey.name)) !== -1)) {
            val = JSON.stringify(objKey.data.join(me.pilcrow));
          } else if (params && params.dataAsArray && (params.dataAsArray.length > 0) && (params.dataAsArray.indexOf(String(objKey.name)) !== -1)) {
            val = JSON.stringify(objKey.data.map(adjustValueFunction));
          } else {
            val = (objKey.data && (objKey.data.length > 0)) ? objKey.data[0] : "";
            val = adjustValueFunction(val);
          }
          // < adjust val

          formattedSord.push(val);
        }
      },

      /**
       * @param {String[]} formattedSord
       * @param {de.elo.ix.client.Sord} sord
       */
      addSordTypeKind: function (formattedSord, sord) {
        var me = this,
            value;

        formattedSord.push('"typeKind":');
        value = me.getSordTypeKind(String(sord["type"]));
        formattedSord.push(JSON.stringify(value) || "null");
      },

      /**
       * @param {Object} params
       * @returns {boolean} should set sord type kind
       */
      shouldAddSordTypeKind: function (params) {
        return (params || {}).addSordTypeKind;
      },

      /**
       * @param {Number} id
       * @returns {String}
       */
      getSordTypeKind: function (id) {
        if (id == "9999") {
          return "REPOSITORY";
        } else if (id < SordC.LBT_DOCUMENT) {
          return "FOLDER";
        } else if (id <= SordC.LBT_DOCUMENT_MAX) {
          return "DOCUMENT";
        }
        return "UNKNOWN";
      },

      /**
       * Reads the list of all actions for a given post.
       *
       * This is an experimental feature. Don't rely on its existence.
       * This feature should be used with care since this leads to performance issues if used within large datasets.
       * @private
       * @param {String} guid GUID
       * @returns {String} json array that contains all actions pre formatted as json.
       */
      getFeedActionsJson: function (guid) {
        var me = this,
            findInfo, findResult, o, i, action;
        try {
          findInfo = new FindActionsInfo();
          findInfo.objId = guid;
          findInfo.sordZ = SordC.mbOnlyGuid;
          findInfo.actionTypes = me.feedActionTypes || [];
          findResult = ixConnect.getFeedService().findFirstActions(findInfo, me.feedActionsMax, ActionC.mbAll);

          // using a string builder in order to speed up system performance
          o = ["["];
          for (i = 0; i < findResult.actions.length; i += 1) {
            action = findResult.actions[i];
            if (findInfo.actionTypes.length === 0 || findInfo.actionTypes.indexOf(action.type) !== -1) {
              if (o.length !== 1) {
                o.push(",");
              }
              o.push("{");
              o.push('"text": ');
              o.push(JSON.stringify(String(action.text)));
              o.push(', "userName": ');
              o.push(JSON.stringify(String(action.userName)));
              o.push(', "userId": ');
              o.push(action.userId);
              o.push(', "type": "');
              o.push(action.type);
              o.push('", "createDateIso": "');
              o.push(action.createDateIso);
              o.push('", "guid": "');
              o.push(action.guid);
              o.push('", "updateDateIso": "');
              o.push(action.updateDateIso);
              o.push('"');
              o.push("}");
            }
          }
          o.push("]");
          return o.join("");
        } catch (ex) {
          me.logger.error("reading feed comments failed.", ex);
          return "[]";
        }
      },

      /**
       * @private
       * If feed action types are defined as strings a mapping to EActionType is required.
       * @param {String[]|de.elo.ix.client.feed.EActionType[]} feedActionTypes
       * @return {de.elo.ix.client.feed.EActionType[]}
       */
      normalizeFeedActionTypes: function (feedActionTypes) {
        var me = this,
            normalizedTypes = [];

        if (feedActionTypes && (feedActionTypes.length > 0)) {
          feedActionTypes.forEach(function (type) {
            var typeEnum = (type instanceof EActionType) ? type : me.actionTypeMappings[type];
            if (typeEnum) {
              normalizedTypes.push(typeEnum);
            }
          });
        }

        return normalizedTypes;
      }

    });

    /**
     * Represents limited compact data of an ELO object for dashboards.
     *
     * If working with large amounts of data it is mandatory to define the properties required for the data analys in order
     * to reduce the amount of data loaded from the client. IndexServer Sord-Objects contain a lot of information that might
     * not be required for the analysis. sol.common.ObjectFormatter.StatisticSord allows defining the sord keys, objKeys,
     * ... information that is required.
     *
     * For more information on configuration properties please refer to sol.common.ObjectFormatter.BaseSord.
     *
     * This functionality is used by sol.common.ix.functions.ChildrenDataCollector while collecting data for dashboard apps
     * developed in angular js.
     *
     *     var data = sol.common.ObjectFormatter.format({
     *       sord: {
     *         formatter: 'sol.common.ObjectFormatter.StatisticSord',
     *         // instance of de.elo.ix.client.Sord
     *         data: sord,
     *         config: {
     *           sordKeys: ['id', 'maskName', 'name', 'IDateIso', 'XDateIso'],
     *           objKeys: ['VENDOR_NAME', 'INVOICE_DATE', 'INVOICE_CASH_DISCOUNT_AMOUNT']
     *         }
     *       }
     *     });
     *
     * The transformed object results as followed.
     *
     *     data = {
     *       "id": "7572",
     *       "maskName": "Incoming invoice",
     *       "name": "Invoice 0000",
     *       "IDateIso": "20150720142400",
     *       "XDateIso": "",
     *       "O_VENDOR_NAME": "Weiler KG",
     *       "O_INVOICE_DATE": "20150601162415",
     *       "O_INVOICE_CASH_DISCOUNT_AMOUNT": 554,
     *     }
     *
     * @author Michael Weiler, ELO Digital Office GmbH
     * @author Nils Mosbach, ELO Digital Office GmbH
     * @version 1.0
     *
     * @eloix
     * @elojc
     * @eloas
     */
    sol.define("sol.common.ObjectFormatter.StatisticSord", {

      extend: "sol.common.ObjectFormatter.BaseSord",
      requiredProperties: ["sord"],

      flatObjStructure: true,
      objKeyPrefix: "O_",
      hiddenObjKeyPrefix: "H_",
      mapKeyPrefix: "M_"

    });

    /**
     * Represents limited compact data of an ELO object.
     *
     * This class includes all objKeys by default. For more information on configuration properties please refer to sol.common.ObjectFormatter.BaseSord.
     *
     * Templates can't access objKeys stored in array values in an easy way.
     *
     *     var data = sol.common.ObjectFormatter.format({
     *       sord: {
     *         formatter: 'sol.common.ObjectFormatter.TemplateSord',
     *         // instance of de.elo.ix.client.Sord
     *         data: sord
     *       }
     *     });
     *
     * The transformed object results as followed.
     *
     *     data = {
     *       "id": "7572",
     *       "maskName": "Incoming invoice",
     *       "name": "Invoice 0000",
     *       "IDateIso": "20150720142400",
     *       "XDateIso": "",
     *       "objKeys": {
     *         "VENDOR_NAME": "ELO Inc.",
     *         "INVOICE_DATE": "20150601162415",
     *         "INVOICE_CASH_DISCOUNT_AMOUNT": 554
     *       }
     *     }
     *
     * @author Michael Weiler, ELO Digital Office GmbH
     * @author Nils Mosbach, ELO Digital Office GmbH
     * @version 1.0
     *
     * @eloix
     * @elojc
     * @eloas
     */
    sol.define("sol.common.ObjectFormatter.TemplateSord", {

      extend: "sol.common.ObjectFormatter.BaseSord",
      requiredProperties: ["sord"],

      sordKeys: ["guid", "id", "name", "maskName", "desc"],
      allObjKeys: true,
      allMapFields: false,
      allFormBlobFields: false,

      flatObjStructure: false,
      objKeyPrefix: "",
      mapKeyPrefix: ""

    });

    sol.define("sol.common.ObjectFormatter.WfMap", {

      extend: "sol.common.ObjectFormatter.BaseSord",
      requiredProperties: ["sord", "flowId"],

      sordKeys: ["guid", "id", "name", "maskName", "desc"],
      allObjKeys: true,
      allMapFields: false,
      allFormBlobFields: false,

      flatObjStructure: false,
      objKeyPrefix: "",
      mapKeyPrefix: ""

    });

    /**
     * Represents limited data of a task
     *
     * @author Michael Weiler, ELO Digital Office GmbH
     * @version 1.0
     *
     * @eloix
     * @elojc
     * @eloas
     */
    sol.define("sol.common.ObjectFormatter.BaseTask", {

      requiredProperties: "task",

      /**
       * @cfg {String} formatter
       * Class name of the task formatter
       */

      /**
       * @cfg {Boolean} limitToSordList
       * True if the tasks should be limited to the Sord objects in the Sord list.
       */

      /**
       * @cfg {Array} wfKeys
       * Names of properties of the class WFCollectNode that should be included.
       */

      initialize: function (cfg) {
        var me = this;
        me.wfKeyMap = {};
        me.task = cfg.data;
        me.config = cfg.config || {};
      },

      /**
       * Builds the base representation of the task
       * @private
       * @param {Object} originalTask
       * @return {Object}
       */
      build: function (originalTask) {
        var me = this,
            key;
        if (originalTask) {
          me.task = originalTask;
        }
        me.getValues();
        me.element = { flowId: me.task.wfNode.flowId, nodeId: me.task.wfNode.nodeId };
        for (key in me.wfKeyMap) {
          me.element[key] = me.wfKeyMap[key].value;
        }
        return me.element;
      },

      /**
       * Retrieves the data of the ELO object from the original Java object
       * @private
       */
      getValues: function () {
        var me = this,
            key;
        me.prepareMaps();
        if (me.task.wfNode) {
          for (key in me.wfKeyMap) {
            me.wfKeyMap[key].value = String(me.task.wfNode[key]);
          }
        }
      },

      /**
       * Prepares JavaScript objects to gather the ELO object data
       * @private
       */
      prepareMaps: function () {
        var me = this;
        if (me.config.wfKeys) {
          me.config.wfKeys.forEach(function (key) {
            me.wfKeyMap[key] = { value: "" };
          });
        }
      }
    });

    /**
     * Represents limited compact data of an ELO object
     * for dashboards
     *
     * @author Michael Weiler, ELO Digital Office GmbH
     * @version 1.0
     *
     * @eloix
     * @elojc
     * @eloas
     */
    sol.define("sol.common.ObjectFormatter.StatisticTask", {

      extend: "sol.common.ObjectFormatter.BaseTask"
    });

    /**
     * Represents limited compact data of an ELO object
     * for dashboards
     *
     * @author Michael Weiler, ELO Digital Office GmbH
     * @version 1.0
     *
     * @eloix
     *
     */
    sol.define("sol.common.ObjectFormatter.TemplateTask", {

      extend: "sol.common.ObjectFormatter.BaseTask",

      initialize: function (cfg) {
        var me = this;
        me.$super("sol.common.ObjectFormatter.BaseTask", "initialize", [cfg]);
      },

      wfKeys: ["flowId", "flowName", "nodeId", "nodeName"]
    });

    /**
     * Represents limited data of a workflow collect node
     *
     * @author MW, ELO Digital Office GmbH
     * @version 1.0
     *
     * @eloix
     * @elojc
     * @eloas
     */
    sol.define("sol.common.ObjectFormatter.BaseWfDiagramNode", {

      requiredProperties: ["flowId", "nodeId"],

      /**
       * @cfg {String} formatter
       * Class name of the task formatter
       */

      /**
       * @cfg {Array} wfKeys
       * Names of properties of the class WFDiagram that should be included.
       */

      /**
       * @cfg {Array} nodeKeys
       * Names of properties of the class WFNode that should be included.
       */

      initialize: function (cfg) {
        var me = this;
        me.wfDiagram = cfg.data;
        me.config = cfg.config || {};
        me.node = sol.common.WfUtils.getNode(me.wfDiagram, cfg.config.nodeId);
      },

      /**
       * Builds the base representation of the task
       *
       * @private
       * @return {Object} Node object
       * @return {String} return.flowId Flow ID
       * @return {String} return.flowName Flow name
       * @return {String} return.id Node ID
       * @return {String} return.name Node name
       */
      build: function () {
        var me = this;

        me.element = { flowId: String(me.config.flowId), id: String(me.config.nodeId) };

        if (me.wfDiagram) {
          me.element.flowName = String(me.wfDiagram.name);
        }

        if (me.node) {
          me.element.name = String(me.node.name);
        }

        return me.element;
      }
    });

    /**
     * Represents limited compact data of an ELO workflow diagram node
     *
     * @author MW, ELO Digital Office GmbH
     * @version 1.0
     *
     * @eloix
     *
     */
    sol.define("sol.common.ObjectFormatter.TemplateWfDiagramNode", {

      extend: "sol.common.ObjectFormatter.BaseWfDiagramNode",

      initialize: function (cfg) {
        var me = this;
        me.$super("sol.common.ObjectFormatter.BaseWfDiagramNode", "initialize", [cfg]);
      }
    });