/*
*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU General Public License, version 2 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/gpl-2.0.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
*
* Copyright 2005 - 2008 Pentaho Corporation. All rights reserved.
*
* @created Aug 23, 2005
* @author James Dixon
*/
package org.pentaho.platform.uifoundation.component.xml;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Node;
import org.pentaho.commons.connection.IPentahoMetaData;
import org.pentaho.commons.connection.IPentahoResultSet;
import org.pentaho.platform.api.engine.IActionParameter;
import org.pentaho.platform.api.engine.IActionSequenceResource;
import org.pentaho.platform.api.engine.ILogger;
import org.pentaho.platform.api.engine.IPentahoUrlFactory;
import org.pentaho.platform.api.engine.IRuntimeContext;
import org.pentaho.platform.api.engine.ISolutionEngine;
import org.pentaho.platform.api.repository.ISolutionRepository;
import org.pentaho.platform.engine.core.output.SimpleOutputHandler;
import org.pentaho.platform.engine.core.solution.ActionInfo;
import org.pentaho.platform.engine.core.system.PentahoSessionHolder;
import org.pentaho.platform.engine.core.system.PentahoSystem;
import org.pentaho.platform.engine.services.actionsequence.ActionSequenceResource;
import org.pentaho.platform.engine.services.runtime.TemplateUtil;
import org.pentaho.platform.uifoundation.chart.DialWidgetDefinition;
import org.pentaho.platform.uifoundation.chart.JFreeChartEngine;
import org.pentaho.platform.uifoundation.chart.WidgetDefinition;
import org.pentaho.platform.uifoundation.messages.Messages;
import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper;
public class WidgetGridComponent extends XmlComponent {
/**
*
*/
private static final long serialVersionUID = -3952161695550067971L;
private String definitionPath;
private int widgetWidth;
private int widgetHeight;
private String solution = null;
private String actionPath = null;
private String actionName = null;
private String valueItem = null;
private String nameItem = null;
private int columns = 0;
private String instanceId = null;
private String actionOutput = null;
private String urlTemplate = null;
private String style = null;
private IRuntimeContext context;
private static final Log logger = LogFactory.getLog(WidgetGridComponent.class);
@Override
public Log getLogger() {
return WidgetGridComponent.logger;
}
/**
* Creates a WidgetGrid
* <p>
* After creating an instance of this class <CODE>validate</CODE> should
* be called.
*
* @param type
* The type of the widget, currently only TYPE_DIAL is supported
* @param definitionPath
* The path and name of the XML definition of the dial
* @param widgetWidth
* The width of the image to be created
* @param widgetHeight
* The height of the image to be created
* @param urlFactory
* The urlFactory for the content
* @param messages
* The messages list for any logger messages
*/
public WidgetGridComponent(final String definitionPath, final IPentahoUrlFactory urlFactory, final List messages) {
super(urlFactory, messages, null);
this.definitionPath = definitionPath;
ActionInfo info = ActionInfo.parseActionString(definitionPath);
if (info != null) {
setSourcePath(info.getSolutionName() + File.separator + info.getPath());
}
setXsl("text/html", "DialWidget.xsl"); //$NON-NLS-1$ //$NON-NLS-2$
}
/**
* Sets the width (in pixels) of the widget images that will be created
*
* @param widgetWidth
*/
public void setWidgetWidth(final int widgetWidth) {
this.widgetWidth = widgetWidth;
}
/**
* Sets the height (in pixels) of the widget images that will be created
*
* @param widgetHeight
*/
public void setWidgetHeight(final int widgetHeight) {
this.widgetHeight = widgetHeight;
}
/**
* Sets the number of widgets that will be dispayed in a row before another
* row of widgets is created
*
* @param instanceId
*/
public void setColumns(final int columns) {
this.columns = columns;
}
/**
* Sets the instance id for this execution
*
* @param instanceId
* The instance id of the parent object or process
*/
public void setInstanceId(final String instanceId) {
this.instanceId = instanceId;
}
public boolean setDataAction(final String widgetGridDataDefinition) {
IActionSequenceResource resource = new ActionSequenceResource(
"", IActionSequenceResource.SOLUTION_FILE_RESOURCE, "text/xml", //$NON-NLS-1$ //$NON-NLS-2$
widgetGridDataDefinition);
try {
Document dataActionDocument = PentahoSystem.get(ISolutionRepository.class, getSession()).getResourceAsDocument(resource, ISolutionRepository.ACTION_EXECUTE);
if (dataActionDocument == null) {
return false;
}
Node dataNode = dataActionDocument.selectSingleNode("widgetgrid/data"); //$NON-NLS-1$
solution = XmlDom4JHelper.getNodeText("data-solution", dataNode); //$NON-NLS-1$
actionPath = XmlDom4JHelper.getNodeText("data-path", dataNode); //$NON-NLS-1$
actionName = XmlDom4JHelper.getNodeText("data-action", dataNode); //$NON-NLS-1$
actionOutput = XmlDom4JHelper.getNodeText("data-output", dataNode); //$NON-NLS-1$
valueItem = XmlDom4JHelper.getNodeText("data-value", dataNode); //$NON-NLS-1$
nameItem = XmlDom4JHelper.getNodeText("data-name", dataNode); //$NON-NLS-1$
widgetWidth = (int) XmlDom4JHelper.getNodeText("widgetgrid/width", dataActionDocument, 125); //$NON-NLS-1$
widgetHeight = (int) XmlDom4JHelper.getNodeText("widgetgrid/height", dataActionDocument, 125); //$NON-NLS-1$
columns = (int) XmlDom4JHelper.getNodeText("widgetgrid/columns", dataActionDocument, 2); //$NON-NLS-1$
style = XmlDom4JHelper.getNodeText("widgetgrid/style", dataActionDocument); //$NON-NLS-1$
} catch (Exception e) {
error(Messages.getInstance().getErrorString("WidgetGrid.ERROR_0003_DEFINITION_NOT_VALID", widgetGridDataDefinition), e); //$NON-NLS-1$
return false;
}
return true;
}
/**
* Sets the action to be executed to get the data for the widgets
*
* @param solution
* @param actionPath
* @param actionName
* @param actionOutput
* @param nameItem
* @param valueItem
*/
public void setDataAction(final String solution, final String actionPath, final String actionName,
final String actionOutput, final String nameItem, final String valueItem) {
this.solution = solution;
this.actionPath = actionPath;
this.actionName = actionName;
this.actionOutput = actionOutput;
this.valueItem = valueItem;
this.nameItem = nameItem;
}
public void setDrillUrlTemplate(final String urlTemplate) {
this.urlTemplate = urlTemplate;
}
@Override
public boolean validate() {
return true;
}
@Override
public Document getXmlContent() {
// get the data to populate the widgets
IPentahoResultSet resultSet = null;
if (solution != null) {
resultSet = getActionData();
} else {
// TODO support other methods of getting data
}
// create the widget to use
// load the XML document that defines the dial
IActionSequenceResource resource = new ActionSequenceResource(
"", IActionSequenceResource.SOLUTION_FILE_RESOURCE, "text/xml", //$NON-NLS-1$ //$NON-NLS-2$
definitionPath);
Document dialDefinition = null;
try {
dialDefinition = PentahoSystem.get(ISolutionRepository.class, getSession()).getResourceAsDocument(resource, ISolutionRepository.ACTION_EXECUTE);
} catch (IOException e) {
}
// create a dial definition from the XML definition
WidgetDefinition widgetDefinition = new DialWidgetDefinition(dialDefinition, 0, widgetWidth, widgetHeight,
getSession());
return createDials(resultSet, widgetDefinition);
}
protected Document createDials(final IPentahoResultSet resultSet, final WidgetDefinition widgetDefinition) {
if (resultSet == null) {
error(Messages.getInstance().getErrorString("WidgetGrid.ERROR_0001_NO_RESULTS_FROM_ACTION")); //$NON-NLS-1$
return null;
}
if (valueItem == null) {
error(Messages.getInstance().getErrorString("WidgetGrid.ERROR_0002_NO_VALUE_ITEM")); //$NON-NLS-1$
}
// Create a document that describes the result
Document result = DocumentHelper.createDocument();
String baseUrl = PentahoSystem.getApplicationContext().getBaseUrl();
setXslProperty("baseUrl", baseUrl); //$NON-NLS-1$
Element root = result.addElement("widgets"); //$NON-NLS-1$
IPentahoMetaData metaData = resultSet.getMetaData();
// TODO support multiple column headers / row headers
// TODO support an iteration across columns for a given row
// find the column that we have been told to you
Object columnHeaders[][] = metaData.getColumnHeaders();
int nameColumnNo = -1;
int valueColumnNo = -1;
for (int idx = 0; idx < columnHeaders[0].length; idx++) {
if (columnHeaders[0][idx].toString().equalsIgnoreCase(nameItem)) {
nameColumnNo = idx;
}
if (columnHeaders[0][idx].toString().equalsIgnoreCase(valueItem)) {
valueColumnNo = idx;
}
}
if (nameColumnNo == -1) {
// we did not find the specified name column
error(Messages.getInstance().getErrorString("WidgetGrid.ERROR_0004_NAME_COLUMN_MISSING", nameItem)); //$NON-NLS-1$
return null;
}
if (valueColumnNo == -1) {
// we did not find the specified name column
error(Messages.getInstance().getErrorString("WidgetGrid.ERROR_0005_VALUE_COLUMN_MISSING", valueItem)); //$NON-NLS-1$
return null;
}
double value;
String name;
Object row[] = resultSet.next();
while (row != null) {
name = row[nameColumnNo].toString();
try {
value = Double.parseDouble(row[valueColumnNo].toString());
createDial(value, name, root, widgetDefinition);
} catch (Exception e) {
}
row = resultSet.next();
}
setXslProperty("urlTarget", "pentaho_popup"); //$NON-NLS-1$ //$NON-NLS-2$
setXslProperty("columns", Integer.toString(columns)); //$NON-NLS-1$
if (style != null) {
setXslProperty("style", style); //$NON-NLS-1$
}
return result;
}
protected void createDial(final double value, final String name, final Element root,
final WidgetDefinition widgetDefinition) {
widgetDefinition.setValue(new Double(value));
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
// TODO get units from somewhere
String units = ""; //$NON-NLS-1$
String dialName = ""; //$NON-NLS-1$
// create temporary file names
String solutionDir = "system/tmp/"; //$NON-NLS-1$
String fileNamePrefix = "tmp_pie_"; //$NON-NLS-1$
String extension = ".png"; //$NON-NLS-1$
String fileName = null;
String filePathWithoutExtension = null;
try {
File file = PentahoSystem.getApplicationContext().createTempFile(getSession(), fileNamePrefix, extension, true);
fileName = file.getName();
filePathWithoutExtension = solutionDir + fileName.substring(0, fileName.indexOf('.'));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
JFreeChartEngine.saveChart(widgetDefinition, dialName, units, filePathWithoutExtension, widgetWidth, widgetHeight,
JFreeChartEngine.OUTPUT_PNG, printWriter, this);
Element widgetNode = root.addElement("widget"); //$NON-NLS-1$
widgetNode.addElement("title").setText(name); //$NON-NLS-1$
widgetNode.addElement("units").setText(units); //$NON-NLS-1$
widgetNode.addElement("width").setText(Integer.toString(widgetWidth)); //$NON-NLS-1$
widgetNode.addElement("height").setText(Integer.toString(widgetHeight)); //$NON-NLS-1$
Element valueNode = widgetNode.addElement("value");//$NON-NLS-1$
valueNode.setText(Double.toString(value));
valueNode.addAttribute("in-image", Boolean.toString(widgetDefinition.getValueFont() != null)); //$NON-NLS-1$
root.addElement("image").setText(fileName); //$NON-NLS-1$
widgetNode.addElement("image").setText(fileName); //$NON-NLS-1$
// apply the current data item name to the URL template
String drillUrl = TemplateUtil.applyTemplate(urlTemplate, nameItem, name);
// now apply any parameters to the URL template
drillUrl = TemplateUtil.applyTemplate(drillUrl, context);
widgetNode.addElement("urlDrill").setText(drillUrl); //$NON-NLS-1$
}
public void dispose() {
if (context != null) {
context.dispose();
}
}
protected IPentahoResultSet getActionData() {
// create an instance of the solution engine to execute the specified
// action
ISolutionEngine solutionEngine = PentahoSystem.get(ISolutionEngine.class, getSession());
solutionEngine.setLoggingLevel(ILogger.DEBUG);
solutionEngine.init(getSession());
HashMap parameterProviders = getParameterProviders();
OutputStream outputStream = null;
SimpleOutputHandler outputHandler = null;
outputHandler = new SimpleOutputHandler(outputStream, false);
ArrayList messages = new ArrayList();
String processId = this.getClass().getName();
context = solutionEngine.execute(solution, actionPath, actionName, processId, false, true, instanceId, false,
parameterProviders, outputHandler, null, urlFactory, messages);
if (actionOutput != null) {
if (context.getOutputNames().contains(actionOutput)) {
IActionParameter output = context.getOutputParameter(actionOutput);
IPentahoResultSet results = output.getValueAsResultSet();
if (results != null) {
results = results.memoryCopy();
}
return results;
} else {
// this is an error
return null;
}
} else {
// return the first list that we find...
Iterator it = context.getOutputNames().iterator();
while (it.hasNext()) {
IActionParameter output = (IActionParameter) it.next();
if (output.getType().equalsIgnoreCase(IActionParameter.TYPE_RESULT_SET)) {
IPentahoResultSet results = output.getValueAsResultSet();
if (results != null) {
results = results.memoryCopy();
}
return results;
}
}
}
return null;
}
}