package org.jdal.swing.report;
import java.awt.Desktop;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.zip.ZipFile;
import javax.sql.DataSource;
import net.sf.jasperreports.engine.JRDataSource;
import net.sf.jasperreports.engine.JRParameter;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.util.SimpleFileResolver;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdal.dao.Dao;
import org.jdal.dao.Page;
import org.jdal.reporting.Report;
import org.jdal.reporting.ReportingException;
import org.jdal.reporting.datasource.PageJRDatasourceAdapter;
import org.jdal.util.ZipFileUtils;
import org.jdal.util.processor.FileProcessor;
import org.jdal.util.processor.JasperReportFileProcessor;
import org.jdal.util.processor.JasperReportXMLFileProcessor;
/**
* This class is used to generate and display reports.
* Combine it with info.joseluismartin.util.processor.FileProcessor
*
* @author Jose A. Corbacho
*/
public abstract class ReportManager {
/** apache common log */
private static final Log log = LogFactory.getLog(ReportManager.class);
private DataSource dataSource;
/**
* Default ctor.
*/
public ReportManager(){
}
/**
* Display a report in <code>outputType</code> using the service and available ids to create
* the data source
* @param report report to display
* @param filter filter to use when query data to persistent service
* @param sortPropertyName sort property name
* @param sortOrder sort order (ASC, DESC)
* @param service persistent service to use
* @param outputType report output type (pdf, xml)
*/
public void showReport(Report report, Dao<Object, Serializable> service, Object filter, String sortPropertyName, Page.Order sortOrder, String outputType) throws Exception{
PageJRDatasourceAdapter dataSource = new PageJRDatasourceAdapter(true);
Page<Object> page = new Page<Object>(10, 0, sortPropertyName, sortOrder);
page.setFilter(filter);
page.setPageableDataSource(service);
dataSource.setPage(page);
ProcessFileStrategy st = new JRDataSourceFileStrategy();
try {
st.processFile(report, outputType, dataSource);
} catch (IOException e) {
log.error(e);
}
}
/**
* Displays the report in a new window as a file
* @param report the report to be displayed
* @throws ReportingException
*/
public void showReport(Report report, DataSource dataSource, String outputType) throws ReportingException {
ProcessFileStrategy st = new ConnectionFileStrategy();
Connection conn = null;
try {
conn = dataSource.getConnection();
boolean continueWithReport = st.preprocessFile(report);
if (continueWithReport) st.processFile(report, outputType, conn);
} catch (Exception e) {
log.error(e);
} finally {
if (conn != null)
try {
conn.close();
} catch (SQLException e) {
log.error(e);
}
}
}
public static String getPrefix(String fileName) {
String name = fileName != null ? fileName : "";
int index = name.lastIndexOf('.');
return index == -1 ? name : name.substring(0, name.lastIndexOf('.'));
}
public static String getSuffix(String fileName) {
String name = fileName != null ? fileName : "";
int index = name.lastIndexOf('.');
return index == -1 ? "" : name.substring(index);
}
protected abstract JRParameterEditorDialog createEditorDialog();
/**
* Implementation of FileStrategy using a JRDataSource as report data source
* @author Jose A. Corbacho
*
*/
public class JRDataSourceFileStrategy extends ProcessFileStrategy {
@Override
public void setReportDataSource(FileProcessor fp, Object obj) {
fp.setService((JRDataSource)obj);
}
}
/**
* Implementation of FileStrategy using a Connection as report data source
* @author Jose A. Corbacho
*
*/
public class ConnectionFileStrategy extends ProcessFileStrategy{
@Override
public void setReportDataSource(FileProcessor fp, Object obj) {
fp.setConnection((Connection)obj);
}
}
/**
* Strategy to process the jasper file. Different strategies depending on the system used to retrieve
* the data (JRDataSource, java.sql.Connection)
* @author Jose A. Corbacho
*
*/
public abstract class ProcessFileStrategy {
private Map<String, Object> parameters = new HashMap<String, Object>();
private boolean interactive = true;
private FileProcessor fileProcessor;
/**
* Set the data source in this file processor.
* @param fp
* @param obj
*/
public abstract void setReportDataSource(FileProcessor fp, Object obj);
/**
* Pre-process file. In case this report includes params, display it.
* The fact a report has params can be determined at the moment of registering
* it the system. At this point, it could be possible to have this information
* thus making this process faster
* @param report report to process
*/
public boolean preprocessFile(Report report){
if (log.isDebugEnabled())
log.debug("ReportManager. Preprocess file: hasQuery -> " + report.getHasQuery());
if (report.getHasQuery()) {
JasperReport jasperReport = report.newJasperReport();
if (log.isDebugEnabled())
log.debug("Parameters in jasperReport");
Map<String, JRParameter> jrParameters = new HashMap<String, JRParameter>();
for (JRParameter param : jasperReport.getParameters()) {
if (!param.isSystemDefined() && param.isForPrompting()){
if (log.isDebugEnabled())
log.debug("Param to fill from paramEntry: " + param.getName());
jrParameters.put(param.getName(), param);
}
}
if (!jrParameters.isEmpty() && interactive) {
if (!showParameterDialog(jrParameters)) return false;
}
}
return true;
}
private boolean showParameterDialog(Map<String, JRParameter> jrParameters){
JRParameterEditorDialog dialog = createEditorDialog();
dialog.setParameters(jrParameters);
dialog.initialize();
dialog.setVisible(true);
if (dialog.isCanceled()) return false;
this.parameters = dialog.getReturnValues();
return true;
}
/**
* Process the report with this reportDataSource. The data source can be JRDataSource or Connection
* @param report
* @param outputType
* @param reportDataSource
* @throws ReportingException
* @throws IOException
*/
public void processFile(Report report, String outputType, Object reportDataSource ) throws ReportingException, IOException{
if (report == null || report.getData() == null)
return;
File file;
String suffix = getSuffix(report.getFileName());
try {
file = File.createTempFile(getPrefix(report.getFileName()), suffix);
file.deleteOnExit();
FileUtils.writeByteArrayToFile(file, report.getData());
if (".zip".equalsIgnoreCase(suffix)) {
String dir = System.getProperty("java.io.tmpdir") + "/" + getPrefix(report.getFileName());
ZipFileUtils.unzip(new ZipFile(file), dir);
File dirFile = new File(dir);
parameters.put(JRParameter.REPORT_FILE_RESOLVER, new ReportFileResolver(dirFile));
Iterator<File> iter = FileUtils.iterateFiles(dirFile, new String[] {"jrxml", "jasper"}, false);
while (iter.hasNext()) {
file = iter.next();
suffix = getSuffix(file.getName());
break;
}
}
// TODO In case more file types are allowed in the system, create Factory
if (".jasper".equals(suffix)){
fileProcessor = new JasperReportFileProcessor();
}
else if (".jrxml".equals(suffix)){
fileProcessor = new JasperReportXMLFileProcessor();
}
else {
throw new ReportingException("Process not yet implemented for file type " + suffix);
}
fileProcessor.setParameters(parameters);
setReportDataSource(fileProcessor, reportDataSource);
// Process the file. This method sets its rawData attribute to the result of the processing.
fileProcessor.processFile(file, outputType, report.getHasQuery());
if (interactive && Desktop.isDesktopSupported()) {
Desktop desktop = Desktop.getDesktop();
File outputFile;
try {
outputFile = File.createTempFile(getPrefix(report.getFileName()), "." + outputType);
outputFile.deleteOnExit();
// Create the file with the raw data provided by the file processor
FileUtils.writeByteArrayToFile(outputFile, fileProcessor.getRawData());
desktop.open(outputFile);
} catch (IOException e) {
throw new ReportingException("No ha sido posible abrir el fichero del informe: " + report.getFileName(), e);
}
}
} catch (IOException e) {
throw new ReportingException("No ha sido posible abrir el fichero del informe: " + report.getFileName(), e);
}
}
/**
* @return the interactive
*/
public boolean isInteractive() {
return interactive;
}
/**
* @param interactive the interactive to set
*/
public void setInteractive(boolean interactive) {
this.interactive = interactive;
}
/**
* @return the parameters
*/
public Map<String, Object> getParameters() {
return parameters;
}
/**
* @param parameters the parameters to set
*/
public void setParameters(Map<String, Object> parameters) {
this.parameters = parameters;
}
/**
* @return the fileProcessor
*/
public FileProcessor getFileProcessor() {
return fileProcessor;
}
/**
* @param fileProcessor the fileProcessor to set
*/
public void setFileProcessor(FileProcessor fileProcessor) {
this.fileProcessor = fileProcessor;
}
}
/**
* @return the dataSource
*/
public DataSource getDataSource() {
return dataSource;
}
/**
* @param dataSource the dataSource to set
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
class ReportFileResolver extends SimpleFileResolver {
/**
* @param parentFolder
*/
public ReportFileResolver(File parentFolder) {
super(parentFolder);
}
/**
* {@inheritDoc}
*/
@Override
public File resolveFile(String fileName) {
return super.resolveFile(FilenameUtils.getName((fileName)));
}
}