/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2010-2015, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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.
*/
package org.geotoolkit.report;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.ServiceLoader;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRField;
import net.sf.jasperreports.engine.JRPropertiesMap;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperExportManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.design.JasperDesign;
import net.sf.jasperreports.engine.export.oasis.JROdtExporter;
import net.sf.jasperreports.engine.xml.JRXmlLoader;
import net.sf.jasperreports.export.SimpleExporterInput;
import net.sf.jasperreports.export.SimpleOdtReportConfiguration;
import net.sf.jasperreports.export.SimpleOutputStreamExporterOutput;
import org.apache.sis.feature.builder.FeatureTypeBuilder;
import org.geotoolkit.data.FeatureStoreRuntimeException;
import org.geotoolkit.display2d.service.OutputDef;
import org.geotoolkit.lang.Static;
import org.apache.sis.util.ObjectConverters;
import org.apache.sis.internal.util.UnmodifiableArrayList;
import static org.apache.sis.util.ArgumentChecks.*;
import org.opengis.feature.AttributeType;
import org.opengis.feature.FeatureType;
/**
* Utility class to generate html or pdf reports using JasperReport library.
*
* @author Johann Sorel (Geomatys)
* @module
*/
public final class JasperReportService extends Static {
private static final Collection<JRFieldRenderer> RENDERERS;
public static final String MIME_PDF = "application/pdf";
public static final String MIME_HTML = "text/html";
public static final String MIME_ODT = "application/vnd.oasis.opendocument.text";
static {
final ServiceLoader<JRFieldRenderer> service = ServiceLoader.load(JRFieldRenderer.class);
final List<JRFieldRenderer> renderers = new ArrayList<JRFieldRenderer>();
for(final JRFieldRenderer r : service){
renderers.add(r);
}
RENDERERS = UnmodifiableArrayList.wrap(renderers.toArray(new JRFieldRenderer[renderers.size()]));
}
private JasperReportService(){}
/**
* Parse the given input to a JasperReport and a feature type describing the remplate record.
* This feature type must be used to generate the featureCollection that will be used
* by the generateReport method.
*
* @param jrxml
* @return Entry<JasperReport,FeatureType>
* @throws JRException
*/
public static Entry<JasperReport,FeatureType> prepareTemplate(final Object jrxml) throws JRException{
// load and compile the template
final JasperDesign jasperDesign;
if(jrxml instanceof File){
jasperDesign = JRXmlLoader.load((File)jrxml);
}else if(jrxml instanceof InputStream){
jasperDesign = JRXmlLoader.load((InputStream)jrxml);
}else if(jrxml instanceof URL){
try {
jasperDesign = JRXmlLoader.load( ((URL)jrxml).openStream() );
} catch (IOException ex) {
throw new JRException(ex);
}
}else if(jrxml instanceof String){
jasperDesign = JRXmlLoader.load((String)jrxml);
}else{
//last chance : try to convert the source to a file
final File candidate = ObjectConverters.convert(jrxml, File.class);
if(candidate instanceof File){
jasperDesign = JRXmlLoader.load(candidate);
}else{
throw new IllegalArgumentException("Unsupported input type : " + jrxml);
}
}
//generate the report and extract the feature type.
final JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
final FeatureType type = extractType(jasperDesign);
return new SimpleImmutableEntry<JasperReport, FeatureType>(jasperReport, type);
}
/**
* Generate a report from the given JasperReport. It we be filled with the features
* provided in the FeatureCollection.
*
* @param report : report to generate
* @param col : if featureCollection, feature type must match the given one from the prepareTemplace method.
* @param parameters : Map of parameters passed to the JasperFillManager.
* @param output : output definition
* @throws JRException
*/
public static void generateReport(final JasperReport report, final Collection col, final Map parameters,
final OutputDef output) throws JRException, FeatureStoreRuntimeException{
final CollectionDataSource source = new CollectionDataSource(col);
final JasperPrint print = JasperFillManager.fillReport(report, parameters, source);
generate(print, output);
}
/**
* Generate a report from the given JasperReport. It we be filled with the features
* provided in the FeatureIterator.
*
* @param report : report to generate
* @param ite : if featureCollection, feature type must match the given one from the prepareTemplace method.
* @param parameters : Map of parameters passed to the JasperFillManager.
* @param output : output definition
* @throws JRException
*/
public static void generateReport(final JasperReport report, final Iterator ite, final Map parameters,
final OutputDef output) throws JRException, FeatureStoreRuntimeException{
final CollectionDataSource source = new CollectionDataSource(ite);
final JasperPrint print = JasperFillManager.fillReport(report, parameters, source);
generate(print, output);
}
/**
* Write the jasper print in the defined output.
* @param print
* @param output : output definition
* @throws net.sf.jasperreports.engine.JRException
*/
public static void generate(final JasperPrint print, final OutputDef output) throws JRException{
final String mime = output.getMime();
Object target = output.getOutput();
ensureNonNull("mime", mime);
ensureNonNull("output target", target);
//we adjust the output target to a knowned type
if(!(target instanceof OutputStream)){
//we try to convert it to a file
target = ObjectConverters.convert(target, File.class);
}
if(mime.equalsIgnoreCase(MIME_PDF)){
if(target instanceof OutputStream){
JasperExportManager.exportReportToPdfStream(print, (OutputStream) target);
}else{
JasperExportManager.exportReportToPdfFile(print, ((File) target).getPath());
}
}else if(mime.equalsIgnoreCase(MIME_HTML)){
if(target instanceof File){
JasperExportManager.exportReportToHtmlFile(print, ((File) target).getPath());
}else{
throw new IllegalArgumentException("Unsupported output : " + target + " for mime type : "+ mime);
}
}else if(mime.equalsIgnoreCase(MIME_ODT)){
if(target instanceof File){
final JROdtExporter exporter = new JROdtExporter();
exporter.setExporterInput(new SimpleExporterInput(print));
exporter.setExporterOutput(new SimpleOutputStreamExporterOutput((File)target));
SimpleOdtReportConfiguration config = new SimpleOdtReportConfiguration();
exporter.setConfiguration(config);
exporter.exportReport();
}else{
throw new IllegalArgumentException("Unsupported output : " + target + " for mime type : "+ mime);
}
}else{
throw new IllegalArgumentException("Unsupported mime type : " + mime);
}
}
/**
* Explore the report design and generate a FeatureType that match the records definition.
*
* @param design
* @return FeatureType
*/
private static FeatureType extractType(final JasperDesign design){
final FeatureTypeBuilder ftb = new FeatureTypeBuilder();
ftb.setName(design.getName());
//find a description for each field
final Collection<JRFieldRenderer> renderers = JasperReportService.getFieldRenderers();
fields:
for(final JRField field : design.getFields()){
//search for special fields
for(JRFieldRenderer renderer : renderers){
if(renderer.canHandle(field)){
final AttributeType desc = renderer.createDescriptor(field);
ftb.addAttribute(desc);
continue fields;
}
}
//handle it as a casual field
ftb.addAttribute(field.getValueClass())
.setName(field.getName());
//TODO
//toParameterMap(field.getPropertiesMap()));
}
return ftb.build();
}
/**
* Change a jasper report property map in a casual java Map.
*/
private static Map<Object,Object> toParameterMap(final JRPropertiesMap params){
if(params == null){
return null;
}
final String[] propertyNames = params.getPropertyNames();
if(propertyNames.length == 0){
return null;
}
final Map<Object,Object> map = new HashMap<Object, Object>(propertyNames.length);
for(String name : propertyNames){
map.put(name, params.getProperty(name));
}
return map;
}
public static Collection<JRFieldRenderer> getFieldRenderers(){
return RENDERERS;
}
}