/** * Hudson Serenitec plugin * * @author Georges Bossert <gbossert@gmail.com> * @version $Revision: 1.5 $ * @since $Date: 2008/07/24 09:44:14 ${date} * @copyright Universit� de Rennes 1 */ package hudson.plugins.serenitec.util; import hudson.model.AbstractBuild; import hudson.model.HealthReport; import hudson.model.HealthReportingAction; import hudson.plugins.serenitec.SerenitecDescriptor; import hudson.plugins.serenitec.parseur.ReportEntry; import hudson.plugins.serenitec.util.model.EntriesProvider; import hudson.util.ChartUtil; import hudson.util.DataSetBuilder; import hudson.util.ChartUtil.NumberOnlyBuildLabel; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.commons.lang.StringUtils; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.plot.CategoryPlot; import org.jfree.chart.plot.PiePlot3D; import org.jfree.chart.plot.PlotOrientation; import org.jfree.chart.renderer.category.CategoryItemRenderer; import org.jfree.data.category.CategoryDataset; import org.jfree.data.category.DefaultCategoryDataset; import org.jfree.data.general.DefaultPieDataset; import org.kohsuke.stapler.StaplerProxy; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerResponse; /** * Controls the live cycle of Hudson results. This action persists the results of a build and displays them on the build page. The actual * visualization of the results is defined in the matching <code>summary.jelly</code> file. * <p> * Moreover, this class renders the results trend. * </p> * * @param <T> * type of the result of this action * @author Ulli Hafner */ public abstract class AbstractResultAction<T extends EntriesProvider> implements StaplerProxy, HealthReportingAction, ToolTipProvider, ResultAction<T> { /** Unique identifier of this class. */ private static final long serialVersionUID = -7201451538713818948L; /** Width of the graph. */ private static final int WIDTH = 500; /** The associated build of this action. */ @SuppressWarnings("Se") private final AbstractBuild<?, ?> owner; /** Builds a health report. */ private HealthReportBuilder healthReportBuilder; /** The actual result of this action. */ private T result; /** * Creates a new instance of <code>AbstractResultAction</code>. * * @param owner * the associated build of this action * @param healthReportBuilder * health builder to use */ public AbstractResultAction(final AbstractBuild<?, ?> owner, final HealthReportBuilder healthReportBuilder) { super(); this.owner = owner; this.healthReportBuilder = healthReportBuilder; } /** * Creates a new instance of <code>AbstractResultAction</code>. * * @param owner * the associated build of this action * @param healthReportBuilder * health builder to use * @param result * the result of the action */ public AbstractResultAction(final AbstractBuild<?, ?> owner, final HealthReportBuilder healthReportBuilder, final T result) { this(owner, healthReportBuilder); this.result = result; } /** * Returns the data set that represents the result. For each build, the number of warnings is used as result value. * * @param useHealthBuilder * determines whether the health builder should be used to create the data set * @return the data set */ protected CategoryDataset buildDataSet(final boolean useHealthBuilder) { final DataSetBuilder<Integer, NumberOnlyBuildLabel> builder = new DataSetBuilder<Integer, NumberOnlyBuildLabel>(); for (AbstractResultAction<T> action = this; action != null; action = action.getPreviousBuild()) { final T current = action.getResult(); if (current != null) { List<Integer> series; series = new ArrayList<Integer>(); series.add(current.getNumberOfSeverityFormatage()); series.add(current.getNumberOfSeverityPerformance()); series.add(current.getNumberOfSeverityDesign()); series.add(current.getNumberOfSeverityLowSecurity()); series.add(current.getNumberOfSeverityHighSecurity()); int level = 0; for (final Integer integer : series) { builder.add(integer, level, new NumberOnlyBuildLabel(action.getOwner())); level++; } } } return builder.build(); } protected CategoryDataset buildDetailsDataSet(final boolean useHealthBuilder, String param) { final DataSetBuilder<Integer, NumberOnlyBuildLabel> builder = new DataSetBuilder<Integer, NumberOnlyBuildLabel>(); for (AbstractResultAction<T> action = this; action != null; action = action.getPreviousBuild()) { final T current = action.getResult(); if (current != null) { List<Integer> series; series = new ArrayList<Integer>(); if (param.equals("testedRules")) { series.add(current.getNumberOfRules()); } else if (param.equals("errors")) { series.add(current.getNumberOfEntry()); } else if (param.equals("newErrors")) { series.add(current.getNumberOfNewEntry()); } else if (param.equals("fixedErrors")) { series.add(current.getNumberOfFixedEntry()); } else if (param.equals("unfixedErrors")) { series.add(current.getNumberOfNotFixedEntry()); } else if (param.equals("patterns")) { series.add(current.getNumberOfPointeurs()); } int level = 0; for (final Integer integer : series) { builder.add(integer, level, new NumberOnlyBuildLabel(action.getOwner())); level++; } } } return builder.build(); } /** * Creates the chart for this action. * * @param request * Stapler request * @param response * Stapler response * @return the chart for this action. */ private JFreeChart createChart(final StaplerRequest request, final StaplerResponse response) { System.out.println("---createChart------"); final String parameter = request.getParameter("useHealthBuilder"); final boolean useHealthBuilder = Boolean.valueOf(StringUtils.defaultIfEmpty(parameter, "true")); return getHealthReportBuilder().createGraph(useHealthBuilder, getDescriptor().getPluginResultUrlName(), buildDataSet(useHealthBuilder), this); } /** * Creates the chart for this action. * * @param request * Stapler request * @param response * Stapler response * @return the chart for this action. */ private JFreeChart createRulesRepartitionChart(final StaplerRequest request, final StaplerResponse response, final int n1, final int n2, final String titre) { JFreeChart chart = null; DefaultPieDataset pieDataset = new DefaultPieDataset(); pieDataset.setValue(titre + "(" + n1 * 100.0f / n2 + "%)", n1); pieDataset.setValue("Remains", (n2 - n1)); chart = ChartFactory.createPieChart3D(null, pieDataset, false, true, false); PiePlot3D p = (PiePlot3D) chart.getPlot(); p.setForegroundAlpha(0.5f); return chart; } /** * Creates the chart for this action. * * @param request * Stapler request * @param response * Stapler response * @return the chart for this action. */ private JFreeChart createPersonalChart(final StaplerRequest request, final StaplerResponse response, final String type) { JFreeChart chart = null; if (type.equals("errorBySeverityTrend")) { System.out.println("errorBySeverityTrend"); DefaultPieDataset pieDataset = new DefaultPieDataset(); pieDataset.setValue("Formating", getResult().getNumberOfSeverityFormatage()); pieDataset.setValue("Language evolution", getResult().getNumberOfSeverityPerformance()); pieDataset.setValue("Design", getResult().getNumberOfSeverityDesign()); pieDataset.setValue("Low Security", getResult().getNumberOfSeverityLowSecurity()); pieDataset.setValue("High Security", getResult().getNumberOfSeverityHighSecurity()); chart = ChartFactory.createPieChart3D("Errors by Severity", pieDataset, false, true, false); PiePlot3D p = (PiePlot3D) chart.getPlot(); p.setForegroundAlpha(0.5f); } else if (type.equals("patternsBySeverityTrend")) { System.out.println("patternsBySeverityTrend"); DefaultPieDataset pieDataset = new DefaultPieDataset(); pieDataset.setValue("Formating", getResult().getNumberOfSeverityFormatagePatterns()); pieDataset.setValue("Language evolution", getResult().getNumberOfSeverityPerformancePatterns()); pieDataset.setValue("Design", getResult().getNumberOfSeverityDesignPatterns()); pieDataset.setValue("Low Security", getResult().getNumberOfSeverityLowSecurityPatterns()); pieDataset.setValue("High Security", getResult().getNumberOfSeverityHighSecurityPatterns()); chart = ChartFactory.createPieChart3D("Patterns by Severity", pieDataset, false, true, false); PiePlot3D p = (PiePlot3D) chart.getPlot(); p.setForegroundAlpha(0.5f); } else if (type.equals("topFiveTrend")) { System.out.println("topFiveTrend"); DefaultCategoryDataset categorieDataset = new DefaultCategoryDataset(); int numberOfPatternsForTopFive = 0; int numberOfPatternsForNotTopFive = 0; for (ReportEntry topfiveEntry : getResult().getTopFiveEntries()) { numberOfPatternsForTopFive += topfiveEntry.getNumberOfPointeurs(); } numberOfPatternsForNotTopFive = getResult().getNumberOfPointeurs() - numberOfPatternsForTopFive; categorieDataset.addValue(numberOfPatternsForTopFive, "TopFive (" + numberOfPatternsForTopFive * 100.0f / getResult().getNumberOfPointeurs() + "%)", "Patterns"); categorieDataset.addValue(numberOfPatternsForNotTopFive, "Remains (" + numberOfPatternsForNotTopFive * 100.0f / getResult().getNumberOfPointeurs() + "%)", "Patterns"); chart = ChartFactory.createStackedBarChart3D(null, // chart title null, // domain axis label null, // range axis label categorieDataset, // data PlotOrientation.HORIZONTAL, // the plot orientation true, // include legend true, // tooltips false // urls ); final CategoryPlot plot = (CategoryPlot) chart.getPlot(); final CategoryItemRenderer renderer = plot.getRenderer(); plot.setForegroundAlpha(0.5f); return chart; } else if (type.equals("coverage")) { System.out.println("Coverage"); DefaultCategoryDataset categorieDataset = new DefaultCategoryDataset(); int numberOfFilesWithErrors = 0; int numberOfFilesWithNoErrors = 0; numberOfFilesWithErrors = getResult().getNumberOfFilesWithErrors(); numberOfFilesWithNoErrors = getResult().getNumberOfFilesWithNoErrors(); categorieDataset.addValue(numberOfFilesWithErrors, "Files with Errors (" + numberOfFilesWithErrors * 100.0f / getResult().getNumberOfFiles() + "%)", "Files"); categorieDataset.addValue(numberOfFilesWithNoErrors, "Remains (" + numberOfFilesWithNoErrors * 100.0f / getResult().getNumberOfFiles() + "%)", "Files"); chart = ChartFactory.createStackedBarChart3D(null, // chart title null, // domain axis label null, // range axis label categorieDataset, // data PlotOrientation.HORIZONTAL, // the plot orientation true, // include legend true, // tooltips false // urls ); final CategoryPlot plot = (CategoryPlot) chart.getPlot(); final CategoryItemRenderer renderer = plot.getRenderer(); plot.setForegroundAlpha(0.5f); return chart; } return chart; /** * return getHealthReportBuilder().createPersonalGraph( getDescriptor().getPluginResultUrlName(), buildPersonalDataSet(type), this); */ } /** * Creates the chart for this action. * * @param request * Stapler request * @param response * Stapler response * @return the chart for this action. */ private JFreeChart createDetailsChart(final StaplerRequest request, final StaplerResponse response, String param) { System.out.println("---createRepositoryChart------"); return getHealthReportBuilder().createGraph(false, getDescriptor().getPluginResultUrlName(), buildDetailsDataSet(false, param), this); } /** * Generates a PNG image for the result trend. * * @param request * Stapler request * @param response * Stapler response * @param height * the height of the trend graph * @throws IOException * in case of an error */ public final void doGraph(final StaplerRequest request, final StaplerResponse response, final int height) throws IOException { System.out.println("---doGraph------"); if (ChartUtil.awtProblemCause != null) { response.sendRedirect2(request.getContextPath() + "/images/headless.png"); return; } ChartUtil.generateGraph(request, response, createChart(request, response), WIDTH, height); } /** * Generates a PNG image for the result personal trend. * * @param request * Stapler request * @param response * Stapler response * @param height * the height of the trend graph * @throws IOException * in case of an error */ public final void doPersonalGraph(final StaplerRequest request, final StaplerResponse response, int height, final String type) throws IOException { System.out.println("---doGraph------"); if (ChartUtil.awtProblemCause != null) { response.sendRedirect2(request.getContextPath() + "/images/headless.png"); return; } int width = 400; if (type.equals("topFiveTrend") || type.equals("coverage")) { width = 800; height = 100; } ChartUtil.generateGraph(request, response, createPersonalChart(request, response, type), width, height); } /** * Generates a PNG image for the result personal trend. * * @param request * Stapler request * @param response * Stapler response * @param height * the height of the trend graph * @throws IOException * in case of an error */ public final void doRulesRepartitionPie(final StaplerRequest request, final StaplerResponse response, int size, final int n1, final int n2, final String titre) throws IOException { System.out.println("---doGraph------"); if (ChartUtil.awtProblemCause != null) { response.sendRedirect2(request.getContextPath() + "/images/headless.png"); return; } ChartUtil.generateGraph(request, response, createRulesRepartitionChart(request, response, n1, n2, titre), 300, 150); } /** * Generates a PNG image for the repository trend * * @param request * Stapler request * @param response * Stapler response * @throws IOException * in case of an error */ public void doDetailsGraph(StaplerRequest request, StaplerResponse response, String param) throws IOException { System.out.println("---doRepositoryGraph------"); if (ChartUtil.awtProblemCause != null) { response.sendRedirect2(request.getContextPath() + "/images/headless.png"); return; } ChartUtil.generateGraph(request, response, createDetailsChart(request, response, param), 850, 200); } /** * Generates a PNG image for the result trend. * * @param request * Stapler request * @param response * Stapler response * @param height * the height of the trend graph * @throws IOException * in case of an error */ public final void doGraphMap(final StaplerRequest request, final StaplerResponse response, final int height) throws IOException { ChartUtil.generateClickableMap(request, response, createChart(request, response), WIDTH, height); } /** {@inheritDoc} */ public final HealthReport getBuildHealth() { return healthReportBuilder .computeHealth(getResult().getNumberOfPointeurs() - getPreviousBuild().getResult().getNumberOfPointeurs()); } /** * Returns the descriptor of the associated plug-in. * * @return the descriptor of the associated plug-in */ protected abstract SerenitecDescriptor getDescriptor(); /** * Returns the associated health report builder. * * @return the associated health report builder */ public final HealthReportBuilder getHealthReportBuilder() { if (healthReportBuilder == null) { healthReportBuilder = new HealthReportBuilder(); } return healthReportBuilder; } /** {@inheritDoc} */ public String getIconFileName() { String resultat = null; if (getResult().getNumberOfEntry() > 0) { resultat = getDescriptor().getIconUrl(); } return resultat; } /** * Returns the associated build of this action. * * @return the associated build of this action */ public final AbstractBuild<?, ?> getOwner() { return owner; } /** * Gets the result of a previous build if it's recorded, or <code>null</code> if not. * * @return the result of a previous build, or <code>null</code> */ @java.lang.SuppressWarnings("unchecked") protected AbstractResultAction<T> getPreviousBuild() { AbstractBuild<?, ?> build = getOwner(); while (true) { build = build.getPreviousBuild(); if (build == null) { return null; } final AbstractResultAction<T> action = build.getAction(getClass()); if (action != null) { return action; } } } /** {@inheritDoc} */ public final T getResult() { return result; } /** {@inheritDoc} */ public final Object getTarget() { return getResult(); } /** {@inheritDoc} */ public String getUrlName() { return getDescriptor().getPluginResultUrlName(); } /** {@inheritDoc} */ public boolean hasPreviousResultAction() { return getPreviousBuild() != null; } /** {@inheritDoc} */ public final void setResult(final T result) { this.result = result; } }