blob: 54741006df9705a0f7d495d5bfb40ca398b658b8 [file] [log] [blame]
/*
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
/*
* $Id: ChartServlet.java,v 1.4 2004/11/14 07:33:14 tcfujii Exp $
*/
package components.renderkit;
import components.model.ChartItem;
import com.sun.image.codec.jpeg.*;
import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.geom.Ellipse2D;
import java.awt.RenderingHints;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
/**
* <p><strong>ChartServlet</strong> is used to render the chart image.
*/
public final class ChartServlet extends HttpServlet {
/**
* <p>The <code>ServletConfig</code> instance for this servlet.</p>
*/
private ServletConfig servletConfig = null;
/**
* <p>Release all resources acquired at startup time.</p>
*/
public void destroy() {
servletConfig = null;
}
/**
* <p>Return the <code>ServletConfig</code> instance for this servlet.</p>
*/
public ServletConfig getServletConfig() {
return (this.servletConfig);
}
/**
* <p>Return information about this Servlet.</p>
*/
public String getServletInfo() {
return (this.getClass().getName());
}
/**
* <p>Perform initialization.</p>
*
* @exception ServletException if, for any reason,
* bn error occurred during the processing of
* this <code>init()</code> method.
*/
public void init(ServletConfig servletConfig) throws ServletException {
// Save our ServletConfig instance
this.servletConfig = servletConfig;
}
/**
* <p>Process an incoming request, and create the corresponding
* response.</p>
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs during processing
* @exception ServletException if a servlet error occurs during processing
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
// Here's where we'd get the ChartBean from the session and determine
// whether we're generating a pie chart or bar chart...
//
String type = request.getParameter("type");
if ((type == null) ||
(!type.equals("bar")) && (!type.equals("pie"))) {
type = "bar";
}
if (type.equals("bar")) {
generateBarChart(request, response);
} else {
generatePieChart(request, response);
}
}
/**
* <p>Process an incoming request, and create the corresponding
* response.</p>
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs during processing
* @exception ServletException if a servlet error occurs during processing
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
doGet(request, response);
}
/**
* <p> Generate a bar chart from data.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs during processing
* @exception ServletException if a servlet error occurs during processing
*/
private void generateBarChart(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
final int VERTICAL = 0;
final int HORIZONTAL = 1;
response.setContentType("image/jpeg");
String id = request.getParameter("chartId");
// get chart parameters
String title = request.getParameter("title");
if (title == null) {
title = "Chart";
}
int orientation = VERTICAL;
String orientationStr = request.getParameter("orientation");
if ((orientationStr == null) ||
(!orientationStr.equals("horizontal")) && (!orientationStr.equals("vertical"))) {
orientation = VERTICAL;
} else if (orientationStr.equals("vertical")) {
orientation = VERTICAL;
} else {
orientation = HORIZONTAL;
}
// label for x/y axis
String xLabel = request.getParameter("xlabel");
String yLabel = request.getParameter("ylabel");
// default image size
int width = 400;
int height = 300;
String widthStr = request.getParameter("width");
String heightStr = request.getParameter("height");
if (widthStr != null) {
width = Integer.parseInt(widthStr);
}
if (heightStr != null) {
height = Integer.parseInt(heightStr);
}
// get an array of chart items containing our data..
HttpSession session = request.getSession(true);
ChartItem[] chartItems = (ChartItem[])session.getAttribute(id);
if (chartItems == null) {
System.out.println("No data items specified...");
throw new ServletException("No data items specified...");
}
// remove the chart data from session now that chart has been rendered.
session.removeAttribute(id);
// maximum data value
int maxDataValue = 0;
// maximum label width
int maxLabelWidth = 0;
// space between bars
int barSpacing = 10;
// width of each bar
int barWidth = 0;
// x,y coordinates
int cx, cy;
// number of chart items
int columns = chartItems.length;
int scale = 10;
// an individual chart data item
ChartItem chartItem = null;
String label = null;
int value = 0;
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bi.createGraphics();
Font titleFont = new java.awt.Font("Courier", Font.BOLD, 12);
FontMetrics titleFontMetrics = g2d.getFontMetrics(titleFont);
// loop through and figure out the the widest item label, as well as
// the maximum value.
for (int i=0; i < columns; i++) {
chartItem = chartItems[i];
label = chartItem.getLabel();
value = chartItem.getValue();
if (value > maxDataValue) {
maxDataValue = value;
}
maxLabelWidth = Math.max(titleFontMetrics.stringWidth(label), maxLabelWidth);
}
// calculate chart dimensions
int[] xcoords = new int[columns];
int[] ycoords = new int[columns];
int totalWidth = 0;
int totalHeight = 0;
for (int i=0; i < columns; i++) {
switch (orientation) {
case VERTICAL:
default:
barWidth = maxLabelWidth;
cx = (Math.max((barWidth + barSpacing),maxLabelWidth) * i) +
barSpacing;
totalWidth = cx + (4 * titleFont.getSize());
break;
case HORIZONTAL:
barWidth = titleFont.getSize();
cy = ((barWidth + barSpacing) * i) + barSpacing;
totalHeight = cy + (4 * titleFont.getSize());
break;
}
}
if (orientation == VERTICAL) {
totalHeight = maxDataValue + (8 * titleFont.getSize());
totalWidth = totalWidth + 50;
} else {
totalWidth = maxDataValue + (4 * titleFont.getSize() +
(Integer.toString(maxDataValue).length() * titleFont.getSize())+50);
}
// Make sure the the total height of the chart provides enough room
// for the vertical label..
//
int yLabelHeight = 0;
for (int i=0; i<yLabel.length(); i++) {
yLabelHeight += titleFontMetrics.getAscent();
}
if ((yLabelHeight+(12 * titleFontMetrics.getDescent())) > totalHeight) {
totalHeight = yLabelHeight+(8*titleFont.getSize());
}
bi = new BufferedImage(totalWidth, totalHeight, BufferedImage.TYPE_INT_RGB);
g2d = bi.createGraphics();
titleFontMetrics = g2d.getFontMetrics(titleFont);
// graph dimensions
Dimension graphDim = new Dimension(totalWidth,totalHeight);
Rectangle graphRect = new Rectangle(graphDim);
// border dimensions
Dimension borderDim = new Dimension(totalWidth-2,totalHeight-2);
Rectangle borderRect = new Rectangle(borderDim);
// background color
g2d.setColor(Color.white);
g2d.fill(graphRect);
// draw border
g2d.setColor(Color.black);
borderRect.setLocation(1,1);
g2d.draw(borderRect);
// draw the title centered at the bottom of the bar graph
int i = titleFontMetrics.stringWidth(title);
g2d.setFont(titleFont);
g2d.setColor(Color.black);
g2d.drawString(title, Math.max((totalWidth - i)/2, 0),
totalHeight - titleFontMetrics.getDescent());
// draw the x axis label
i = titleFontMetrics.stringWidth(xLabel);
g2d.drawString(xLabel, Math.max((totalWidth - i)/2, 0),
totalHeight - (6 * titleFontMetrics.getDescent()));
// draw the y axis label
i = titleFontMetrics.stringWidth(yLabel);
cx = totalWidth-(totalWidth-6);
cy = totalHeight - (12 * titleFontMetrics.getDescent());
for (int j=yLabel.length(); j>0; j--) {
g2d.drawString(yLabel.substring(j-1,j), cx, cy);
cy -= titleFontMetrics.getAscent();
}
// loop through to draw the chart items.
for (i=0; i < columns; i++) {
chartItem = chartItems[i];
label = chartItem.getLabel();
value = chartItem.getValue();
String colorStr = chartItem.getColor();
Object color = getColor(colorStr);
switch (orientation) {
case VERTICAL:
default:
barWidth = maxLabelWidth;
// set the next X coordinate to account for the label
// being wider than the bar width.
cx = (Math.max((barWidth + barSpacing),maxLabelWidth) * i) +
barSpacing + 12;
// center the bar chart
cx += Math.max((totalWidth - (columns * (barWidth +
(2 * barSpacing))))/2,0);
// set the next Y coordinate to account for the height
// of the bar as well as the title and labels painted
// at the bottom of the chart.
cy = totalHeight - (value) - 1 - (2 * titleFont.getSize());
// draw the label
g2d.setColor(Color.black);
g2d.drawString((String)label, cx,
totalHeight - titleFont.getSize() - (8 * titleFontMetrics.getDescent()));
// draw the shadow bar
if (color == Color.black) {
g2d.setColor(Color.gray);
}
g2d.fillRect(cx + 5, cy - 28, barWidth, (value));
// draw the bar with the specified color
g2d.setColor((Color)(color));
g2d.fillRect(cx, cy - 30, barWidth, (value));
g2d.drawString("" + value, cx, cy - 30 - titleFontMetrics.getDescent());
break;
case HORIZONTAL:
barWidth = titleFont.getSize();
// set the Y coordinate
cy = totalHeight - (((barWidth + barSpacing) * i) + barSpacing +
(12 * titleFontMetrics.getDescent()));
// set the X coordinate to be the width of the widest label
cx = maxLabelWidth + 1;
cx += Math.max((totalWidth - (maxLabelWidth + 1 +
titleFontMetrics.stringWidth("" + maxDataValue) +
(maxDataValue))) / 2, 0);
// draw the labels and the shadow
g2d.setColor(Color.black);
g2d.drawString((String)label, cx - maxLabelWidth - 1,
cy + titleFontMetrics.getAscent());
if (color == Color.black) {
g2d.setColor(Color.gray);
}
g2d.fillRect(cx + 3, cy + 5, (value), barWidth);
// draw the bar in the current color
g2d.setColor((Color)(color));
g2d.fillRect(cx, cy, (value), barWidth);
g2d.drawString("" + value, cx + (value ) + 3,
cy + titleFontMetrics.getAscent());
break;
}
}
OutputStream output = response.getOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(output);
encoder.encode(bi);
output.close();
}
/**
* <p> Generate a pie chart from data.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs during processing
* @exception ServletException if a servlet error occurs during processing
*/
private void generatePieChart(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
response.setContentType("image/jpeg");
// get chart parameters
String id = request.getParameter("chartId");
String title = request.getParameter("title");
if (title == null) {
title = "Chart";
}
// label for x/y axis
String xLabel = request.getParameter("xlabel");
String yLabel = request.getParameter("ylabel");
// default image size
int width = 400;
int height = 200;
String widthStr = request.getParameter("width");
String heightStr = request.getParameter("height");
if (widthStr != null) {
width = Integer.parseInt(widthStr);
}
if (heightStr != null) {
height = Integer.parseInt(heightStr);
}
// get an array of chart items containing our data..
HttpSession session = request.getSession();
ChartItem[] chartItems = (ChartItem[])session.getAttribute(id);
if (chartItems == null) {
System.out.println("No data items specified...");
throw new ServletException("No data items specified...");
}
// remove the chart data from session now that chart has been rendered.
session.removeAttribute(id);
// begin pie chart
Color dropShadow = new Color(240,240,240);
//inner padding to make sure bars never touch the outer border
int innerOffset = 20;
int pieHeight = height - (innerOffset * 2);
int pieWidth = pieHeight;
int halfWidth = width/2;
//Width of the inner graphable area
int innerWidth = width - (innerOffset * 2);
//graph dimension
Dimension graphDim = new Dimension(width, height);
Rectangle graphRect = new Rectangle(graphDim);
//border dimensions
Dimension borderDim = new Dimension(halfWidth-2,height-2);
Rectangle borderRect = new Rectangle(borderDim);
//Set content type
response.setContentType("image/jpeg");
//Create BufferedImage & Graphics2D
BufferedImage bi = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = bi.createGraphics();
// Set Antialiasing
RenderingHints renderHints =
new RenderingHints(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHints(renderHints);
//Set graph background color to white:
g2d.setColor(Color.white);
g2d.fill(graphRect);
//Draw black border
g2d.setColor(Color.black);
borderRect.setLocation(1,1);
g2d.draw(borderRect);
//Now draw border for legend
borderRect.setLocation((width/2) + 1,1);
g2d.draw(borderRect);
//Draw data onto the graph:
int x_pie = innerOffset;
int y_pie = innerOffset;
int border = 20;
//Main chart Ellipse
Ellipse2D.Double elb = new Ellipse2D.Double(x_pie - border/2,
y_pie - border/2, pieWidth + border, pieHeight + border);
//Shadow
g2d.setColor(dropShadow);
g2d.fill(elb);
//Border
g2d.setColor(Color.black);
g2d.draw(elb);
// Calculate the total value so that the pies can be calculated.
float yTotal = 0.0f;
int lastElement = 0;
for(int i=0; i<chartItems.length; i++) {
int ycoord = chartItems[i].getValue();
if(ycoord > 0.0f) {
yTotal += ycoord;
lastElement = i;
}
}
// Draw the pie chart
int startAngle = 0;
//Legend variables
int legendWidth = 20;
int x_legendText = halfWidth + innerOffset/2 + legendWidth + 5;
int x_legendBar = halfWidth + innerOffset/2;
int textHeight = 20;
int curElement = 0;
int y_legend = 0;
//Dimensions of the legend bar
Dimension legendDim = new Dimension(legendWidth , textHeight/2);
Rectangle legendRect = new Rectangle(legendDim);
for(int i=0; i< chartItems.length; i++) {
int ycoord = chartItems[i].getValue();
if(ycoord > 0.0f) {
//Calculate percentage sales
float perc = (ycoord/yTotal);
//Calculate new angle
int sweepAngle = (int)(perc * 360);
//Check that the last element goes back to 0 position
if (i == lastElement) {sweepAngle = 360-startAngle;}
// Draw Arc
g2d.setColor(getColor(chartItems[i].getColor()));
g2d.fillArc(x_pie, y_pie, pieWidth, pieHeight, startAngle,
sweepAngle);
//Increment startAngle with the sweepAngle
startAngle += sweepAngle;
//Draw Legend
//Set y position for bar
y_legend = curElement * textHeight + innerOffset;
//Display the current column
String display = chartItems[i].getLabel();
g2d.setColor(Color.black);
g2d.drawString(display, x_legendText, y_legend);
//Display the total sales
display = "" + ycoord;
g2d.setColor(Color.black);
g2d.drawString(display, x_legendText + 80, y_legend);
//Display the sales percentage
display = " (" + (int)(perc*100) + "%)";
g2d.setColor(Color.red);
g2d.drawString(display, x_legendText + 110, y_legend);
//Draw the bar
g2d.setColor(getColor(chartItems[i].getColor()));
legendRect.setLocation(x_legendBar,y_legend - textHeight/2);
g2d.fill(legendRect);
//Increment
curElement++;
}
}
// Encode the graph
OutputStream output = response.getOutputStream();
JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(output);
encoder.encode(bi);
output.close();
}
/**
* Returns the Color instance corresponding the color passed in.
*
* @param colorStr a string representing a color instance.
* @return Color instance corresponding to the input color.
*/
protected Color getColor(String colorStr) {
Color color = null;
if (colorStr == null) {
color = Color.gray;
}
if (colorStr.equals("red")) {
color = Color.red;
} else if (colorStr.equals("green")) {
color = Color.green;
} else if (colorStr.equals("blue")) {
color = Color.blue;
} else if (colorStr.equals("pink")) {
color = Color.pink;
} else if (colorStr.equals("orange")) {
color = Color.orange;
} else if (colorStr.equals("magenta")) {
color = Color.magenta;
} else if (colorStr.equals("cyan")) {
color = Color.cyan;
} else if (colorStr.equals("white")) {
color = Color.white;
} else if (colorStr.equals("yellow")) {
color = Color.yellow;
} else if (colorStr.equals("gray")) {
color = Color.gray;
} else if (colorStr.equals("darkGray")) {
color = Color.darkGray;
} else {
color = Color.gray;
}
return color;
}
}