<commit_before>'use strict';
var path = require('path'),
    util = require('util'),
    _ = require('lodash'),
    yeoman = require('yeoman-generator'),
    chalk = require('chalk'),
    jhipsterUtils = require('./util'),
    Insight = require('insight'),
    fs = require('fs'),
    shelljs = require('shelljs'),
    packagejs = require('../package.json'),
    semver = require('semver'),
    exec = require('child_process').exec,
    os = require('os'),
    http = require('http'),
    pluralize = require('pluralize');

const JHIPSTER_CONFIG_DIR = '.jhipster';
const MODULES_HOOK_FILE = JHIPSTER_CONFIG_DIR + '/modules/jhi-hooks.json';
const GENERATOR_JHIPSTER = 'generator-jhipster';

const constants = require('./generator-constants'),
    CLIENT_MAIN_SRC_DIR = constants.CLIENT_MAIN_SRC_DIR,
    SERVER_MAIN_RES_DIR = constants.SERVER_MAIN_RES_DIR;

module.exports = Generator;

function Generator() {
    yeoman.Base.apply(this, arguments);
    this.env.options.appPath = this.config.get('appPath') || CLIENT_MAIN_SRC_DIR;
}

util.inherits(Generator, yeoman.Base);

/**
 * Add a new menu element, at the root of the menu.
 *
 * @param {string} routerName - The name of the AngularJS router that is added to the menu.
 * @param {string} glyphiconName - The name of the Glyphicon (from Bootstrap) that will be displayed.
 * @param {boolean} enableTranslation - If translations are enabled or not
 */
Generator.prototype.addElementToMenu = function (routerName, glyphiconName, enableTranslation) {
    try {
        var navbarPath;
        if (this.clientFramework === 'angular1') {
            navbarPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.html';
        } else {
            navbarPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.component.html';
        }

        jhipsterUtils.rewriteFile({
            file: navbarPath,
            needle: 'jhipster-needle-add-element-to-menu',
            splicable: [`<li ui-sref-active="active">
                            <a ui-sref="${routerName}" ng-click="vm.collapseNavbar()">
                                <span class="glyphicon glyphicon-${glyphiconName}"></span>&nbsp;
                                <span ${enableTranslation ? 'data-translate="global.menu.' + routerName + '"' : ''}>${_.startCase(routerName)}</span>
                            </a>
                        </li>`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + navbarPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + routerName + ' ' + chalk.yellow('not added to menu.\n'));
    }
};

/**
 * Add a new menu element to the admin menu.
 *
 * @param {string} routerName - The name of the AngularJS router that is added to the admin menu.
 * @param {string} glyphiconName - The name of the Glyphicon (from Bootstrap) that will be displayed.
 * @param {boolean} enableTranslation - If translations are enabled or not
 */
Generator.prototype.addElementToAdminMenu = function (routerName, glyphiconName, enableTranslation) {
    try {
        var navbarAdminPath;
        if (this.clientFramework === 'angular1') {
            navbarAdminPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.html';
        } else {
            navbarAdminPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.component.html';
        }
        jhipsterUtils.rewriteFile({
            file: navbarAdminPath,
            needle: 'jhipster-needle-add-element-to-admin-menu',
            splicable: [`<li ui-sref-active="active" >
                            <a ui-sref="${routerName}" ng-click="vm.collapseNavbar()">
                                <span class="glyphicon glyphicon-${glyphiconName}"></span>&nbsp;
                                <span ${enableTranslation ? 'data-translate="global.menu.admin.' + routerName + '"' : ''}>${_.startCase(routerName)}</span>
                            </a>
                        </li>`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + navbarAdminPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + routerName + ' ' + chalk.yellow('not added to admin menu.\n'));
    }
};

/**
 * Add a new entity in the "entities" menu.
 *
 * @param {string} routerName - The name of the AngularJS router (which by default is the name of the entity).
 * @param {boolean} enableTranslation - If translations are enabled or not
 */
Generator.prototype.addEntityToMenu = function (routerName, enableTranslation) {
    try {
        var entityMenuPath;
        if (this.clientFramework === 'angular1') {
            entityMenuPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.html';
            jhipsterUtils.rewriteFile({
                file: entityMenuPath,
                needle: 'jhipster-needle-add-entity-to-menu',
                splicable: [`<li ui-sref-active="active">
                                <a ui-sref="${routerName}" ng-click="vm.collapseNavbar()">
                                    <span class="glyphicon glyphicon-asterisk"></span>&nbsp;
                                    <span ${enableTranslation ? 'data-translate="global.menu.entities.' + _.camelCase(routerName) + '"' : ''}>${_.startCase(routerName)}</span>
                                </a>
                            </li>`
                ]
            }, this);
        } else {
            entityMenuPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.component.html';
            jhipsterUtils.rewriteFile({
                file: entityMenuPath,
                needle: 'jhipster-needle-add-entity-to-menu',
                splicable: [`<li uiSrefActive="active">
                        <a class="dropdown-item" uiSref="${routerName}" (click)="collapseNavbar()">
                            <i class="fa fa-fw fa-asterisk" aria-hidden="true"></i>
                            <span ${enableTranslation ? 'jhiTranslate="global.menu.entities.' + _.camelCase(routerName) + '"' : ''}>${_.startCase(routerName)}</span>
                        </a>
                    </li>`
                ]
            }, this);
        }
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + entityMenuPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + routerName + ' ' + chalk.yellow('not added to menu.\n'));
    }
};
/**
 * Add a new entity in the TS modules file.
 *
 * @param {string} routerName - The name of the AngularJS router (which by default is the name of the entity).
 * @param {boolean} enableTranslation - If translations are enabled or not
 */
Generator.prototype.addEntityToModule = function (entityInstance, entityClass, entityAngularJSName, entityFolderName, entityFileName, enableTranslation) {
    try {
        if (this.clientFramework === 'angular1') {
            return;
        }
        var entityPath = CLIENT_MAIN_SRC_DIR + 'app/entities/entity.module.ts';
        var indexPath = CLIENT_MAIN_SRC_DIR + 'app/entities/index.ts';
        jhipsterUtils.rewriteFile({
            file: indexPath,
            needle: 'jhipster-needle-add-entity-to-index-export',
            splicable: [
                this.stripMargin(
                    `|export * from './${entityFolderName}/${entityFileName}-dialog.component';
                     |export * from './${entityFolderName}/${entityFileName}-delete-dialog.component';
                     |export * from './${entityFolderName}/${entityFileName}-detail.component';
                     |export * from './${entityFolderName}/${entityFileName}.component';
                     |export * from './${entityFolderName}/${entityFileName}.state';`
                )
            ]
        }, this);
        jhipsterUtils.rewriteFile({
            file: indexPath,
            needle: 'jhipster-needle-add-entity-to-index-model-export',
            splicable: [`export * from './${entityFolderName}/${entityFileName}.model';`
            ]
        }, this);
        jhipsterUtils.rewriteFile({
            file: indexPath,
            needle: 'jhipster-needle-add-entity-to-index-service-export',
            splicable: [`export * from './${entityFolderName}/${entityFileName}.service';`
            ]
        }, this);

        jhipsterUtils.rewriteFile({
            file: entityPath,
            needle: 'jhipster-needle-add-entity-to-module-states',
            splicable: [
                this.stripMargin(
                    `|${entityInstance}State,
                     |    ${entityInstance}NewState,
                     |    ${entityInstance}DetailState,
                     |    ${entityInstance}EditState,
                     |    ${entityInstance}DeleteState,`
                )
            ]
        }, this);

        jhipsterUtils.rewriteFile({
            file: entityPath,
            needle: 'jhipster-needle-add-entity-to-module-entryComponents',
            splicable: [
                this.stripMargin(
                    `|${entityAngularJSName}DialogComponent,
                     |        ${entityAngularJSName}DeleteDialogComponent,`
                )
            ]
        }, this);

        jhipsterUtils.rewriteFile({
            file: entityPath,
            needle: 'jhipster-needle-add-entity-to-module-declarations',
            splicable: [
                this.stripMargin(
                    `|${entityAngularJSName}Component,
                     |        ${entityAngularJSName}DetailComponent,
                     |        ${entityAngularJSName}DialogComponent,
                     |        ${entityAngularJSName}DeleteDialogComponent,`
                )
            ]
        }, this);

        jhipsterUtils.rewriteFile({
            file: entityPath,
            needle: 'jhipster-needle-add-entity-to-module-providers',
            splicable: [`${entityClass}Service,`]
        }, this);

        jhipsterUtils.rewriteFile({
            file: entityPath,
            needle: 'jhipster-needle-add-entity-to-module-import',
            splicable: [
                this.stripMargin(
                    `|${entityClass}Service,
                     |    ${entityAngularJSName}Component,
                     |    ${entityAngularJSName}DetailComponent,
                     |    ${entityAngularJSName}DialogComponent,
                     |    ${entityAngularJSName}DeleteDialogComponent,
                     |    ${entityInstance}State,
                     |    ${entityInstance}DetailState,
                     |    ${entityInstance}NewState,
                     |    ${entityInstance}EditState,
                     |    ${entityInstance}DeleteState,`
                )
            ]
        }, this);
    } catch (e) {this.log(e);
        this.log(chalk.yellow('\nUnable to find ') + indexPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + entityInstance+ entityClass+ entityFolderName+ entityFileName + ' ' + chalk.yellow('not added to menu.\n'));
    }
};

/**
 * A a new element in the "global.json" translations.
 *
 * @param {string} key - Key for the menu entry
 * @param {string} value - Default translated value
 * @param {string} language - The language to which this translation should be added
 */
Generator.prototype.addElementTranslationKey = function (key, value, language) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'i18n/' + language + '/global.json';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-menu-add-element',
            splicable: [
                `"${key}": "${_.startCase(value)}",`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + language + chalk.yellow(' not added as a new entity in the menu.\n'));
    }
};

/**
 * A a new element in the admin section of "global.json" translations.
 *
 * @param {string} key - Key for the menu entry
 * @param {string} value - Default translated value
 * @param {string} language - The language to which this translation should be added
 */
Generator.prototype.addAdminElementTranslationKey = function (key, value, language) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'i18n/' + language + '/global.json';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-menu-add-admin-element',
            splicable: [
                `"${key}": "${_.startCase(value)}",`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + language + chalk.yellow(' not added as a new entry in the admin menu.\n'));
    }
};

