/**
  * Getter for property 'targets'.
  *
  * @return Value for property 'targets'.
  */
 public List<CoberturaPublisherTarget> getTargets() {
   Map<CoverageMetric, CoberturaPublisherTarget> targets =
       new TreeMap<CoverageMetric, CoberturaPublisherTarget>();
   float checker;
   for (CoverageMetric metric : healthyTarget.getTargets()) {
     CoberturaPublisherTarget target = targets.get(metric);
     if (target == null) {
       target = new CoberturaPublisherTarget();
       target.setMetric(metric);
     }
     checker = (float) healthyTarget.getTarget(metric) / 100000f;
     if (checker <= 0.001f) {
       checker = (float) (Math.round(checker * 100000f));
     }
     target.setHealthy(checker);
     targets.put(metric, target);
   }
   for (CoverageMetric metric : unhealthyTarget.getTargets()) {
     CoberturaPublisherTarget target = targets.get(metric);
     if (target == null) {
       target = new CoberturaPublisherTarget();
       target.setMetric(metric);
     }
     checker = (float) unhealthyTarget.getTarget(metric) / 100000f;
     if (checker <= 0.001f) {
       checker = (float) (Math.round(checker * 100000f));
     }
     target.setUnhealthy(checker);
     targets.put(metric, target);
   }
   for (CoverageMetric metric : failingTarget.getTargets()) {
     CoberturaPublisherTarget target = targets.get(metric);
     if (target == null) {
       target = new CoberturaPublisherTarget();
       target.setMetric(metric);
     }
     checker = (float) failingTarget.getTarget(metric) / 100000f;
     if (checker <= 0.001f) {
       checker = (float) (Math.round(checker * 100000f));
     }
     target.setUnstable(checker);
     targets.put(metric, target);
   }
   List<CoberturaPublisherTarget> result =
       new ArrayList<CoberturaPublisherTarget>(targets.values());
   return result;
 }
 /** Changes unhealthy or unstable percentage fields for ratcheting. */
 private void setNewPercentages(CoverageResult result, boolean select, BuildListener listener) {
   Set<CoverageMetric> healthyMetrics = healthyTarget.getAllMetrics(result);
   float newPercent;
   float oldPercent;
   if (!healthyMetrics.isEmpty()) {
     for (CoverageMetric metric : healthyMetrics) {
       newPercent = healthyTarget.getObservedPercent(result, metric);
       newPercent = (float) (Math.round(newPercent * 100f));
       if (select) {
         oldPercent = unhealthyTarget.getSetPercent(result, metric);
         oldPercent = (float) (Math.round(oldPercent * 100f));
       } else {
         oldPercent = failingTarget.getSetPercent(result, metric);
         oldPercent = (float) (Math.round(oldPercent * 100f));
       }
       if (newPercent > oldPercent) {
         if (select) {
           unhealthyTarget.setTarget(metric, (int) (newPercent * 1000f));
           listener
               .getLogger()
               .println(
                   "    "
                       + metric.getName()
                       + "'s new health minimum is: "
                       + roundDecimalFloat(newPercent));
         } else {
           failingTarget.setTarget(metric, (int) (newPercent * 1000f));
           listener
               .getLogger()
               .println(
                   "    "
                       + metric.getName()
                       + "'s new stability minimum is: "
                       + roundDecimalFloat(newPercent));
         }
       }
     }
   }
 }
 /**
  * Setter for property 'targets'.
  *
  * @param targets Value to set for property 'targets'.
  */
 private void setTargets(List<CoberturaPublisherTarget> targets) {
   healthyTarget.clear();
   unhealthyTarget.clear();
   failingTarget.clear();
   float rounded;
   for (CoberturaPublisherTarget target : targets) {
     if (target.getHealthy() != null) {
       rounded = (Math.round((float) 100f * target.getHealthy()));
       rounded = roundDecimalFloat(rounded);
       healthyTarget.setTarget(target.getMetric(), (int) ((float) 100000f * rounded));
     }
     if (target.getUnhealthy() != null) {
       rounded = (Math.round((float) 100f * target.getUnhealthy()));
       rounded = roundDecimalFloat(rounded);
       unhealthyTarget.setTarget(target.getMetric(), (int) ((float) 100000f * rounded));
     }
     if (target.getUnstable() != null) {
       rounded = (Math.round((float) 100f * target.getUnstable()));
       rounded = roundDecimalFloat(rounded);
       failingTarget.setTarget(target.getMetric(), (int) ((float) 100000f * rounded));
     }
   }
 }
  /** {@inheritDoc} */
  @SuppressWarnings("unchecked")
  public HealthReport getBuildHealth() {
    if (health != null) {
      return health;
    }
    // try to get targets from root project (for maven modules targets are null)
    DescribableList rootpublishers = owner.getProject().getRootProject().getPublishersList();

    if (rootpublishers != null) {
      CoberturaPublisher publisher =
          (CoberturaPublisher) rootpublishers.get(CoberturaPublisher.class);
      if (publisher != null) {
        healthyTarget = publisher.getHealthyTarget();
        unhealthyTarget = publisher.getUnhealthyTarget();
      }
    }

    if (healthyTarget == null || unhealthyTarget == null) {
      return null;
    }

    if (result == null) {
      CoverageResult projectCoverage = getResult();
      result = new EnumMap<CoverageMetric, Ratio>(CoverageMetric.class);
      result.putAll(projectCoverage.getResults());
    }
    Map<CoverageMetric, Integer> scores = healthyTarget.getRangeScores(unhealthyTarget, result);
    int minValue = 100;
    CoverageMetric minKey = null;
    for (Map.Entry<CoverageMetric, Integer> e : scores.entrySet()) {
      if (e.getValue() < minValue) {
        minKey = e.getKey();
        minValue = e.getValue();
      }
    }
    if (minKey == null) {
      if (result == null || result.size() == 0) {
        return null;
      } else {
        for (Map.Entry<CoverageMetric, Integer> e : scores.entrySet()) {
          minKey = e.getKey();
        }
        if (minKey != null) {
          Localizable localizedDescription =
              Messages._CoberturaBuildAction_description(
                  result.get(minKey).getPercentage(),
                  result.get(minKey).toString(),
                  minKey.getName());
          health = new HealthReport(minValue, localizedDescription);
          return health;
        }
        return null;
      }

    } else {
      Localizable localizedDescription =
          Messages._CoberturaBuildAction_description(
              result.get(minKey).getPercentage(), result.get(minKey).toString(), minKey.getName());
      health = new HealthReport(minValue, localizedDescription);
      return health;
    }
  }
  /** {@inheritDoc} */
  @Override
  public boolean perform(AbstractBuild<?, ?> build, Launcher launcher, BuildListener listener)
      throws InterruptedException, IOException {
    Result threshold = onlyStable ? Result.SUCCESS : Result.UNSTABLE;
    if (build.getResult().isWorseThan(threshold)) {
      listener
          .getLogger()
          .println(
              "Skipping Cobertura coverage report as build was not "
                  + threshold.toString()
                  + " or better ...");
      return true;
    }

    listener.getLogger().println("[Cobertura] Publishing Cobertura coverage report...");
    final FilePath[] moduleRoots = build.getModuleRoots();
    final boolean multipleModuleRoots = moduleRoots != null && moduleRoots.length > 1;
    final FilePath moduleRoot = multipleModuleRoots ? build.getWorkspace() : build.getModuleRoot();
    final File buildCoberturaDir = build.getRootDir();
    FilePath buildTarget = new FilePath(buildCoberturaDir);

    FilePath[] reports = new FilePath[0];
    try {
      reports = moduleRoot.act(new ParseReportCallable(coberturaReportFile));

      // if the build has failed, then there's not
      // much point in reporting an error
      if (build.getResult().isWorseOrEqualTo(Result.FAILURE) && reports.length == 0) {
        return true;
      }

    } catch (IOException e) {
      Util.displayIOException(e, listener);
      e.printStackTrace(listener.fatalError("Unable to find coverage results"));
      build.setResult(Result.FAILURE);
    }

    if (reports.length == 0) {
      String msg =
          "[Cobertura] No coverage results were found using the pattern '"
              + coberturaReportFile
              + "' relative to '"
              + moduleRoot.getRemote()
              + "'."
              + "  Did you enter a pattern relative to the correct directory?"
              + "  Did you generate the XML report(s) for Cobertura?";
      listener.getLogger().println(msg);
      if (failNoReports) {
        build.setResult(Result.FAILURE);
      } else {
        listener.getLogger().println("[Cobertura] Skipped cobertura reports.");
      }
      return true;
    }

    for (int i = 0; i < reports.length; i++) {
      final FilePath targetPath =
          new FilePath(buildTarget, "coverage" + (i == 0 ? "" : i) + ".xml");
      try {
        reports[i].copyTo(targetPath);
      } catch (IOException e) {
        Util.displayIOException(e, listener);
        e.printStackTrace(
            listener.fatalError(
                "Unable to copy coverage from " + reports[i] + " to " + buildTarget));
        build.setResult(Result.FAILURE);
      }
    }

    listener.getLogger().println("Publishing Cobertura coverage results...");
    Set<String> sourcePaths = new HashSet<String>();
    CoverageResult result = null;
    for (File coberturaXmlReport : getCoberturaReports(build)) {
      try {
        result = CoberturaCoverageParser.parse(coberturaXmlReport, result, sourcePaths);
      } catch (IOException e) {
        Util.displayIOException(e, listener);
        e.printStackTrace(listener.fatalError("Unable to parse " + coberturaXmlReport));
        build.setResult(Result.FAILURE);
      }
    }
    if (result != null) {
      listener.getLogger().println("Cobertura coverage report found.");
      result.setOwner(build);
      final FilePath paintedSourcesPath =
          new FilePath(new File(build.getProject().getRootDir(), "cobertura"));
      paintedSourcesPath.mkdirs();

      if (sourcePaths.contains(".")) {
        sourcePaths.remove(".");
        for (FilePath f : reports) {
          FilePath p = f.getParent();
          if (p != null && p.isDirectory()) {
            sourcePaths.add(p.getRemote());
          }
        }
      }

      SourceCodePainter painter =
          new SourceCodePainter(
              paintedSourcesPath,
              sourcePaths,
              result.getPaintedSources(),
              listener,
              getSourceEncoding());

      moduleRoot.act(painter);

      final CoberturaBuildAction action =
          CoberturaBuildAction.load(
              build,
              result,
              healthyTarget,
              unhealthyTarget,
              getOnlyStable(),
              getFailUnhealthy(),
              getFailUnstable(),
              getAutoUpdateHealth(),
              getAutoUpdateStability());

      build.getActions().add(action);
      Set<CoverageMetric> failingMetrics = failingTarget.getFailingMetrics(result);
      if (!failingMetrics.isEmpty()) {
        listener.getLogger().println("Code coverage enforcement failed for the following metrics:");
        float oldStabilityPercent;
        float setStabilityPercent;
        for (CoverageMetric metric : failingMetrics) {
          oldStabilityPercent = failingTarget.getObservedPercent(result, metric);
          setStabilityPercent = failingTarget.getSetPercent(result, metric);
          listener
              .getLogger()
              .println(
                  "    "
                      + metric.getName()
                      + "'s stability is "
                      + roundDecimalFloat(oldStabilityPercent * 100f)
                      + " and set mininum stability is "
                      + roundDecimalFloat(setStabilityPercent * 100f)
                      + ".");
        }
        if (!getFailUnstable()) {
          listener.getLogger().println("Setting Build to unstable.");
          build.setResult(Result.UNSTABLE);
        } else {
          listener.getLogger().println("Failing build due to unstability.");
          build.setResult(Result.FAILURE);
        }
      }
      if (getFailUnhealthy()) {
        Set<CoverageMetric> unhealthyMetrics = unhealthyTarget.getFailingMetrics(result);
        if (!unhealthyMetrics.isEmpty()) {
          listener.getLogger().println("Unhealthy for the following metrics:");
          float oldHealthyPercent;
          float setHealthyPercent;
          for (CoverageMetric metric : unhealthyMetrics) {
            oldHealthyPercent = unhealthyTarget.getObservedPercent(result, metric);
            setHealthyPercent = unhealthyTarget.getSetPercent(result, metric);
            listener
                .getLogger()
                .println(
                    "    "
                        + metric.getName()
                        + "'s health is "
                        + roundDecimalFloat(oldHealthyPercent * 100f)
                        + " and set minimum health is "
                        + roundDecimalFloat(setHealthyPercent * 100f)
                        + ".");
          }
          listener.getLogger().println("Failing build because it is unhealthy.");
          build.setResult(Result.FAILURE);
        }
      }
      if (build.getResult() == Result.SUCCESS) {
        if (getAutoUpdateHealth()) {
          setNewPercentages(result, true, listener);
        }

        if (getAutoUpdateStability()) {
          setNewPercentages(result, false, listener);
        }
      }
    } else {
      listener
          .getLogger()
          .println(
              "No coverage results were successfully parsed.  Did you generate "
                  + "the XML report(s) for Cobertura?");
      build.setResult(Result.FAILURE);
    }

    return true;
  }