/*
* 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 java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import javax.swing.table.TableModel;
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;
/**
* @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;
}
}