/**
 * A a new entity in the "global.json" translations.
 *
 * @param {string} key - Key for the entity name
 * @param {string} value - Default translated value
 * @param {string} language - The language to which this translation should be added
 */
Generator.prototype.addEntityTranslationKey = function (key, value, language) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'i18n/' + language + '/global.json';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-menu-add-entry',
            splicable: [
                `"${key}": "${_.startCase(value)}",`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + language + chalk.yellow(' not added as a new entity in the menu.\n'));
    }
};

/**
 * A a new entry as a root param in "global.json" translations.
 *
 * @param {string} key - Key for the entry
 * @param {string} value - Default translated value or object with multiple key and translated value
 * @param {string} language - The language to which this translation should be added
 */
Generator.prototype.addGlobalTranslationKey = function (key, value, language) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'i18n/' + language + '/global.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj[key] = value;
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + '(key: ' + key + ', value:' + value + ')' + chalk.yellow(' not added to global translations.\n'));
    }
};

/**
 * Add a translation key to all installed languages
 *
 * @param {string} key - Key for the entity name
 * @param {string} value - Default translated value
 * @param {string} method - The method to be run with provided key and value from above
 * @param {string} enableTranslation - specify if i18n is enabled
 */
Generator.prototype.addTranslationKeyToAllLanguages = function (key, value, method, enableTranslation) {
    if (enableTranslation) {
        this.getAllInstalledLanguages().forEach(function (language) {
            this[method](key, value, language);
        }, this);
    }
};

/**
 * get all the languages installed currently
 */
Generator.prototype.getAllInstalledLanguages = function () {
    var languages = [];
    this.getAllSupportedLanguages().forEach(function (language) {
        try {
            var stats = fs.lstatSync(CLIENT_MAIN_SRC_DIR + 'i18n/' + language);
            if (stats.isDirectory()) {
                languages.push(language);
            }
        } catch (e) {
            // An exception is thrown if the folder doesn't exist
            // do nothing as the language might not be installed
        }
    });
    return languages;
};

/**
 * get all the languages supported by JHipster
 */
Generator.prototype.getAllSupportedLanguages = function () {
    return _.map(this.getAllSupportedLanguageOptions(), 'value');
};

/**
 * check if a language is supported by JHipster
 * @param {string} language - Key for the language
 */
Generator.prototype.isSupportedLanguage = function (language) {
    return _.includes(this.getAllSupportedLanguages(), language);
};

/**
 * get all the languages options supported by JHipster
 */
Generator.prototype.getAllSupportedLanguageOptions = function () {
    return [
        {name: 'Catalan', value: 'ca'},
        {name: 'Chinese (Simplified)', value: 'zh-cn'},
        {name: 'Chinese (Traditional)', value: 'zh-tw'},
        {name: 'Czech', value: 'cs'},
        {name: 'Danish', value: 'da'},
        {name: 'Dutch', value: 'nl'},
        {name: 'English', value: 'en'},
        {name: 'Estonian', value: 'et'},
        {name: 'French', value: 'fr'},
        {name: 'Galician', value: 'gl'},
        {name: 'German', value: 'de'},
        {name: 'Greek', value: 'el'},
        {name: 'Hindi', value: 'hi'},
        {name: 'Hungarian', value: 'hu'},
        {name: 'Italian', value: 'it'},
        {name: 'Japanese', value: 'ja'},
        {name: 'Korean', value: 'ko'},
        {name: 'Marathi', value: 'mr'},
        {name: 'Polish', value: 'pl'},
        {name: 'Portuguese (Brazilian)', value: 'pt-br'},
        {name: 'Portuguese', value: 'pt-pt'},
        {name: 'Romanian', value: 'ro'},
        {name: 'Russian', value: 'ru'},
        {name: 'Slovak', value: 'sk'},
        {name: 'Serbian', value: 'sr'},
        {name: 'Spanish', value: 'es'},
        {name: 'Swedish', value: 'sv'},
        {name: 'Turkish', value: 'tr'},
        {name: 'Tamil', value: 'ta'},
        {name: 'Vietnamese', value: 'vi'}
    ];
};

/**
 * Add new social configuration in the "application.yml".
 *
 * @param {string} name - social name (twitter, facebook, ect.)
 * @param {string} clientId - clientId
 * @param {string} clientSecret - clientSecret
 * @param {string} comment - url of how to configure the social service
 */
Generator.prototype.addSocialConfiguration = function (name, clientId, clientSecret, comment) {
    var fullPath = SERVER_MAIN_RES_DIR + 'config/application.yml';
    try {
        this.log(chalk.yellow('   update ') + fullPath);
        var config = '';
        if (comment) {
            config += '# ' + comment + '\n        ';
        }
        config += name + ':\n' +
            '            clientId: ' + clientId + '\n' +
            '            clientSecret: ' + clientSecret + '\n';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-add-social-configuration',
            splicable: [
                config
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'social configuration ' + name + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new dependency in the "bower.json".
 *
 * @param {string} name - dependency name
 * @param {string} version - dependency version
 */
Generator.prototype.addBowerDependency = function (name, version) {
    var fullPath = 'bower.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj.dependencies[name] = version;
        }, this);
    } catch (e) {
        this.log(e);
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'bower dependency (name: ' + name + ', version:' + version + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new override configuration in the "bower.json".
 *
 * @param {string} bowerPackageName - Bower package name use in dependencies
 * @param {array} main - You can specify which files should be selected
 * @param {boolean} isIgnored - Default: false, Set to true if you want to ignore this package.
 * @param {object} dependencies - You can override the dependencies of a package. Set to null to ignore the dependencies.
 *
 */
Generator.prototype.addBowerOverride = function (bowerPackageName, main, isIgnored, dependencies) {
    var fullPath = 'bower.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            var override = {};
            if (main !== undefined && main.length > 0) {
                override['main'] = main;
            }
            if (isIgnored) {
                override['ignore'] = true;
            }
            if (dependencies) {
                override['dependencies'] = dependencies;
            }
            if (jsonObj.overrides === undefined) {
                jsonObj.overrides = {};
            }
            jsonObj.overrides[bowerPackageName] = override;
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'bower override configuration (bowerPackageName: ' + bowerPackageName + ', main:' + JSON.stringify(main) + ', ignore:' + isIgnored + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new parameter in the ".bowerrc".
 *
 * @param {string} key - name of the parameter
 * @param {string, obj, bool, etc.} value - value of the parameter
 */
Generator.prototype.addBowerrcParameter = function (key, value) {
    var fullPath = '.bowerrc';
    try {
        this.log(chalk.yellow('   update ') + fullPath);
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj[key] = value;
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'bowerrc parameter (key: ' + key + ', value:' + value + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new dependency in the "package.json".
 *
 * @param {string} name - dependency name
 * @param {string} version - dependency version
 */
Generator.prototype.addNpmDependency = function (name, version) {
    var fullPath = 'package.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj.dependencies[name] = version;
        }, this);
    } catch (e) {
        this.log(e);
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'npm dependency (name: ' + name + ', version:' + version + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new devDependency in the "package.json".
 *
 * @param {string} name - devDependency name
 * @param {string} version - devDependency version
 */
Generator.prototype.addNpmDevDependency = function (name, version) {
    var fullPath = 'package.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj.devDependencies[name] = version;
        }, this);
    } catch (e) {
        this.log(e);
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'npm devDependency (name: ' + name + ', version:' + version + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new script in the "package.json".
 *
 * @param {string} name - script name
 * @param {string} data - script version
 */
Generator.prototype.addNpmScript = function (name, data) {
    var fullPath = 'package.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj.scripts[name] = data;
        }, this);
    } catch (e) {
        this.log(e);
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'npm script (name: ' + name + ', data:' + data + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new module to the angular application in "app.module.js".
 *
 * @param {string} moduleName - module name
 *
 */
Generator.prototype.addAngularJsModule = function (moduleName) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'app/app.module.js';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-angularjs-add-module',
            splicable: [
                `'${moduleName}',`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + moduleName + chalk.yellow(' not added to JHipster app.\n'));
    }
};

