Wednesday, January 27, 2010

BIRT: Get Access to Chart Element and Modify Properties from ReportRunnable in BIRT 2.5

Helping someone out from the forums on BIRT Exchange. The question was how can you get access to a chart in a ReportRunnable object, modify its properties, and run the report with the modified properties. While I have given pieces of how to do that here and here, I have never put them together in a single example. Below is an example of how to do this.

package com.digiassn.blogspot.birt;

import java.util.logging.Level;

import org.eclipse.birt.chart.model.ChartWithoutAxes;
import org.eclipse.birt.chart.model.attribute.ChartDimension;
import org.eclipse.birt.core.exception.BirtException;
import org.eclipse.birt.core.framework.Platform;
import org.eclipse.birt.report.engine.api.EngineConfig;
import org.eclipse.birt.report.engine.api.EngineException;
import org.eclipse.birt.report.engine.api.HTMLRenderOption;
import org.eclipse.birt.report.engine.api.IReportEngine;
import org.eclipse.birt.report.engine.api.IReportEngineFactory;
import org.eclipse.birt.report.engine.api.IReportRunnable;
import org.eclipse.birt.report.engine.api.IRunAndRenderTask;
import org.eclipse.birt.report.model.api.ExtendedItemHandle;
import org.eclipse.birt.report.model.api.extension.ExtendedElementException;

public class ModifyChart {

/**
* @param args
*/
public static void main(String[] args) {
try {
IReportEngine engine = null;
EngineConfig config = null;

// Configure the Engine and start the Platform
config = new EngineConfig();
config
.setEngineHome("C:/Libraries/birt-runtime-2_5_1/ReportEngine");
// set log config using ( null, Level ) if you do not want a log file
config.setLogConfig("C:/temp", Level.FINE);

//start birt platform and create factories
Platform.startup(config);
IReportEngineFactory factory = (IReportEngineFactory) Platform
.createFactoryObject(IReportEngineFactory.EXTENSION_REPORT_ENGINE_FACTORY);
engine = factory.createReportEngine(config);
engine.changeLogLevel(Level.WARNING);

//open report design
IReportRunnable design = engine
.openReportDesign("C:/contracts/BirtExamples/BirtReportEngine/PieChart.rptdesign");

//get chart element handle called "PieChart"
ExtendedItemHandle eih = (ExtendedItemHandle) design.getDesignHandle()
.getDesignHandle().findElement("PieChart");

//get the actual chart instance since Charts are extended item handles
ChartWithoutAxes cwa = (ChartWithoutAxes) eih.getReportItem().getProperty(
"chart.instance");

//set title of chart to New Label
cwa.getTitle().getLabel().getCaption().setValue("New Label");

//set 2d with depth
cwa.setDimension(ChartDimension.TWO_DIMENSIONAL_WITH_DEPTH_LITERAL);

// Create task to run and render the report,
IRunAndRenderTask task = engine.createRunAndRenderTask(design);

// Set rendering options - such as file or stream output,
// output format, whether it is embeddable, etc
HTMLRenderOption options = new HTMLRenderOption();
options.setBaseURL("http://localhost/");
options.setBaseImageURL("http://localhost/myimages");
options.setImageDirectory("C:/xampplite/htdocs/myimages");
options.setSupportedImageFormats("JPG;PNG;BMP;SVG");

options.setOutputStream(System.out);

// Set output format
options.setOutputFormat("html");
task.setRenderOption(options);

// run the report and destroy the engine
// Note - If the program stays resident do not shutdown the Platform or
// the Engine
task.run();
task.close();

task.close();
Platform.shutdown();
System.out.println("Finished");
} catch (ExtendedElementException e) {
e.printStackTrace();
} catch (EngineException e) {
e.printStackTrace();
} catch (BirtException e) {
e.printStackTrace();
}

}

}


The thing to remember about charts in BIRT reports is that they are Chart types stored withing a ExtendedItemHandler. So you need to find the ExtendedItemHandler first, then get the chart inside of it. Charts can be ChartsWithAxis (bar charts, lines charts), or ChartsWithoutAxis (pie charts). Reference on the chart types can be found here.

ChartWithAxes
ChartWithoutAxes

5 comments:

Kiran Vaidya said...

Hi John,

Thanks a million for posting the blog. Its working perfectly fine.
God bless u for ur prompt reply as well as posting it as a blog so that other users facing similar issues will also get quick help.

Regards,
Kiran

Kiran Vaidya said...

However when changing the chart to 3D its throwing a runtime exception and I can only view a blank chart

Seems to be a bug in Birt Charts

Since I am using a Bar Graph I have casted the ExtendedItemHandle object as a Chart Object (also works with ChartWithAxesImpl and ChartWithAxes)

ExtendedItemHandle eih = (ExtendedItemHandle) design.getDesignHandle().getDesignHandle().findElement("NewChart");

