/** For each project in the current build set, reset the version if using project.version */
  @Override
  public Set<Project> applyChanges(final List<Project> projects, final ManipulationSession session)
      throws ManipulationException {
    final ProjectVersionEnforcingState state = session.getState(ProjectVersionEnforcingState.class);
    if (!session.isEnabled()
        || !session.anyStateEnabled(State.activeByDefault)
        || state == null
        || !state.isEnabled()) {
      logger.debug("Project version enforcement is disabled.");
      return Collections.emptySet();
    }

    final Set<Project> changed = new HashSet<Project>();

    for (final Project project : projects) {
      final Model model = project.getModel();

      if (model.getPackaging().equals("pom")) {
        enforceProjectVersion(project, model.getDependencies(), changed);

        if (model.getDependencyManagement() != null) {
          enforceProjectVersion(
              project, model.getDependencyManagement().getDependencies(), changed);
        }

        final List<Profile> profiles = model.getProfiles();
        if (profiles != null) {
          for (final Profile profile : model.getProfiles()) {
            enforceProjectVersion(project, profile.getDependencies(), changed);
            if (profile.getDependencyManagement() != null) {
              enforceProjectVersion(
                  project, profile.getDependencyManagement().getDependencies(), changed);
            }
          }
        }
      }
    }
    if (changed.size() > 0) {
      logger.warn(
          "Using ${project.version} in pom files may lead to unexpected errors with inheritance.");
    }
    return changed;
  }
  /** Apply the groovy script changes to the top level pom. */
  @Override
  public Set<Project> applyChanges(final List<Project> projects, final ManipulationSession session)
      throws ManipulationException {
    final GroovyState state = session.getState(GroovyState.class);
    if (!session.isEnabled() || !state.isEnabled()) {
      logger.debug(getClass().getSimpleName() + ": Nothing to do!");
      return Collections.emptySet();
    }

    final Set<Project> changed = new HashSet<>();
    final List<ArtifactRef> scripts = state.getGroovyScripts();

    for (ArtifactRef ar : scripts) {
      logger.info(
          "Attempting to read GAV {} with classifier {} and type {} ",
          ar.asProjectVersionRef(),
          ar.getClassifier(),
          ar.getType());

      final File groovyScript = modelBuilder.resolveRawFile(ar);

      GroovyShell shell = new GroovyShell();
      Script script;

      for (final Project project : projects) {
        if (project.isExecutionRoot()) {
          logger.info("Executing {} on {}", groovyScript, project);

          try {
            script = shell.parse(groovyScript);

            script.invokeMethod(
                "setValues", new Object[] {session.getUserProperties(), projects, project});
          } catch (MissingMethodException e) {
            try {
              logger.debug(
                  "Failure when injecting into script {} ",
                  FileUtils.readFileToString(groovyScript));
            } catch (IOException e1) {
              logger.debug("Unable to read script file {} for debugging! {} ", groovyScript, e1);
            }
            throw new ManipulationException("Unable to inject values into base script", e);
          } catch (CompilationFailedException e) {
            try {
              logger.debug(
                  "Failure when parsing script {} ", FileUtils.readFileToString(groovyScript));
            } catch (IOException e1) {
              logger.debug("Unable to read script file {} for debugging! {} ", groovyScript, e1);
            }
            throw new ManipulationException("Unable to parse script", e);
          } catch (IOException e) {
            throw new ManipulationException("Unable to parse script", e);
          }
          try {
            script.run();
          } catch (Exception e) {
            throw new ManipulationException("Unable to parse script", e);
          }

          changed.add(project);
        }
      }
    }
    return changed;
  }
  /**
   * If enabled, grab the execution root pom (which will be the topmost POM in terms of directory
   * structure). Check for the presence of the project-sources-maven-plugin in the base build
   * (/project/build/plugins/). Inject a new plugin execution for creating project sources if this
   * plugin has not already been declared in the base build section.
   */
  @Override
  public Set<Project> applyChanges(final List<Project> projects, final ManipulationSession session)
      throws ManipulationException {
    final ProjectSourcesInjectingState state = session.getState(ProjectSourcesInjectingState.class);

    // This manipulator will only run if its enabled *and* at least one other manipulator is
    // enabled.
    if (state.isEnabled() && session.anyStateEnabled(State.activeByDefault)) {
      for (final Project project : projects) {
        if (project.isExecutionRoot()) {
          logger.info("Examining {} to apply sources/metadata plugins.", project);

          final Model model = project.getModel();
          Build build = model.getBuild();
          if (build == null) {
            build = new Build();
            model.setBuild(build);
          }

          boolean changed = false;
          final Map<String, Plugin> pluginMap = build.getPluginsAsMap();
          if (state.isProjectSourcesPluginEnabled()
              && !pluginMap.containsKey(PROJECT_SOURCES_COORD)) {
            final PluginExecution execution = new PluginExecution();
            execution.setId(PROJECT_SOURCES_EXEC_ID);
            execution.setPhase(INITIALIZE_PHASE);
            execution.setGoals(Collections.singletonList(PROJECT_SOURCES_GOAL));

            final Plugin plugin = new Plugin();
            plugin.setGroupId(PROJECT_SOURCES_GID);
            plugin.setArtifactId(PROJECT_SOURCES_AID);
            plugin.setVersion(state.getProjectSourcesPluginVersion());
            plugin.addExecution(execution);

            build.addPlugin(plugin);

            changed = true;
          }

          if (state.isBuildMetadataPluginEnabled() && !pluginMap.containsKey(BMMP_COORD)) {
            final PluginExecution execution = new PluginExecution();
            execution.setId(BMMP_EXEC_ID);
            execution.setPhase(VALIDATE_PHASE);
            execution.setGoals(Collections.singletonList(BMMP_GOAL));

            final Xpp3Dom xml = new Xpp3Dom("configuration");

            final Map<String, Object> config = new HashMap<>();
            config.put("createPropertiesReport", true);
            config.put("hideCommandLineInfo", false);
            config.put("hideJavaOptsInfo", false);
            config.put("activateOutputFileMapping", true);
            config.put("addJavaRuntimeInfo", true);

            // Default name is build.properties but we currently prefer build.metadata.
            config.put("propertiesOutputFile", "build.metadata");
            // Deactivate features we don't want.
            config.put("createXmlReport", false);
            config.put("addLocallyModifiedTagToFullVersion", false);
            config.put("addToGeneratedSources", false);
            config.put("validateCheckout", false);
            config.put("forceNewProperties", true);
            config.put("addBuildDateToFullVersion", false);
            config.put("addHostInfo", false);
            config.put("addBuildDateInfo", false);
            config.put("addOsInfo", false);
            config.put("addMavenExecutionInfo", false);
            config.put("addToFilters", false);

            final Xpp3Dom additionalLocations = new Xpp3Dom("addToLocations");
            final Xpp3Dom additionalLocation = new Xpp3Dom("addToLocation");

            xml.addChild(additionalLocations);
            additionalLocations.addChild(additionalLocation);
            additionalLocation.setValue("${session.executionRootDirectory}");

            for (final Map.Entry<String, Object> entry : config.entrySet()) {
              final Xpp3Dom child = new Xpp3Dom(entry.getKey());
              if (entry.getValue() != null) {
                child.setValue(entry.getValue().toString());
              }

              xml.addChild(child);
            }

            execution.setConfiguration(xml);

            final Plugin plugin = new Plugin();
            plugin.setGroupId(BMMP_GID);
            plugin.setArtifactId(BMMP_AID);
            plugin.setVersion(state.getBuildMetadataPluginVersion());
            plugin.addExecution(execution);

            build.addPlugin(plugin);

            changed = true;
          }

          if (changed) {
            return Collections.singleton(project);
          }
        }
      }
    }

    return Collections.emptySet();
  }