/**
 * Add a new http interceptor to the angular application in "blocks/config/http.config.js".
 * The interceptor should be in its own .js file inside app/blocks/interceptor folder
 * @param {string} interceptorName - angular name of the interceptor
 *
 */
Generator.prototype.addAngularJsInterceptor = function (interceptorName) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'app/blocks/config/http.config.js';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-angularjs-add-interceptor',
            splicable: [
                `$httpProvider.interceptors.push('${interceptorName}');`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Interceptor not added to JHipster app.\n'));
    }
};

/**
 * Add a new entity to the Ehcache, for the 2nd level cache of an entity and its relationships.
 *
 * @param {string} name - the entity to cache.
 * @parma {array} relationships - the relationships of this entity
 */
Generator.prototype.addEntityToEhcache = function (entityClass, relationships) {
    // Add the entity to ehcache
    this.addEntryToEhcache(`${this.packageName}.domain.${entityClass}`);
    // Add the collections linked to that entity to ehcache
    for (var idx in relationships) {
        var relationshipType = relationships[idx].relationshipType;
        if (relationshipType === 'one-to-many' || relationshipType === 'many-to-many') {
            this.addEntryToEhcache(`${this.packageName}.domain.${entityClass}.${relationships[idx].relationshipFieldNamePlural}`);
        }
    }
};

/**
 * Add a new entry to the ehcache.xml file
 *
 * @param {string} name - the entry (including package name) to cache.
 */
