
hudson.util.ChartUtil Maven / Gradle / Ivy
package hudson.util;
import hudson.model.AbstractBuild;
import org.jfree.chart.ChartRenderingInfo;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.data.category.CategoryDataset;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import java.awt.Font;
import java.awt.HeadlessException;
import java.awt.image.BufferedImage;
import java.io.IOException;
/**
* Chart generation utility code around JFreeChart.
*
* @see StackedAreaRenderer2
* @see DataSetBuilder
* @see ShiftedCategoryAxis
* @author Kohsuke Kawaguchi
*/
public class ChartUtil {
/**
* Can be used as a graph label. Only displays numbers.
*/
public static final class NumberOnlyBuildLabel implements Comparable {
public final AbstractBuild build;
public NumberOnlyBuildLabel(AbstractBuild build) {
this.build = build;
}
public int compareTo(NumberOnlyBuildLabel that) {
return this.build.number-that.build.number;
}
public boolean equals(Object o) {
if(!(o instanceof NumberOnlyBuildLabel)) return false;
NumberOnlyBuildLabel that = (NumberOnlyBuildLabel) o;
return build==that.build;
}
public int hashCode() {
return build.hashCode();
}
public String toString() {
return build.getDisplayName();
}
}
/**
* See issue 93. Detect an error in X11 and handle it gracefully.
*/
public static boolean awtProblem = false;
/**
* Generates the graph in PNG format and sends that to the response.
*
* @param defaultSize
* The size of the picture to be generated. These values can be overridden
* by the query paramter 'width' and 'height' in the request.
*/
public static void generateGraph(StaplerRequest req, StaplerResponse rsp, JFreeChart chart, Area defaultSize) throws IOException {
generateGraph(req,rsp,chart,defaultSize.width, defaultSize.height);
}
/**
* Generates the graph in PNG format and sends that to the response.
*
* @param defaultW
* @param defaultH
* The size of the picture to be generated. These values can be overridden
* by the query paramter 'width' and 'height' in the request.
*/
public static void generateGraph(StaplerRequest req, StaplerResponse rsp, JFreeChart chart, int defaultW, int defaultH) throws IOException {
try {
String w = req.getParameter("width");
if(w==null) w=String.valueOf(defaultW);
String h = req.getParameter("height");
if(h==null) h=String.valueOf(defaultH);
BufferedImage image = chart.createBufferedImage(Integer.parseInt(w),Integer.parseInt(h));
rsp.setContentType("image/png");
ServletOutputStream os = rsp.getOutputStream();
ImageIO.write(image, "PNG", os);
os.close();
} catch(HeadlessException e) {
// not available. send out error message
rsp.sendRedirect2(req.getContextPath()+"/images/headless.png");
}
}
/**
* Generates the clickable map info and sends that to the response.
*/
public static void generateClickableMap(StaplerRequest req, StaplerResponse rsp, JFreeChart chart, Area defaultSize) throws IOException {
generateClickableMap(req,rsp,chart,defaultSize.width,defaultSize.height);
}
/**
* Generates the clickable map info and sends that to the response.
*/
public static void generateClickableMap(StaplerRequest req, StaplerResponse rsp, JFreeChart chart, int defaultW, int defaultH) throws IOException {
String w = req.getParameter("width");
if(w==null) w=String.valueOf(defaultW);
String h = req.getParameter("height");
if(h==null) h=String.valueOf(defaultH);
ChartRenderingInfo info = new ChartRenderingInfo();
chart.createBufferedImage(Integer.parseInt(w),Integer.parseInt(h),info);
rsp.setContentType("text/plain;charset=UTF-8");
rsp.getWriter().println(ChartUtilities.getImageMap( "map", info ));
}
/**
* Adjusts the Y-axis so that abnormally large value won't spoil the whole chart
* by making everything look virtually 0.
*
*
* The algorithm is based on Chebyshev's inequality,
* which states that given any number sequence, nore more than 1/(N^2) values are more than N x stddev away
* from the average.
*
*
* So the algorithm is to set Y-axis range so that we can see all data points that are within N x stddev
* of the average. Most of the time, Cebyshev's inequality is very conservative, so it shouldn't do
* much harm.
*
*
* When the algorithm does kick in, however, we can kick out at most 1 in N^2 data points.
* (So for example if N=3 then we can "fix" the graph as long as we only have less than 1/(3*3)=11.111...% bad data.
*
*
* Also see issue #1246.
*/
public static void adjustChebyshev(CategoryDataset dataset, NumberAxis yAxis) {
// first compute E(X) and Var(X)
double sum=0,sum2=0;
final int nColumns = dataset.getColumnCount();
final int nRows = dataset.getRowCount();
for (int i=0; i