/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.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 Lesser General Public License for more details. * * Copyright (c) 2011 - 2012 De Bortoli Wines Pty Limited (Australia). All Rights Reserved. */ package org.pentaho.reporting.engine.classic.extensions.datasources.openerp; import com.debortoliwines.openerp.api.Field.FieldType; import com.debortoliwines.openerp.reporting.di.OpenERPConfiguration; import com.debortoliwines.openerp.reporting.di.OpenERPFieldInfo; import com.debortoliwines.openerp.reporting.di.OpenERPFilterInfo; import com.debortoliwines.openerp.reporting.di.OpenERPHelper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ws.commons.util.Base64; import org.apache.ws.commons.util.Base64.DecodingException; import org.pentaho.reporting.engine.classic.core.AbstractDataFactory; import org.pentaho.reporting.engine.classic.core.DataRow; import org.pentaho.reporting.engine.classic.core.ReportDataFactoryException; import org.pentaho.reporting.engine.classic.core.util.PropertyLookupParser; import org.pentaho.reporting.engine.classic.core.util.TypedTableModel; import javax.swing.table.TableModel; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; /** * @author Pieter van der Merwe */ public class OpenERPDataFactory extends AbstractDataFactory { private static final long serialVersionUID = -6235833289788633577L; private static final Log logger = LogFactory.getLog( OpenERPDataFactory.class ); private OpenERPConfiguration config; private String queryName; public OpenERPDataFactory() { } /** * Checks whether the query would be executable by this datafactory. This performs a rough check, not a full query. * * @param query * @param parameters * @return */ public boolean isQueryExecutable( final String query, final DataRow parameters ) { return queryName.equals( query ); } public String[] getQueryNames() { return new String[] { queryName }; } /** * Queries a datasource. The string 'query' defines the name of the query. The Parameterset given here may contain * more data than actually needed. * <p/> * The dataset may change between two calls, do not assume anything! * * @param query * @param parameters * @return */ public synchronized TableModel queryData( final String query, final DataRow parameters ) throws ReportDataFactoryException { /// TODO: Add more validation Here if ( config == null ) { throw new ReportDataFactoryException( "Configuration is empty." ); //$NON-NLS-1$ } final TypedTableModel resultSet = new TypedTableModel(); final int queryLimit = calculateQueryLimit( parameters ); final OpenERPHelper helper = new OpenERPHelper(); final OpenERPConfiguration targetConfig = config.clone(); final ArrayList<OpenERPFilterInfo> configFilters = targetConfig.getFilters(); // Build a hashmap to pass all parameters as a dictionary to a custom OpenERP procedure final HashMap<String, Object> openERPParams = new HashMap<String, Object>(); for ( final String paramName : parameters.getColumnNames() ) { Object value = parameters.get( paramName ); if ( value == null ) { value = false; } openERPParams.put( paramName, value ); } // Can't get selected fields from config, because we may be calling a custom function ArrayList<OpenERPFieldInfo> selectedFields = null; try { selectedFields = helper.getFields( targetConfig, openERPParams ); } catch ( Exception e1 ) { throw new ReportDataFactoryException( "Failed to select field", e1 ); } // Build a field list for ( final OpenERPFieldInfo selectedFld : selectedFields ) { resultSet.addColumn( selectedFld.getRenamedFieldName(), convertFieldType( selectedFld.getFieldType() ) ); } // Called by the designer to get column layout, return a empty resultSet with columns already set if ( queryLimit == 1 ) { return resultSet; } // Parameter parser to replace parameters in strings final PropertyLookupParser parameterParser = new PropertyLookupParser() { private static final long serialVersionUID = -7264648195698966110L; @Override protected String lookupVariable( final String property ) { return parameters.get( property ).toString(); } }; // Replace parameterized filters with values from parameters if ( configFilters != null ) { for ( final OpenERPFilterInfo filter : configFilters ) { // You could have set a filter without using the Designer. Then the filter could be any data type that // should not be converted to a String. if ( filter.getValue() instanceof String ) { try { final String realFilterValue = filter.getValue().toString(); // If you specify the filter on its own, try in get the object value // Not all parameter values are a string. Could be an Object[] of ids for example in a multi-select // parameter final Object filterValue; if ( realFilterValue.length() >= 4 && realFilterValue.substring( 0, 2 ).equals( "${" ) && realFilterValue.endsWith( "}" ) ) { final String parameterName = realFilterValue.substring( 2, realFilterValue.length() - 1 ); filterValue = parameters.get( parameterName ); } // Cater for cases where users specify compound filer: "name" "like" "some${post_fix}" else { filterValue = parameterParser.translateAndLookup( realFilterValue, parameters ); } // If the value is null, this may be a dependent query and it is waiting for a parameter. // just return and wait if ( filterValue == null ) { return resultSet; } filter.setValue( filterValue ); } catch ( Exception e ) { throw new ReportDataFactoryException( e.getMessage(), e ); } } } } // Get the data final Object[][] rows; try { rows = helper.getData( targetConfig, openERPParams ); } catch ( Exception e ) { throw new ReportDataFactoryException( e.getMessage(), e ); } // Add data to resultSet and do some data transformations for ( int row = 0; row < rows.length; row++ ) { final Object[] rowData = rows[ row ]; for ( int column = 0; column < selectedFields.size(); column++ ) { final OpenERPFieldInfo fld = selectedFields.get( column ); // Base64 Decode Binary if ( fld.getFieldType() == FieldType.BINARY && rowData[ column ] != null ) { try { rowData[ column ] = Base64.decode( rowData[ column ].toString() ); } catch ( DecodingException e ) { rowData[ column ] = "Unable to decode string"; } catch ( Exception e ) { logger.debug( "Failed to decode string on query-result: Row=" + row + " Col=" + column, e ); } } // Only return integer part (exclude name) from many2one field if ( fld.getFieldType() == FieldType.MANY2ONE && rowData[ column ] instanceof Object[] ) { final Object[] value = (Object[]) rowData[ column ]; rowData[ column ] = Integer.parseInt( String.valueOf( value[ 0 ] ) ); } // make many2many and one2many a comma separated list of values if ( ( fld.getFieldType() == FieldType.MANY2MANY || fld.getFieldType() == FieldType.ONE2MANY ) && rowData[ column ] instanceof Object[] ) { final StringBuilder stringValue = new StringBuilder(); final Object[] mcolumn = (Object[]) rowData[ column ]; for ( int x = 0; x < mcolumn.length; x += 1 ) { if ( x != 0 ) { stringValue.append( ',' ); } stringValue.append( mcolumn[ x ] ); } rowData[ column ] = stringValue.toString(); } } resultSet.addRow( rowData ); } return resultSet; } private Class<?> convertFieldType( final FieldType fieldType ) { switch( fieldType ) { case BINARY: return Byte[].class; case BOOLEAN: return Boolean.class; case INTEGER: return Integer.class; case FLOAT: return Float.class; case DATETIME: case DATE: return Date.class; case MANY2ONE: return Integer.class; case ONE2MANY: case MANY2MANY: case CHAR: case TEXT: default: return String.class; } } public void setConfig( final OpenERPConfiguration config ) { this.config = config; } public String getQueryName() { return queryName; } public void setQueryName( final String queryName ) { this.queryName = queryName; } public void close() { } public OpenERPDataFactory clone() { final OpenERPDataFactory dataFactory = (OpenERPDataFactory) super.clone(); if ( this.config != null ) { dataFactory.config = this.config.clone(); } return dataFactory; } public OpenERPConfiguration getConfig() { return config; } }