Generator.prototype.addEntryToEhcache = function (entry) {
    try {
        var fullPath = SERVER_MAIN_RES_DIR + 'ehcache.xml';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-ehcache-add-entry',
            splicable: [`<cache alias="${entry}" uses-template="simple"/>
`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to add ' + entry + ' to ehcache.xml file.\n'));
    }
};

/**
 * Add a new changelog to the Liquibase master.xml file.
 *
 * @param {string} changelogName - The name of the changelog (name of the file without .xml at the end).
 */
Generator.prototype.addChangelogToLiquibase = function (changelogName) {
    this.addLiquibaseChangelogToMaster(changelogName, 'jhipster-needle-liquibase-add-changelog');
};

/**
 * Add a new constraints changelog to the Liquibase master.xml file.
 *
 * @param {string} changelogName - The name of the changelog (name of the file without .xml at the end).
 */
Generator.prototype.addConstraintsChangelogToLiquibase = function (changelogName) {
    this.addLiquibaseChangelogToMaster(changelogName, 'jhipster-needle-liquibase-add-constraints-changelog');
};

/**
 * Add a new changelog to the Liquibase master.xml file.
 *
 * @param {string} changelogName - The name of the changelog (name of the file without .xml at the end).
 * @param {string} needle - The needle at where it has to be added.
 */
Generator.prototype.addLiquibaseChangelogToMaster = function (changelogName, needle) {
    try {
        var fullPath = SERVER_MAIN_RES_DIR + 'config/liquibase/master.xml';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: needle,
            splicable: [
                `<include file="classpath:config/liquibase/changelog/${changelogName}.xml" relativeToChangelogFile="false"/>`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + changelogName + '.xml ' + chalk.yellow('not added.\n'));
    }
};

/**
 * A a new column to a Liquibase changelog file for entity.
 *
 * @param {string} filePath - The full path of the changelog file.
 * @param {string} content - The content to be added as column, can have multiple columns as well
 */
Generator.prototype.addColumnToLiquibaseEntityChangeset = function (filePath, content) {
    try {
        jhipsterUtils.rewriteFile({
            file: filePath,
            needle: 'jhipster-needle-liquibase-add-column',
            splicable: [
                content
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + filePath + chalk.yellow(' or missing required jhipster-needle. Column not added.\n') + e);
    }
};

/**
 * Add a new social button in the login and register modules
 *
 * @param {string} socialName - name of the social module. ex: 'facebook'
 * @param {string} socialParameter - parameter to send to social connection ex: 'public_profile,email'
 * @param {string} buttonColor - color of the social button. ex: '#3b5998'
 * @param {string} buttonHoverColor - color of the social button when is hover. ex: '#2d4373'
 */
Generator.prototype.addSocialButton = function (isUseSass, socialName, socialParameter, buttonColor, buttonHoverColor) {
    var socialServicefullPath = CLIENT_MAIN_SRC_DIR + 'app/account/social/social.service.js';
    var loginfullPath, registerfullPath;
    if (this.clientFramework === 'angular1') {
        loginfullPath = CLIENT_MAIN_SRC_DIR + 'app/account/login/login.html';
        registerfullPath = CLIENT_MAIN_SRC_DIR + 'app/account/register/register.html';
    } else {
        loginfullPath = CLIENT_MAIN_SRC_DIR + 'app/account/login/login.component.html';
        registerfullPath = CLIENT_MAIN_SRC_DIR + 'app/account/register/register.component.html';
    }
    try {
        this.log(chalk.yellow('\nupdate ') + socialServicefullPath);
        var serviceCode = `case '${socialName}': return '${socialParameter}';`;
        jhipsterUtils.rewriteFile({
            file: socialServicefullPath,
            needle: 'jhipster-needle-add-social-button',
            splicable: [
                serviceCode
            ]
        }, this);

        var buttonCode = `<jh-social ng-provider="${socialName}"></jh-social>`;
        this.log(chalk.yellow('update ') + loginfullPath);
        jhipsterUtils.rewriteFile({
            file: loginfullPath,
            needle: 'jhipster-needle-add-social-button',
            splicable: [
                buttonCode
            ]
        }, this);
        this.log(chalk.yellow('update ') + registerfullPath);
        jhipsterUtils.rewriteFile({
            file: registerfullPath,
            needle: 'jhipster-needle-add-social-button',
            splicable: [
                buttonCode
            ]
        }, this);

        var buttonStyle = `.jh-btn-${socialName} {
                 background-color: ${buttonColor};
                 border-color: rgba(0, 0, 0, 0.2);
                 color: #fff;
            }\n
            .jh-btn-${socialName}:hover, .jh-btn-${socialName}:focus, .jh-btn-${socialName}:active, .jh-btn-${socialName}.active, .open > .dropdown-toggle.jh-btn-${socialName} {
                background-color: ${buttonHoverColor};
                border-color: rgba(0, 0, 0, 0.2);
                color: #fff;
            }`;
        this.addMainCSSStyle(isUseSass, buttonStyle, 'Add sign in style for ' + socialName);

    } catch (e) {
        this.log(chalk.yellow('\nUnable to add social button modification.\n' + e));
    }
};

/**
 * Add a new social connection factory in the SocialConfiguration.java file.
 *
 * @param {string} javaDir - default java directory of the project (JHipster var)
 * @param {string} importPackagePath - package path of the ConnectionFactory class
 * @param {string} socialName - name of the social module
 * @param {string} connectionFactoryClassName - name of the ConnectionFactory class
 * @param {string} configurationName - name of the section in the config yaml file
 */
Generator.prototype.addSocialConnectionFactory = function (javaDir, importPackagePath, socialName, connectionFactoryClassName, configurationName) {
    var fullPath = javaDir + 'config/social/SocialConfiguration.java';
    try {
        this.log(chalk.yellow('\nupdate ') + fullPath);
        var javaImport = 'import ' + importPackagePath + ';\n';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-add-social-connection-factory-import-package',
            splicable: [
                javaImport
            ]
        }, this);

        var clientId = socialName + 'ClientId';
        var clientSecret = socialName + 'ClientSecret';
        var javaCode = '// ' + socialName + ' configuration\n' +
            '        String ' + clientId + ' = environment.getProperty("spring.social.' + configurationName + '.clientId");\n' +
            '        String ' + clientSecret + ' = environment.getProperty("spring.social.' + configurationName + '.clientSecret");\n' +
            '        if (' + clientId + ' != null && ' + clientSecret + ' != null) {\n' +
            '            log.debug("Configuring ' + connectionFactoryClassName + '");\n' +
            '            connectionFactoryConfigurer.addConnectionFactory(\n' +
            '                new ' + connectionFactoryClassName + '(\n' +
            '                    ' + clientId + ',\n' +
            '                    ' + clientSecret + '\n' +
            '                )\n' +
            '            );\n' +
            '        } else {\n' +
            '            log.error("Cannot configure ' + connectionFactoryClassName + ' id or secret null");\n' +
            '        }\n';

        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-add-social-connection-factory',
            splicable: [
                javaCode
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Social connection ') + e + ' ' + chalk.yellow('not added.\n'));
    }
};

/**
 * Add new css style to the angular application in "main.css".
 *
 * @param {string} style - css to add in the file
 * @param {string} comment - comment to add before css code
 *
 * example:
 *
 * style = '.jhipster {\n     color: #baa186;\n}'
 * comment = 'New JHipster color'
 *
 * * ==========================================================================
 * New JHipster color
 * ========================================================================== *
 * .jhipster {
 *     color: #baa186;
 * }
 *
 */
Generator.prototype.addMainCSSStyle = function (isUseSass, style, comment) {
    if (isUseSass) {
        this.addMainSCSSStyle(style, comment);
    }

    var fullPath = CLIENT_MAIN_SRC_DIR + 'content/css/main.css';
    var styleBlock = '';
    if (comment) {
        styleBlock += '/* ==========================================================================\n';
        styleBlock += comment + '\n';
        styleBlock += '========================================================================== */\n';
    }
    styleBlock += style + '\n';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-css-add-main',
            splicable: [
                styleBlock
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Style not added to JHipster app.\n'));
    }
};

/**
 * Add new scss style to the angular application in "main.scss".
 *
 * @param {string} style - scss to add in the file
 * @param {string} comment - comment to add before css code
 *
 * example:
 *
 * style = '.success {\n     @extend .message;\n    border-color: green;\n}'
 * comment = 'Message'
 *
 * * ==========================================================================
 * Message
 * ========================================================================== *
 * .success {
 *     @extend .message;
 *     border-color: green;
 * }
 *
 */
Generator.prototype.addMainSCSSStyle = function (style, comment) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'scss/main.scss';
    var styleBlock = '';
    if (comment) {
        styleBlock += '/* ==========================================================================\n';
        styleBlock += comment + '\n';
        styleBlock += '========================================================================== */\n';
    }
    styleBlock += style + '\n';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-scss-add-main',
            splicable: [
                styleBlock
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Style not added to JHipster app.\n'));
    }
};

/**
 * Add a new Maven dependency.
 *
 * @param {string} groupId - dependency groupId
 * @param {string} artifactId - dependency artifactId
 * @param {string} version - explicit dependency version number
 * @param {string} other - explicit other thing: scope, exclusions...
 */
Generator.prototype.addMavenDependency = function (groupId, artifactId, version, other) {
    try {
        var fullPath = 'pom.xml';
        var dependency = '<dependency>\n' +
            '            <groupId>' + groupId + '</groupId>\n' +
            '            <artifactId>' + artifactId + '</artifactId>\n';
        if (version) {
            dependency += '            <version>' + version + '</version>\n';
        }
        if (other) {
            dependency += other + '\n';
        }
        dependency += '        </dependency>';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-maven-add-dependency',
            splicable: [
                dependency
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + 'maven dependency (groupId: ' + groupId + ', artifactId:' + artifactId + ', version:' + version + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new Maven plugin.
 *
 * @param {string} groupId - plugin groupId
 * @param {string} artifactId - plugin artifactId
 * @param {string} version - explicit plugin version number
 * @param {string} other - explicit other thing: executions, configuration...
 */
Generator.prototype.addMavenPlugin = function (groupId, artifactId, version, other) {
    try {
        var fullPath = 'pom.xml';
        var plugin = '<plugin>\n' +
            '                <groupId>' + groupId + '</groupId>\n' +
            '                <artifactId>' + artifactId + '</artifactId>\n';
        if (version) {
            plugin += '                <version>' + version + '</version>\n';
        }
        if (other) {
            plugin += other + '\n';
        }
        plugin += '            </plugin>';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-maven-add-plugin',
            splicable: [
                plugin
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + 'maven plugin (groupId: ' + groupId + ', artifactId:' + artifactId + ', version:' + version + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * A new Gradle plugin.
 *
 * @param {string} group - plugin GroupId
 * @param {string} name - plugin name
 * @param {string} version - explicit plugin version number
 */
Generator.prototype.addGradlePlugin = function (group, name, version) {
    try {
        var fullPath = 'build.gradle';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-gradle-buildscript-dependency',
            splicable: [
                `classpath '${group}:${name}:${version}'`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + 'classpath: ' + group + ':' + name + ':' + version + chalk.yellow(' not added.\n'));
    }
};

/**
 * A new dependency to build.gradle file.
 *
 * @param {string} scope - scope of the new dependency, e.g. compile
 * @param {string} group - maven GroupId
 * @param {string} name - maven ArtifactId
 * @param {string} version - explicit version number
 */
Generator.prototype.addGradleDependency = function (scope, group, name, version) {
    try {
        var fullPath = 'build.gradle';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-gradle-dependency',
            splicable: [
                `${scope} '${group}:${name}:${version}'`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + group + ':' + name + ':' + version + chalk.yellow(' not added.\n'));
    }
};

/**
 * Apply from an external Gradle build script.
 *
 * @param {string} name - name of the file to apply from, must be 'fileName.gradle'
 */
Generator.prototype.applyFromGradleScript = function (name) {
    try {
        var fullPath = 'build.gradle';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-gradle-apply-from',
            splicable: [
                `apply from: '${name}.gradle'`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + name + chalk.yellow(' not added.\n'));
    }
};

/**
 * Generate a date to be used by Liquibase changelogs.
 */
Generator.prototype.dateFormatForLiquibase = function () {
    var now = new Date();
    var now_utc = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds());
    var year = '' + now_utc.getFullYear();
    var month = '' + (now_utc.getMonth() + 1);
    if (month.length === 1) {
        month = '0' + month;
    }
    var day = '' + now_utc.getDate();
    if (day.length === 1) {
        day = '0' + day;
    }
    var hour = '' + now_utc.getHours();
    if (hour.length === 1) {
        hour = '0' + hour;
    }
    var minute = '' + now_utc.getMinutes();
    if (minute.length === 1) {
        minute = '0' + minute;
    }
    var second = '' + now_utc.getSeconds();
    if (second.length === 1) {
        second = '0' + second;
    }
    return `${year}${month}${day}${hour}${minute}${second}`;
};

/**
 * Copy templates with all the custom logic applied according to the type.
 *
 * @param {string} source - path of the source file to copy from
 * @param {string} dest - path of the destination file to copy to
 * @param {string} action - type of the action to be performed on the template file, i.e: stripHtml | stripJs | template | copy
 * @param {object} generator - context that can be used as the generator instance or data to process template
 * @param {object} opt - options that can be passed to template method
 * @param {boolean} template - flag to use template method instead of copy method
 */
Generator.prototype.copyTemplate = function (source, dest, action, generator, opt, template) {

    var _this = generator || this;
    var _opt = opt || {};
    var regex;
    switch (action) {
    case 'stripHtml' :
        regex = /( (data-t|jhiT)ranslate\="([a-zA-Z0-9\ \+\{\}\'](\.)?)+")|( translate(-v|V)alues\="\{([a-zA-Z]|\d|\:|\{|\}|\[|\]|\-|\'|\s|\.)*?\}")|( translate-compile)|( translate-value-max\="[0-9\{\}\(\)\|]*")/g;
        //looks for something like data-translate="foo.bar.message" and translate-values="{foo: '{{ foo.bar }}'}"
        jhipsterUtils.copyWebResource(source, dest, regex, 'html', _this, _opt, template);
        break;
    case 'stripJs' :
        regex = /(\,[\s]*(resolve)\:[\s]*[\[]?[\{][\s]*(translatePartialLoader|token: 'translate')[\[]?[\'a-zA-Z0-9\$\,\(\)\{\}\.\<\%\=\-\>\;\s\:\[\]]*(\;[\s]*\}\][\s]*\}|\)[\s]*\}\]))|(import\s\{\s?[a-zA-Z0-9\=\<\>\%]*LanguageService\s?\}\sfrom\s[\"|\']ng-jhipster[\"|\']\;)/g;
        //looks for something like translatePartialLoader: [*] or token: 'translate'
        jhipsterUtils.copyWebResource(source, dest, regex, 'js', _this, _opt, template);
        break;
    case 'copy' :
        _this.copy(source, dest);
        break;
    default:
        _this.template(source, dest, _this, _opt);
    }
};

/**
 * Copy html templates after stripping translation keys when translation is disabled.
 *
 * @param {string} source - path of the source file to copy from
 * @param {string} dest - path of the destination file to copy to
 * @param {object} generator - context that can be used as the generator instance or data to process template
 * @param {object} opt - options that can be passed to template method
 * @param {boolean} template - flag to use template method instead of copy
 */
Generator.prototype.copyHtml = function (source, dest, generator, opt, template) {
    this.copyTemplate(source, dest, 'stripHtml', generator, opt, template);
};

/**
 * Copy Js templates after stripping translation keys when translation is disabled.<commit_msg>Fix regex for stripJs<commit_after>'use strict';
var path = require('path'),
    util = require('util'),
    _ = require('lodash'),
    yeoman = require('yeoman-generator'),
    chalk = require('chalk'),
    jhipsterUtils = require('./util'),
    Insight = require('insight'),
    fs = require('fs'),
    shelljs = require('shelljs'),
    packagejs = require('../package.json'),
    semver = require('semver'),
    exec = require('child_process').exec,
    os = require('os'),
    http = require('http'),
    pluralize = require('pluralize');

const JHIPSTER_CONFIG_DIR = '.jhipster';
const MODULES_HOOK_FILE = JHIPSTER_CONFIG_DIR + '/modules/jhi-hooks.json';
const GENERATOR_JHIPSTER = 'generator-jhipster';

const constants = require('./generator-constants'),
    CLIENT_MAIN_SRC_DIR = constants.CLIENT_MAIN_SRC_DIR,
    SERVER_MAIN_RES_DIR = constants.SERVER_MAIN_RES_DIR;

module.exports = Generator;

function Generator() {
    yeoman.Base.apply(this, arguments);
    this.env.options.appPath = this.config.get('appPath') || CLIENT_MAIN_SRC_DIR;
}

util.inherits(Generator, yeoman.Base);

/**
 * Add a new menu element, at the root of the menu.
 *
 * @param {string} routerName - The name of the AngularJS router that is added to the menu.
 * @param {string} glyphiconName - The name of the Glyphicon (from Bootstrap) that will be displayed.
 * @param {boolean} enableTranslation - If translations are enabled or not
 */
Generator.prototype.addElementToMenu = function (routerName, glyphiconName, enableTranslation) {
    try {
        var navbarPath;
        if (this.clientFramework === 'angular1') {
            navbarPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.html';
        } else {
            navbarPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.component.html';
        }

        jhipsterUtils.rewriteFile({
            file: navbarPath,
            needle: 'jhipster-needle-add-element-to-menu',
            splicable: [`<li ui-sref-active="active">
                            <a ui-sref="${routerName}" ng-click="vm.collapseNavbar()">
                                <span class="glyphicon glyphicon-${glyphiconName}"></span>&nbsp;
                                <span ${enableTranslation ? 'data-translate="global.menu.' + routerName + '"' : ''}>${_.startCase(routerName)}</span>
                            </a>
                        </li>`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + navbarPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + routerName + ' ' + chalk.yellow('not added to menu.\n'));
    }
};

/**
 * Add a new menu element to the admin menu.
 *
 * @param {string} routerName - The name of the AngularJS router that is added to the admin menu.
 * @param {string} glyphiconName - The name of the Glyphicon (from Bootstrap) that will be displayed.
 * @param {boolean} enableTranslation - If translations are enabled or not
 */
Generator.prototype.addElementToAdminMenu = function (routerName, glyphiconName, enableTranslation) {
    try {
        var navbarAdminPath;
        if (this.clientFramework === 'angular1') {
            navbarAdminPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.html';
        } else {
            navbarAdminPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.component.html';
        }
        jhipsterUtils.rewriteFile({
            file: navbarAdminPath,
            needle: 'jhipster-needle-add-element-to-admin-menu',
            splicable: [`<li ui-sref-active="active" >
                            <a ui-sref="${routerName}" ng-click="vm.collapseNavbar()">
                                <span class="glyphicon glyphicon-${glyphiconName}"></span>&nbsp;
                                <span ${enableTranslation ? 'data-translate="global.menu.admin.' + routerName + '"' : ''}>${_.startCase(routerName)}</span>
                            </a>
                        </li>`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + navbarAdminPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + routerName + ' ' + chalk.yellow('not added to admin menu.\n'));
    }
};

/**
 * Add a new entity in the "entities" menu.
 *
 * @param {string} routerName - The name of the AngularJS router (which by default is the name of the entity).
 * @param {boolean} enableTranslation - If translations are enabled or not
 */
Generator.prototype.addEntityToMenu = function (routerName, enableTranslation) {
    try {
        var entityMenuPath;
        if (this.clientFramework === 'angular1') {
            entityMenuPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.html';
            jhipsterUtils.rewriteFile({
                file: entityMenuPath,
                needle: 'jhipster-needle-add-entity-to-menu',
                splicable: [`<li ui-sref-active="active">
                                <a ui-sref="${routerName}" ng-click="vm.collapseNavbar()">
                                    <span class="glyphicon glyphicon-asterisk"></span>&nbsp;
                                    <span ${enableTranslation ? 'data-translate="global.menu.entities.' + _.camelCase(routerName) + '"' : ''}>${_.startCase(routerName)}</span>
                                </a>
                            </li>`
                ]
            }, this);
        } else {
            entityMenuPath = CLIENT_MAIN_SRC_DIR + 'app/layouts/navbar/navbar.component.html';
            jhipsterUtils.rewriteFile({
                file: entityMenuPath,
                needle: 'jhipster-needle-add-entity-to-menu',
                splicable: [`<li uiSrefActive="active">
                        <a class="dropdown-item" uiSref="${routerName}" (click)="collapseNavbar()">
                            <i class="fa fa-fw fa-asterisk" aria-hidden="true"></i>
                            <span ${enableTranslation ? 'jhiTranslate="global.menu.entities.' + _.camelCase(routerName) + '"' : ''}>${_.startCase(routerName)}</span>
                        </a>
                    </li>`
                ]
            }, this);
        }
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + entityMenuPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + routerName + ' ' + chalk.yellow('not added to menu.\n'));
    }
};
/**
 * Add a new entity in the TS modules file.
 *
 * @param {string} routerName - The name of the AngularJS router (which by default is the name of the entity).
 * @param {boolean} enableTranslation - If translations are enabled or not
 */
Generator.prototype.addEntityToModule = function (entityInstance, entityClass, entityAngularJSName, entityFolderName, entityFileName, enableTranslation) {
    try {
        if (this.clientFramework === 'angular1') {
            return;
        }
        var entityPath = CLIENT_MAIN_SRC_DIR + 'app/entities/entity.module.ts';
        var indexPath = CLIENT_MAIN_SRC_DIR + 'app/entities/index.ts';
        jhipsterUtils.rewriteFile({
            file: indexPath,
            needle: 'jhipster-needle-add-entity-to-index-export',
            splicable: [
                this.stripMargin(
                    `|export * from './${entityFolderName}/${entityFileName}-dialog.component';
                     |export * from './${entityFolderName}/${entityFileName}-delete-dialog.component';
                     |export * from './${entityFolderName}/${entityFileName}-detail.component';
                     |export * from './${entityFolderName}/${entityFileName}.component';
                     |export * from './${entityFolderName}/${entityFileName}.state';`
                )
            ]
        }, this);
        jhipsterUtils.rewriteFile({
            file: indexPath,
            needle: 'jhipster-needle-add-entity-to-index-model-export',
            splicable: [`export * from './${entityFolderName}/${entityFileName}.model';`
            ]
        }, this);
        jhipsterUtils.rewriteFile({
            file: indexPath,
            needle: 'jhipster-needle-add-entity-to-index-service-export',
            splicable: [`export * from './${entityFolderName}/${entityFileName}.service';`
            ]
        }, this);

        jhipsterUtils.rewriteFile({
            file: entityPath,
            needle: 'jhipster-needle-add-entity-to-module-states',
            splicable: [
                this.stripMargin(
                    `|${entityInstance}State,
                     |    ${entityInstance}NewState,
                     |    ${entityInstance}DetailState,
                     |    ${entityInstance}EditState,
                     |    ${entityInstance}DeleteState,`
                )
            ]
        }, this);

        jhipsterUtils.rewriteFile({
            file: entityPath,
            needle: 'jhipster-needle-add-entity-to-module-entryComponents',
            splicable: [
                this.stripMargin(
                    `|${entityAngularJSName}DialogComponent,
                     |        ${entityAngularJSName}DeleteDialogComponent,`
                )
            ]
        }, this);

        jhipsterUtils.rewriteFile({
            file: entityPath,
            needle: 'jhipster-needle-add-entity-to-module-declarations',
            splicable: [
                this.stripMargin(
                    `|${entityAngularJSName}Component,
                     |        ${entityAngularJSName}DetailComponent,
                     |        ${entityAngularJSName}DialogComponent,
                     |        ${entityAngularJSName}DeleteDialogComponent,`
                )
            ]
        }, this);

        jhipsterUtils.rewriteFile({
            file: entityPath,
            needle: 'jhipster-needle-add-entity-to-module-providers',
            splicable: [`${entityClass}Service,`]
        }, this);

        jhipsterUtils.rewriteFile({
            file: entityPath,
            needle: 'jhipster-needle-add-entity-to-module-import',
            splicable: [
                this.stripMargin(
                    `|${entityClass}Service,
                     |    ${entityAngularJSName}Component,
                     |    ${entityAngularJSName}DetailComponent,
                     |    ${entityAngularJSName}DialogComponent,
                     |    ${entityAngularJSName}DeleteDialogComponent,
                     |    ${entityInstance}State,
                     |    ${entityInstance}DetailState,
                     |    ${entityInstance}NewState,
                     |    ${entityInstance}EditState,
                     |    ${entityInstance}DeleteState,`
                )
            ]
        }, this);
    } catch (e) {this.log(e);
        this.log(chalk.yellow('\nUnable to find ') + indexPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + entityInstance+ entityClass+ entityFolderName+ entityFileName + ' ' + chalk.yellow('not added to menu.\n'));
    }
};

/**
 * A a new element in the "global.json" translations.
 *
 * @param {string} key - Key for the menu entry
 * @param {string} value - Default translated value
 * @param {string} language - The language to which this translation should be added
 */
Generator.prototype.addElementTranslationKey = function (key, value, language) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'i18n/' + language + '/global.json';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-menu-add-element',
            splicable: [
                `"${key}": "${_.startCase(value)}",`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + language + chalk.yellow(' not added as a new entity in the menu.\n'));
    }
};

/**
 * A a new element in the admin section of "global.json" translations.
 *
 * @param {string} key - Key for the menu entry
 * @param {string} value - Default translated value
 * @param {string} language - The language to which this translation should be added
 */
Generator.prototype.addAdminElementTranslationKey = function (key, value, language) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'i18n/' + language + '/global.json';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-menu-add-admin-element',
            splicable: [
                `"${key}": "${_.startCase(value)}",`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + language + chalk.yellow(' not added as a new entry in the admin menu.\n'));
    }
};

/**
 * A a new entity in the "global.json" translations.
 *
 * @param {string} key - Key for the entity name
 * @param {string} value - Default translated value
 * @param {string} language - The language to which this translation should be added
 */
Generator.prototype.addEntityTranslationKey = function (key, value, language) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'i18n/' + language + '/global.json';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-menu-add-entry',
            splicable: [
                `"${key}": "${_.startCase(value)}",`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + language + chalk.yellow(' not added as a new entity in the menu.\n'));
    }
};

/**
 * A a new entry as a root param in "global.json" translations.
 *
 * @param {string} key - Key for the entry
 * @param {string} value - Default translated value or object with multiple key and translated value
 * @param {string} language - The language to which this translation should be added
 */
Generator.prototype.addGlobalTranslationKey = function (key, value, language) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'i18n/' + language + '/global.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj[key] = value;
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + '(key: ' + key + ', value:' + value + ')' + chalk.yellow(' not added to global translations.\n'));
    }
};

/**
 * Add a translation key to all installed languages
 *
 * @param {string} key - Key for the entity name
 * @param {string} value - Default translated value
 * @param {string} method - The method to be run with provided key and value from above
 * @param {string} enableTranslation - specify if i18n is enabled
 */
Generator.prototype.addTranslationKeyToAllLanguages = function (key, value, method, enableTranslation) {
    if (enableTranslation) {
        this.getAllInstalledLanguages().forEach(function (language) {
            this[method](key, value, language);
        }, this);
    }
};

/**
 * get all the languages installed currently
 */
Generator.prototype.getAllInstalledLanguages = function () {
    var languages = [];
    this.getAllSupportedLanguages().forEach(function (language) {
        try {
            var stats = fs.lstatSync(CLIENT_MAIN_SRC_DIR + 'i18n/' + language);
            if (stats.isDirectory()) {
                languages.push(language);
            }
        } catch (e) {
            // An exception is thrown if the folder doesn't exist
            // do nothing as the language might not be installed
        }
    });
    return languages;
};

/**
 * get all the languages supported by JHipster
 */
Generator.prototype.getAllSupportedLanguages = function () {
    return _.map(this.getAllSupportedLanguageOptions(), 'value');
};

/**
 * check if a language is supported by JHipster
 * @param {string} language - Key for the language
 */
Generator.prototype.isSupportedLanguage = function (language) {
    return _.includes(this.getAllSupportedLanguages(), language);
};

/**
 * get all the languages options supported by JHipster
 */
Generator.prototype.getAllSupportedLanguageOptions = function () {
    return [
        {name: 'Catalan', value: 'ca'},
        {name: 'Chinese (Simplified)', value: 'zh-cn'},
        {name: 'Chinese (Traditional)', value: 'zh-tw'},
        {name: 'Czech', value: 'cs'},
        {name: 'Danish', value: 'da'},
        {name: 'Dutch', value: 'nl'},
        {name: 'English', value: 'en'},
        {name: 'Estonian', value: 'et'},
        {name: 'French', value: 'fr'},
        {name: 'Galician', value: 'gl'},
        {name: 'German', value: 'de'},
        {name: 'Greek', value: 'el'},
        {name: 'Hindi', value: 'hi'},
        {name: 'Hungarian', value: 'hu'},
        {name: 'Italian', value: 'it'},
        {name: 'Japanese', value: 'ja'},
        {name: 'Korean', value: 'ko'},
        {name: 'Marathi', value: 'mr'},
        {name: 'Polish', value: 'pl'},
        {name: 'Portuguese (Brazilian)', value: 'pt-br'},
        {name: 'Portuguese', value: 'pt-pt'},
        {name: 'Romanian', value: 'ro'},
        {name: 'Russian', value: 'ru'},
        {name: 'Slovak', value: 'sk'},
        {name: 'Serbian', value: 'sr'},
        {name: 'Spanish', value: 'es'},
        {name: 'Swedish', value: 'sv'},
        {name: 'Turkish', value: 'tr'},
        {name: 'Tamil', value: 'ta'},
        {name: 'Vietnamese', value: 'vi'}
    ];
};

/**
 * Add new social configuration in the "application.yml".
 *
 * @param {string} name - social name (twitter, facebook, ect.)
 * @param {string} clientId - clientId
 * @param {string} clientSecret - clientSecret
 * @param {string} comment - url of how to configure the social service
 */
Generator.prototype.addSocialConfiguration = function (name, clientId, clientSecret, comment) {
    var fullPath = SERVER_MAIN_RES_DIR + 'config/application.yml';
    try {
        this.log(chalk.yellow('   update ') + fullPath);
        var config = '';
        if (comment) {
            config += '# ' + comment + '\n        ';
        }
        config += name + ':\n' +
            '            clientId: ' + clientId + '\n' +
            '            clientSecret: ' + clientSecret + '\n';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-add-social-configuration',
            splicable: [
                config
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'social configuration ' + name + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new dependency in the "bower.json".
 *
 * @param {string} name - dependency name
 * @param {string} version - dependency version
 */
Generator.prototype.addBowerDependency = function (name, version) {
    var fullPath = 'bower.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj.dependencies[name] = version;
        }, this);
    } catch (e) {
        this.log(e);
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'bower dependency (name: ' + name + ', version:' + version + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new override configuration in the "bower.json".
 *
 * @param {string} bowerPackageName - Bower package name use in dependencies
 * @param {array} main - You can specify which files should be selected
 * @param {boolean} isIgnored - Default: false, Set to true if you want to ignore this package.
 * @param {object} dependencies - You can override the dependencies of a package. Set to null to ignore the dependencies.
 *
 */
Generator.prototype.addBowerOverride = function (bowerPackageName, main, isIgnored, dependencies) {
    var fullPath = 'bower.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            var override = {};
            if (main !== undefined && main.length > 0) {
                override['main'] = main;
            }
            if (isIgnored) {
                override['ignore'] = true;
            }
            if (dependencies) {
                override['dependencies'] = dependencies;
            }
            if (jsonObj.overrides === undefined) {
                jsonObj.overrides = {};
            }
            jsonObj.overrides[bowerPackageName] = override;
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'bower override configuration (bowerPackageName: ' + bowerPackageName + ', main:' + JSON.stringify(main) + ', ignore:' + isIgnored + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new parameter in the ".bowerrc".
 *
 * @param {string} key - name of the parameter
 * @param {string, obj, bool, etc.} value - value of the parameter
 */
Generator.prototype.addBowerrcParameter = function (key, value) {
    var fullPath = '.bowerrc';
    try {
        this.log(chalk.yellow('   update ') + fullPath);
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj[key] = value;
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'bowerrc parameter (key: ' + key + ', value:' + value + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new dependency in the "package.json".
 *
 * @param {string} name - dependency name
 * @param {string} version - dependency version
 */
Generator.prototype.addNpmDependency = function (name, version) {
    var fullPath = 'package.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj.dependencies[name] = version;
        }, this);
    } catch (e) {
        this.log(e);
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'npm dependency (name: ' + name + ', version:' + version + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new devDependency in the "package.json".
 *
 * @param {string} name - devDependency name
 * @param {string} version - devDependency version
 */
Generator.prototype.addNpmDevDependency = function (name, version) {
    var fullPath = 'package.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj.devDependencies[name] = version;
        }, this);
    } catch (e) {
        this.log(e);
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'npm devDependency (name: ' + name + ', version:' + version + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new script in the "package.json".
 *
 * @param {string} name - script name
 * @param {string} data - script version
 */
Generator.prototype.addNpmScript = function (name, data) {
    var fullPath = 'package.json';
    try {
        jhipsterUtils.rewriteJSONFile(fullPath, function (jsonObj) {
            jsonObj.scripts[name] = data;
        }, this);
    } catch (e) {
        this.log(e);
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow('. Reference to ') + 'npm script (name: ' + name + ', data:' + data + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new module to the angular application in "app.module.js".
 *
 * @param {string} moduleName - module name
 *
 */
Generator.prototype.addAngularJsModule = function (moduleName) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'app/app.module.js';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-angularjs-add-module',
            splicable: [
                `'${moduleName}',`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + moduleName + chalk.yellow(' not added to JHipster app.\n'));
    }
};

/**
 * Add a new http interceptor to the angular application in "blocks/config/http.config.js".
 * The interceptor should be in its own .js file inside app/blocks/interceptor folder
 * @param {string} interceptorName - angular name of the interceptor
 *
 */
Generator.prototype.addAngularJsInterceptor = function (interceptorName) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'app/blocks/config/http.config.js';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-angularjs-add-interceptor',
            splicable: [
                `$httpProvider.interceptors.push('${interceptorName}');`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Interceptor not added to JHipster app.\n'));
    }
};

/**
 * Add a new entity to the Ehcache, for the 2nd level cache of an entity and its relationships.
 *
 * @param {string} name - the entity to cache.
 * @parma {array} relationships - the relationships of this entity
 */
Generator.prototype.addEntityToEhcache = function (entityClass, relationships) {
    // Add the entity to ehcache
    this.addEntryToEhcache(`${this.packageName}.domain.${entityClass}`);
    // Add the collections linked to that entity to ehcache
    for (var idx in relationships) {
        var relationshipType = relationships[idx].relationshipType;
        if (relationshipType === 'one-to-many' || relationshipType === 'many-to-many') {
            this.addEntryToEhcache(`${this.packageName}.domain.${entityClass}.${relationships[idx].relationshipFieldNamePlural}`);
        }
    }
};

/**
 * Add a new entry to the ehcache.xml file
 *
 * @param {string} name - the entry (including package name) to cache.
 */
Generator.prototype.addEntryToEhcache = function (entry) {
    try {
        var fullPath = SERVER_MAIN_RES_DIR + 'ehcache.xml';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-ehcache-add-entry',
            splicable: [`<cache alias="${entry}" uses-template="simple"/>
`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to add ' + entry + ' to ehcache.xml file.\n'));
    }
};

/**
 * Add a new changelog to the Liquibase master.xml file.
 *
 * @param {string} changelogName - The name of the changelog (name of the file without .xml at the end).
 */
Generator.prototype.addChangelogToLiquibase = function (changelogName) {
    this.addLiquibaseChangelogToMaster(changelogName, 'jhipster-needle-liquibase-add-changelog');
};

/**
 * Add a new constraints changelog to the Liquibase master.xml file.
 *
 * @param {string} changelogName - The name of the changelog (name of the file without .xml at the end).
 */
Generator.prototype.addConstraintsChangelogToLiquibase = function (changelogName) {
    this.addLiquibaseChangelogToMaster(changelogName, 'jhipster-needle-liquibase-add-constraints-changelog');
};

/**
 * Add a new changelog to the Liquibase master.xml file.
 *
 * @param {string} changelogName - The name of the changelog (name of the file without .xml at the end).
 * @param {string} needle - The needle at where it has to be added.
 */
Generator.prototype.addLiquibaseChangelogToMaster = function (changelogName, needle) {
    try {
        var fullPath = SERVER_MAIN_RES_DIR + 'config/liquibase/master.xml';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: needle,
            splicable: [
                `<include file="classpath:config/liquibase/changelog/${changelogName}.xml" relativeToChangelogFile="false"/>`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + changelogName + '.xml ' + chalk.yellow('not added.\n'));
    }
};

/**
 * A a new column to a Liquibase changelog file for entity.
 *
 * @param {string} filePath - The full path of the changelog file.
 * @param {string} content - The content to be added as column, can have multiple columns as well
 */
Generator.prototype.addColumnToLiquibaseEntityChangeset = function (filePath, content) {
    try {
        jhipsterUtils.rewriteFile({
            file: filePath,
            needle: 'jhipster-needle-liquibase-add-column',
            splicable: [
                content
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + filePath + chalk.yellow(' or missing required jhipster-needle. Column not added.\n') + e);
    }
};

/**
 * Add a new social button in the login and register modules
 *
 * @param {string} socialName - name of the social module. ex: 'facebook'
 * @param {string} socialParameter - parameter to send to social connection ex: 'public_profile,email'
 * @param {string} buttonColor - color of the social button. ex: '#3b5998'
 * @param {string} buttonHoverColor - color of the social button when is hover. ex: '#2d4373'
 */
Generator.prototype.addSocialButton = function (isUseSass, socialName, socialParameter, buttonColor, buttonHoverColor) {
    var socialServicefullPath = CLIENT_MAIN_SRC_DIR + 'app/account/social/social.service.js';
    var loginfullPath, registerfullPath;
    if (this.clientFramework === 'angular1') {
        loginfullPath = CLIENT_MAIN_SRC_DIR + 'app/account/login/login.html';
        registerfullPath = CLIENT_MAIN_SRC_DIR + 'app/account/register/register.html';
    } else {
        loginfullPath = CLIENT_MAIN_SRC_DIR + 'app/account/login/login.component.html';
        registerfullPath = CLIENT_MAIN_SRC_DIR + 'app/account/register/register.component.html';
    }
    try {
        this.log(chalk.yellow('\nupdate ') + socialServicefullPath);
        var serviceCode = `case '${socialName}': return '${socialParameter}';`;
        jhipsterUtils.rewriteFile({
            file: socialServicefullPath,
            needle: 'jhipster-needle-add-social-button',
            splicable: [
                serviceCode
            ]
        }, this);

        var buttonCode = `<jh-social ng-provider="${socialName}"></jh-social>`;
        this.log(chalk.yellow('update ') + loginfullPath);
        jhipsterUtils.rewriteFile({
            file: loginfullPath,
            needle: 'jhipster-needle-add-social-button',
            splicable: [
                buttonCode
            ]
        }, this);
        this.log(chalk.yellow('update ') + registerfullPath);
        jhipsterUtils.rewriteFile({
            file: registerfullPath,
            needle: 'jhipster-needle-add-social-button',
            splicable: [
                buttonCode
            ]
        }, this);

        var buttonStyle = `.jh-btn-${socialName} {
                 background-color: ${buttonColor};
                 border-color: rgba(0, 0, 0, 0.2);
                 color: #fff;
            }\n
            .jh-btn-${socialName}:hover, .jh-btn-${socialName}:focus, .jh-btn-${socialName}:active, .jh-btn-${socialName}.active, .open > .dropdown-toggle.jh-btn-${socialName} {
                background-color: ${buttonHoverColor};
                border-color: rgba(0, 0, 0, 0.2);
                color: #fff;
            }`;
        this.addMainCSSStyle(isUseSass, buttonStyle, 'Add sign in style for ' + socialName);

    } catch (e) {
        this.log(chalk.yellow('\nUnable to add social button modification.\n' + e));
    }
};

/**
 * Add a new social connection factory in the SocialConfiguration.java file.
 *
 * @param {string} javaDir - default java directory of the project (JHipster var)
 * @param {string} importPackagePath - package path of the ConnectionFactory class
 * @param {string} socialName - name of the social module
 * @param {string} connectionFactoryClassName - name of the ConnectionFactory class
 * @param {string} configurationName - name of the section in the config yaml file
 */
Generator.prototype.addSocialConnectionFactory = function (javaDir, importPackagePath, socialName, connectionFactoryClassName, configurationName) {
    var fullPath = javaDir + 'config/social/SocialConfiguration.java';
    try {
        this.log(chalk.yellow('\nupdate ') + fullPath);
        var javaImport = 'import ' + importPackagePath + ';\n';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-add-social-connection-factory-import-package',
            splicable: [
                javaImport
            ]
        }, this);

        var clientId = socialName + 'ClientId';
        var clientSecret = socialName + 'ClientSecret';
        var javaCode = '// ' + socialName + ' configuration\n' +
            '        String ' + clientId + ' = environment.getProperty("spring.social.' + configurationName + '.clientId");\n' +
            '        String ' + clientSecret + ' = environment.getProperty("spring.social.' + configurationName + '.clientSecret");\n' +
            '        if (' + clientId + ' != null && ' + clientSecret + ' != null) {\n' +
            '            log.debug("Configuring ' + connectionFactoryClassName + '");\n' +
            '            connectionFactoryConfigurer.addConnectionFactory(\n' +
            '                new ' + connectionFactoryClassName + '(\n' +
            '                    ' + clientId + ',\n' +
            '                    ' + clientSecret + '\n' +
            '                )\n' +
            '            );\n' +
            '        } else {\n' +
            '            log.error("Cannot configure ' + connectionFactoryClassName + ' id or secret null");\n' +
            '        }\n';

        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-add-social-connection-factory',
            splicable: [
                javaCode
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Social connection ') + e + ' ' + chalk.yellow('not added.\n'));
    }
};

/**
 * Add new css style to the angular application in "main.css".
 *
 * @param {string} style - css to add in the file
 * @param {string} comment - comment to add before css code
 *
 * example:
 *
 * style = '.jhipster {\n     color: #baa186;\n}'
 * comment = 'New JHipster color'
 *
 * * ==========================================================================
 * New JHipster color
 * ========================================================================== *
 * .jhipster {
 *     color: #baa186;
 * }
 *
 */
Generator.prototype.addMainCSSStyle = function (isUseSass, style, comment) {
    if (isUseSass) {
        this.addMainSCSSStyle(style, comment);
    }

    var fullPath = CLIENT_MAIN_SRC_DIR + 'content/css/main.css';
    var styleBlock = '';
    if (comment) {
        styleBlock += '/* ==========================================================================\n';
        styleBlock += comment + '\n';
        styleBlock += '========================================================================== */\n';
    }
    styleBlock += style + '\n';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-css-add-main',
            splicable: [
                styleBlock
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Style not added to JHipster app.\n'));
    }
};

/**
 * Add new scss style to the angular application in "main.scss".
 *
 * @param {string} style - scss to add in the file
 * @param {string} comment - comment to add before css code
 *
 * example:
 *
 * style = '.success {\n     @extend .message;\n    border-color: green;\n}'
 * comment = 'Message'
 *
 * * ==========================================================================
 * Message
 * ========================================================================== *
 * .success {
 *     @extend .message;
 *     border-color: green;
 * }
 *
 */
Generator.prototype.addMainSCSSStyle = function (style, comment) {
    var fullPath = CLIENT_MAIN_SRC_DIR + 'scss/main.scss';
    var styleBlock = '';
    if (comment) {
        styleBlock += '/* ==========================================================================\n';
        styleBlock += comment + '\n';
        styleBlock += '========================================================================== */\n';
    }
    styleBlock += style + '\n';
    try {
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-scss-add-main',
            splicable: [
                styleBlock
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Style not added to JHipster app.\n'));
    }
};

/**
 * Add a new Maven dependency.
 *
 * @param {string} groupId - dependency groupId
 * @param {string} artifactId - dependency artifactId
 * @param {string} version - explicit dependency version number
 * @param {string} other - explicit other thing: scope, exclusions...
 */
Generator.prototype.addMavenDependency = function (groupId, artifactId, version, other) {
    try {
        var fullPath = 'pom.xml';
        var dependency = '<dependency>\n' +
            '            <groupId>' + groupId + '</groupId>\n' +
            '            <artifactId>' + artifactId + '</artifactId>\n';
        if (version) {
            dependency += '            <version>' + version + '</version>\n';
        }
        if (other) {
            dependency += other + '\n';
        }
        dependency += '        </dependency>';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-maven-add-dependency',
            splicable: [
                dependency
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + 'maven dependency (groupId: ' + groupId + ', artifactId:' + artifactId + ', version:' + version + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * Add a new Maven plugin.
 *
 * @param {string} groupId - plugin groupId
 * @param {string} artifactId - plugin artifactId
 * @param {string} version - explicit plugin version number
 * @param {string} other - explicit other thing: executions, configuration...
 */
Generator.prototype.addMavenPlugin = function (groupId, artifactId, version, other) {
    try {
        var fullPath = 'pom.xml';
        var plugin = '<plugin>\n' +
            '                <groupId>' + groupId + '</groupId>\n' +
            '                <artifactId>' + artifactId + '</artifactId>\n';
        if (version) {
            plugin += '                <version>' + version + '</version>\n';
        }
        if (other) {
            plugin += other + '\n';
        }
        plugin += '            </plugin>';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-maven-add-plugin',
            splicable: [
                plugin
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + 'maven plugin (groupId: ' + groupId + ', artifactId:' + artifactId + ', version:' + version + ')' + chalk.yellow(' not added.\n'));
    }
};

/**
 * A new Gradle plugin.
 *
 * @param {string} group - plugin GroupId
 * @param {string} name - plugin name
 * @param {string} version - explicit plugin version number
 */
Generator.prototype.addGradlePlugin = function (group, name, version) {
    try {
        var fullPath = 'build.gradle';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-gradle-buildscript-dependency',
            splicable: [
                `classpath '${group}:${name}:${version}'`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + 'classpath: ' + group + ':' + name + ':' + version + chalk.yellow(' not added.\n'));
    }
};

/**
 * A new dependency to build.gradle file.
 *
 * @param {string} scope - scope of the new dependency, e.g. compile
 * @param {string} group - maven GroupId
 * @param {string} name - maven ArtifactId
 * @param {string} version - explicit version number
 */
Generator.prototype.addGradleDependency = function (scope, group, name, version) {
    try {
        var fullPath = 'build.gradle';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-gradle-dependency',
            splicable: [
                `${scope} '${group}:${name}:${version}'`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + group + ':' + name + ':' + version + chalk.yellow(' not added.\n'));
    }
};

/**
 * Apply from an external Gradle build script.
 *
 * @param {string} name - name of the file to apply from, must be 'fileName.gradle'
 */
Generator.prototype.applyFromGradleScript = function (name) {
    try {
        var fullPath = 'build.gradle';
        jhipsterUtils.rewriteFile({
            file: fullPath,
            needle: 'jhipster-needle-gradle-apply-from',
            splicable: [
                `apply from: '${name}.gradle'`
            ]
        }, this);
    } catch (e) {
        this.log(chalk.yellow('\nUnable to find ') + fullPath + chalk.yellow(' or missing required jhipster-needle. Reference to ') + name + chalk.yellow(' not added.\n'));
    }
};

/**
 * Generate a date to be used by Liquibase changelogs.
 */
Generator.prototype.dateFormatForLiquibase = function () {
    var now = new Date();
    var now_utc = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds());
    var year = '' + now_utc.getFullYear();
    var month = '' + (now_utc.getMonth() + 1);
    if (month.length === 1) {
        month = '0' + month;
    }
    var day = '' + now_utc.getDate();
    if (day.length === 1) {
        day = '0' + day;
    }
    var hour = '' + now_utc.getHours();
    if (hour.length === 1) {
        hour = '0' + hour;
    }
    var minute = '' + now_utc.getMinutes();
    if (minute.length === 1) {
        minute = '0' + minute;
    }
    var second = '' + now_utc.getSeconds();
    if (second.length === 1) {
        second = '0' + second;
    }
    return `${year}${month}${day}${hour}${minute}${second}`;
};

/**
 * Copy templates with all the custom logic applied according to the type.
 *
 * @param {string} source - path of the source file to copy from
 * @param {string} dest - path of the destination file to copy to
 * @param {string} action - type of the action to be performed on the template file, i.e: stripHtml | stripJs | template | copy
 * @param {object} generator - context that can be used as the generator instance or data to process template
 * @param {object} opt - options that can be passed to template method
 * @param {boolean} template - flag to use template method instead of copy method
 */
Generator.prototype.copyTemplate = function (source, dest, action, generator, opt, template) {

    var _this = generator || this;
    var _opt = opt || {};
    var regex;
    switch (action) {
    case 'stripHtml' :
        regex = /( (data-t|jhiT)ranslate\="([a-zA-Z0-9\ \+\{\}\'](\.)?)+")|( translate(-v|V)alues\="\{([a-zA-Z]|\d|\:|\{|\}|\[|\]|\-|\'|\s|\.)*?\}")|( translate-compile)|( translate-value-max\="[0-9\{\}\(\)\|]*")/g;
        //looks for something like data-translate="foo.bar.message" and translate-values="{foo: '{{ foo.bar }}'}"
        jhipsterUtils.copyWebResource(source, dest, regex, 'html', _this, _opt, template);
        break;
    case 'stripJs' :
        regex = /(\,[\s]*(resolve)\:[\s]*[\[]?[\{][\s]*(translatePartialLoader|token: 'translate')[\[]?[\'a-zA-Z0-9\$\,\(\)\{\.\<\%\=\-\>\;\s\:\[\]]*(\;[\s]*\}\][\s]*\}|\)[\s]*\}\]))|(import\s\{\s?[a-zA-Z0-9\=\<\>\%]*LanguageService\s?\}\sfrom\s[\"|\']ng-jhipster[\"|\']\;)/g;
        //looks for something like translatePartialLoader: [*] or token: 'translate'
        jhipsterUtils.copyWebResource(source, dest, regex, 'js', _this, _opt, template);
        break;
    case 'copy' :
        _this.copy(source, dest);
        break;
    default:
        _this.template(source, dest, _this, _opt);
    }
};

/**
 * Copy html templates after stripping translation keys when translation is disabled.
 *
 * @param {string} source - path of the source file to copy from
 * @param {string} dest - path of the destination file to copy to
 * @param {object} generator - context that can be used as the generator instance or data to process template
 * @param {object} opt - options that can be passed to template method
 * @param {boolean} template - flag to use template method instead of copy
 */
Generator.prototype.copyHtml = function (source, dest, generator, opt, template) {
    this.copyTemplate(source, dest, 'stripHtml', generator, opt, template);
};

/**
 * Copy Js templates after stripping translation keys when translation is disabled.
{'fraction_non_alphanumeric': 0.15175961136878202, 'fraction_numerical': 0.0014068098388763106, 'mean_word_length': 2.381536403166462, 'pattern_counts': {'":': 6, '<': 111, '<?xml version=': 0, '>': 115, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 28}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '491168', 'n_tokens_mistral': 26797, 'n_tokens_neox': 25922, 'n_words': 6574}