/** @see org.apache.maven.plugin.Mojo#execute() */
  @Override
  public void execute() throws MojoExecutionException, MojoFailureException {
    getLog().info("-------------------------------------------------"); // $NON-NLS-1$
    getLog().info("Generating Karaf compatible features.xml file to:"); // $NON-NLS-1$
    getLog().info("   " + outputFile); // $NON-NLS-1$
    getLog().info("-------------------------------------------------"); // $NON-NLS-1$

    try {
      FeaturesXml featuresXml = new FeaturesXml();
      generate(featuresXml);
      File file = new File(outputFile);
      file.getParentFile().mkdirs();
      featuresXml.writeTo(file);

      if ("true".equals(attach)) { // $NON-NLS-1$
        attachToBuild(file);
      }
    } catch (Exception e) {
      throw new MojoExecutionException(e.getMessage(), e);
    }
  }
  /** @param featuresXml */
  private void generate(FeaturesXml featuresXml) throws Exception {
    // Add the repositories
    if (this.repositories != null) {
      for (String repo : repositories) {
        featuresXml.addRepository(repo);
      }
    }

    // Collect all dependencies (bundle candidates)
    ScopeArtifactFilter filter = new ScopeArtifactFilter(DefaultArtifact.SCOPE_RUNTIME);
    DependencyNode dependencyGraph = dependencyGraphBuilder.buildDependencyGraph(project, filter);
    CollectingDependencyNodeVisitor collectingVizzy = new CollectingDependencyNodeVisitor();
    dependencyGraph.accept(collectingVizzy);
    List<DependencyNode> nodes = collectingVizzy.getNodes();

    // Iterate all features
    for (Feature feature : features) {
      getLog().info("Generating feature '" + feature.getName() + "'"); // $NON-NLS-1$ //$NON-NLS-2$
      // Create the feature
      featuresXml.addFeature(feature.getName(), feature.getVersion(), feature.getComment());
      // Add any feature dependencies
      List<Feature> onFeatures = feature.getDependsOnFeatures();
      if (onFeatures != null && !onFeatures.isEmpty()) {
        for (Feature onFeature : onFeatures) {
          getLog()
              .info(
                  "   Depends on feature: "
                      + onFeature.getName()
                      + "/"
                      + onFeature.getVersion()); // $NON-NLS-1$ //$NON-NLS-2$
          featuresXml.addFeatureDependency(
              feature.getName(), feature.getVersion(), onFeature.getName(), onFeature.getVersion());
        }
      }
      // Add any included or non-excluded bundles (from artifact
      // dependency graph)
      PatternIncludesArtifactFilter includesFilter =
          new PatternIncludesArtifactFilter(feature.getIncludes());
      PatternExcludesArtifactFilter excludesFilter =
          new PatternExcludesArtifactFilter(feature.getExcludes());
      String startLevel = feature.getStartLevel();
      for (DependencyNode dependencyNode : nodes) {
        if (isSelf(dependencyNode)) continue;
        Artifact artifact = dependencyNode.getArtifact();
        // If no includes, assume everything
        boolean includeBundle = feature.getIncludes() == null || feature.getIncludes().isEmpty();
        if (includeBundle) {
          getLog()
              .debug(
                  "   Artifact "
                      + artifact
                      + " matches default [all] filter (including)."); //$NON-NLS-1$ //$NON-NLS-2$
        }
        if (includesFilter.include(artifact)) {
          getLog()
              .debug(
                  "   Artifact "
                      + artifact
                      + " matched include filter (including)."); //$NON-NLS-1$ //$NON-NLS-2$
          includeBundle = true;
        }
        // Excludes must be explicit.
        if (!excludesFilter.include(artifact)) {
          getLog()
              .debug(
                  "   Artifact "
                      + artifact
                      + " matched exclude filter (excluding)."); //$NON-NLS-1$ //$NON-NLS-2$
          includeBundle = false;
        }

        if (includeBundle) {
          featuresXml.addBundle(
              feature.getName(),
              feature.getVersion(),
              formatArtifactAsBundle(artifact),
              startLevel);
        }
      }

      // Add additional explicit bundles specified in the config
      List<String> bundles = feature.getBundles();
      if (bundles != null && !bundles.isEmpty()) {
        for (String bundle : bundles) {
          getLog().debug("   Adding explicit bundle: " + bundle); // $NON-NLS-1$
          featuresXml.addBundle(feature.getName(), feature.getVersion(), bundle, startLevel);
        }
      }

      // Add config files if any are specified in the pom
      List<ConfigFile> configFiles = feature.getConfigFiles();
      if (configFiles != null && !configFiles.isEmpty()) {
        for (ConfigFile configFile : configFiles) {
          getLog().debug("   Adding config file: " + configFile.getValue()); // $NON-NLS-1$
          featuresXml.addConfigFile(
              feature.getName(),
              feature.getVersion(),
              configFile.getFinalName(),
              configFile.getValue());
        }
      }
    }
  }