001: /*
002: * Copyright 2006 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt. The Original Code is the Pentaho
007: * BI Platform. The Initial Developer is Pentaho Corporation.
008: *
009: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
011: * the license for the specific language governing your rights and limitations.
012: *
013: * Created Nov 17, 2005
014: * @author wseyler
015: */
016:
017: package org.pentaho.ui.component.charting;
018:
019: import java.io.IOException;
020: import java.io.PrintWriter;
021: import java.io.StringWriter;
022: import java.util.ArrayList;
023: import java.util.List;
024:
025: import org.dom4j.Document;
026: import org.dom4j.DocumentHelper;
027: import org.dom4j.Element;
028: import org.dom4j.Node;
029: import org.jfree.chart.ChartRenderingInfo;
030: import org.jfree.chart.entity.StandardEntityCollection;
031: import org.jfree.chart.imagemap.ImageMapUtilities;
032: import org.jfree.data.general.Dataset;
033: import org.jfree.data.general.DefaultValueDataset;
034: import org.pentaho.commons.connection.IPentahoResultSet;
035: import org.pentaho.commons.connection.memory.MemoryResultSet;
036: import org.pentaho.core.solution.ActionResource;
037: import org.pentaho.core.solution.IActionResource;
038: import org.pentaho.core.system.PentahoSystem;
039: import org.pentaho.core.ui.IPentahoUrlFactory;
040: import org.pentaho.messages.Messages;
041: import org.pentaho.plugin.jfreechart.DialWidgetDefinition;
042: import org.pentaho.plugin.jfreechart.JFreeChartEngine;
043:
044: public class DialChartComponent extends AbstractJFreeChartComponent {
045: private static final long serialVersionUID = -6268840271596447555L;
046:
047: public static final String VALUE_NODE_NAME = "dialValue"; //$NON-NLS-1$
048:
049: public static final String MAXVALUE_NODE_NAME = "dialMaximum"; //$NON-NLS-1$
050:
051: public static final String MINVALUE_NODE_NAME = "dialMinimum"; //$NON-NLS-1$
052:
053: public static final int TYPE_DIAL = 1;
054:
055: public static final int TYPE_THERMOMETER = 2;
056:
057: private Double value = null;
058:
059: public DialChartComponent(int chartType, String definitionPath,
060: int width, int height, IPentahoUrlFactory urlFactory,
061: List messages) {
062: super (chartType, definitionPath, width, height, urlFactory,
063: messages);
064: }
065:
066: public DialChartComponent(String definitionPath,
067: IPentahoUrlFactory urlFactory, ArrayList messages) {
068: super (definitionPath, urlFactory, messages);
069: }
070:
071: public DialChartComponent(IPentahoUrlFactory urlFactory,
072: List messages) {
073: super (urlFactory, messages);
074: }
075:
076: public Dataset createChart(Document doc) {
077:
078: // get the chart node from the document
079: Node chartAttributes = doc
080: .selectSingleNode("//" + CHART_NODE_NAME); //$NON-NLS-1$
081:
082: // Try to retrieve the data values from the <data> XML block
083: if (solution != null) {
084: values = getActionData();
085: }
086:
087: // No <data> node; check for <dialvalue>, <dialmaximum>, <dialminimum> nodes
088: if (values == null) {
089:
090: Node valueNode = chartAttributes
091: .selectSingleNode(VALUE_NODE_NAME);
092: Node maxValueNode = chartAttributes
093: .selectSingleNode(MAXVALUE_NODE_NAME);
094: Node minValueNode = chartAttributes
095: .selectSingleNode(MINVALUE_NODE_NAME);
096:
097: double val = 0;
098: double min = -1;
099: double max = -1;
100:
101: if (valueNode != null) {
102:
103: try {
104: val = Double.parseDouble(valueNode.getText());
105: } catch (Exception e) {
106: logger
107: .error(
108: Messages
109: .getErrorString(
110: "DIALCHARTCOMPONENT.ERROR_0001_ERROR_PARSING_VALUE", valueNode.getText()), e); //$NON-NLS-1$
111: val = 0;
112: }
113:
114: if ((minValueNode != null) && (maxValueNode != null)) {
115:
116: try {
117: min = Double
118: .parseDouble(minValueNode.getText());
119: max = Double
120: .parseDouble(maxValueNode.getText());
121: } catch (Exception e) {
122: logger
123: .error(
124: Messages
125: .getErrorString(
126: "DIALCHARTCOMPONENT.ERROR_0001_ERROR_PARSING_VALUE", //$NON-NLS-1$
127: minValueNode
128: .getText(),
129: maxValueNode
130: .getText()),
131: e);
132: min = -1;
133: max = -1;
134: }
135: }
136:
137: // Its OK if there is no min and max - we will derive it later if absent.
138: MemoryResultSet set = new MemoryResultSet();
139: if ((min == -1) && (max == -1)) {
140: set.addRow(new Object[] { new Double(val) });
141: } else {
142: set.addRow(new Object[] { new Double(val),
143: new Double(min), new Double(max) });
144: }
145: values = set;
146: }
147: }
148:
149: // If, at this point, the values object is null, we will continue to execute, and assume
150: // the user is has set the dial value using the API.
151:
152: // create the definition
153: DialWidgetDefinition chartDefinition = new DialWidgetDefinition(
154: (IPentahoResultSet) values, byRow, chartAttributes,
155: width, height, getSession());
156:
157: // This local variable "value" is here for parity with the way the DashboardWidgetComponent functions...
158: // If value gets set, all other data methods executed previously are overridden.
159: if (value != null) {
160: chartDefinition.setValue(value.doubleValue());
161: chartDefinition.deriveMinMax(value.doubleValue());
162: }
163:
164: // set the misc values from chartDefinition
165: setChartType(JFreeChartEngine.DIAL_CHART_TYPE);
166: setTitle(chartDefinition.getTitle());
167:
168: // get the URL template
169: Node urlTemplateNode = chartAttributes
170: .selectSingleNode(URLTEMPLE_NODE_NAME);
171: if (urlTemplateNode != null) {
172: setUrlTemplate(urlTemplateNode.getText());
173: }
174:
175: if (chartDefinition.getWidth() != -1 && (width == -1)) {
176: setWidth(chartDefinition.getWidth());
177: }
178: if (chartDefinition.getHeight() != -1 && (height == -1)) {
179: setHeight(chartDefinition.getHeight());
180: }
181:
182: return chartDefinition;
183: }
184:
185: public Document getXmlContent() {
186:
187: // Create a document that describes the result
188: Document result = DocumentHelper.createDocument();
189: String baseUrl = PentahoSystem.getApplicationContext()
190: .getBaseUrl();
191: setXslProperty("baseUrl", baseUrl); //$NON-NLS-1$
192:
193: // load the XML document that defines the pie
194: ActionResource resource = new ActionResource(title,
195: IActionResource.SOLUTION_FILE_RESOURCE, "text/xml", //$NON-NLS-1$
196: definitionPath);
197: Document chartDefinition = null;
198: String mapName = "chart" + chartCount++; //$NON-NLS-1$
199: try {
200: chartDefinition = getResourceAsDocument(getSession(),
201: resource);
202: } catch (IOException e) {
203: }
204:
205: if (chartDefinition == null) {
206: Element errorElement = result.addElement("error"); //$NON-NLS-1$
207: errorElement
208: .addElement("title").setText(Messages.getString("ABSTRACTCHARTEXPRESSION.ERROR_0001_ERROR_GENERATING_CHART")); //$NON-NLS-1$ //$NON-NLS-2$
209: String message = Messages
210: .getString(
211: "CHARTS.ERROR_0001_CHART_DEFINIION_MISSING", definitionPath); //$NON-NLS-1$
212: errorElement.addElement("message").setText(message); //$NON-NLS-1$
213: error(message);
214: return result;
215: }
216:
217: dataDefinition = createChart(chartDefinition);
218:
219: if (dataDefinition == null) {
220: Element errorElement = result.addElement("error"); //$NON-NLS-1$
221: errorElement
222: .addElement("title").setText(Messages.getString("ABSTRACTCHARTEXPRESSION.ERROR_0001_ERROR_GENERATING_CHART")); //$NON-NLS-1$ //$NON-NLS-2$
223: String message = Messages
224: .getString(
225: "CHARTS.ERROR_0002_CHART_DATA_MISSING", solution + "/" + actionPath + "/" + actionName); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
226: errorElement.addElement("message").setText(message); //$NON-NLS-1$
227: //System .out.println( result.asXML() );
228: return result;
229: }
230:
231: // create an image for the dial using the JFreeChart engine
232: PrintWriter printWriter = new PrintWriter(new StringWriter());
233: // we'll dispay the title in HTML so that the dial image does not have
234: // to
235: // accommodate it
236: String chartTitle = ""; //$NON-NLS-1$
237: try {
238: if (width == -1) {
239: width = Integer.parseInt(chartDefinition
240: .selectSingleNode("/chart/width").getText()); //$NON-NLS-1$
241: }
242: if (height == -1) {
243: height = Integer.parseInt(chartDefinition
244: .selectSingleNode("/chart/height").getText()); //$NON-NLS-1$
245: }
246: } catch (Exception e) {
247: // go with the default
248: }
249: if (chartDefinition
250: .selectSingleNode("/chart/" + URLTEMPLE_NODE_NAME) != null) { //$NON-NLS-1$
251: urlTemplate = chartDefinition.selectSingleNode(
252: "/chart/" + URLTEMPLE_NODE_NAME).getText(); //$NON-NLS-1$
253: }
254:
255: if (chartDefinition.selectSingleNode("/chart/paramName") != null) { //$NON-NLS-1$
256: paramName = chartDefinition.selectSingleNode(
257: "/chart/paramName").getText(); //$NON-NLS-1$
258: }
259:
260: Element root = result.addElement("charts"); //$NON-NLS-1$
261: DefaultValueDataset chartDataDefinition = (DefaultValueDataset) dataDefinition;
262:
263: //if (dataDefinition.getRowCount() > 0) {
264: // create temporary file names
265: String[] tempFileInfo = createTempFile();
266: String fileName = tempFileInfo[AbstractChartComponent.FILENAME_INDEX];
267: String filePathWithoutExtension = tempFileInfo[AbstractChartComponent.FILENAME_WITHOUT_EXTENSION_INDEX];
268:
269: ChartRenderingInfo info = new ChartRenderingInfo(
270: new StandardEntityCollection());
271: JFreeChartEngine
272: .saveChart(
273: chartDataDefinition,
274: chartTitle,
275: "", filePathWithoutExtension, width, height, JFreeChartEngine.OUTPUT_PNG, printWriter, info, this ); //$NON-NLS-1$
276: applyOuterURLTemplateParam();
277: populateInfo(info);
278: Element chartElement = root.addElement("chart"); //$NON-NLS-1$
279: chartElement.addElement("mapName").setText(mapName); //$NON-NLS-1$
280: chartElement
281: .addElement("width").setText(Integer.toString(width)); //$NON-NLS-1$
282: chartElement
283: .addElement("height").setText(Integer.toString(height)); //$NON-NLS-1$
284: // for (int row = 0; row < chartDataDefinition.getRowCount(); row++) {
285: // for (int column = 0; column < chartDataDefinition.getColumnCount(); column++) {
286: // Number value = chartDataDefinition.getValue(row, column);
287: // Comparable rowKey = chartDataDefinition.getRowKey(row);
288: // Comparable columnKey = chartDataDefinition.getColumnKey(column);
289: // Element valueElement = chartElement.addElement("value2D"); //$NON-NLS-1$
290: // valueElement.addElement("value").setText(value.toString()); //$NON-NLS-1$
291: // valueElement.addElement("row-key").setText(rowKey.toString()); //$NON-NLS-1$
292: // valueElement.addElement("column-key").setText(columnKey.toString()); //$NON-NLS-1$
293: // }
294: // }
295: String mapString = ImageMapUtilities.getImageMap(mapName, info);
296: chartElement.addElement("imageMap").setText(mapString); //$NON-NLS-1$
297: chartElement.addElement("image").setText(fileName); //$NON-NLS-1$
298: //}
299: return result;
300: }
301:
302: private void populateInfo(ChartRenderingInfo info) {
303: }
304:
305: public boolean validate() {
306: return true;
307: }
308:
309: /**
310: * Sets the value to be displayed by the dial.
311: *
312: * @param value
313: * The dial value
314: */
315: public void setValue(double value) {
316: this .value = new Double(value);
317: }
318:
319: }
|