Mark Lorenz on Technology

Wednesday, March 01, 2006

More BIRT Tips for Java Developers

Last month, I wrote some tips for using BIRT from Eclipse to do your report generation. I promised more BIRT tips, so here they are:
  • Control page breaks
Thanks to Jason Weathersby for this tip.
For example, you may want to only have a page break after the first page (or last or whatever conditions). To do this you would:
  • Select the report page and go to the Script page. Add "firstPage = 0;" on a separate line.
  • Add a table to your report.
  • Add a group to your table.
  • Select the group header row and go to the Script page.
  • Go to the onCreate scripting page and add this logic:
          if( firstPage > 0 ) {
this.getStyle().pageBreakBefore = "Always";
} else {
this.getStyle().pageBreakBefore = "Avoid";
firstPage = firstPage + 1;
}
That's it! Now all pages after the first will have a page break before them.

Note: This does not work with a List. Believe me, I tried. So unless you just want the learning experience of a hard spanking, stick to Tables.

Note: To force page "breaks" for HTML output, use the Page Break Interval field. E.g. you could specify a new page every 50 lines. This is necessary because browsers can be resized, so the number of lines can change.

  • Set up your Master page properly
Thanks to Diana Peh for this tip.
The Master page of your report has some rules that must be followed:
  • You can insert only one top-level element in the header or footer. By default
    there's already a date element in the footer. You must delete the date element if you want to add something else (e.g. a page element).
  • If you want to insert multiple elements, delete the date element first,
    insert a grid, then insert as many elements as you want in the grid.
  • No data set fields can be used on the Master page.

  • Avoid specify dimensions in percentages (%)
Percentages for width or height does not work as you might expect. E.g. resizing a browser window will affect the artwork or chart that is sized by percentages. Use inches or some other metric.

A small Report framework to reuse

This isn't exactly a tip, but more of a leg up. Here's an abstract Report class that you can set up a small framework to support BIRT reporting:


/**
*
*/
package com.yourcompany.domain.reporting;

import java.security.InvalidParameterException;
import java.util.HashMap;

import org.eclipse.birt.report.engine.api.EngineConfig;
import org.eclipse.birt.report.engine.api.EngineConstants;
import org.eclipse.birt.report.engine.api.EngineException;
import org.eclipse.birt.report.engine.api.HTMLCompleteImageHandler;
import org.eclipse.birt.report.engine.api.HTMLEmitterConfig;
import org.eclipse.birt.report.engine.api.HTMLRenderContext;
import org.eclipse.birt.report.engine.api.HTMLRenderOption;
import org.eclipse.birt.report.engine.api.IReportRunnable;
import org.eclipse.birt.report.engine.api.IRunAndRenderTask;
import org.eclipse.birt.report.engine.api.ReportEngine;

/**
* @author lorenzm A formatted output document that can be viewed, printed,
* faxed, stored.
* @version $Revision: 1.1 $
*/
public abstract class Report {

/**
* A descriptive String set by my concrete subclasses at construction time.
*/
protected String name;
protected String format =PDF_FORMAT; //default PDF
public static final String PDF_FORMAT =HTMLRenderOption.OUTPUT_FORMAT_PDF;
public static final String HTML_FORMAT =HTMLRenderOption.OUTPUT_FORMAT_HTML;
protected static EngineConfig config;
protected static ReportEngine engine; //only create ONE engine for ALL reports

/**
* Constructor with required associations and/or state
*
* @pre name != null && name.length >0
* @param name
*/
public Report(String name) {
super();
this.name = name;
}

/**
* Set up for BIRT report generation. Subclasses will
* do the actual generation based on their own designs,
* but using the one and only engine I create.
* @return Returns the engine.
*/
protected ReportEngine getEngine() {
if( engine == null ) {
config = new EngineConfig( );
config.setEngineHome( "C:\\BIRT\\birt-runtime-2_0_0\\Report Engine" ); //TODO Set CORRECT home!!
// config.setLogConfig("C:\\BIRT\\logs\\", Level.FINER);
//only create ONE engine for ALL reports
engine = new ReportEngine( config );
// Create the emitter configuration.
HTMLEmitterConfig hc = new HTMLEmitterConfig( );
// Use the "HTML complete" image handler to write the files to disk.
HTMLCompleteImageHandler imageHandler = new HTMLCompleteImageHandler( );
hc.setImageHandler( imageHandler );
// Associate the configuration with the HTML output format.
config.setEmitterConfiguration( HTML_FORMAT, hc );
}
return engine;
}

/**
* Create a formatted output file using a BIRT .rptdesign XML file.
*/
public void generate() {
//------ Run reports
//--> IF params are needed for the report,
// add logic at http://eclipse.org/birt/index.php?page=deploy/engine.html

// Output can be HTML or PDF. PDF is default.
IReportRunnable report = null;
try {
System.out.println(getDesignFilepath()); //TODO remove for production
report = getEngine().openReportDesign( getDesignFilepath() );
} catch (EngineException e) {
e.printStackTrace();
return;
}
// Create a separate task for EACH report
IRunAndRenderTask task = getEngine().createRunAndRenderTask( report );
// Set up options.
HTMLRenderOption options = new HTMLRenderOption( );
// HTML (if specified) or PDF (default)
options.setOutputFormat( format );
String outputPath = "C:\\BIRT\\"; //TODO change to deployment location
outputPath += getOutputFilename();
options.setOutputFileName(outputPath);
task.setRenderOption( options );
HTMLRenderContext renderContext = new HTMLRenderContext();
renderContext.setImageDirectory( "./images" );
HashMap appContext = new HashMap();
appContext.put( EngineConstants.APPCONTEXT_HTML_RENDER_CONTEXT, renderContext );
task.setAppContext( appContext );
// Set parameter values using a HashMap. Parameters are name/value pairs.
// The values must be Java objects of the correct type.
HashMap params = getParameters(); //each report has its own params
task.setParameterValues( params );
task.validateParameters();

// Run the report.
try {
task.run( );
} catch (EngineException e) {
e.printStackTrace();
}
}

/**
* Only destroy the BIRT engine when exiting.
*/
public static void shutdown() {
engine.destroy( ); //cleanup
}

/**
* @return Parameter values to use
* Key = param name, Value = param value
*/
protected abstract HashMap getParameters();

/**
* @post $result != null
* @return The location of my BIRT .rptdesign file.
*/
protected abstract String getDesignFilepath();

/**
* @pre format == HTML_FORMAT || format == PDF_FORMAT
* @param format
* Must be HTML or PDF BIRT constant!
* @throws InvalidParameterException
* @throws Exception
*/
public void setFormat(String format) throws InvalidParameterException {
if( format.equals( PDF_FORMAT ) || format.equals( HTML_FORMAT ) ) {
this.format = format;
} else {
throw new InvalidParameterException("Invalid report format: " + format );
}
}
/**
* Return my name as the filename, with ".pdf" or ".html" as the extension
* based on my current format.
*
* @return String my name as the filename
*/
public String getOutputFilename() {
if( format.equals( PDF_FORMAT ) ) {
return getName() + ".pdf";
} else {
return getName() + ".html";
}
}

/**
* @return String
*/
public String getName() {
return name;
}

/**
* Method toString.
* @return String
*/
public String toString() {
return getName();
}
}