ChartWithAxes cwa = (ChartWithAxes) eih.getReportItem().getProperty(
"chart.instance"); //You can use Chart or ChartWithAxesImpl as well
cwa.setDimension(ChartDimension.THREE_DIMENSIONAL_LITERAL);


It throws a runtime exception as
org.eclipse.birt.chart.exception.ChartException
at org.eclipse.birt.chart.factory.Generator.build(Generator.java:1020)
at org.eclipse.birt.chart.reportitem.ChartReportItemPresentationBase.buildChart(ChartReportItemPresentationBase.java:926)
at org.eclipse.birt.chart.reportitem.ChartReportItemPresentationBase.onRowSets(ChartReportItemPresentationBase.java:799)
at org.eclipse.birt.chart.reportitem.ChartReportItemPresentationProxy.onRowSets(ChartReportItemPresentationProxy.java:116)
at org.eclipse.birt.report.engine.presentation.LocalizedContentVisitor.processExtendedContent(LocalizedContentVisitor.java:964)
at org.eclipse.birt.report.engine.presentation.LocalizedContentVisitor.localizeForeign(LocalizedContentVisitor.java:553)
at org.eclipse.birt.report.engine.presentation.LocalizedContentVisitor.localize(LocalizedContentVisitor.java:161)
at org.eclipse.birt.report.engine.internal.executor.l18n.LocalizedReportItemExecutor.execute(LocalizedReportItemExecutor.java:37)
at org.eclipse.birt.report.engine.layout.html.HTMLBlockStackingLM.layoutNodes(HTMLBlockStackingLM.java:65)
at org.eclipse.birt.report.engine.layout.html.HTMLPageLM.layout(HTMLPageLM.java:90)
at org.eclipse.birt.report.engine.layout.html.HTMLReportLayoutEngine.layout(HTMLReportLayoutEngine.java:99)
at org.eclipse.birt.report.engine.api.impl.RunAndRenderTask.doRun(RunAndRenderTask.java:170)
at org.eclipse.birt.report.engine.api.impl.RunAndRenderTask.run(RunAndRenderTask.java:75)

John Ward said...

Basically, what is happening is that 3D charts require particular formatting, separate from 2d charts. 3D charts require an additional axis that is not required in 2D charts. You will want to check your chart type, and if it is 3D, use something like the following to format the axis correctly:

//get chart element handle called "PieChart"
ExtendedItemHandle eih = (ExtendedItemHandle) design.getDesignHandle()
.getDesignHandle().findElement("PieChart");

//get the actual chart instance since Charts are extended item handles
ChartWithAxes cwaBar = (ChartWithAxes) eih.getReportItem().getProperty(
"chart.instance");

//set title of chart to New Label
cwaBar.getTitle().getLabel().getCaption().setValue("New Label");

//set 2d with depth
cwaBar.setDimension(ChartDimension.THREE_DIMENSIONAL_LITERAL);

// X-Axis
Axis xAxisPrimary = cwaBar.getPrimaryBaseAxes( )[0];
xAxisPrimary.setType( AxisType.TEXT_LITERAL );
xAxisPrimary.getMajorGrid( ).setTickStyle( TickStyle.BELOW_LITERAL );
xAxisPrimary.getOrigin( ).setType( IntersectionType.MIN_LITERAL );

// Y-Axis
Axis yAxisPrimary = cwaBar.getPrimaryOrthogonalAxis( xAxisPrimary );
yAxisPrimary.getMajorGrid( ).setTickStyle( TickStyle.LEFT_LITERAL );
yAxisPrimary.setType( AxisType.LINEAR_LITERAL );
yAxisPrimary.getLabel( ).getCaption( ).getFont( ).setRotation( 90 );

// Z-Axis
Axis zAxis = AxisImpl.create( Axis.ANCILLARY_BASE );
zAxis.setType( AxisType.TEXT_LITERAL );
zAxis.setLabelPosition( Position.BELOW_LITERAL );
zAxis.setTitlePosition( Position.BELOW_LITERAL );
zAxis.getMajorGrid( ).setTickStyle( TickStyle.BELOW_LITERAL );
zAxis.setOrientation( Orientation.HORIZONTAL_LITERAL );
xAxisPrimary.getAncillaryAxes( ).add( zAxis );

Kiran Vaidya said...

The code works perfect :) . You are a true BIRT Evangelist. Thanks a lot. I hope I get to work under someone like you in the future.

Now one last query (And i promise its the last doubt ;) )

I want to change the subtype of the chart. So at design time I have a Bar graph with subtype as "Side-by-side". Now I want to change it to "Percent Stacked" at runtime through Java code.

I tried with cwaBar.setSubType( "Percent Stacked" ); but it ofcourse didnt work since I am sure I need to set a few more parameters which I am not sure of.

Thanks in advance,

Kiran

Roxanne said...

Thank you very much for sharing this.

You make me win a lot of time.