/* * 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 2006 - 2016 Pentaho Corporation. All rights reserved. */ package org.pentaho.platform.uifoundation.component.xml; 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.ILogger; import org.pentaho.platform.api.engine.IPentahoRequestContext; 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.engine.core.output.SimpleOutputHandler; import org.pentaho.platform.engine.core.solution.ActionInfo; import org.pentaho.platform.engine.core.system.PentahoRequestContextHolder; import org.pentaho.platform.engine.core.system.PentahoSystem; import org.pentaho.platform.engine.services.SolutionURIResolver; 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.messages.LocaleHelper; import org.pentaho.platform.util.xml.XMLParserFactoryProducer; import org.pentaho.platform.util.xml.dom4j.XmlDom4JHelper; 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; 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 ) { try { Document dataActionDocument = null; try { org.dom4j.io.SAXReader reader = XMLParserFactoryProducer.getSAXReader( new SolutionURIResolver() ); dataActionDocument = reader.read( ActionSequenceResource.getInputStream( widgetGridDataDefinition, LocaleHelper.getLocale() ) ); } catch ( Throwable t ) { 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(); } // create the widget to use // load the XML document that defines the dial Document dialDefinition = null; try { org.dom4j.io.SAXReader reader = new org.dom4j.io.SAXReader(); reader.setEntityResolver( new SolutionURIResolver() ); dialDefinition = reader.read( ActionSequenceResource.getInputStream( definitionPath, LocaleHelper.getLocale() ) ); } catch ( Throwable t ) { // XML document can't be read. We'll just return a null document. } // 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(); IPentahoRequestContext requestContext = PentahoRequestContextHolder.getRequestContext(); setXslProperty( "baseUrl", requestContext.getContextPath() ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ setXslProperty( "fullyQualifiedServerUrl", PentahoSystem.getApplicationContext().getFullyQualifiedServerURL() ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 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 ) { //ignore } 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(); String actionSeqPath = ActionInfo.buildSolutionPath( solution, actionPath, actionName ); context = solutionEngine.execute( actionSeqPath, 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; } }