A concrete subclass would look like this:


package com.yourcompany.domain.reporting;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
* A Report of ...
* @author lorenzm
* @version $Revision: 1.1 $
*/
public class MyReport extends Report {
protected One one;
protected Two two;

/**
* Constructor with required parameters.
* @param one ...
* @param two ...
*/
public MyReport(One one, Two two) {
super("My Report");
this.one= one;
this.two= two;
}
/**
* Method getDesignFilepath.
* @return the complete path to my BIRT design file
*/
protected String getDesignFilepath() {
return "C:\\BIRT\\MyReport.rptdesign";
}
/**
* Method getParameters.
* @return my BIRT design input params
*/
protected HashMap getParameters() {
HashMap params = new HashMap();
params.put("one", one);
params.put("two", two);
return params;
}
/**
* Method toString.
* @return String
*/
public String toString() {
return "meaningful string, such as getName() + getParamString()"
;
}
}


That's it for now! Hope this has been beneficial.
Mark

Labels:


| 3 comments links to this post

Cool Tools

There are some huge benefits to the web. One benefit is there are a lot of great things available, often for free. One drawback is there is so much to offer that it takes a lot of time and energy (and sometimes pain) to pick the good from the bad and the ugly.

With that in mind, I thought I'd talk about some really cool finds that you may enjoy, in no particular order:
This gem of a program carries a lot of nice features in a compact tool. It will save your copious user IDs and passwords in a secure manner that still allows ease of use. E.g. you can have Password Safe go to a URL and then enter the user ID and password and press submit.
I know ... everyone has their favorites. These are mine:
  • CustomizeGoogle - strip out ads in search results
  • Google Suggest - suggests search terms as you type
  • Map This - select an address on a web page and get a map
  • Tab Mix Plus - the greatest multi-tab extension ever; you should try this
  • Fetch Text URL - go to a URL straight from text on a web page
  • TargetAlert - shows visual cues for destinations of hyperlinks
  • QuickNote - Note taking from your browser
  • Copy URL+ - Copy selection, page URL, and/or title to the clipboard
  • Answers - Alt+Select on a word to get a definition and references
  • Fasterfox - Performance enhancements to Firefox
  • Forecastfox - Nicely done weather info
  • Sage - view and manage RSS feeds
  • ReminderFox- view and manage reminders and to-do's
  • (not an extension, but my favorite theme is Silver Skin)
Excellent and free side-by-side differences and merging of files.
Quick and (relatively) easy storyboarding. Put together UI pages and navigate between them right away. A bit quirky to get used to, but fast and useful output.
Some overlap with Sage, but AWR allows you to do more. You can add web pages, not just feeds...and you can see highlighted changes. Nice.
  • MagicDraw community edition (Eclipse plugin too)
Certainly, if you have the money in your budget, the full versions are highly recommended. The nice thing is the community edition lets you try out UML modeling on a limited scale.
The commercial version has some neat visual development tools, but the free community edition still will help if you are doing JSF and Ajax development.
If you're a Java developer, you should get CodePro. It will intelligently generate meaningful unit tests, fill in JavaDocs, examine metrics, and do much more.
Very nice screensaver. View real satellite images of the planet with accurate daylight and times at various places across the world. Different resolutions available. Update as often or seldom as you like.
Free tool that will remove spyware and adware from your computer and protect you from further infestations.
(Just about) everything you'd ever want for text editing, including sorting, side-by-side scrolling, marking all instances of a string, and so much more.
A super, free set of desktop tools, including working with documents, spreadsheets, presentations, drawings, and more. I'd be very afraid if I was the evil empire!


| 7 comments links to this post