private Map<String, Map<String, ConfigParameterInfo>> getPluginConfig(
     ProjectState project,
     DynamicMap<ProjectConfigEntry> pluginConfigEntries,
     PluginConfigFactory cfgFactory,
     AllProjectsName allProjects) {
   TreeMap<String, Map<String, ConfigParameterInfo>> pluginConfig = new TreeMap<>();
   for (Entry<ProjectConfigEntry> e : pluginConfigEntries) {
     ProjectConfigEntry configEntry = e.getProvider().get();
     PluginConfig cfg = cfgFactory.getFromProjectConfig(project, e.getPluginName());
     String configuredValue = cfg.getString(e.getExportName());
     ConfigParameterInfo p = new ConfigParameterInfo();
     p.displayName = configEntry.getDisplayName();
     p.description = configEntry.getDescription();
     p.warning = configEntry.getWarning(project);
     p.type = configEntry.getType();
     p.permittedValues = configEntry.getPermittedValues();
     p.editable = configEntry.isEditable(project) ? true : null;
     if (configEntry.isInheritable() && !allProjects.equals(project.getProject().getNameKey())) {
       PluginConfig cfgWithInheritance =
           cfgFactory.getFromProjectConfigWithInheritance(project, e.getPluginName());
       p.inheritable = true;
       p.value =
           configEntry.onRead(
               project,
               cfgWithInheritance.getString(e.getExportName(), configEntry.getDefaultValue()));
       p.configuredValue = configuredValue;
       p.inheritedValue = getInheritedValue(project, cfgFactory, e);
     } else {
       if (configEntry.getType() == ProjectConfigEntryType.ARRAY) {
         p.values =
             configEntry.onRead(project, Arrays.asList(cfg.getStringList(e.getExportName())));
       } else {
         p.value =
             configEntry.onRead(
                 project,
                 configuredValue != null ? configuredValue : configEntry.getDefaultValue());
       }
     }
     Map<String, ConfigParameterInfo> pc = pluginConfig.get(e.getPluginName());
     if (pc == null) {
       pc = new TreeMap<>();
       pluginConfig.put(e.getPluginName(), pc);
     }
     pc.put(e.getExportName(), p);
   }
   return !pluginConfig.isEmpty() ? pluginConfig : null;
 }
    @Override
    public void onPreMerge(
        final Repository repo,
        final CodeReviewCommit commit,
        final ProjectState destProject,
        final Branch.NameKey destBranch,
        final PatchSet.Id patchSetId)
        throws MergeValidationException {
      if (RefNames.REFS_CONFIG.equals(destBranch.get())) {
        final Project.NameKey newParent;
        try {
          ProjectConfig cfg = new ProjectConfig(destProject.getProject().getNameKey());
          cfg.load(repo, commit);
          newParent = cfg.getProject().getParent(allProjectsName);
          final Project.NameKey oldParent = destProject.getProject().getParent(allProjectsName);
          if (oldParent == null) {
            // update of the 'All-Projects' project
            if (newParent != null) {
              throw new MergeValidationException(ROOT_NO_PARENT);
            }
          } else {
            if (!oldParent.equals(newParent)) {
              PatchSetApproval psa = approvalsUtil.getSubmitter(db, commit.notes(), patchSetId);
              if (psa == null) {
                throw new MergeValidationException(SET_BY_ADMIN);
              }
              final IdentifiedUser submitter = identifiedUserFactory.create(psa.getAccountId());
              if (!submitter.getCapabilities().canAdministrateServer()) {
                throw new MergeValidationException(SET_BY_ADMIN);
              }

              if (projectCache.get(newParent) == null) {
                throw new MergeValidationException(PARENT_NOT_FOUND);
              }
            }
          }

          for (Entry<ProjectConfigEntry> e : pluginConfigEntries) {
            PluginConfig pluginCfg = cfg.getPluginConfig(e.getPluginName());
            ProjectConfigEntry configEntry = e.getProvider().get();

            String value = pluginCfg.getString(e.getExportName());
            String oldValue =
                destProject
                    .getConfig()
                    .getPluginConfig(e.getPluginName())
                    .getString(e.getExportName());

            if ((value == null ? oldValue != null : !value.equals(oldValue))
                && !configEntry.isEditable(destProject)) {
              throw new MergeValidationException(PLUGIN_VALUE_NOT_EDITABLE);
            }

            if (ProjectConfigEntry.Type.LIST.equals(configEntry.getType())
                && value != null
                && !configEntry.getPermittedValues().contains(value)) {
              throw new MergeValidationException(PLUGIN_VALUE_NOT_PERMITTED);
            }
          }
        } catch (ConfigInvalidException | IOException e) {
          throw new MergeValidationException(INVALID_CONFIG);
        }
      }
    }