/*-
 * ~~~~~~licensing~~~~~~
 * entaxy-management-plugin
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */

var Entaxy;
(function (Entaxy) {
    Entaxy._module.factory('entaxyService', ['$q', 'workspace', 'jolokia', 'jolokiaService', 'operationsService',
        '$uibModal', 'entaxyAttributesCacheService', 'entaxyPrivateObjectsCacheService', 'entaxyResourcesService',
        function($q, workspace, jolokia, jolokiaService, operationsService, $uibModal, entaxyAttributesCacheService,
                 entaxyPrivateObjectsCacheService, entaxyResourcesService) {

            const FIELD_COMMON_KEYS = {
                TYPEINFO: '@TYPEINFO',
                UI: '@UI',
                RESOURCE: '@RESOURCE',
                RESOURCE_REF: '@RESOURCE_REF',
                FACADE: '@FACADE',
                INTERNAL: '@INTERNAL',
                CALCULATED: '@CALCULATED'
            };

            const CONFIG_PROPERTIES_KEYS = {
                CONFIG_LINK: '##config-link',
                CONFIG: '##config'
            };

            const objectMbean = getDomainFolder().findDescendant(child =>
                child.objectName && child.objectName.endsWith('category=objects'));

            const searchMbean = getDomainFolder().findDescendant(child =>
                child.objectName && child.objectName.endsWith('util=search'));

            return {
                getDomainFolder: function () {
                    return getDomainFolder();
                },
                getFolderByTitle: function (title) {
                    return getFolderByTitle(title);
                },
                getFactoryByTitle: function (title) {
                    return getFactoryByTitle(title);
                },
                getFactoriesByType: function (factoryType, filter, includeAttributes) {
                    return getFactoriesByType(factoryType, filter, includeAttributes);
                },
                getFactoriesByFilterSearch: function (filter, includeAttributes) {
                    return getFactoriesByFilterSearch(filter, includeAttributes);
                },
                getArgumentsFromConfig: function (configInfo) {
                    return getArgumentsFromConfig(configInfo);
                },
                getFields: function (mbeanName, outputType) {
                    return getFields(mbeanName, outputType);
                },
                makeFormField: function (field, objectId, properties, configurableOnly, mode, mbeanName, profileName) {
                    return makeFormField(field, objectId, properties, configurableOnly, mode, mbeanName, profileName);
                },
                getXmlFromConfigPropertyValue: function (value) {
                    return getXmlFromConfigPropertyValue(value);
                },
                isSupposedlyXml: function (xml) {
                    return isSupposedlyXml(xml);
                },
                isSupposedlyJson: function (json) {
                    return isSupposedlyJson(json);
                },
                getArguments: function (fields, factories) {
                    return getArguments(fields, factories);
                },
                getDependentFormFields: function (formFields) {
                    return getDependentFormFields(formFields);
                },
                getDefiningFormFields: function (dependentFormFields, formFields) {
                    return getDefiningFormFields(dependentFormFields, formFields);
                },
                processDependencies: function (dependentFormFields, definingFormFields, currentDefiningFormFieldName, newValue, oldValue) {
                    return processDependencies(dependentFormFields, definingFormFields, currentDefiningFormFieldName, newValue, oldValue);
                },
                computeDependentFormFieldValue: function (formField, definingFormFields) {
                    return computeDependentFormFieldValue(formField, definingFormFields);
                },
                validateFields: function (fields, selectedFactory, profileMBeanName, checkUniquenessParentFields) {
                    return validateFields(fields, selectedFactory, profileMBeanName, checkUniquenessParentFields);
                },
                validateByRules: function (field, rules, errors) {
                    return validateByRules(field, rules, errors);
                },
                openAddItemModalAndProcessResults: function (resolve, runtimeType, updateFn) {
                    return openAddItemModalAndProcessResults(resolve, runtimeType, updateFn);
                },
                openAddItemFromTemplateModalAndProcessResults: function (resolve, item, runtimeType, updateFn) {
                    return openAddItemFromTemplateModalAndProcessResults(resolve, item, runtimeType, updateFn);
                },
                openEditItemModalAndProcessResults: function (item, itemType, runtimeType, mode, resolvedProperties, updateFn) {
                    return openEditItemModalAndProcessResults(item, itemType, runtimeType, mode, resolvedProperties, updateFn);
                },
                openModalAndProcessResults: function (resolve, isLocal, mbeanNameToUpdateAttributesCache, updateFn) {
                    return openModalAndProcessResults(resolve, isLocal, mbeanNameToUpdateAttributesCache, updateFn);
                },
                requestConfirmationForSavingIfNeededAndProceed: function (isConfirmationNeeded, saveFn) {
                    return requestConfirmationForSavingIfNeededAndProceed(isConfirmationNeeded, saveFn);
                },
                requestConfirmationForSavingOwnerAndProceed: function (saveFn) {
                    return requestConfirmationForSavingOwnerAndProceed(saveFn);
                },
                saveItem: function (args, isLocal, updateFn) {
                    return saveItem(args, isLocal, updateFn);
                },
                downloadFileFromBase64StringContent: function (base64String, fileName) {
                    return downloadFileFromBase64StringContent(base64String, fileName);
                },
                processTableOperation: function (operationType, selectedItemName, runtimeType,
                                                 objectDisplayName, forced, soft) {
                    return processTableOperation(operationType, selectedItemName, runtimeType,
                        objectDisplayName, forced, soft);
                },
                processUserManagementOperation: function (operationName, args, populateTableFn) {
                    return processUserManagementOperation(operationName, args, populateTableFn);
                },
                getAllChildMBeansByAttribute: function (folder, attributeName, attributeValue) {
                    return getAllChildMBeansByAttribute(folder, attributeName, attributeValue);
                },
                getMBeansByObjectFullIds: function (objectFullIds) {
                    return getMBeansByObjectFullIds(objectFullIds);
                },
                getAllChildMBeansByRuntimeType: function (folder, runtimeType) {
                    return getAllChildMBeansByRuntimeType(folder, runtimeType);
                },
                findRouteContainerMbean: function (mbean) {
                    return findRouteContainerMbean(mbean);
                },
                findRouteContainerMbeanName: function (mbean) {
                    return findRouteContainerMbeanName(mbean);
                },
                findObjectsByFilter: function (filter) {
                    return findObjectsByFilter(filter);
                },
                readConfiguration: function (mbeanName) {
                    return readConfiguration(mbeanName);
                },
                getBundleDiag: function (bundleId) {
                    return getBundleDiag(bundleId);
                },
                readExtendedData: function (mbeanName) {
                    return readExtendedData(mbeanName);
                },
                readObjectClusterState: function (mbeanName) {
                    return readObjectClusterState(mbeanName);
                },
                resolveRoutesFieldValueAndAddNew: function (routesField, ownerMbean) {
                    return resolveRoutesFieldValueAndAddNew(routesField, ownerMbean);
                },
                resolveResourceRefFields: function (fields) {
                    return resolveResourceRefFields(fields);
                },
                getProfiles: function () {
                    return getProfiles();
                },
                openConfirmationWindow: function (title, message) {
                    return openConfirmationWindow(title, message);
                },
                getActiveTabIndex: function () {
                    return getActiveTabIndex();
                },
                updateTabSelection: function (targetTabIndex) {
                    return updateTabSelection(targetTabIndex);
                }
            };

            function getDomainFolder() {
                let entaxyJmxDomain = localStorage['entaxyJmxDomain'] || "ru.entaxy.esb";
                return workspace.tree.get(entaxyJmxDomain);
            }

            function getFolderByTitle(title) {
                return getDomainFolder().findDescendant(child => child.title === title);
            }

            function getFactoryByTitle(title) {
                let factory = getFolderByTitle(title);
                let factoryAttributes = entaxyAttributesCacheService.getAttributes(factory.objectName);
                return {
                    name: factory.title,
                    displayName: factoryAttributes && factoryAttributes.DisplayName ?
                        factoryAttributes.DisplayName : factory.title,
                    description: factoryAttributes && factoryAttributes.Description ?
                        factoryAttributes.Description : undefined,
                    mbeanName: factory.objectName
                };
            }

            function getFactoriesByType(factoryType, filter, includeAttributes) {
                return $q(function (resolve) {
                    let factoryFolder = getFolderByTitle(factoryType);

                    if (factoryFolder && factoryFolder.children) {
                        let promises = [];
                        let factories = [];

                        factoryFolder.children.forEach((factory) => {
                            promises.push(jolokiaService.getAttributes(factory.objectName)
                            .then((attributes) => {
                                if (!attributes.Abstract && !attributes.Internal) {
                                    if (!filter || (filter && attributes.Label && attributes.Label.match(filter))) {
                                        factories.push({
                                            name: factory.title,
                                            displayName: attributes.DisplayName ?
                                                attributes.DisplayName : factory.title,
                                            label: attributes.Label,
                                            mbeanName: factory.objectName,
                                            description: attributes.Description ?
                                                attributes.Description : 'There is no description for this factory.',
                                            isDeprecated: attributes.Deprecated,
                                            additionalInfo: attributes.Deprecated ? ['[DEPRECATED]'] : undefined,
                                            direction: attributes.TypeInfo ? attributes.TypeInfo.direction : undefined,
                                            attributes: includeAttributes ? attributes : undefined
                                        });
                                    }
                                }
                            }));
                        });

                        $q.all(promises).then(() => resolve(factories));
                    }
                });
            }

            function getFactoriesByFilterSearch(filter, includeAttributes) {
                return $q(function (resolve) {
                    operationsService
                    .executeOperation(searchMbean.objectName, { name: 'search' }, [ 'factory', filter ])
                    .then(result => {
                        let factories = JSON.parse(result);
                        factories = factories.map(factory => {
                            let factoryContainerFolder = getFolderByTitle(factory.type);
                            let factoryFolder = factoryContainerFolder
                                .findDescendant(child => child.title === factory.id);
                            return {
                                name: factory.id,
                                displayName: factory.displayName ? factory.displayName : factory.id,
                                label: factory.label,
                                mbeanName: factoryFolder.objectName,
                                description: factory.description ?
                                    factory.description : 'There is no description for this factory.',
                                isDeprecated: factory.isDeprecated,
                                additionalInfo: factory.isDeprecated ? ['[DEPRECATED]'] : undefined,
                                direction: factory['typeinfo.direction'],
                                attributes: includeAttributes ? factory : undefined
                            };
                        });
                        resolve(factories);
                    });
                });
            }

            function getArgumentsFromConfig(configInfo) {
                let properties = Object.entries(configInfo.properties);
                properties = properties.map(property => {
                    return { name: property[0], value: property[1] };
                });
                properties = properties.filter(property =>
                    property.name !== '__objectId' && property.name !== CONFIG_PROPERTIES_KEYS.CONFIG &&
                    property.name !== '##publish');

                properties.unshift({ name: 'objectId', value: configInfo.objectId });

                let factoryFolder = getFolderByTitle(configInfo.factoryId);
                let factory = { name: factoryFolder.title, mbeanName: factoryFolder.objectName };

                return { fields: properties, factoryId: factory };
            }

            function getFields(mbeanName, outputType) {
                if (outputType === undefined) {
                    outputType = 'init';
                }
                return operationsService.executeOperation(mbeanName, { name: 'getFields' }, [ outputType ]);
            }

            function makeFormField(field, objectId, properties, configurableOnly, mode, mbeanName, profileName) {

                let formField;

                preProcessTypeInfoTag(field);
                preProcessSingleElementEnum(field, mode);
                preProcessUiTag(field);
                preProcessCalculatedField(field);
                preProcessResourceField(field);

                field.type = Entaxy.convertToHtmlInputType(field.type);

                if (mode !== Entaxy.MODAL_MODES.ADD) {

                    if (field.name === 'objectId') {
                        field.value = objectId;
                    }

                    if (field.fixedValue !== undefined) {
                        field.immutable = true;
                    }

                    let suitableProperty;

                    if (doesConfigIncludeProperty(properties, field.name)) {
                        field.configured = true;
                        suitableProperty = getPropertyValueFromConfig(properties, field);
                    } else {
                        suitableProperty = properties[field.name];
                    }

                    if (suitableProperty !== undefined) {
                        if ((field.type.startsWith(Entaxy.RUNTIME_TYPE_PREFIX) ||
                                field.type.startsWith(Entaxy.RUNTIME_TYPE_SECURITY_PREFIX)) && field.immutable) {
                            field.type = 'string';

                            if (field[FIELD_COMMON_KEYS.TYPEINFO]) {
                                delete field[FIELD_COMMON_KEYS.TYPEINFO];
                            }
                        }

                        field.value = suitableProperty;
                    }

                    setFacade(field);

                    if (field[FIELD_COMMON_KEYS.INTERNAL] === true ||
                            (field.isHidden && !field.transmitAlways &&
                                field.defaultValue !== undefined && field.value !== undefined &&
                                (!field[FIELD_COMMON_KEYS.UI] ||
                                    !field[FIELD_COMMON_KEYS.UI].manageDisplayBlocksByValue)) ||
                            (mode === Entaxy.MODAL_MODES.VIEW && field.type === 'resource')) {
                        formField = undefined;
                    } else {
                        let baseFormFieldProperties = createBaseFormField(field, mode);
                        let additionalFormFieldProperties = {
                            value: field.value !== undefined ?
                                field.value : !field.isCalculated ? field.defaultValue : undefined,
                            originalValue: field.value !== undefined ? field.value : field.defaultValue,
                            configured: field.configured,
                            ignoreConfirmationValue: field.type === 'password' && field.value ? true : undefined,
                            readOnly: mode === Entaxy.MODAL_MODES.VIEW ? true :
                                (configurableOnly ? (field.configured ? field.immutable : true) : field.immutable),
                            useFacade: (field[FIELD_COMMON_KEYS.FACADE] && field[FIELD_COMMON_KEYS.FACADE].useFacade) ?
                                field[FIELD_COMMON_KEYS.FACADE].useFacade : undefined
                        };
                        formField = { ...baseFormFieldProperties, ...additionalFormFieldProperties };

                        if (formField.dependsOn && formField.readOnly) {
                            delete formField.dependsOn;
                        }

                        if (formField.readOnly && formField.typeInfo &&
                                (formField.typeInfo.type === 'enum' || formField.typeInfo.type === 'list')) {
                            delete formField.typeInfo;
                        }

                        if (formField.isFromResource && formField.value) {
                            getXmlFromConfigPropertyValue(formField.value).then(xml => {
                                assignXmlValue(formField, xml);
                                formField.originalValue = xml;
                            });
                        }
                    }
                } else {
                    if (properties && !field.isHidden &&
                            (!field[FIELD_COMMON_KEYS.UI] || !field[FIELD_COMMON_KEYS.UI].ignoreValueIfTemplate)) {
                        let suitableProperty;
                        if (doesConfigIncludeProperty(properties, field.name)) {
                            suitableProperty = getPropertyValueFromConfig(properties, field);
                        } else {
                            suitableProperty = properties[field.name];
                        }
                        if (suitableProperty) {
                            field.value = suitableProperty;
                        }
                    }

                    let baseFormFieldProperties =  createBaseFormField(field, mode);
                    let additionalFormFieldProperties = {
                        value: field.value ? field.value :
                            (field.isCalculated || field.isFromResource) ? undefined : field.defaultValue,
                        isFromResource: field.isFromResource,
                        uniquenessCurrentOnly: hasValidationRules(field) &&
                            field.validation.rules.checkUniqueness && field.validation.rules.checkUniqueness.currentOnly,
                        uniquenessExtraProperties: hasValidationRules(field) && field.validation.rules.checkUniqueness ?
                            field.validation.rules.checkUniqueness.extraProperties : undefined,
                        checkUniquenessByParentField: hasValidationRules(field) &&
                            field.validation.rules.checkUniquenessByParentFieldValues,
                        readOnly: field.fixedValue !== undefined ? true : undefined
                    };
                    formField = { ...baseFormFieldProperties, ...additionalFormFieldProperties };

                    if (formField.isFromResource && formField.value) {
                        getXmlFromConfigPropertyValue(formField.value)
                        .then(xml => assignXmlValue(formField, xml));
                    } else if (formField.isFromResource && !formField.dependsOn) {
                        entaxyResourcesService.getResource(
                            getLocationFromResourceInfo(formField.defaultValue[FIELD_COMMON_KEYS.RESOURCE]))
                        .then((response) => assignXmlValue(formField, response));
                    }

                    setEntaxyContainerId(formField, profileName);
                }

                if (formField && formField.type === 'list') {
                    let localMode = formField.readOnly ? Entaxy.MODAL_MODES.VIEW : mode;
                    if (formField.value) {
                        resolveListFieldValue(formField, localMode, mbeanName);
                    } else {
                        if (mode !== Entaxy.MODAL_MODES.ADD) {
                            resolveAddedToListField(formField, localMode, mbeanName);
                        }
                    }
                }

                if (formField && formField.typeInfo && formField.typeInfo.type === 'list' &&
                        formField.typeInfo.source && formField.typeInfo.source['@SEARCH']) {
                    searchEnumListValues(formField);
                }

                return formField;
            }

            function createBaseFormField(field, mode) {
                return {
                    label: field.displayName ? field.displayName : field.name,
                    name: field.name,
                    type: field.type,
                    useTextarea: field.useTextarea,
                    filter: field.filter,
                    description: field.description,
                    defaultValue: field.defaultValue,
                    placeholder: field.placeholder,
                    isCalculated: field.isCalculated,
                    isFromResource: field.isFromResource,
                    group: field.group ? field.group : 'general',
                    tag: field.label,
                    isRef: field.isRef,
                    isBackRef: field.isBackRef,
                    isHidden: field.isHidden,
                    required: mode !== Entaxy.MODAL_MODES.VIEW ? field.required : undefined,
                    typeInfo: mode !== Entaxy.MODAL_MODES.VIEW || field.type === 'list' ?
                        field[FIELD_COMMON_KEYS.TYPEINFO] : undefined,
                    ui: field[FIELD_COMMON_KEYS.UI],
                    preSaveProcessStage: field.preSaveProcessStage,
                    postSaveProcessStage: field.postSaveProcessStage,
                    ignoreChildChanges: field[FIELD_COMMON_KEYS.UI] ?
                        field[FIELD_COMMON_KEYS.UI].ignoreChildChanges : undefined,
                    dependsOn: field.dependsOn,
                    validation: field.validation,
                    excludeLabel: field.excludeLabel,
                    transmitAlways: field.transmitAlways,
                    itemFactoryFilter: field.itemFactory ? field.itemFactory.filter : undefined,
                    objectFactoryFilter: field.objectFactory ? field.objectFactory.filter : undefined,
                    innerObject: field.innerObject
                };
            }

            function preProcessTypeInfoTag(field) {
                if (field[FIELD_COMMON_KEYS.TYPEINFO]) {
                    if (field[FIELD_COMMON_KEYS.TYPEINFO].dependsOn) {
                        field.dependsOn = field[FIELD_COMMON_KEYS.TYPEINFO].dependsOn;
                        delete field[FIELD_COMMON_KEYS.TYPEINFO].dependsOn;
                    }
                    if (field[FIELD_COMMON_KEYS.TYPEINFO].validation) {
                        field.validation = field[FIELD_COMMON_KEYS.TYPEINFO].validation;
                        delete field[FIELD_COMMON_KEYS.TYPEINFO].validation;
                    }
                    if (field[FIELD_COMMON_KEYS.TYPEINFO].excludeLabel) {
                        field.excludeLabel = field[FIELD_COMMON_KEYS.TYPEINFO].excludeLabel;
                        delete field[FIELD_COMMON_KEYS.TYPEINFO].excludeLabel;
                    }
                    if (field[FIELD_COMMON_KEYS.TYPEINFO].innerObject) {
                        field.innerObject = field[FIELD_COMMON_KEYS.TYPEINFO].innerObject;
                        delete field[FIELD_COMMON_KEYS.TYPEINFO].innerObject;
                    }
                    if (field[FIELD_COMMON_KEYS.TYPEINFO].useTextarea !== undefined) {
                        field.useTextarea = field[FIELD_COMMON_KEYS.TYPEINFO].useTextarea;
                        delete field[FIELD_COMMON_KEYS.TYPEINFO].useTextarea;
                    }
                    if (Object.keys(field[FIELD_COMMON_KEYS.TYPEINFO]).length == 0) {
                        field[FIELD_COMMON_KEYS.TYPEINFO] = undefined;
                    }
                }
            }

            function preProcessSingleElementEnum(field, mode) {
                if (field[FIELD_COMMON_KEYS.TYPEINFO] && mode !== Entaxy.MODAL_MODES.VIEW &&
                        field[FIELD_COMMON_KEYS.TYPEINFO].type === 'enum' && field.required &&
                        field[FIELD_COMMON_KEYS.TYPEINFO].values.length == 1) {

                    field[FIELD_COMMON_KEYS.TYPEINFO].isEmptyIncluded = false;
                }
            }

            function preProcessUiTag(field) {
                if (field[FIELD_COMMON_KEYS.UI]) {
                    if (field[FIELD_COMMON_KEYS.UI].processStage === 'post-save') {
                        field.postSaveProcessStage = true;
                    } else if (field[FIELD_COMMON_KEYS.UI].processStage === 'pre-save') {
                        field.preSaveProcessStage = true;
                    }
                    if (field[FIELD_COMMON_KEYS.UI].transmitAlways) {
                        field.transmitAlways = true;
                    }
                    if (field[FIELD_COMMON_KEYS.UI].required !== undefined) {
                        field.required = field[FIELD_COMMON_KEYS.UI].required;
                        if (field.required) {
                            field.transmitAlways = true;
                        }
                    }
                }
            }

            function preProcessCalculatedField(field) {
                if (field.defaultValue &&
                        (JSON.stringify(field.defaultValue).indexOf(FIELD_COMMON_KEYS.CALCULATED) > -1)) {
                    field.isCalculated = true;
                    field.placeholder = 'has calculated default value';
                }
            }

            function preProcessResourceField(field) {
                if ((field.defaultValue && field.defaultValue[FIELD_COMMON_KEYS.RESOURCE]) ||
                        (field[FIELD_COMMON_KEYS.UI] && field[FIELD_COMMON_KEYS.UI].isResource)) {
                    field.isFromResource = true;
                }
            }

            function doesConfigIncludeProperty(properties, name) {
                return properties[CONFIG_PROPERTIES_KEYS.CONFIG_LINK] &&
                    Object.keys(properties[CONFIG_PROPERTIES_KEYS.CONFIG].properties).includes(name);
            }

            function getPropertyValueFromConfig(properties, field) {
                return field.type === 'number' ?
                    Number(properties[CONFIG_PROPERTIES_KEYS.CONFIG].properties[field.name]) :
                    properties[CONFIG_PROPERTIES_KEYS.CONFIG].properties[field.name];
            }

            function setFacade(field) {
                if (field[FIELD_COMMON_KEYS.FACADE] && field[FIELD_COMMON_KEYS.FACADE].useFacade) {
                    field.type = field[FIELD_COMMON_KEYS.FACADE].type;
                    field.value = field[FIELD_COMMON_KEYS.FACADE].value;
                    field.defaultValue = undefined;
                    field.immutable = true;
                }
            }

            function setEntaxyContainerId(formField, profileName) {
                if (formField.name === '__entaxyContainerId') {
                    if (profileName) {
                        formField.value = profileName;
                    } else {
                        let selectedMbeanName = workspace.getSelectedMBeanName();

                        let attributes = entaxyAttributesCacheService.getAttributes(selectedMbeanName);
                        if (attributes.RuntimeType) {
                            formField.value = attributes.Name;
                        } else {
                            Entaxy.log.error('There is no runtime type');
                        }
                    }
                }
            }

            function getXmlFromConfigPropertyValue(value) { // fixme show error in other places besides xmlInput
                return $q(function (resolve) {
                    if (angular.isString(value)) {
                        resolve(Entaxy.base64ToString(value));
                    } else if (angular.isObject(value)) {
                        entaxyResourcesService.getResource(value[FIELD_COMMON_KEYS.RESOURCE_REF])
                        .then((response) => resolve(response));
                    }
                });
            }

            function assignXmlValue(formField, value) {
                formField.value = value;
                if (!isSupposedlyXml(value)) {
                    formField.error = Entaxy.ERROR_MESSAGE.INVALID;
                }
            }

            function isSupposedlyXml(xml) {
                if (angular.isString(xml)) {
                    let parser = new DOMParser;
                    let xmlDoc = parser.parseFromString(xml, "application/xml");
                    return xmlDoc.getElementsByTagName("parsererror").length == 0;
                } else {
                    return false;
                }
            }

            function isSupposedlyJson(json) {
                if (angular.isString(json)) {
                    try {
                        let result = JSON.parse(json);
                        return result && angular.isObject(result) && !Array.isArray(result);
                    } catch (error) {
                        return false;
                    }
                }
            }

            function resolveListFieldValue(field, mode, mbeanName) {

                let items = field.value.reduce((obj, cur) =>
                    ({ ...obj, [cur.targetId + ':' + cur.targetType] : cur.scope }), {});

                getConfigsForListItems(items, mbeanName).then(() => {
                    let resolvedItems = {};
                    getFormFieldsForListItems(field, items, resolvedItems, mode).then(() => {
                        field.value = field.value.map(item => {
                            let formFields = resolvedItems[item.targetId];

                            let fullTargetId = item.targetId + ':' + item.targetType;
                            let changedItemInfo = entaxyPrivateObjectsCacheService.getChangedObjectInfo(fullTargetId);

                            return {
                                formFields: formFields,
                                toDeletion: changedItemInfo && changedItemInfo.toRemoval ? true : undefined
                            };
                        });

                        resolveAddedToListField(field, mode, mbeanName);
                    });
                });
            }

            function resolveAddedToListField(field, mode, mbeanName) {

                if (field.typeInfo.privateObjectTypes &&
                        field.typeInfo.privateObjectTypes.map(type => type.name).includes(Entaxy.RUNTIME_TYPE.ROUTE)) {
                    let mbean = mbeanName ?
                        getDomainFolder().findDescendant(folder => folder.objectName === mbeanName) :
                        workspace.getSelectedMBean();
                    let containerMbeanName = findRouteContainerMbeanName(mbean);

                    if (entaxyPrivateObjectsCacheService.hasAddedChildren(containerMbeanName)) {
                        let addedItemsInfo = entaxyPrivateObjectsCacheService.getAddedObjectsInfo(containerMbeanName);

                        if (addedItemsInfo && addedItemsInfo.length > 0) {
                            if (!field.value) {
                                field.value = [];
                            }
                            addedItemsInfo.forEach(addedItemInfo => {
                                let addedItemFormFields = mode === Entaxy.MODAL_MODES.VIEW ?
                                    addedItemInfo.ui.formFields.map(formField => {
                                        formField.readOnly = true;
                                        return formField;
                                    }) : addedItemInfo.ui.formFields;
                                field.value.push({
                                    new: true,
                                    toDeletion: addedItemInfo.toRemoval,
                                    formFields: addedItemFormFields
                                });
                            });
                        }
                    }
                }
            }

            function findRouteContainerMbean(mbean) {
                let attributes = entaxyAttributesCacheService.getAttributes(mbean.objectName);
                let containerMbean = mbean.findDescendant(child => {
                    if (child.objectName) {
                        let childAttributes = entaxyAttributesCacheService.getAttributes(child.objectName);
                        return childAttributes &&
                            childAttributes.RuntimeType === Entaxy.RUNTIME_TYPE.ROUTE_CONTAINER &&
                            attributes && attributes.RuntimeType === childAttributes.ObjectType;
                    }
                });
                return containerMbean;
            }

            function findRouteContainerMbeanName(mbean) {
                let containerMbean = findRouteContainerMbean(mbean);
                return containerMbean ? containerMbean.objectName : undefined;
            }

            function getConfigsForListItems(items, mbeanName) {
                let mbean = mbeanName ?
                    getDomainFolder().findDescendant(folder => folder.objectName === mbeanName) :
                    workspace.getSelectedMBean();

                let promises = [];
                let children = Entaxy.getChildrenRecursive(mbean);
                if (children) {
                    children.forEach(child => {
                        if (child.objectName) {
                            promises.push(jolokiaService.getAttribute(child.objectName).then(attributes => {
                                let objectFullId = attributes ? attributes.ObjectFullId : undefined;
                                if (objectFullId && items[objectFullId]) {
                                    if (items[objectFullId] === Entaxy.SCOPE.PRIVATE) {
                                        let config = entaxyPrivateObjectsCacheService
                                            .getChangedObjectConfig(attributes.ObjectFullId);
                                        return config ? $q.resolve(items[objectFullId] = config) :
                                            readConfiguration(child.objectName)
                                            .then(result => {
                                                items[objectFullId] = result;
                                            }).catch(error => {
                                                Entaxy.notificationError(error);
                                                Entaxy.log.error(error);
                                            })
                                    }
                                }
                            }));
                        }
                    });
                }

                return $q.all(promises);
            }

            function getFormFieldsForListItems(field, items, resolvedItems, mode) {
                let promises = [];

                field.value.forEach(item => {
                    let itemConfig = items[item.targetId + ':' + item.targetType];

                    let factory = getFactoryByTitle(itemConfig.factoryId);

                    promises.push(
                        getFields(factory.mbeanName)
                        .then((response) => {

                            let formFields = [];
                            formFields.push({
                                label: 'Factory',
                                name: 'factoryId',
                                type: 'java.lang.String',
                                description: factory.description,
                                value: factory.displayName,
                                originalValue: factory.name,
                                readOnly: true,
                                required: mode === Entaxy.MODAL_MODES.VIEW ? false : true,
                                group: 'general'
                            });
                            _.forEach(JSON.parse(response), (field) => {

                                let objectId = undefined;
                                if (mode !== Entaxy.MODAL_MODES.ADD) {
                                    objectId = itemConfig['objectId'];
                                }
                                let formField = makeFormField(field, objectId, itemConfig.properties,
                                    itemConfig.configurableOnly, mode);
                                if (formField) {
                                    formFields.push(formField);
                                }
                            });

                            resolvedItems[item.targetId] = formFields;
                        })
                    );
                });

                return $q.all(promises);
            }

            function searchEnumListValues(formField) {
                let searchProperties = formField.typeInfo.source['@SEARCH'];
                let properties = [ searchProperties.type, searchProperties.filter ];

                operationsService
                .executeOperation(searchMbean.objectName, { name: 'search' }, properties)
                .then(result => {
                    let list = JSON.parse(result);

                    formField.typeInfo.values = list.map(item => item[searchProperties.displayProperty]);
                });
            }

            function getLocationFromResourceInfo(resourceInfo) {
                return resourceInfo.provider + ':' + resourceInfo.location;
            }

            function getArguments(originalFields, factories) {

                let preSaveFields = originalFields.filter(field => field.preSaveProcessStage);
                if (preSaveFields && preSaveFields.length > 0) {
                    processPreSaveFields(preSaveFields, originalFields);
                }
                let postSaveFields = originalFields.filter(field => field.postSaveProcessStage);
                let fields = Entaxy.deepCopy(originalFields.filter(field => !field.postSaveProcessStage));
                let factoryField = fields.shift();
                let factoryIdName = factoryField.originalValue ? factoryField.originalValue : factoryField.value;

                let args = {
                    factoryId: factories ?
                        factories.find((factory) => factory.name === factoryIdName) : factoryIdName,
                    fields: fields.map(field => {
                        if (field.isFromResource && ((field.defaultValue &&
                                field.defaultValue[FIELD_COMMON_KEYS.RESOURCE].format === 'base64') ||
                                (field.ui && field.ui.resourceFormat === 'base64'))) {
                            field.value = Entaxy.stringToBase64(field.value);
                            field.originalValue = Entaxy.stringToBase64(field.originalValue);
                        }

                        if (field.isCalculated && field.value === '') {
                            delete field.value;
                        }

                        if (field.type === 'list') {
                            if (field.value && field.value.length > 0) {
                                let value = [];
                                field.value.filter(item => !item.toDeletion).forEach(item => {
                                    let args = getArguments(item.formFields);

                                    let objectId = args.fields.find((field) => field.name === 'objectId').value;
                                    let fields = objectId ?
                                        args.fields.filter((field) => field.name !== 'objectId') : args.fields;
                                    let properties = fields.reduce((obj, cur) => ({...obj, [cur.name]: cur.value}), {});

                                    value.push({
                                        factoryId: args.factoryId,
                                        objectId: objectId,
                                        properties: properties,
                                        scope: Entaxy.SCOPE.PRIVATE
                                    });
                                });
                                field.value = value;
                            } else {
                                field.value = undefined;
                            }
                        }

                        if (field.useFacade) {
                            delete field.defaultValue;
                            delete field.value;
                        }

                        if (field.validation && field.validation.doFix === true && field.value) {
                            field.value = fixFromRules(field.validation.fixRules, field.value);
                        }

                        if (!field.required && typeof field.value === 'string' && field.value.trim().length === 0) {
                            delete field.value;
                        }

                        if (!field.required && field.type === 'number' && field.value === null) {
                            delete field.value;
                        }

                        const isValueEqualToDefault = field.defaultValue !== undefined
                            && field.defaultValue === field.value;
                        const hasValueChanged = field.value !== field.originalValue;
                        const isValueEqualToDefaultAndChanged = isValueEqualToDefault && hasValueChanged;

                        return {
                            name: field.name,
                            value: (isValueEqualToDefault && !field.transmitAlways) || field.ignored ?
                                undefined : field.value,
                            changed: hasValueChanged ? true : undefined,
                            configured: field.configured,
                            valueToConfig: field.configured && isValueEqualToDefaultAndChanged ? field.value : undefined
                        };
                    })
                };

                if (postSaveFields && postSaveFields.length > 0) {
                    args.fields.push(...postSaveFields);
                }

                return args;
            }

            function processPreSaveFields(preSaveFields, fields) {
                preSaveFields.forEach(preSaveField => {
                    if (preSaveField.ui.sourceObjectIdField) {
                        let sourceObjectIdField = fields
                            .find(field => field.name === preSaveField.ui.sourceObjectIdField);
                        if (sourceObjectIdField) {
                            if (sourceObjectIdField.type.startsWith(Entaxy.RUNTIME_TYPE_PREFIX) ||
                                    sourceObjectIdField.type.startsWith(Entaxy.RUNTIME_TYPE_SECURITY_PREFIX)) {
                                let chosenOption = sourceObjectIdField.options
                                    .find(option => option.name === sourceObjectIdField.value);
                                let sourceObjectMbeanName = chosenOption.mbeanName;
                                let attributes = entaxyAttributesCacheService.getAttributes(sourceObjectMbeanName);
                                let attribute = attributes[Entaxy.capitalize(preSaveField.ui.sourceAttributeName)];
                                preSaveField.value = attribute;
                            }
                        }
                    }
                });
            }

            function fixFromRules(rules, value) {
                if (rules.forbid) {
                    let symbols = rules.forbid.symbols;
                    if (symbols) {
                        angular.forEach(symbols, (symbol) => {
                            if (rules.forbid.start === true && value.startsWith(symbol)) {
                                value = value.substring(1, value.length);
                            }
                        });
                    }
                }
                return value;
            }

            function getDependentFormFields(formFields, configurableOnly) {
                let dependentFormFields = formFields.filter(formField => formField.dependsOn);
                dependentFormFields.forEach(formField => {
                    if (!configurableOnly) {
                        if (formField.typeInfo && formField.typeInfo.type !== 'String' && formField.value === undefined) {
                            formField.readOnly = true;
                        }
                    }
                });
                return dependentFormFields;
            }

            function getDefiningFormFields(dependentFormFields, formFields) {
                let listOfDefiningNames = dependentFormFields.map(field => field.dependsOn);
                let definingNames = [];
                listOfDefiningNames.forEach(names => { definingNames = definingNames.concat(names); });
                definingNames = [...(new Set(definingNames))];

                return formFields.filter(formField => definingNames.includes(formField.name));
            }

            function processDependencies(dependentFormFields, definingFormFields, currentDefiningFormFieldName, newValue, oldValue, index) {

                if (oldValue) {
                    dependentFormFields.forEach(formField => {
                        if (formField.dependsOn.includes(currentDefiningFormFieldName)) {
                            formField.readyToCompute = false;
                            if (formField.typeInfo.type !== 'String') {
                                formField.readOnly = true;
                            }

                            if (formField.typeInfo && formField.typeInfo.type === 'list') {
                                formField.typeInfo.values = undefined;
                                formField.typeInfo.isEmptyIncluded = undefined;
                            }
                            if (formField.value !== undefined) {
                                setPreviousValue(formField, definingFormFields, currentDefiningFormFieldName, oldValue);
                            }
                            formField.value = undefined;
                        }
                    });
                }

                if (newValue) {
                    dependentFormFields.forEach(formField => {
                        if (formField.dependsOn.includes(currentDefiningFormFieldName)) {
                            let definingFormFieldsValues = getDefiningFormFieldsValues(formField, definingFormFields);
                            let readyToCompute =
                                formField.dependsOn.length == Object.keys(definingFormFieldsValues).length;
                            formField.readyToCompute = readyToCompute;
                            if (formField.typeInfo.type !== 'String') {
                                formField.readOnly = !readyToCompute;
                            }

                            if (formField.readyToCompute) {
                                let skipComputing = false;
                                if (formField.previousValuesInfo && formField.previousValuesInfo.length > 0) {
                                    let previousValueInfo = formField.previousValuesInfo.find(info =>
                                        comparePreviousValueInfoToCurrentInfo(info, definingFormFieldsValues));

                                    if (previousValueInfo) {
                                        formField.value = previousValueInfo['@value'];
                                        skipComputing = true;
                                    }
                                }

                                if (!skipComputing || (formField.typeInfo && formField.typeInfo.type === 'list')) {
                                    computeDependentFormFieldValue(formField, definingFormFields);
                                }
                            }
                        }
                    });
                }
            }

            function setPreviousValue(formField, definingFormFields, currentDefiningFormFieldName, oldValue) {
                formField.previousValue = formField.value;
                let definingFormFieldsPreviousValues =
                    getDefiningFormFieldsPreviousValues(formField, definingFormFields);
                definingFormFieldsPreviousValues[currentDefiningFormFieldName] = oldValue;
                if (Object.keys(definingFormFieldsPreviousValues).length === formField.dependsOn.length) {
                    if (formField.previousValuesInfo && formField.previousValuesInfo.length > 0) {
                        let previousValueInfo = formField.previousValuesInfo.find(info =>
                            comparePreviousValueInfoToCurrentInfo(info, definingFormFieldsPreviousValues));
                        if (previousValueInfo) {
                            previousValueInfo['@value'] = formField.value;
                        } else {
                            formField.previousValuesInfo.push({
                                ...definingFormFieldsPreviousValues,
                                ['@value']: formField.value
                            });
                        }
                    } else {
                        formField.previousValuesInfo = [{
                            ...definingFormFieldsPreviousValues,
                            ['@value']: formField.value
                        }];
                    }
                }
            }

            function getDefiningFormFieldsValues(formField, definingFormFields) {
                return getDefiningFormFieldsPropertyValue(formField, definingFormFields, 'value');
            }

            function getDefiningFormFieldsPreviousValues(formField, definingFormFields) {
                return getDefiningFormFieldsPropertyValue(formField, definingFormFields, 'previousValue');
            }

            function getDefiningFormFieldsPropertyValue(formField, definingFormFields, propertyName) {
                let definingFormFieldsValues = {};
                formField.dependsOn.forEach(name => {
                    let definingFormFieldValue = definingFormFields.find(field => field.name === name)[propertyName];
                    if (definingFormFieldValue !== undefined) {
                        definingFormFieldsValues[name] = definingFormFieldValue;
                    }
                });
                return definingFormFieldsValues;
            }

            function comparePreviousValueInfoToCurrentInfo(previousValueInfo, definingFormFieldValues) {
                let previousValueDefiningFormFieldNames = Object.keys(previousValueInfo)
                    .filter(key => key !== '@value');
                let currentDefiningFormFieldNames = Object.keys(definingFormFieldValues);
                let allDefiningFormFieldNames =
                    new Set([...previousValueDefiningFormFieldNames, ...currentDefiningFormFieldNames]);
                if (allDefiningFormFieldNames.size !== previousValueDefiningFormFieldNames.length ||
                    allDefiningFormFieldNames.size !== currentDefiningFormFieldNames.length) {
                    return false;
                }

                for (let i = 0; i < previousValueDefiningFormFieldNames.length; i++) {
                    let name = previousValueDefiningFormFieldNames[i];
                    if (previousValueInfo[name] !== definingFormFieldValues[name]) {
                        return false;
                    }
                }
                return true;
            }

            function computeDependentFormFieldValue(formField, definingFormFields) {
                if (formField.typeInfo && (formField.typeInfo.type === 'list' || formField.typeInfo.type === 'String')) {
                    let sourcePropertyName = formField.typeInfo.source.property;
                    let sourceType = formField.typeInfo.source.sourceType;
                    let sourcePath = formField.typeInfo.source.path;

                    // we assume that if the source type is resource
                    // then source property contains its location
                    if (sourceType === 'resource') {
                        let promise = computeResourcePropertyValue(sourcePropertyName, sourcePath, definingFormFields);
                        if (promise) {
                            promise
                            .then(result => {
                                if (formField.typeInfo.type === 'list') {
                                    formField.typeInfo.values = result;
                                    if (formField.required) {
                                        formField.typeInfo.isEmptyIncluded = (result.length == 1) ? false : undefined;
                                    }
                                } else if (formField.typeInfo.type === 'String') {
                                    formField.value = result;
                                }
                            })
                            .catch((error) => {
                                Entaxy.notificationError(error);
                                Entaxy.log.error(error);
                            });
                        }
                    }
                } else if (formField.type === 'xml:route') {
                    let resourceProvider = formField.defaultValue[FIELD_COMMON_KEYS.RESOURCE].provider;
                    let resourceLocation = formField.typeInfo.location;
                    let splitResourceLocation = resourceLocation.split('/');
                    let regex = /\${[\w#.-]+}/g;
                    for (let i = 0; i < splitResourceLocation.length; i++) {
                        if (splitResourceLocation[i].match(regex)) {
                            let property = splitResourceLocation[i].substring(
                                splitResourceLocation[i].indexOf(`\${`) + 2,
                                splitResourceLocation[i].indexOf('}'));
                            if (formField.typeInfo[property]) {
                                let sourcePropertyName = formField.typeInfo[property].property;
                                let sourceType = formField.typeInfo[property].sourceType;
                                let sourcePath = formField.typeInfo[property].path;

                                // we assume that if the source type is resource
                                // then source property contains its location
                                if (sourceType === 'resource') {
                                    splitResourceLocation[i] = {
                                        promise: computeResourcePropertyValue(
                                            sourcePropertyName, sourcePath, definingFormFields)
                                    };
                                }
                            } else {
                                let value = definingFormFields.find(field => field.name === property).value;
                                splitResourceLocation[i] = splitResourceLocation[i].replace(regex, value);
                            }
                        }
                    }

                    let promises = splitResourceLocation
                        .filter(chunk => chunk.promise)
                        .map(chunk => chunk.promise);

                    $q.all(promises)
                    .then(results => {
                        results.forEach(result => {
                            let index = splitResourceLocation.findIndex(chunk => chunk.promise);
                            splitResourceLocation[index] = result;
                        });

                        let computedResourceLocation = splitResourceLocation.join('/');

                        entaxyResourcesService
                        .getResource(resourceProvider + ':' + computedResourceLocation)
                        .then(response => assignXmlValue(formField, response));
                    })
                    .catch((error) => {
                        Entaxy.notificationError(error);
                        Entaxy.log.error(error);
                    });
                }
            }

            function computeResourcePropertyValue(sourcePropertyName, sourcePath, definingFormFields) {
                let deferred = $q.defer();

                let splitSourcePath = sourcePath.split('.');
                let sourcePropertyValue = definingFormFields.find(field => field.name === sourcePropertyName).value;

                // for now we assume there is always #metadata#
                if (splitSourcePath[0] === '#metadata#') {
                    let promise = entaxyResourcesService.getResourceMetadata(sourcePropertyValue);

                    if (promise) {
                        promise
                        .then(response => {
                            let metadata = JSON.parse(response);
                            let section = JSON.parse(metadata[splitSourcePath[1]]);

                            let regex = /\${[\w#.-]+}/g;
                            let result = section;
                            for (let i = 2; i < splitSourcePath.length; i++) {
                                if (!splitSourcePath[i].match(regex)) {
                                    result = result[splitSourcePath[i]];
                                } else {
                                    let fieldName = splitSourcePath[i].substring(2, splitSourcePath[i].length - 1);
                                    let value = definingFormFields.find(field => field.name === fieldName).value;
                                    result = result[value];
                                }
                            }
                            deferred.resolve(result);
                        });
                    } else {
                        deferred.reject('Resource service is not found');
                    }
                }
                return deferred.promise;
            }

            function validateFields(fields, selectedFactory, profileMBeanName, checkUniquenessParentFields) {
                let errors = {};

                _.forEach(fields, (field) => {
                    if (isValidationNeeded(field)) {

                        if (field.type === 'list' && field.value) {
                            validateListField(field, errors);
                        }

                        if (field.required && field.type !== 'list' && isEmpty(field)) {
                            errors[field.name] = Entaxy.ERROR_MESSAGE.EMPTY;
                        }

                        if (field.type === 'xml:route' && !isSupposedlyXml(field.value)) {
                            errors[field.name] = Entaxy.ERROR_MESSAGE.INVALID;
                        }

                        if (!errors[field.name] && field.type === 'map' && field.hasErrors) {
                            errors[field.name] = 'Map must not include empty or duplicate keys';
                        }

                        if (!errors[field.name] && field.type === 'password' && isPasswordConfirmed(field)) {
                            errors[field.name] = Entaxy.ERROR_MESSAGE.PASSWORD_CONFIRMATION;
                        }

                        if (!errors[field.name] && hasValidationRules(field)) {
                            validateByRules(field, field.validation.rules, errors);
                        }

                        if (!errors[field.name] && field.checkUniquenessByParentField && checkUniquenessParentFields) {
                            if (checkUniquenessParentFields[field.name].includes(field.value)) {
                                errors[field.name] = Entaxy.ERROR_MESSAGE.UNIQUE;
                            }
                        }

                        if (!errors[field.name] && (field.uniquenessExtraProperties || field.uniquenessCurrentOnly)) {
                            errors[field.name] = checkUniqueness(field.name, field.value,
                                field.uniquenessCurrentOnly, field.uniquenessExtraProperties,
                                fields, selectedFactory, profileMBeanName);
                        }
                    }
                });

                let deferred = $q.defer();

                if (Object.keys(errors).length > 0) {
                    let errorArrayWithPromises = [];

                    Object.keys(errors).forEach(key => {
                        if (typeof errors[key] !== 'string') {
                            errorArrayWithPromises.push({ name: key, promise: errors[key] });
                        }
                    });

                    if (errorArrayWithPromises.length !== 0) {

                        $q.all(errorArrayWithPromises.map(obj => { return obj.promise; }))
                        .then(result => {
                            for (let i = 0; i < errorArrayWithPromises.length; i++) {
                                if (result[i]) {
                                    errors[errorArrayWithPromises[i].name] = result[i];
                                } else {
                                    delete errors[errorArrayWithPromises[i].name];
                                }
                            }

                            deferred.resolve(errors);
                        });
                    } else {
                        deferred.resolve(errors);
                    }
                } else {
                    deferred.resolve(errors);
                }

                return deferred.promise;
            }

            function isValidationNeeded(field) {
                return !field.isBackRef && !field.isHidden && !field.isCalculated;
            }

            function isEmpty(field) {
                return (field.value === undefined ||
                    (typeof field.value === 'string' ? field.value.trim().length === 0 : false) ||
                    (field.typeInfo && field.typeInfo.type === 'enum' &&
                        field.typeInfo.multiple && field.value.length === 0));
            }
            
            function isPasswordConfirmed(field) {
                return !field.ignoreConfirmationValue && field.value && field.value !== field.confirmationValue;
            }

            function hasValidationRules(field) {
                return field.validation && field.validation.rules;
            }

            function validateListField(field, errors) {
                field.value.filter(item => !item.toDeletion).forEach(item => {
                    item.errors = {};
                    item.formFields.forEach(embedField => {
                        if (isValidationNeeded(embedField)) {
                            if (embedField.required && isEmpty(embedField)) {
                                item.errors[embedField.name] = Entaxy.ERROR_MESSAGE.EMPTY;
                            }

                            if (!item.errors[embedField.name] && embedField.type === 'password'
                                    && isPasswordConfirmed(embedField)) {
                                item.errors[embedField.name] = Entaxy.ERROR_MESSAGE.PASSWORD_CONFIRMATION;
                            }

                            if (!item.errors[embedField.name] && hasValidationRules(embedField)) {
                                validateByRules(embedField, embedField.validation.rules, item.errors);
                            }
                        }
                    });

                    if (Object.keys(item.errors).length > 0) {
                        if (!errors[field.name]) {
                            errors[field.name] = 'hasErrors';
                        }
                    } else {
                        item.errors = undefined;
                    }
                });
            }

            function validateByRules(field, rules, errors) {
                let rulesNames = Object.keys(rules);
                for (let i = 0; i < rulesNames.length; i++) {
                    validateByRule(field, rulesNames[i], rules[rulesNames[i]], errors);
                    if (errors[field.name]) {
                        break;
                    }
                }
            }

            function validateByRule(field, ruleName, rule, errors) {
                if (ruleName === 'length') {
                    if (rule.min) {
                        if (field.value.trim().length < rule.min) {
                            errors[field.name] = 'Value must contain at least ' + rule.min + ' characters';
                        }
                    } else if (rule.max) {
                        if (field.value.trim().length > rule.max) {
                            errors[field.name] = 'Value must not contain greater then ' + rule.max + ' characters';
                        }
                    }
                } else if (ruleName === 'content') {
                    if (rule.regex) {
                        let regex = new RegExp(rule.regex);
                        const checkedValue = field.value !== undefined ? field.value : '';
                        if (!checkedValue.match(regex)) {
                            errors[field.name] = rule.errorMessage ?
                                rule.errorMessage : 'Value contains invalid characters';
                        }
                    }
                } else if (ruleName === 'checkChildrenUniqueness') {
                    let error = {};
                    if (field.value && field.value.length > 0) {
                        rule.fields.forEach(fieldName => {
                            let values = [];
                            let duplicateValues = [];
                            field.value.filter(item => !item.toDeletion).forEach(fieldItem => {
                                let formField = fieldItem.formFields.find(formField => formField.name === fieldName);
                                if (formField) {
                                    if (values.includes(formField.value)) {
                                        duplicateValues.push(formField.value);
                                    }
                                    values.push(formField.value);
                                }
                            });
                            if (duplicateValues.length > 0) {
                                error[fieldName] = {
                                    values: duplicateValues,
                                    message: Entaxy.ERROR_MESSAGE.UNIQUE
                                };
                            }
                        });
                        errors[field.name] = Object.keys(error).length > 0 ? error : undefined;
                    }
                }
            }

            function checkUniqueness(fieldName, fieldValue, currentOnly, properties, fields, selectedFactory,
                                     profileMBeanName) {
                let isInvalid = false;

                let selectedFactoryAttributes = entaxyAttributesCacheService.getAttributes(selectedFactory.mbeanName);
                let fieldNameProperty = Entaxy.capitalize(fieldName);
                if (properties) {
                    properties = properties.map(property => {
                        return Entaxy.capitalize(property);
                    });
                }

                let profileMBean;
                if (profileMBeanName) {
                    profileMBean = getDomainFolder().findDescendant(child => child.objectName === profileMBeanName);
                } else {
                    profileMBean = workspace.getSelectedMBean();
                }

                if (profileMBean && profileMBean.isFolder) {
                    return getAllChildMBeansByRuntimeType(profileMBean, selectedFactoryAttributes['Type'])
                        .then((mbeans) => {

                            mbeans.forEach(mbean => {
                                if (fieldValue === mbean.attributes[fieldNameProperty]) {
                                    if (currentOnly) {
                                        isInvalid = true;
                                    } else {
                                        for (let i = 0; i < properties.length; i++) {
                                            let relatedField = fields.find(field =>
                                                field.name === Entaxy.uncapitalize(properties[i]));

                                            if (relatedField) {
                                                if (relatedField.value === mbean.attributes[properties[i]]) {
                                                    isInvalid = true;
                                                }
                                            } else {
                                                let typeInfo = selectedFactoryAttributes['TypeInfo'];
                                                let typeInfoDesiredValue = typeInfo[Entaxy.uncapitalize(properties[i])];

                                                if (typeInfoDesiredValue === mbean.attributes[properties[i]]) {
                                                    isInvalid = true;
                                                }
                                            }
                                        }
                                    }
                                }
                            });

                            return isInvalid ? Entaxy.ERROR_MESSAGE.UNIQUE : '';
                        });
                }

                return '';
            }

            function openAddItemModalAndProcessResults(resolve, runtimeType, updateFn) {
                getFactoriesByType(runtimeType).then(factories => {
                    resolve.factories = () => factories;
                    openModalAndProcessResults(resolve, false, null, updateFn);
                });
            }

            function openAddItemFromTemplateModalAndProcessResults(resolve, item, runtimeType, updateFn) {

                readConfiguration(item.mbeanName)
                .then(itemInfo => {

                    let factory = getFactoryByTitle(itemInfo.factoryId);

                    let resultResolve = {
                        ...resolve,
                        mode: () => Entaxy.MODAL_MODES.ADD,
                        factories: () => [ factory ],
                        info: () => itemInfo,
                        useTemplate: () => true,
                        itemMbeanName: () => item.mbeanName
                    };

                    openModalAndProcessResults(resultResolve, item.isLocal, null, updateFn);
                }).catch(error => {
                    Entaxy.notificationError('Unable to view configuration of selected item.');
                    Entaxy.log.error(error);
                });
            }

            function openEditItemModalAndProcessResults(item, itemType, runtimeType, mode, resolvedProperties, updateFn) {

                readConfiguration(item.mbeanName)
                .then(itemInfo => {

                    let factory = getFactoryByTitle(itemInfo.factoryId);

                    if (resolvedProperties) {
                        let properties = itemInfo.properties;
                        resolvedProperties.forEach(resolvedProperty => {
                            if (properties[resolvedProperty.name] !== resolvedProperty.value) {
                                properties[resolvedProperty.name] = resolvedProperty.value;
                            }
                        });
                    }

                    openModalAndProcessResults({
                        itemName: () => item.name,
                        itemType: () => itemType,
                        itemMbeanName: () => item.mbeanName,
                        mode: () => mode,
                        info: () => itemInfo,
                        factories: () => [ factory ]
                    }, item.isLocal, item.mbeanName, updateFn);
                }).catch(error => {
                    Entaxy.notificationError('Unable to view configuration of selected item.');
                    Entaxy.log.error(error);
                });
            }

            function openModalAndProcessResults(resolve, isLocal, mbeanNameToUpdateAttributesCache, updateFn) {
                openEntaxyModal(resolve)
                .then(args => {
                    if (mbeanNameToUpdateAttributesCache) {
                        entaxyAttributesCacheService.setToUpdate(mbeanNameToUpdateAttributesCache);
                    }
                    saveItem(args, isLocal, updateFn);
                },
                reason => {
                    if (reason && reason !== Entaxy.MODAL_CANCEL_REASON.ESCAPE_KEY_PRESS) {
                        Entaxy.notificationError(reason);
                    }
                });
            }

            function openEntaxyModal(resolve) {
                return $uibModal.open({
                    component: 'entaxyModal',
                    resolve: resolve,
                    size: 'xl',
                    backdrop: 'static'
                }).result;
            }

            function requestConfirmationForSavingIfNeededAndProceed(isConfirmationNeeded, saveFn) {
                if (isConfirmationNeeded) {
                    let title = 'Confirm Saving';
                    let message = 'There are no changes to this object. ' +
                        'Do you want to continue and recreate object with the same properties?';
                    openConfirmationWindow(title, message).then(() => {
                        saveFn();
                    });
                } else {
                    saveFn();
                }
            }

            function requestConfirmationForSavingOwnerAndProceed(saveFn) {
                let title = 'Confirm Saving';
                let message = 'This will save changes of all private objects of the owner. ' +
                    'Do you want to proceed?';
                openConfirmationWindow(title, message).then(() => {
                    saveFn();
                });
            }

            function saveItem(args, isLocal, updateFn) {
                let objectId = args.fields.find(field => field.name === 'objectId').value;

                let allFields = args.fields.filter(field => field.name !== 'objectId' && !field.postSaveProcessStage);
                let postSaveProcessStageFields = args.fields.filter(field => field.postSaveProcessStage);

                let mbeanName;
                let scope;
                let fields;

                if (args.configurableOnly) {
                    let config = args.configProperties[CONFIG_PROPERTIES_KEYS.CONFIG];
                    if (!config) {
                        Entaxy.notificationInfo('Object does not have configurable fields, ' +
                            'therefore can not be changed');
                        return;
                    }
                    let configFactoryFolder = getFolderByTitle(config.factoryId);
                    mbeanName = configFactoryFolder.objectName;
                    objectId = config.targetId;
                    scope = config.scope;
                    fields = getConfigurableOnlyFields(allFields, config);
                    addUnchangedAndHiddenConfigFields(fields, config);
                } else {
                    let changedFields = allFields.filter(field => field.changed);
                    let configChangedFields =
                        args.configProperties && args.configProperties[CONFIG_PROPERTIES_KEYS.CONFIG_LINK] ?
                            changedFields.filter(field => field.configured) : [];

                    if (configChangedFields.length === 0 ||
                        configChangedFields.length !== changedFields.length ||
                        configChangedFields.filter(field =>
                            field.value !== undefined || field.valueToConfig !== undefined).length === 0) {
                        mbeanName = args.factoryId.mbeanName;
                        scope = Entaxy.SCOPE.PUBLIC;
                        fields = allFields.filter(field => field.value !== undefined);
                    } else {
                        let config = args.configProperties[CONFIG_PROPERTIES_KEYS.CONFIG];
                        let configFactoryFolder = getFolderByTitle(config.factoryId);
                        mbeanName = configFactoryFolder.objectName;
                        objectId = config.targetId;
                        scope = config.scope;
                        fields = changedFields.map(field => {
                            if (field.valueToConfig !== undefined) {
                                field.value = field.valueToConfig;
                            }
                            return field;
                        });
                        addUnchangedAndHiddenConfigFields(fields, config);
                    }
                }

                let properties = fields.reduce((obj, cur) => ({ ...obj, [cur.name] : cur.value }), {});
                let instructions = isLocal ? {"@LIFECYCLE":["general", "local"]} : {"@LIFECYCLE":["general"]};

                operationsService.executeOperation(mbeanName,
                                                    { name: 'createObjectByInstructions' },
                                                    [ objectId, scope, instructions, properties ] )
                .then(result => {
                    let resultObject = JSON.parse(result);
                    let tableHtml = Entaxy.createTableFromResponse(resultObject);
                    let notificationType = Entaxy.getNotificationTypeFromResponse(resultObject);
                    const isSucceeded = notificationType === Entaxy.NOTIFICATION_TYPE.SUCCESS;
                    let mainMessage = isSucceeded ? 'Operation Succeeded' : 'Operation Failed';
                    Entaxy.notificationWithDetails(notificationType, mainMessage, tableHtml,
                        Entaxy.configuration[Entaxy.CONFIGURATION_KEYS.NOTIFICATION_TIMEOUT_TABLE]);

                    if (objectId && isSucceeded) {
                        let factoryAttributes = jolokia.getAttribute(mbeanName);
                        let owner = objectId + ':' + factoryAttributes.Type;
                        entaxyPrivateObjectsCacheService.removeFromCache(owner);
                    }

                    postSave(postSaveProcessStageFields, objectId, properties);

                    if (updateFn) {
                        updateFn(properties, objectId);
                    }
                }).catch(error => {
                    Entaxy.notificationError(error);
                    Entaxy.log.error(error);
                });
            }

            function getConfigurableOnlyFields(allFields, config) {
                let configHint = JSON.parse(Entaxy.base64ToString(config.properties.__entaxy_config_hint));
                let fields = allFields.filter(field => field.configured).map(field => {
                    if (field.changed && field.value === undefined) {
                        if (field.valueToConfig === undefined) {
                            let type = configHint && configHint.fields[field.name] ?
                                Entaxy.convertToHtmlInputType(configHint.fields[field.name].type) : undefined;
                            field.value = getFixedConfigValue(type, field.value);
                        } else {
                            field.value = field.valueToConfig;
                        }
                    }
                    return field;
                });
                fields = fields.filter(field => field.value !== undefined);
                return fields;
            }

            function getFixedConfigValue(type, value) {
                switch (type) {
                    case 'checkbox':
                        return value;
                    case 'number':
                        return  null;
                    default:
                        return '';
                }
            }

            function addUnchangedAndHiddenConfigFields(fields, config) {
                Object.keys(config.properties).forEach(key => {
                    if (!fields.find(field => field.name === key)) {
                        fields.push({name: key, value: config.properties[key]});
                    }
                });
            }

            function postSave(fields, objectId, properties) {
                if (fields && fields.length > 0) {
                    fields.forEach(field => {
                        if (field.type === 'resource' && field.value) {
                            let fileName;
                            if (field.ui.resourceFilenameSourceField) {
                                fileName = field.ui.resourceFilenameSourceField === 'objectId' ?
                                    objectId : properties[field.ui.resourceFilenameSourceField];
                            }
                            if (fileName && field.ui.resourceFilenameExtensionSourceField) {
                                fileName += '.' + properties[field.ui.resourceFilenameExtensionSourceField];
                            }
                            if (fileName && field.ui.resourceProviderProtocol) {
                                entaxyResourcesService
                                    .uploadFile(field.value, field.ui.resourceProviderProtocol, fileName).then(() => {});
                            }
                        }
                    })
                }
            }

            function downloadFileFromBase64StringContent(base64String, fileName) {
                let fileContent = Uint8Array.from(atob(base64String), (m) => m.codePointAt(0));
                let file = new File([fileContent], fileName);

                let url = window.URL || window.webkitURL;

                let downloadLink = angular.element('<a></a>');
                downloadLink.attr('href', url.createObjectURL(file));
                downloadLink.attr('target', '_self');
                downloadLink.attr('download', fileName);
                downloadLink[0].click();
            }

            function processTableOperation(operationType, objectName, runtimeType, objectDisplayName, forced, soft) {
                let operation = { name: operationType + 'Bundle' };
                let properties = [ objectName, runtimeType, forced ? true : false, soft ? true : false ];

                operationsService.executeOperation(objectMbean.objectName, operation, properties)
                .then(resultString => {
                    let result = JSON.parse(resultString);
                    let affectedObjects = result.affected ? JSON.parse(result.affected) : undefined;

                    switch (result.result) {
                        case 'DONE':
                            Entaxy.notificationSuccess('Operation Succeeded');
                            break;
                        case 'REQUEST':
                            processRequest(operationType, objectName, runtimeType, objectDisplayName, affectedObjects);
                            break;
                        case 'BLOCKED':
                            let affectedObjectsTable = Entaxy
                                .createTableFromOperationAffectedObjects(affectedObjects);
                            let mainMessage = 'Operation blocked, '
                                + 'the following operations must be applied before invoking the current one';
                            Entaxy.notificationWithDetails(Entaxy.NOTIFICATION_TYPE.DANGER,
                                mainMessage, affectedObjectsTable,
                                Entaxy.configuration[Entaxy.CONFIGURATION_KEYS.NOTIFICATION_TIMEOUT_TABLE]);
                            break;
                        case 'FAILED':
                            Entaxy.notificationError('Operation failed: ' + result.message);
                            break;
                        default:
                            break;
                    }
                }).catch(error => {
                    Entaxy.notificationError(error);
                    Entaxy.log.error(error);
                });
            }

            function processRequest(operationType, objectName, runtimeType, objectDisplayName, affectedObjects) {
                $uibModal.open({
                    component: 'entaxyOperationTableRequestModal',
                    resolve: {
                        operationType: () => operationType,
                        objectFullId: () => objectName + ':' + runtimeType,
                        objectDisplayName: () => objectDisplayName,
                        affectedObjects: () => affectedObjects
                    },
                    size: 'sm-custom',
                    backdrop: 'static'
                })
                .result.then(flag => {
                    switch (flag) {
                        case 'all':
                            processTableOperation(operationType, objectName, runtimeType, objectDisplayName,
                                true, false);
                            break;
                        case 'current':
                            processTableOperation(operationType, objectName, runtimeType, objectDisplayName,
                                false, true);
                            break;
                        default:
                            break;
                    }
                });
            }

            function processUserManagementOperation(operationName, args, populateTableFn) {
                let selectedMbeanName = workspace.getSelectedMBeanName();
                operationsService.executeOperation(selectedMbeanName, { name: operationName }, args)
                .then(result => {
                    Entaxy.notificationSuccess(result);
                    if (populateTableFn) {
                        populateTableFn();
                    }
                }).catch(error => {
                    Entaxy.notificationError(error);
                    Entaxy.log.error(error);
                });
            }

            function getAllChildMBeansByAttribute(folder, attributeName, attributeValue) {
                return $q(function (resolve) {
                    let promises = [];
                    let mbeans = [];
                    if (folder.children) {
                        getAllChildMBeansByAttributeRecursive(folder.children, attributeName, attributeValue,
                            promises, mbeans);
                    }
                    $q.all(promises).then(() => resolve(mbeans));
                });
            }

            function getAllChildMBeansByAttributeRecursive(children, attributeName, attributeValue, promises, mbeans) {
                children.forEach(child => {
                    if (child.objectName) {
                        promises.push(
                            jolokiaService.getAttribute(child.objectName).then((attributes) => {
                                if (attributes[attributeName] === attributeValue) {
                                    mbeans.push({ mbean: child, attributes: attributes });
                                }
                            }).catch(error => Entaxy.log.error(error))
                        );
                    }
                    if (child.children) {
                        getAllChildMBeansByAttributeRecursive(child.children, attributeName, attributeValue,
                            promises, mbeans);
                    }
                });
            }

            function getMBeansByObjectFullIds(objectFullIds) {
                return $q(function (resolve) {
                    let domain = getDomainFolder();
                    let promises = [];
                    let mbeans = [];

                    if (domain.children && angular.isArray(objectFullIds) && objectFullIds.length > 0) {
                        getMBeansByObjectFullIdsRecursive(domain.children, objectFullIds, promises, mbeans);
                    }
                    $q.all(promises).then(() => resolve(mbeans));
                });
            }

            function getMBeansByObjectFullIdsRecursive(children, objectFullIds, promises, mbeans) {
                children.forEach(child => {
                    if (child.objectName) {
                        promises.push(
                            jolokiaService.getAttribute(child.objectName).then((attributes) => {
                                if (attributes['ObjectFullId'] && objectFullIds.includes(attributes['ObjectFullId'])) {
                                    mbeans.push({ mbean: child, attributes: attributes });
                                }
                            })
                        );
                    }
                    if (child.children) {
                        getMBeansByObjectFullIdsRecursive(child.children, objectFullIds, promises, mbeans);
                    }
                });
            }

            function getAllChildMBeansByRuntimeType(folder, runtimeType) {
                return getAllChildMBeansByAttribute(folder, 'RuntimeType', runtimeType);
            }

            function findObjectsByFilter(filter) {
                let operation = { name: 'findObjects' };
                let properties = [ filter ];

                return operationsService.executeOperation(objectMbean.objectName, operation, properties);
            }

            function readConfiguration(mbeanName) {
                const readConfigurationOperation = { name: 'readConfiguration' };
                return $q(function (resolve) {
                    operationsService.executeOperation(mbeanName, readConfigurationOperation, [])
                    .then(result => {
                        let objectInfo = JSON.parse(result);

                        readExtendedData(mbeanName).then(resultExtendedData => {
                            let extendedData = JSON.parse(resultExtendedData);
                            if (extendedData.applications) {
                                objectInfo.configurableOnly = true;
                            }

                            if (objectInfo.properties && objectInfo.properties[CONFIG_PROPERTIES_KEYS.CONFIG_LINK]) {
                                let configFolder =
                                    getFolderByTitle(objectInfo.properties[CONFIG_PROPERTIES_KEYS.CONFIG_LINK]);

                                operationsService
                                .executeOperation(configFolder.objectName, readConfigurationOperation, [])
                                .then(resultConfig => {
                                    let configInfo = JSON.parse(resultConfig);

                                    if (configInfo.properties) {
                                        objectInfo.properties[CONFIG_PROPERTIES_KEYS.CONFIG].properties =
                                            configInfo.properties;

                                        resolve(objectInfo);
                                    } else {
                                        resolve(objectInfo);
                                    }
                                });
                            } else {
                                resolve(objectInfo);
                            }
                        });
                    });
                });
            }

            function getBundleDiag(bundleId) {
                let karafDomainFolder = workspace.tree.get('org.apache.karaf');
                if (karafDomainFolder) {
                    let karafRootMbean = karafDomainFolder.findDescendant(child => {
                        if (child.objectName) {
                            let parsedMbean = Core.parseMBean(child.objectName);
                            return parsedMbean.attributes.type === 'bundle' && parsedMbean.attributes.name === 'root';
                        }
                    });

                    if (karafRootMbean) {
                        return operationsService
                            .executeOperation(karafRootMbean.objectName, {name: 'getDiag'}, [ bundleId ]);
                    } else {
                        return $q.reject('Cannot find karaf root mbean to get diag');
                    }
                } else {
                    return $q.reject('Cannot find karaf domain to get diag');
                }
            }

            function readExtendedData(mbeanName) {
                return operationsService.executeOperation(mbeanName, {name: 'readExtendedData'}, []);
            }

            function readObjectClusterState(mbeanName) {
                return $q(function (resolve, reject) {
                    readExtendedData(mbeanName).then(result => {
                        let extendedData = JSON.parse(result);

                        if (extendedData.cluster) {
                            resolve(extendedData.cluster.objectClusterState);
                        } else {
                            reject('Object cluster state information is not found');
                        }
                    });
                });
            }

            function resolveRoutesFieldValueAndAddNew(args, ownerMbean) {
                let ROUTES_FIELD_NAME = 'routes';
                let routesField = args.fields.find(field => field.name === ROUTES_FIELD_NAME);

                return $q(function (resolve) {
                    resolveRoutesFieldValue(routesField, ownerMbean).then(resolvedValue => {
                        let addedRoutes = getAddedRoutes(ownerMbean);

                        if (routesField) {
                            routesField.value = resolvedValue.concat(addedRoutes);
                        } else {
                            if (addedRoutes.length > 0) {
                                args.fields.push({
                                    name: ROUTES_FIELD_NAME,
                                    value: addedRoutes
                                });
                            }
                        }
                        resolve();
                    });
                });
            }

            function resolveResourceRefFields(fields) {
                let promises = [];
                fields.forEach(field => {
                    if (field.value[FIELD_COMMON_KEYS.RESOURCE_REF]) {
                        promises.push(
                            getXmlFromConfigPropertyValue(field.value).then(xml => {
                                field.value = Entaxy.stringToBase64(xml);
                            })
                        );
                    }
                });
                return $q.all(promises);
            }

            function resolveRoutesFieldValue(routesField, ownerMbean) {
                return $q(function (resolve) {
                    let promises = [];
                    let resolvedValue = [];
                    if (routesField) {
                        routesField.value.forEach(route => {
                            let routeFullId = route.targetId + ':' + route.targetType;
                            let routeMbean = ownerMbean.findDescendant(child => {
                                if (child.objectName) {
                                    let attributes = entaxyAttributesCacheService.getAttributes(child.objectName);
                                    return attributes && attributes.ObjectFullId === routeFullId;
                                }
                            });
                            let routeMbeanName = routeMbean.objectName;

                            if (entaxyPrivateObjectsCacheService.isChanged(routeMbeanName)) {
                                let objectInfo = entaxyPrivateObjectsCacheService.getChangedObjectInfo(routeFullId);
                                if (!objectInfo.toRemoval) {
                                    let config = processRouteConfig(objectInfo.config);
                                    let routeContent = config.properties.routeContent;
                                    if (angular.isObject(routeContent)) {
                                        promises.push(getXmlFromConfigPropertyValue(routeContent).then(xml => {
                                            config.properties.routeContent = Entaxy.stringToBase64(xml);
                                            resolvedValue.push(config);
                                        }));
                                    } else {
                                        promises.push($q.resolve(resolvedValue.push(config)));
                                    }
                                }
                            } else {
                                promises.push(
                                    readConfiguration(routeMbeanName).then(originConfig => {
                                        let config = processRouteConfig(originConfig);
                                        let routeContent = config.properties.routeContent;
                                        return getXmlFromConfigPropertyValue(routeContent).then(xml => {
                                            config.properties.routeContent = Entaxy.stringToBase64(xml);
                                            resolvedValue.push(config);
                                        });
                                    })
                                );
                            }
                        });
                    }
                    $q.all(promises).then(() => resolve(resolvedValue));
                });
            }

            function getAddedRoutes(mbean) {
                let containerMbeanName = findRouteContainerMbeanName(mbean);
                let addedRoutes = [];
                if (entaxyPrivateObjectsCacheService.hasAddedChildren(containerMbeanName)) {
                    let addedObjectsInfo = entaxyPrivateObjectsCacheService.getAddedObjectsInfo(containerMbeanName);
                    addedObjectsInfo.forEach(objectInfo => {
                        if (!objectInfo.toRemoval) {
                            addedRoutes.push({
                                factoryId: objectInfo.factoryId,
                                scope: objectInfo.scope,
                                type: objectInfo.type,
                                properties: objectInfo.properties
                            });
                        }
                    });
                }
                return addedRoutes;
            }

            function processRouteConfig(config) {
                delete config['__uuid__'];
                delete config.isEmbedded;
                return config;
            }

            function getProfiles() {
                return $q(function (resolve) {
                    let profilesFolder = getDomainFolder()
                        .findDescendant(child => child.objectName && child.objectName.endsWith('category=profiles'));
                    getAllChildMBeansByRuntimeType(profilesFolder, Entaxy.RUNTIME_TYPE.PROFILE)
                    .then((mbeans) => {
                        if (mbeans.length > 0) {
                            resolve(mbeans.map(mbean => {
                                let displayName = mbean.attributes.DisplayName &&
                                mbean.attributes.DisplayName.trim().length > 0 ?
                                    mbean.attributes.DisplayName : mbean.attributes.Name;
                                return {
                                    name: mbean.attributes.Name,
                                    displayName: displayName,
                                    mbeanName: mbean.mbean.objectName
                                };
                            }));
                        } else {
                            resolve(mbeans);
                        }
                    });
                });
            }

            function openConfirmationWindow(title, message) {
                return $uibModal.open({
                    component: 'entaxyConfirmationModal',
                    resolve: {
                        title: () => title,
                        message: () => message
                    },
                    size: 'sm-custom',
                    backdrop: 'static',
                    windowTopClass: 'modal-top-margin-center-override'
                }).result;
            }

            function getActiveTabIndex() {
                let hawtioTabs = document.getElementsByClassName("hawtio-tab");

                for (let i = 0; i < hawtioTabs.length; i++) {
                    if (hawtioTabs[i].classList.contains('active')) {
                        return i;
                    }
                }

                return -1;
            }

            function updateTabSelection(targetTabIndex) {
                let currentActiveTabIndex = getActiveTabIndex();

                if (targetTabIndex !== currentActiveTabIndex) {
                    document.getElementsByClassName("hawtio-tab")[targetTabIndex].children[0].click();
                }
            }
        }
    ])
})(Entaxy || (Entaxy = {}));
