/*
* 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) 2001 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core.modules.parser.base;
import org.pentaho.reporting.engine.classic.core.MasterReport;
import org.pentaho.reporting.libraries.base.util.IOUtils;
import org.pentaho.reporting.libraries.base.util.MemoryByteArrayOutputStream;
import org.pentaho.reporting.libraries.resourceloader.FactoryParameterKey;
import org.pentaho.reporting.libraries.resourceloader.Resource;
import org.pentaho.reporting.libraries.resourceloader.ResourceCreationException;
import org.pentaho.reporting.libraries.resourceloader.ResourceException;
import org.pentaho.reporting.libraries.resourceloader.ResourceKey;
import org.pentaho.reporting.libraries.resourceloader.ResourceKeyCreationException;
import org.pentaho.reporting.libraries.resourceloader.ResourceLoadingException;
import org.pentaho.reporting.libraries.resourceloader.ResourceManager;
import org.xml.sax.InputSource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
/**
* The reportgenerator initializes the parser and provides an interface the the default parser.
* <p/>
* To create a report from an URL, use <code> ReportGenerator.getInstance().parseReport (URL myURl, URL contentBase);
* </code>
*
* @author Thomas Morgner
* @deprecated Use LibLoader directly.
*/
public class ReportGenerator {
/**
* Enable DTD validation of the parsed XML.
*/
public static final String PARSER_VALIDATE_KEY =
"org.pentaho.reporting.engine.classic.core.modules.parser.base.Validate";
/**
* disable DTD validation by default.
*/
public static final boolean PARSER_VALIDATE_DEFAULT = true;
/**
* The report generator.
*/
private static ReportGenerator generator;
private HashMap helperObjects;
private boolean validateDTD;
/**
* Creates a new report generator. The generator uses the singleton pattern by default, so use generator.getInstance()
* to get the generator.
*/
protected ReportGenerator() {
helperObjects = new HashMap();
}
/**
* Set to false, to globaly disable the xml-validation.
*
* @param validate
* true, if the parser should validate the xml files.
*/
public void setValidateDTD( final boolean validate ) {
this.validateDTD = validate;
}
/**
* returns true, if the parser should validate the xml files against the DTD supplied with JFreeReport.
*
* @return true, if the parser should validate, false otherwise.
*/
public boolean isValidateDTD() {
return validateDTD;
}
/**
* Parses a report using the given parameter as filename and the directory containing the file as content base.
*
* @param file
* the file name.
* @return the report.
* @throws java.io.IOException
* if an I/O error occurs.
*/
public MasterReport parseReport( final String file ) throws IOException, ResourceException {
if ( file == null ) {
throw new NullPointerException( "File may not be null" );
}
return parseReport( new File( file ) );
}
/**
* Parses an XML file which is loaded using the given URL. All needed relative file- and resourcespecification are
* loaded using the URL <code>file</code> as base.
*
* @param file
* the URL for the report template file.
* @return the report.
* @throws java.io.IOException
* if an I/O error occurs.
*/
public MasterReport parseReport( final URL file ) throws IOException, ResourceException {
return parseReport( file, file );
}
/**
* Parses an XML file which is loaded using the given URL. All needed relative file- and resourcespecification are
* loaded using the URL <code>contentBase</code> as base.
* <p/>
* After the report is generated, the ReportDefinition-source and the contentbase are stored as string in the
* reportproperties.
*
* @param file
* the URL for the report template file.
* @param contentBase
* the URL for the report template content base.
* @return the parsed report.
*/
public MasterReport parseReport( final URL file, final URL contentBase ) throws ResourceException {
return parse( file, contentBase );
}
/**
* Parses the report from a given URL.
*
* @param file
* the report definition location.
* @param contentBase
* the report's context (used to load content that has been referenced with relative URLs).
* @return the parsed report.
* @throws ResourceException
* if parsing or loading failed for some reason.
*/
private MasterReport parse( final URL file, final URL contentBase ) throws ResourceException {
final ResourceManager resourceManager = new ResourceManager();
final ResourceKey contextKey = resourceManager.createKey( contentBase );
// Build the main key. That key also contains all context/parse-time
// parameters as they will influence the resulting report. It is not
// wise to keep caching independent from that.
final HashMap map = new HashMap();
final Iterator it = this.helperObjects.keySet().iterator();
while ( it.hasNext() ) {
final String name = (String) it.next();
map.put( new FactoryParameterKey( name ), helperObjects.get( name ) );
}
final ResourceKey key = resourceManager.createKey( file, map );
final Resource resource = resourceManager.create( key, contextKey, MasterReport.class );
return (MasterReport) resource.getResource();
}
/**
* @noinspection IOResourceOpenedButNotSafelyClosed
*/
private byte[] extractData( final InputSource input ) throws IOException {
final InputStream byteStream = input.getByteStream();
if ( byteStream != null ) {
try {
final MemoryByteArrayOutputStream bout = new MemoryByteArrayOutputStream();
IOUtils.getInstance().copyStreams( byteStream, bout );
return bout.toByteArray();
} finally {
byteStream.close();
}
}
final Reader characterStream = input.getCharacterStream();
if ( characterStream == null ) {
throw new IOException( "InputSource has neither an Byte nor a CharacterStream" );
}
try {
final MemoryByteArrayOutputStream bout = new MemoryByteArrayOutputStream();
final OutputStreamWriter owriter = new OutputStreamWriter( bout );
IOUtils.getInstance().copyWriter( characterStream, owriter );
owriter.close();
return bout.toByteArray();
} finally {
characterStream.close();
}
}
/**
* Parses an XML file which is loaded using the given file. All needed relative file- and resourcespecification are
* loaded using the parent directory of the file <code>file</code> as base.
*
* @param file
* the report template file.
* @return the parsed report.
* @throws java.io.IOException
* if an I/O error occurs.
*/
public MasterReport parseReport( final File file ) throws IOException, ResourceException {
if ( file == null ) {
throw new NullPointerException();
}
if ( file.isDirectory() ) {
throw new IOException( "File is not a directory." );
}
final File contentBase = file.getCanonicalFile().getParentFile();
final ResourceManager resourceManager = new ResourceManager();
final ResourceKey contextKey = resourceManager.createKey( contentBase );
// Build the main key. That key also contains all context/parse-time
// parameters as they will influence the resulting report. It is not
// wise to keep caching independent from that.
final HashMap map = new HashMap();
final Iterator it = this.helperObjects.keySet().iterator();
while ( it.hasNext() ) {
final String name = (String) it.next();
map.put( new FactoryParameterKey( name ), helperObjects.get( name ) );
}
final ResourceKey key = resourceManager.createKey( file, map );
final Resource resource = resourceManager.create( key, contextKey, MasterReport.class );
return (MasterReport) resource.getResource();
}
/**
* Parses the report from a given SAX-InputSource.
*
* @param input
* the report definition location.
* @param contentBase
* the report's context (used to load content that has been referenced with relative URLs).
* @return the parsed report.
* @throws ResourceException
* if parsing or loading failed for some reason.
* @throws IOException
* if an IO-related error occurs.
*/
public MasterReport parseReport( final InputSource input, final URL contentBase ) throws IOException,
ResourceException {
if ( input.getCharacterStream() != null ) {
// Sourceforge Bug #1712734. We cannot safely route the character-stream through libloader.
// Therefore we skip libloader and parse the report directly. This is for backward compatibility,
// all other xml-based objects will still rely on LibLoader.
return parseReportDirectly( input, contentBase );
}
final byte[] bytes = extractData( input );
final ResourceManager resourceManager = new ResourceManager();
final ResourceKey contextKey;
if ( contentBase != null ) {
contextKey = resourceManager.createKey( contentBase );
} else {
contextKey = null;
}
final HashMap map = new HashMap();
final Iterator it = this.helperObjects.keySet().iterator();
while ( it.hasNext() ) {
final String name = (String) it.next();
map.put( new FactoryParameterKey( name ), helperObjects.get( name ) );
}
final ResourceKey key = resourceManager.createKey( bytes, map );
final Resource resource = resourceManager.create( key, contextKey, MasterReport.class );
return (MasterReport) resource.getResource();
}
private MasterReport parseReportDirectly( final InputSource input, final URL contentBase )
throws ResourceKeyCreationException, ResourceCreationException, ResourceLoadingException {
final ResourceManager manager = new ResourceManager();
final HashMap map = new HashMap();
final Iterator it = this.helperObjects.keySet().iterator();
while ( it.hasNext() ) {
final String name = (String) it.next();
map.put( new FactoryParameterKey( name ), helperObjects.get( name ) );
}
final MasterReportXmlResourceFactory resourceFactory = new MasterReportXmlResourceFactory();
resourceFactory.initializeDefaults();
if ( contentBase != null ) {
return (MasterReport) resourceFactory.parseDirectly( manager, input, manager.createKey( contentBase ), map );
} else {
return (MasterReport) resourceFactory.parseDirectly( manager, input, null, map );
}
}
/**
* Parses the report using the provided resource manager.
*
* @param manager
* the resource manager (can be null).
* @param input
* the resource key pointing to the report definition.
* @param contextKey
* the report's context (used to load content that has been referenced with relative URLs).
* @return the parsed report.
* @throws ResourceException
* if parsing or loading failed for some reason.
*/
public MasterReport parseReport( ResourceManager manager, final ResourceKey input, final ResourceKey contextKey )
throws ResourceException {
if ( manager == null ) {
manager = new ResourceManager();
}
final HashMap map = new HashMap( input.getFactoryParameters() );
final Iterator it = this.helperObjects.keySet().iterator();
while ( it.hasNext() ) {
final String name = (String) it.next();
map.put( new FactoryParameterKey( name ), helperObjects.get( name ) );
}
final ResourceKey key =
new ResourceKey( input.getParent(), input.getSchema(), input.getIdentifier(), input.getFactoryParameters() );
final Resource resource = manager.create( key, contextKey, MasterReport.class );
return (MasterReport) resource.getResource();
}
/**
* Returns a single shared instance of the <code>ReportGenerator</code>. This instance cannot add helper objects to
* configure the report parser.
*
* @return The shared report generator.
*/
public static synchronized ReportGenerator getInstance() {
if ( generator == null ) {
generator = new ReportGenerator();
}
return generator;
}
/**
* Returns a private (non-shared) instance of the <code>ReportGenerator</code>. Use this instance when defining helper
* objects.
*
* @return The shared report generator.
*/
public static ReportGenerator createInstance() {
return new ReportGenerator();
}
/**
* Assigns a parse-context object.
*
* @param key
* the parse-context key used to lookup the object later.
* @param value
* the value.
*/
public void setObject( final String key, final Object value ) {
if ( key == null ) {
throw new NullPointerException();
}
if ( value == null ) {
helperObjects.remove( key );
} else {
helperObjects.put( key, value );
}
}
/**
* Returns the parse context object for the given key.
*
* @param key
* the key.
* @return the value or null if there is no such value.
*/
public Object getObject( final String key ) {
return helperObjects.get( key );
}
}