export default function service()

in modules/frontend/app/services/LegacyUtils.service.js [25:554]


export default function service(ErrorPopover) {
    function isDefined(v) {
        return !_.isNil(v);
    }

    function isEmptyString(s) {
        if (isDefined(s))
            return s.trim().length === 0;

        return true;
    }

    const javaBuiltInClasses = [
        'BigDecimal',
        'Boolean',
        'Byte',
        'Date',
        'Double',
        'Float',
        'Integer',
        'Long',
        'Object',
        'Short',
        'String',
        'Time',
        'Timestamp',
        'UUID'
    ];

    const javaBuiltInTypes = [
        'BigDecimal',
        'boolean',
        'Boolean',
        'byte',
        'byte[]',
        'Byte',
        'Date',
        'double',
        'Double',
        'float',
        'Float',
        'int',
        'Integer',
        'long',
        'Long',
        'Object',
        'short',
        'Short',
        'String',
        'Time',
        'Timestamp',
        'UUID'
    ];

    const javaBuiltInFullNameClasses = [
        'java.math.BigDecimal',
        'java.lang.Boolean',
        'java.lang.Byte',
        'java.sql.Date',
        'java.lang.Double',
        'java.lang.Float',
        'java.lang.Integer',
        'java.lang.Long',
        'java.lang.Object',
        'java.lang.Short',
        'java.lang.String',
        'java.sql.Time',
        'java.sql.Timestamp',
        'java.util.UUID'
    ];

    /**
     * @param clsName Class name to check.
     * @param additionalClasses List of classes to check as builtin.
     * @returns {Boolean} 'true' if given class name is a java build-in type.
     */
    function isJavaBuiltInClass(clsName, additionalClasses) {
        if (isEmptyString(clsName))
            return false;

        return _.includes(javaBuiltInClasses, clsName) || _.includes(javaBuiltInFullNameClasses, clsName)
            || (_.isArray(additionalClasses) && _.includes(additionalClasses, clsName));
    }

    const SUPPORTED_JDBC_TYPES = [
        'BIGINT',
        'BIT',
        'BOOLEAN',
        'BLOB',
        'CHAR',
        'CLOB',
        'DATE',
        'DECIMAL',
        'DOUBLE',
        'FLOAT',
        'INTEGER',
        'LONGNVARCHAR',
        'LONGVARCHAR',
        'NCHAR',
        'NUMERIC',
        'NVARCHAR',
        'REAL',
        'SMALLINT',
        'TIME',
        'TIMESTAMP',
        'TINYINT',
        'VARCHAR'
    ];

    /*eslint-disable */
    const JAVA_KEYWORDS = [
        'abstract',
        'assert',
        'boolean',
        'break',
        'byte',
        'case',
        'catch',
        'char',
        'class',
        'const',
        'continue',
        'default',
        'do',
        'double',
        'else',
        'enum',
        'extends',
        'false',
        'final',
        'finally',
        'float',
        'for',
        'goto',
        'if',
        'implements',
        'import',
        'instanceof',
        'int',
        'interface',
        'long',
        'native',
        'new',
        'null',
        'package',
        'private',
        'protected',
        'public',
        'return',
        'short',
        'static',
        'strictfp',
        'super',
        'switch',
        'synchronized',
        'this',
        'throw',
        'throws',
        'transient',
        'true',
        'try',
        'void',
        'volatile',
        'while'
    ];
    /* eslint-enable */

    const VALID_JAVA_IDENTIFIER = new RegExp('^[a-zA-Z_$][a-zA-Z\\d_$]*$');

    function isValidJavaIdentifier(msg, ident, elemId, panels, panelId, stopEdit) {
        if (isEmptyString(ident))
            return !stopEdit && ErrorPopover.show(elemId, msg + ' is invalid!', panels, panelId);

        if (_.includes(JAVA_KEYWORDS, ident))
            return !stopEdit && ErrorPopover.show(elemId, msg + ' could not contains reserved java keyword: "' + ident + '"!', panels, panelId);

        if (!VALID_JAVA_IDENTIFIER.test(ident))
            return !stopEdit && ErrorPopover.show(elemId, msg + ' contains invalid identifier: "' + ident + '"!', panels, panelId);

        return true;
    }

    /**
     * Extract datasource from cache or cluster.
     *
     * @param object Cache or cluster to extract datasource.
     * @returns {*} Datasource object or null if not set.
     */
    function extractDataSource(object) {
        let datasource = null;

        // Extract from cluster object
        if (_.get(object, 'discovery.kind') === 'Jdbc') {
            datasource = object.discovery.Jdbc;

            if (datasource.dataSourceBean && datasource.dialect)
                return datasource;
        } // Extract from JDBC checkpoint configuration.
        else if (_.get(object, 'kind') === 'JDBC') {
            datasource = object.JDBC;

            if (datasource.dataSourceBean && datasource.dialect)
                return datasource;
        } // Extract from cache object
        else if (_.get(object, 'cacheStoreFactory.kind')) {
            datasource = object.cacheStoreFactory[object.cacheStoreFactory.kind];

            if (datasource.dialect || (datasource.connectVia === 'DataSource'))
                return datasource;
        }

        return null;
    }

    const cacheStoreJdbcDialects = [
        {value: 'Generic', label: 'Generic JDBC'},
        {value: 'Oracle', label: 'Oracle'},
        {value: 'DB2', label: 'IBM DB2'},
        {value: 'SQLServer', label: 'Microsoft SQL Server'},
        {value: 'MySQL', label: 'MySQL'},
        {value: 'PostgreSQL', label: 'PostgreSQL'},
        {value: 'H2', label: 'H2 database'}
    ];

    function domainForStoreConfigured(domain) {
        const isEmpty = !isDefined(domain) || (isEmptyString(domain.databaseSchema) &&
            isEmptyString(domain.databaseTable) &&
            _.isEmpty(domain.keyFields) &&
            _.isEmpty(domain.valueFields));

        return !isEmpty;
    }

    const DS_CHECK_SUCCESS = {checked: true};

    /**
     * Compare datasources of caches or clusters.
     *
     * @param firstObj First cache or cluster.
     * @param firstType Type of first object to compare.
     * @param secondObj Second cache or cluster.
     * @param secondType Type of first object to compare.
     * @param index Index of invalid object when check is failed.
     * @returns {*} Check result object.
     */
    function compareDataSources(firstObj, firstType, secondObj, secondType, index) {
        const firstDs = extractDataSource(firstObj);
        const secondDs = extractDataSource(secondObj);

        if (firstDs && secondDs) {
            const firstDB = firstDs.dialect;
            const secondDB = secondDs.dialect;

            if (firstDs.dataSourceBean === secondDs.dataSourceBean && firstDB !== secondDB)
                return {checked: false, firstObj, firstDs, firstType, secondObj, secondDs, secondType, index};
        }

        return DS_CHECK_SUCCESS;
    }

    function compareSQLSchemaNames(firstCache, secondCache) {
        const firstName = firstCache.sqlSchema;
        const secondName = secondCache.sqlSchema;

        if (firstName && secondName && (firstName === secondName))
            return {checked: false, firstCache, secondCache};

        return DS_CHECK_SUCCESS;
    }

    function toJavaName(prefix, name) {
        const javaName = name ? name.replace(/[^A-Za-z_0-9]+/g, '_') : 'dflt';

        return prefix + javaName.charAt(0).toLocaleUpperCase() + javaName.slice(1);
    }

    return {
        VALID_JAVA_IDENTIFIER,
        JAVA_KEYWORDS,
        mkOptions(options) {
            return _.map(options, (option) => {
                return {value: option, label: isDefined(option) ? option : 'Not set'};
            });
        },
        isDefined,
        hasProperty(obj, props) {
            for (const propName in props) {
                if (props.hasOwnProperty(propName)) {
                    if (obj[propName])
                        return true;
                }
            }

            return false;
        },
        isEmptyString,
        SUPPORTED_JDBC_TYPES,
        javaBuiltInClasses,
        javaBuiltInTypes,
        isJavaBuiltInClass,
        isValidJavaIdentifier,
        isValidJavaClass(msg, ident, allowBuiltInClass, elemId, packageOnly, panels, panelId, stopEdit = false) {
            if (isEmptyString(ident))
                return !stopEdit && ErrorPopover.show(elemId, msg + ' could not be empty!', panels, panelId);

            const parts = ident.split('.');

            const len = parts.length;

            if (!allowBuiltInClass && isJavaBuiltInClass(ident))
                return !stopEdit && ErrorPopover.show(elemId, msg + ' should not be the Java build-in class!', panels, panelId);

            if (len < 2) {
                if (isJavaBuiltInClass(ident, allowBuiltInClass))
                    return true;

                if (!packageOnly)
                    return !stopEdit && ErrorPopover.show(elemId, msg + ' does not have package specified!', panels, panelId);
            }

            for (let i = 0; i < parts.length; i++) {
                const part = parts[i];

                if (!isValidJavaIdentifier(msg, part, elemId, panels, panelId, stopEdit))
                    return false;
            }

            return true;
        },
        domainForQueryConfigured(domain) {
            const isEmpty = !isDefined(domain) || (_.isEmpty(domain.fields) &&
                _.isEmpty(domain.aliases) &&
                _.isEmpty(domain.indexes));

            return !isEmpty;
        },
        domainForStoreConfigured,
        download(type = 'application/octet-stream', name = 'file.txt', data = '') {
            const file = new Blob([data], { type: `${type};charset=utf-8`});

            saver.saveAs(file, name, false);
        },
        getQueryVariable(name) {
            const attrs = window.location.search.substring(1).split('&');
            const attr = _.find(attrs, (a) => a === name || (a.indexOf('=') >= 0 && a.substr(0, a.indexOf('=')) === name));

            if (!isDefined(attr))
                return null;

            if (attr === name)
                return true;

            return attr.substr(attr.indexOf('=') + 1);
        },
        cacheStoreJdbcDialects,
        cacheStoreJdbcDialectsLabel(dialect) {
            const found = _.find(cacheStoreJdbcDialects, (dialectVal) => dialectVal.value === dialect);

            return found ? found.label : null;
        },
        checkDataSources(cluster, caches, checkCacheExt) {
            let res = DS_CHECK_SUCCESS;

            _.find(caches, (curCache, curIx) => {
                // Check datasources of cluster JDBC ip finder and cache store factory datasource.
                res = compareDataSources(curCache, 'cache', cluster, 'cluster');

                if (!res.checked)
                    return true;

                _.find(cluster.checkpointSpi, (spi, spiIx) => {
                    res = compareDataSources(curCache, 'cache', spi, 'checkpoint', spiIx);

                    return !res.checked;
                });

                if (!res.checked)
                    return true;

                // Check datasource of current saved cache and datasource of other cache in cluster.
                if (isDefined(checkCacheExt)) {
                    if (checkCacheExt._id !== curCache._id) {
                        res = compareDataSources(checkCacheExt, 'cache', curCache, 'cache');

                        return !res.checked;
                    }

                    return false;
                }

                // Check datasources of specified list of caches.
                return _.find(caches, (checkCache, checkIx) => {
                    if (checkIx < curIx) {
                        res = compareDataSources(checkCache, 'cache', curCache, 'cache');

                        return !res.checked;
                    }

                    return false;
                });
            });

            if (res.checked) {
                _.find(cluster.checkpointSpi, (curSpi, curIx) => {
                    // Check datasources of cluster JDBC ip finder and cache store factory datasource.
                    res = compareDataSources(cluster, 'cluster', curSpi, 'checkpoint', curIx);

                    if (!res.checked)
                        return true;

                    _.find(cluster.checkpointSpi, (spi, spiIx) => {
                        if (spiIx < curIx) {
                            res = compareDataSources(curSpi, 'checkpoint', spi, 'checkpoint', curIx);

                            return !res.checked;
                        }

                        return false;
                    });
                });
            }

            return res;
        },
        checkCacheSQLSchemas(caches, checkCacheExt) {
            let res = DS_CHECK_SUCCESS;

            _.find(caches, (curCache, curIx) => {
                if (isDefined(checkCacheExt)) {
                    if (checkCacheExt._id !== curCache._id) {
                        res = compareSQLSchemaNames(checkCacheExt, curCache);

                        return !res.checked;
                    }

                    return false;
                }

                return _.find(caches, (checkCache, checkIx) => {
                    if (checkIx < curIx) {
                        res = compareSQLSchemaNames(checkCache, curCache);

                        return !res.checked;
                    }

                    return false;
                });
            });

            return res;
        },
        autoCacheStoreConfiguration(cache, domains) {
            const cacheStoreFactory = isDefined(cache.cacheStoreFactory) &&
                isDefined(cache.cacheStoreFactory.kind);

            if (!cacheStoreFactory && _.findIndex(domains, domainForStoreConfigured) >= 0) {
                const dflt = !cache.readThrough && !cache.writeThrough;

                return {
                    cacheStoreFactory: {
                        kind: 'CacheJdbcPojoStoreFactory',
                        CacheJdbcPojoStoreFactory: {
                            dataSourceBean: toJavaName('ds', cache.name),
                            dialect: 'Generic'
                        },
                        CacheJdbcBlobStoreFactory: {connectVia: 'DataSource'}
                    },
                    readThrough: dflt || cache.readThrough,
                    writeThrough: dflt || cache.writeThrough
                };
            }

            return {};
        },
        randomString(len) {
            const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
            const possibleLen = possible.length;

            let res = '';

            for (let i = 0; i < len; i++)
                res += possible.charAt(Math.floor(Math.random() * possibleLen));

            return res;
        },
        checkFieldValidators(ui) {
            const form = ui.inputForm;
            const errors = form.$error;
            const errKeys = Object.keys(errors);

            if (errKeys && errKeys.length > 0) {
                const firstErrorKey = errKeys[0];

                const firstError = errors[firstErrorKey][0];

                const err = firstError.$error[firstErrorKey];

                const actualError = _.isArray(err) ? err[0] : firstError;

                const errNameFull = actualError.$name;
                const errNameShort = errNameFull.endsWith('TextInput') ? errNameFull.substring(0, errNameFull.length - 9) : errNameFull;

                const extractErrorMessage = (errName) => {
                    try {
                        return errors[firstErrorKey][0].$errorMessages[errName][firstErrorKey];
                    }
                    catch (ignored1) {
                        try {
                            return form[firstError.$name].$errorMessages[errName][firstErrorKey];
                        }
                        catch (ignored2) {
                            try {
                                return form.$errorMessages[errName][firstErrorKey];
                            }
                            catch (ignored3) {
                                return false;
                            }
                        }
                    }
                };

                const msg = extractErrorMessage(errNameFull) || extractErrorMessage(errNameShort) || 'Invalid value!';

                return ErrorPopover.show(errNameFull, msg, ui, firstError.$name);
            }

            return true;
        }
    };
}