Side of Software

 

 

Home

 

Contact Us

 

 

 Products

 

Dated Collections

 

Marker Bar

 

Persistence

 

Print Preview

 

Reports

 

FAQs

 

Features

Tutorial

 

API (javadocs)

 

Wizards

Report Library Tutorial

Table of Contents

Introduction

The Report’s Content

The Report

The Report Pane

Putting It Together

The Report’s Appearance

Attributes

Table Attributes

Report Themes

Attribute Resolution

Object Rendering

Putting It Together

The Report’s Behavior

Selection Model

Editor Model

Putting It Together

Advanced Features

Nested Reports

Cell Straddling

Putting It Together

Miscellaneous Features

Printing Reports

Exporting Reports

More Examples

Introduction

The Report Library is a report generation framework for Java. It includes a Swing component to view, edit, and print reports. This component, JReportPane, is a cross between Swing’s JTable and JTextPane. It is similar to JTable in its use of objects, renderers, and editors, while it resembles JTextPane in its use of attribute sets, editor kits, and text highlighters.

 

The library consists of the following main components:

 

Report – A report is a visualization of an object. It breaks the object into hierarchical elements and associates attributes with each element. These attributes describe visual properties, such as the font, border, and background.

Theme – A report theme holds the report’s attributes. The theme is kept separate from the content, making it easy to switch themes at any time.

Report Template – A report template generates a report, given the report content and a theme. The library provides a table template that is flexible enough to produce most needed reports.

Report Element – A hierarchical portion of the report. The library includes four types of elements: Object, Table, Tier, and Cell.

Report Pane – A Swing component that can render a report as a single, continuous view. The report pane also provides methods to break the report into multiple pages.

Report Selection Model – A model that controls which elements the user can select in the report pane.

Report Editor Model – A model that controls how the user edits the report in the report pane.

 

To incorporate a report into an application, you must generally:

                                                                    

  1. Create the report
  2. Create an instance of JReportPane to visualize the report
  3. Specify the selection model (if applicable)
  4. Specify the edit model (if applicable)

 

This tutorial steps you through the creation of a simple report. This simple report is a table that lists family names down the left side and censuses across the top. A value of true at a row-column intersection indicates that the family has been found in the census. The last column leaves room for notes. One can imagine this report being useful to a genealogist.

 

This tutorial is broken into four steps—each step adding functionality to the previous step. If you would like to jump directly to the source code, it can be found here:

 

  1. TutorialExample1. Bare report.
  2. TutorialExample2. Formatted report.
  3. TutorialExample3. Formatted, selectable, and editable report.
  4. TutorialExample4. Formatted, selectable, and editable report with a title and cell straddling.

The Report’s Content

It is the programmer’s responsibility to provide the content of the report. Ideally, the content should come directly from the application’s domain model or a database; however, in this sample application, we merely hard-code the content:

 

  Object[][] data = new Object[][] {
    { "Family", "1850 Census", "1860 Census", "1870 Census", "Notes" },
    { "Smith", Boolean.TRUE, Boolean.FALSE, Boolean.FALSE, "" },
    { "Johnson", Boolean.FALSE, Boolean.TRUE, Boolean.TRUE, 
      "In 1860 spelled Johnssen" },
    { "Russell", Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, "" },
    { "Grant", Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, 
      "Not found in Springfield" },
    { "Baker", Boolean.TRUE, Boolean.FALSE, Boolean.TRUE, "" },
  };
    

We choose to store the content in a two-dimensional array so that we can use TableReportTemplate to easily generate a report with a table. If the content is not in the form of an array, list, list model, map, or table model, then we must do some work either to translate our data into one of these forms or to create a custom report template.

The Report

We create the report by invoking the createReport factory method on an instance of ReportTemplate. In this sample application, we use TableReportTemplate as our instance of ReportTemplate:

 

  TableReportTemplate template = new TableReportTemplate( 
    "Census Template", 1, 1, 0, 0 );
  Report report = template.createReport( data );
 

TableReportTemplate converts the array into a table report. It produces a table element with six row elements, five column elements, and thirty cell elements. Each cell element holds an object element that references a value in our array. It is important that we do not modify the array elsewhere in the application.

 

In the above code, the 1s passed to the constructor of TableReportTemplate tell it to treat the first row and first column as headers. Headers are often formatted specially and are repeated at the start of each page when printed. Thus, the strings “Family,” “1850 Census,” “1860 Census,” “1870 Census,” and “Notes” make up the header row, while the strings “Family,” “Smith,” “Johnson,” “Russell,” Grant,” and Baker” form the header column. The 0s in the constructor signify that no rows or columns serve as footer, or summary, tiers. (The library uses the term tier to denote either a row or a column.)

 

In the above code, the template uses a default theme that describes the report’s appearance. We will specify a custom theme in a later section.

The Report Pane

Once we have created the report, we place it in a report pane and scroll pane:

    
  JReportPane reportPane = new JReportPane( report );
  JScrollPane scrollPane = new JScrollPane( reportPane );
 

The report pane creates views that are responsible for rendering the report. Since the report may wish to be larger than its container (especially if the window is resized), we place the report pane in a scroll pane.

Putting It Together

The complete program, up to this point, is

 

import javax.swing.*;
import sos.reports.*;
 
public class TutorialExample
{
  public static void main( String[] args )
  {
    SwingUtilities.invokeLater( new Runnable() {
      public void run()
      {
        Object[][] data = new Object[][] {
          { "Family", "1850 Census", "1860 Census", "1870 Census", "Notes" },
          { "Smith", Boolean.TRUE, Boolean.FALSE, Boolean.FALSE, "" },
          { "Johnson", Boolean.FALSE, Boolean.TRUE, Boolean.TRUE, 
            "In 1860 spelled Johnssen" },
          { "Russell", Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, "" },
          { "Grant", Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, 
            "Not found in Springfield" },
          { "Baker", Boolean.TRUE, Boolean.FALSE, Boolean.TRUE, "" },
        };
 
        TableReportTemplate template = new TableReportTemplate( 
          "Census Template", 1, 1, 0, 0 );
        Report report = template.createReport( data );
 
        JReportPane reportPane = new JReportPane( report );
        JScrollPane scrollPane = new JScrollPane( reportPane );
        JFrame frame = new JFrame( "Census Report" );
        frame.getContentPane().add( scrollPane );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setSize( 500, 300 );
        frame.show();
      }
    } );
  }
}

 

We have taken a two-dimensional array of data and produced the following report

 

 

As you can see, it only takes a few lines of code to produce a report. It takes a little more effort to get it to look and behave exactly as desired. In the next section, we spice up the report’s appearance by specifying visual attributes and a theme. Then we allow the user to edit and interact with the report.

The Report’s Appearance

The appearance of the above report is not terribly exciting because the default theme for TableReportTemplate is empty. That is, it does not specify how the table should look. By explicitly associating visual attributes with the table element, we can improve its appearance. Specifically, we will add grid lines to our table, color the header row yellow, and display the true/false values as checkboxes.

Attributes

The Report Library uses attributes to determine how to render a report. Attributes are used in the same way that attributes are used in Swing’s text framework. In fact, the same style constants are recognized:

 

Attribute

Default Value

Inherited

Description

Alignment

ALIGN_LEFT

Yes

Paragraph alignment.

Background

Color.BLACK

No

Background color.

Bold

FALSE

Yes

Text appears in bold.

FirstLineIndent

0.0

Yes

Amount to indent the first line of a paragraph.

FontFamily

Monospaced

Yes

Name of the character font.

FontSize

12.0

Yes

Font size.

Foreground

Color.BLACK

Yes

Text color.

Italic

FALSE

Yes

Text is angled.

LeftIndent

0.0

Yes

Left indentation of text.

LineSpacing

0.0

Yes

Line spacing.

RightIndent

0.0

Yes

Right indentation of text.

SpaceAbove

0.0

Yes

Space above a paragraph.

SpaceBelow

0.0

Yes

Space below a paragraph.

StrikeThrough

FALSE

Yes

Text has a line through it.

Subscript

FALSE

Yes

Text is subscript.

Superscript

FALSE

Yes

Text is superscript.

TabSet

null

Yes

The positioning of tabs.

Underline

FALSE

Yes

Text is underlined.

 

Each of these attributes can be set by invoking the appropriate static method in javax.swing.text.StyleConstants, passing the attribute set and the attribute value. Thus, to set the text in the header row and column to be bold, we write

 

  StyleConstants.setBold( headerAttributes, true );

 

and to set the font family for the entire table, we write

 

  StyleConstants.setFontFamily( tableAttributes, "San Serif" );

 

where headerAttributes and tableAttributes are the appropriate header and table attribute sets, respectively.

 

In addition to the attributes defined in StyleConstants, the library recognizes the following new attributes:

 

Attribute

Default Value

Inherited

Description

AllCaps

FALSE

Yes

Characters appear in uppercase.

AutoFit

FALSE

Yes

Text is sized so that it fits in its container.

BackgroundFill

Fill.NONE

No

The background coloring.

BorderFill

Fill.BLACK

No

The left, right, top, and bottom border coloring.

BorderThickness

0.0

No

The left, right, top, and bottom border thickness.

BottomBorderFill

Fill.BLACK

No

The coloring of the bottom border.

BottomBorderThickness

0.0

No

The height of the bottom border.

BottomPadding

0.0

No

The bottom margin.

ColumnCushionAfter

0.0

No

The space after a column.

ColumnCushionBefore

0.0

No

The space before a column.

FillProportion

1

No

The relative proportion of the excess space that a row or column should assume.

Format

DEFAULT_FORMAT

No

The object that converts an object to text.

HorizontalAlignment

ALIGN_JUSTIFIED

Yes

The horizontal alignment of cell contents.

HorizontalGridFill

Fill.BLACK

No

The coloring of horizontal grid lines.

HorizontalGridThickness

0.0

No

The height of horizontal grid lines.

KeptTogetherHorizontally

FALSE

No

Element can break horizontally across pages.

KeptTogetherVertically

FALSE

No

Element can break vertically across pages.

KeptWithNext

FALSE

No

Row or column will appear on the same page as the next one

KeptWithPrevious

FALSE

No

Row or column will appear on the same page as the previous one

LeftBorderFill

Fill.BLACK

No

The coloring of the left border.

LeftBorderThickness

0.0

No

The width of the left border.

LeftPadding

0.0

No

The left margin.

MinimumHeight

0.0

No

The minimum height.

MinimumWidth

0.0

No

The minimum width.

OrphanColumnCount

0

No

The minimum number of columns before a page break is allowed.

OrphanRowCount

2

No

The minimum number of rows before a page break is allowed.

Padding

0.0

No

The left, right, top, and bottom margin.

PageBreak

FALSE

No

A forced page break.

Renderer

null

Yes

The object that renders the report data.

ReportTemplate

null

Yes

The layout of a nested report.

RightBorderFill

Fill.BLACK

No

The coloring of the right border.

RightBorderThickness

0.0

No

The width of the right border.

RightPadding

0.0

No

The right margin.

Rotation

0.0

Yes

The angle of rotation of the rendering (in radians).

RowCushionAfter

0.0

No

The space after a row.

RowCushionBefore

0.0

No

The space before a row.

TableFormat

null

No

The table format.

Theme

EMPTY_TABLE_STYLE

Yes

The appearance of a nested report.

ToolTip

null

No

The text that appears when the mouse is over the element.

TopBorderFill

Fill.BLACK

No

The coloring of the top border.

TopBorderThickness

0.0

No

The height of the top border.

TopPadding

0.0

No

The top margin.

VerticalAlignment

ALIGN_JUSTIFIED

Yes

The vertical alignment of cell contents.

VerticalGridFill

Fill.BLACK

No

The coloring of vertical grid lines.

VerticalGridThickness

0.0

No

The width of vertical grid lines.

WidowColumnCount

0

No

The minimum number of columns after a page break.

WidowRowCount

2

No

The minimum number of rows after a page break.

Wrapped

TRUE

Yes

Text is multi-lined.

 

The class ReportStyleConstants provides type-safe methods to get and set these attributes. For example, to fill our header row with yellow, we write

 

  ReportStyleConstants.setBackgroundFill( headerRowAttributes, 
    Fill.YELLOW );

 

(We recommend using the BackgroundFill attribute, which can handle gradients and multiple colors, instead of StyleConstants.Background.) The following lines of code give our table a grid and a border.

 

  ReportStyleConstants.setHorizontalGridThickness( tableAttributes, 1.0 );
  ReportStyleConstants.setVerticalGridThickness( tableAttributes, 1.0 );
  ReportStyleConstants.setBorderThickness( tableAttributes, 1.0 );

 

The default table-sizing algorithm tries to give all rows and columns their preferred sizes. If there is space left over, it gives them an equal proportion of leftover space. The FillProportion attribute controls how the excess space is divided among the tiers. If our table had excess space 80 and if the column fill proportions were 2, 1, 1, 1, and 3, respectively, then the columns would be given 20, 10, 10, 10, and 30 units of the remaining space. In our application, we want only the last column to take on extra space, so we write

 

  ReportStyleConstants.setFillProportion( tierAttributes, 0 );
  ReportStyleConstants.setFillProportion( lastColumnAttributes, 1 );

 

to indicate that all but the last column have 0 as their fill proportion. We say more about table attributes and attribute resolution in the following sections.

Table Attributes

There is a special attribute, TableFormat, that holds the attributes for an entire table. Having attributes grouped together makes it easy to switch to a different table appearance. The table appearance is controlled by the TableFormat interface, which returns attribute sets for a given table, row, column, or cell.

 

  public interface TableFormat
  {
    public AttributeSet getTableAttributes( TableElement tableElement );
    public AttributeSet getTierAttributes( TierElement tierElement );
    public AttributeSet getCellAttributes( CellElement cellElement );
  }

 

The library provides three factory methods defined in TableFormats to simplify the creation of table formats.

 

  public static TableFormat createColumnOrientedTableFormat(
    AttributeSet[] headerAttributes,
    AttributeSet[] bodyAttributes,
    AttributeSet[] footerAttributes );
  
  public static TableFormat createRowOrientedTableFormat(
    AttributeSet[] headerAttributes,
    AttributeSet[] bodyAttributes,
    AttributeSet[] footerAttributes );
 
  public static TableFormat createCompositeTableFormat(
    TableFormat tableFormat1,
    TableFormat tableFormat2 );

 

The first two methods generate a table format given attribute sets for header tiers, body tiers, and footer tiers. Each parameter is an array so the attribute sets are applied in a cyclic fashion. The third factory method creates a table style that is the composition of two other table styles.

 

Another useful class in the library is DefaultTableFormat, which we describe here and use in our tutorial application. DefaultTableFormat implements TableFormat and stores attribute sets for various levels. It allows us to associate attributes with certain parts of a table (for example, Cell 0,3 or Row 5 or Cells in the column header).

 

Our example uses DefaultTableFormat to handle the table’s appearance:

 

  DefaultTableFormat defaultTableFormat = new DefaultTableFormat();
        

We define and set the table attributes

 

  // the table's attributes
  MutableAttributeSet tableAttributes = new SimpleAttributeSet();
  // ... define the table attributes
  defaultTableFormat.setTableAttributes( tableAttributes );
        

the attributes for the column’s non-header and non-footer cells

 

  // use a checkbox renderer for columns 2, 3, and 4
  MutableAttributeSet bodyAttributes = new SimpleAttributeSet();
  // ... define the attributes for columns 2, 3, and 4
  defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
    1, DefaultTableFormat.BODY );
  defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
    2, DefaultTableFormat.BODY );
  defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
    3, DefaultTableFormat.BODY );
        

the tier attributes

 

  // do not let a row or column be wider than its preferred size
  MutableAttributeSet tierAttributes = new SimpleAttributeSet();
  // ... define the attributes for all rows and columns
  defaultTableFormat.setTierAttributes( tierAttributes );
        

the last column’s attributes

 

  // the last column can wrap and should take up any extra space
  MutableAttributeSet lastColumnAttributes = new SimpleAttributeSet();
 // ... define the attributes for the last column
  defaultTableFormat.setColumnAttributes( lastColumnAttributes, -1 );
        

and the header row and column attributes

 

  // text in the header row and column is bold
  MutableAttributeSet headerAttributes = new SimpleAttributeSet();
  // ... define the attributes for the header rows and columns
 
  // the header row is yellow
  MutableAttributeSet headerRowAttributes = new SimpleAttributeSet();
  // ... define the attributes for the header rows
  
  defaultTableFormat.setRowAttributes( headerRowAttributes, 
    DefaultTableFormat.HEADER );
  defaultTableFormat.setColumnAttributes( headerAttributes, 
    DefaultTableFormat.HEADER );

 

Once we have created our table format, we must associate it with the report’s table element. The next section describes how a report theme accomplishes this.

Report Themes

In order to generate a report, a report template needs a theme. The theme is a named collection of styles that the report may use. (Recall that a style in Swing is merely a named attribute set.) The Theme interface is

 

  public interface Theme
  {
    String getName();
    Style getStyle( String name );
    Set getStyleNames();
  }

 

All report templates are required to provide a default theme. If you choose not to use the default theme, you must supply your own either by defining a class that implements Theme directly or by using DefaultTheme to convert a javax.swing.text.StyleContext into a Theme.

 

Generally, it is up to the report template as to what style names are required or acceptable. TableReportTemplate, which we are using in our sample program, allows the table model to specify the style names to associate with each element. Since our example uses a two-dimensional array instead of a table model, TableReportTemplate resorts to the following naming convention:

 

Style Name

Applicable Element

“Table”

The table element.

“Row” + i

The ith row.

“Column” + i

The ith column.

“Cell” + i + “,” + j

The cell in row i, column j.

“Cell” + i + “,” + j  + “Object”

The contents of cell i,j.

 

For example, when rendering a table, the library searches the theme for a style named “Table.” When rendering the cell content in row 6, column 5, the library use the style named “Cell6,5Object.”

 

Our example makes use of this naming convention. It creates a style named “Table” and defines the TableFormat attribute for this style:

 

  StyleContext styleContext = new StyleContext();
  Style tableStyle = styleContext.addStyle( "Table", null );
  ReportStyleConstants.setTableFormat( tableStyle, defaultTableFormat );
  Theme theme = new DefaultTheme( "Default Table Report Template Theme", 
    styleContext );
 

Here is a diagram of the styles and attributes used in our sample application:

 

 

 

In summary, we have a report that consists of a hierarchy of elements and a theme. The table element’s attributes are those referred to as “Table” in the theme. This attribute set (in this case a Style) contains a single attribute, TableFormat, which refers to an instance of DefaultTableFormat that has attributes defined for various parts of the table.

Attribute Resolution

It is important to understand how the library resolves attributes. The rendering process looks for an attribute in the attribute set associated with the report element. If it does not find the attribute, it looks in the appropriate place in the table format and then checks the parent element’s set. The process continues until it finds the attribute in question. If no attribute set includes the attribute, the default value is used.

 

Specifically, the following pseudo-algorithm shows the order that attribute sets are searched:

 

  RESOLVE_ATTRIBUTE(Element element):
1.             Call RETRIEVE( element )
2.             If cell element and inherited attribute, RETRIEVE( cell’s first row )
3.             If cell element and inherited attribute, RETRIEVE( cell’s first column )
4.             If inherited attribute, RESOLVE_ATTRIBUTE( element’s parent )
 
  RETRIEVE(Element element):
A.            Check element’s attribute set (and the set’s resolve parent recursively)
B.            If cell element, check its enclosing table format (if any)
C.            If tier element, check its enclosing table format (if any)
D.            If table element, check its table format (if any)

 

In our example, text in the last column should wrap so the user does not have to scroll horizontally to read extremely long notes. All other text in the report should appear as a single line.  We achieve this by setting the Wrapped attribute to false for the table attributes in the DefaultTableFormat and to true for the last column’s attributes:

 

  ReportStyleConstants.setWrapped( tableAttributes, false );
  ReportStyleConstants.setWrapped( lastColumnAttributes, true );

 

Let’s examine the resolution order to see why this works. When the library wants to know if it should allow the text “Not found in Springfield” to span multiple lines, it first checks the object element’s attribute set. It discovers that there is no style named “Cell4,4Object” in the theme. Thus, the attribute set associated with the element (Step A) is empty. Steps B – D do not apply since the element is an object element. Similarly, Steps 2 and 3 do not apply. Since the Wrapped attribute is inherited, Step 4 is applicable, and the steps are repeated on the parent element, which in this case is a cell element.

 

The library finds no style named “Cell4,4” in the theme, so Step A does not find the attribute. Step B is applicable, so it searches for the enclosing table format. In the parent table element, it finds our DefaultTableFormat stored with the TableFormat attribute in the style named “Table.” It invokes getCellAttributes on this TableFormat object, passing the cell element in question. Since no attributes have been defined for cells in row 4 or column 4, the table format returns an empty attribute set. Moving to Step 2, the rendering process checks the row containing the cell. Since there is no style named “Row4,” the enclosing table format is consulted via a call to getTierAttributes. Since our DefaultTableFormat does not define the Wrapped attribute for row 4, the resolution continues. Moving to Step 3, it checks the column containing the cell. There is no style named “Column4”, so it invokes getTierAttributes on our DefaultTableFormat. Since column 4 is the last column in the table, the returned attribute set contains the Wrapped attribute. It has the value of true, so the rendering process allows the text to wrap.

 

Now suppose we want to know if the text “Johnson” should wrap. The same logic as before applies, however, when it invokes getTierAttributes on column 0, it finds no Wrapped attribute and continues. It examines the cell’s parent, which is the table element (Step 4). The Wrapped attribute does not appear In the style named “Table” so it consults the table format. The call to getTableAttributes on our DefaultTableElement returns an attribute set that has false for the Wrapped attribute, and the rendering process does not wrap the text. Had we not set this attribute to false, the resolution would have exhausted its possibilities and used the default value of true.

 

We leave it as an exercise to trace the resolution of the other attributes in our example. There are often many ways to achieve the same visual appearance.

Object Rendering

An object in the report may be rendered as a nested report, with a custom renderer, or as text—in that order of precedence.

                                                                    

Rendering as a Nested Report. An object is rendered as a nested report if the ReportTemplate attribute resolves, using the procedure outlined above, to a non-null template. Using this template, a nested report is generated at runtime. The theme it uses is the one defined by the Theme attribute, if any. In a later section, we show how to add a nested report to our example.

 

Rendering with an Element Renderer. If no template is found, then the rendering process looks for an element renderer associated with the Renderer attribute. If a renderer is found, then the renderer is given the responsibility of drawing the object. In our example, we can render the true and false values as checkboxes:

 

  ReportStyleConstants.setRenderer( bodyAttributes, 
    new CheckBoxElementRenderer() );

 

where CheckBoxElementRenderer is a custom element renderer given by

 

  class CheckBoxElementRenderer implements ElementRenderer
  {
    // if the value is null, we'll use a blank label; otherwise, we'll
    // use a checkbox
    static final private Component BLANK = new JLabel();
    private JCheckBox checkBox = new JCheckBox();
 
    public CheckBoxElementRenderer()
    {
      super();
      checkBox.setHorizontalAlignment( JLabel.CENTER );
      checkBox.setOpaque( false );
    }
 
    public Component getElementRendererComponent(JReportPane reportPane, 
      Element element)
    {
      Boolean object = (Boolean)element.getObject();
      
      // don't use a checkbox if null
      if( object == null )
        return BLANK;
      
      // initialize the checkbox appropriately
      boolean selected = object != null && object.booleanValue();
      checkBox.setSelected( selected );
      return checkBox;
    }
  }

 

The renderer must implement ElementRenderer and define the getElementRendererComponent method to return the Swing or AWT component that does the actual painting. In this case, we use an instance of the standard JCheckBox.

 

Rendering as Text. If no template or renderer is found, then the object is rendered as text. The text is gotten by feeding the object into the java.text.Format object associated with the Format attribute. The default Format object simply invokes toString on the object. This is useful for formatting numbers and dates, for example.

Putting It Together

The complete program, with a custom theme, is

 

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import sos.reports.*;
 
public class TutorialExample2
{
  public static void main(String[] args)
  {
    SwingUtilities.invokeLater( new Runnable() {
      public void run()
      {
        Object[][] data = new Object[][] {
          { "Family", "1850 Census", "1860 Census", "1870 Census", "Notes" },
          { "Smith", Boolean.TRUE, Boolean.FALSE, Boolean.FALSE, "" },
          { "Johnson", Boolean.FALSE, Boolean.TRUE, Boolean.TRUE, 
            "In 1860 spelled Johnssen" },
          { "Russell", Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, "" },
          { "Grant", Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, 
            "Not found in Springfield" },
          { "Baker", Boolean.TRUE, Boolean.FALSE, Boolean.TRUE, "" },
        };
 
        // create the table format
        DefaultTableFormat defaultTableFormat = new DefaultTableFormat();
        
        // the table's attributes
        MutableAttributeSet tableAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setHorizontalGridThickness( tableAttributes, 1.0 );
        ReportStyleConstants.setVerticalGridThickness( tableAttributes, 1.0 );
        StyleConstants.setFontFamily( tableAttributes, "San Serif" );
        ReportStyleConstants.setBorderThickness( tableAttributes, 1.0 );
        ReportStyleConstants.setWrapped( tableAttributes, false );
        defaultTableFormat.setTableAttributes( tableAttributes );
        
        // use a checkbox renderer for columns 2, 3, and 4
        MutableAttributeSet bodyAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setRenderer( bodyAttributes, 
          new CheckBoxElementRenderer() );
        defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
          1, DefaultTableFormat.BODY );
        defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
          2, DefaultTableFormat.BODY );
        defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
          3, DefaultTableFormat.BODY );
        
        // do not let a row or column be wider than its preferred size
        MutableAttributeSet tierAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setFillProportion( tierAttributes, 0 );
        defaultTableFormat.setTierAttributes( tierAttributes );
        
        // the last column can wrap and should take up any extra space
        MutableAttributeSet lastColumnAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setWrapped( lastColumnAttributes, true );
        ReportStyleConstants.setFillProportion( lastColumnAttributes, 1 );
        defaultTableFormat.setColumnAttributes( lastColumnAttributes, -1 );
        
        // text in the header row and column is bold
        MutableAttributeSet headerAttributes = new SimpleAttributeSet();
        StyleConstants.setBold( headerAttributes, true );
 
        // the header row is yellow
        MutableAttributeSet headerRowAttributes = new SimpleAttributeSet();
        headerRowAttributes.setResolveParent( headerAttributes );
        ReportStyleConstants.setBackgroundFill( headerRowAttributes, 
          Fill.YELLOW );
        
        defaultTableFormat.setRowAttributes( headerRowAttributes, 
          DefaultTableFormat.HEADER );
        defaultTableFormat.setColumnAttributes( headerAttributes, 
          DefaultTableFormat.HEADER );
 
        StyleContext styleContext = new StyleContext();
        Style tableStyle = styleContext.addStyle( "Table", null );
        ReportStyleConstants.setTableFormat( tableStyle, defaultTableFormat );
        Theme theme = new DefaultTheme( "Default Table Report Template Theme", 
          styleContext );
        
        TableReportTemplate template = new TableReportTemplate( 
          "Census Template", 1, 1, 0, 0 );
        Report report = template.createReport( data, theme );
 
        JReportPane reportPane = new JReportPane( report );
        JScrollPane scrollPane = new JScrollPane( reportPane );
        JFrame frame = new JFrame( "Census Report" );
        frame.getContentPane().add( scrollPane );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setSize( 500, 300 );
        frame.show();
      }
    } );
  }
} 

 

Now our report looks more elegant:

 

The Report’s Behavior

Reports produced with this library are unique in that they do not have to be read-only. If we install a selection model, the user can interact with the report by selecting elements; if we install an editor model, the user can edit the content. In this section, we enhance our sample application by allowing the user to select rows and cells and by making most of the cells editable.

Selection Model

The selection model determines what report elements the user can select. By default, the report pane allows no selection; however, we can install an alternate model with the code:

                                                                                                                                                                       

  reportPane.setSelectionModel( selectionModel );

 

where selectionModel is our desired selection model.

 

The library provides four types of selection models: cell selection, row and column selection, composite selection, and template-based selection. These selection models should meet most applications’ needs. If not, you can create a new selection model by subclassing AbstractSelectionModel or one of these models. This tutorial demonstrates the use of all of these models.

 

Cell Selection. CellReportSelectionModel gives the user the ability to select table cells or the cell content. By default, with this model, the cell content is selectable, rather than the cell itself. In our example, the user cannot distinguish between the cell and the cell content because they occupy the same space. Technically, however, they are different—the cell element is the content’s parent.

 

  ReportSelectionModel cellSelectionModel = new CellReportSelectionModel();
 

CellReportSelectionModel gives selection similar to JTable selection. In both the row and column directions, the client can specify multiple-interval, single-interval, or single selection. If we wanted to restrict the user to single selection, for example, we can invoke setSelectionMode(int,int) on both rows and columns. In addition, cells in header and footer tiers are not selectable by default. If we wanted headers to be selectable, we can use setHeaderSelectable(int,boolean).

 

Tier Selection. TierReportSelectionModel gives users the ability to select table rows or columns. Resembling JList selection, this model gives clients the ability to specify multiple-interval, single-interval, or single selection.

 

When you create a tier selection model, you specify either rows or columns. In our example, we enable row selection, which may be useful for the user to insert or delete family names.

 

  ReportSelectionModel rowSelectionModel = new TierReportSelectionModel( 
    TableElement.ROW );

 

Composite Selection. CompositeReportSelectionModel gives you the ability to combine selection models into one selection model. In our example, we want both row and cell selection, so we write

 

  CompositeReportSelectionModel selectionModel = 
    new CompositeReportSelectionModel();
  selectionModel.addSelectionModel( cellSelectionModel );
  selectionModel.addSelectionModel( rowSelectionModel );

 

Since we add cell selection first, it takes precedence over row selection. You can apply a similar technique to add column selection. It is this composite selection model that is installed in the report pane. When the user selects an element in the first column, the row is highlighted. When the user selects other cells, only the cell is highlighted. Cells in the header row, however, are not selectable.

 

Template-Based Selection. The fourth selection model, TemplateReportSelectionModel, chooses a selection model based on the template that was used to create the element being selected. This is useful only if we have nested reports, which we will add in a later section.

 

To respond to changes in the selection, regardless of the selection model, you can register a ReportSelectionListener with the selection model.

Editor Model

The editor model controls how the user can edit the report. When the user makes an editing gesture (such as double-clicking or pressing F2), the report pane first asks the report if editing is allowed and then asks the editor model for the appropriate editor. If the report returns true and the editor model returns a non-null editor, then the report pane installs an editor component, and editing begins. The editor component may be any AWT or Swing component.

 

By default, the report pane uses an editor model that does not allow any editing; however, we can install an alternative model with the call

 

  reportPane.setEditorModel( editorModel );
 

where editorModel is our desired model.

 

The library provides three types of editor models: editing based on class type, editing based on row or column index, and template-based editing. If these predefined models do not suit your needs, you can create a new editor model by subclassing AbstractReportEditorModel or one of these models.

                                                                                                                                                              

Class-Based Editor Model. The DefaultReportEditorModel class maps class names to editors. When asked for an editor, the model chooses the editor (if any) associated with the class of the object being rendered.

 

Tier Editor Model. The second editor model, TierReportEditorModel, associates rows and columns with editors. When asked for an editor, the model chooses the editor (if any) associated with the row or column containing the object being rendered. By default, objects in header or footer tiers are not editable. To change this, use setHeaderEditable and setFooterEditable.

 

Template-Based Editor Model. Finally, TemplateReportEditorModel, like its related selection model, delegates to an editor model (if any) associated with the template that created the element in question. This is useful only with nested reports.

 

In our example, we want to use a check box to edit the Boolean cells and a text field to edit the notes. Since the Boolean cells appear in columns 1, 2, and 3 and the notes appear in column 4, we can use an instance of TierReportEditorModel to map column indices to editors.

 

  TierReportEditorModel editorModel = new TierReportEditorModel( 
    TableElement.COLUMN );
  editorModel.setTierEditor( 1, checkBoxEditor );
  editorModel.setTierEditor( 2, checkBoxEditor );
  editorModel.setTierEditor( 3, checkBoxEditor );
  editorModel.setTierEditor( 4, textFieldEditor);

  

Since the editors must implement ElementEditor, the standard Swing components like JCheckBox and JTextField cannot serve as editors directly. The library’s DefaultElementEditor class, however, can convert any JCheckBox or JTextField to an element editor:

                                                                                                                                                        

  JCheckBox checkBox = new JCheckBox();
  checkBox.setOpaque( false );
  checkBox.setHorizontalAlignment( SwingConstants.CENTER );
  ElementEditor checkBoxEditor = new DefaultElementEditor( checkBox );
  
  JTextField textField = new JTextField();
  ElementEditor textFieldEditor = new DefaultElementEditor( textField );

 

We center the checkbox and make it transparent so that it matches our custom checkbox renderer.

 

After the user edits a value in the report, the new value is saved in our array. This allows us to access the updated data later, if we need to. If we need to take an action as soon as the value is set, we should use a TableModel that implements setValueAt instead of an array to hold the data.

Putting It Together

Adding a composite selection model and a column-based editor model to our application, we get

 

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import sos.reports.*;
 
public class TutorialExample3
{
  public static void main(String[] args)
  {
    SwingUtilities.invokeLater( new Runnable() {
      public void run()
      {
        Object[][] data = new Object[][] {
          { "Family", "1850 Census", "1860 Census", "1870 Census", "Notes" },
          { "Smith", Boolean.TRUE, Boolean.FALSE, Boolean.FALSE, "" },
          { "Johnson", Boolean.FALSE, Boolean.TRUE, Boolean.TRUE, 
            "In 1860 spelled Johnssen" },
          { "Russell", Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, "" },
          { "Grant", Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, 
            "Not found in Springfield" },
          { "Baker", Boolean.TRUE, Boolean.FALSE, Boolean.TRUE, "" },
        };
 
        // create the table format
        DefaultTableFormat defaultTableFormat = new DefaultTableFormat();
        
        // the table's attributes
        MutableAttributeSet tableAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setHorizontalGridThickness( tableAttributes, 1.0 );
        ReportStyleConstants.setVerticalGridThickness( tableAttributes, 1.0 );
        StyleConstants.setFontFamily( tableAttributes, "San Serif" );
        ReportStyleConstants.setBorderThickness( tableAttributes, 1.0 );
        ReportStyleConstants.setWrapped( tableAttributes, false );
        defaultTableFormat.setTableAttributes( tableAttributes );
        
        // use a checkbox renderer for columns 2, 3, and 4
        MutableAttributeSet bodyAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setRenderer( bodyAttributes, 
          new CheckBoxElementRenderer() );
        defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
          1, DefaultTableFormat.BODY );
        defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
          2, DefaultTableFormat.BODY );
        defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
          3, DefaultTableFormat.BODY );
        
        // do not let a row or column be wider than its preferred size
        MutableAttributeSet tierAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setFillProportion( tierAttributes, 0 );
        defaultTableFormat.setTierAttributes( tierAttributes );
        
        // the last column can wrap and should take up any extra space
        MutableAttributeSet lastColumnAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setWrapped( lastColumnAttributes, true );
        ReportStyleConstants.setFillProportion( lastColumnAttributes, 1 );
        defaultTableFormat.setColumnAttributes( lastColumnAttributes, -1 );
        
        // text in the header row and column is bold
        MutableAttributeSet headerAttributes = new SimpleAttributeSet();
        StyleConstants.setBold( headerAttributes, true );
 
        // the header row is yellow
        MutableAttributeSet headerRowAttributes = new SimpleAttributeSet();
        headerRowAttributes.setResolveParent( headerAttributes );
        ReportStyleConstants.setBackgroundFill( headerRowAttributes, 
          Fill.YELLOW );
        
        defaultTableFormat.setRowAttributes( headerRowAttributes, 
          DefaultTableFormat.HEADER );
        defaultTableFormat.setColumnAttributes( headerAttributes, 
          DefaultTableFormat.HEADER );
 
        StyleContext styleContext = new StyleContext();
        Style tableStyle = styleContext.addStyle( "Table", null );
        ReportStyleConstants.setTableFormat( tableStyle, defaultTableFormat );
        Theme theme = new DefaultTheme( "Default Table Report Template Theme", 
          styleContext );
        
        TableReportTemplate template = new TableReportTemplate( 
          "Census Template", 1, 1, 0, 0 );
        Report report = template.createReport( data, theme );
 
        JReportPane reportPane = new JReportPane( report );
 
        // allow cells to be selected
        ReportSelectionModel cellSelectionModel = 
          new CellReportSelectionModel();
        ReportSelectionModel rowSelectionModel = 
          new TierReportSelectionModel( TableElement.ROW );
        CompositeReportSelectionModel selectionModel = 
          new CompositeReportSelectionModel();
        selectionModel.addSelectionModel( cellSelectionModel );
        selectionModel.addSelectionModel( rowSelectionModel );
        reportPane.setSelectionModel( selectionModel );
 
        // cells with checkboxes can be clicked
        JCheckBox checkBox = new JCheckBox();
        checkBox.setOpaque( false );
        checkBox.setHorizontalAlignment( SwingConstants.CENTER );
        ElementEditor checkBoxEditor = new DefaultElementEditor( checkBox );
        
        // the notes cells can be edited
        JTextField textField = new JTextField();
        ElementEditor textFieldEditor = new DefaultElementEditor( textField );
        
        // the editor to use depends on the column
        TierReportEditorModel editorModel = new TierReportEditorModel( 
          TableElement.COLUMN );
        editorModel.setTierEditor( 1, checkBoxEditor );
        editorModel.setTierEditor( 2, checkBoxEditor );
        editorModel.setTierEditor( 3, checkBoxEditor );
        editorModel.setTierEditor( 4, textFieldEditor );
        
        // allow cells to be edited
        reportPane.setEditorModel( editorModel );
        
        JScrollPane scrollPane = new JScrollPane( reportPane );
        JFrame frame = new JFrame( "Census Report" );
        frame.getContentPane().add( scrollPane );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setSize( 500, 300 );
        frame.show();
      }
    } );
  }
}

 

After the user clicks some checkboxes and edits the notes, our report may look something like this

 

 

Here is a screen shot of the report with some cells selected:

 

Advanced Features

In the previous section, we enhanced the report pane by specifying a selection model and editor model. In this section, we enhance the report further by adding a title and by merging cells.

Nested Reports

A titled report is merely a table report with one column and two rows—a row for the title and a row for the body, where the body is a nested report. The string “Census Checklist” will serve as the report’s title, while the two-dimensional array will be the body of the report. We can create the report just as we did before, using a table report template:

 

    Object[] content = new Object[] { title, body };
    ReportTemplate template = new TableReportTemplate();
    Report report = template.createReport( content, theme );

 

Recall that the attributes control how the body is rendered. Since we wish to render it as a nested report, we must specify the report template and theme in the appropriate attribute set:

 

  ReportStyleConstants.setTemplate( bodyAttributes, bodyTemplate );
  ReportStyleConstants.setTheme( bodyAttributes, bodyTheme );

 

where bodyTemplate and bodyTheme are our original template and theme we created in the first three versions. We again store the attributes in a theme. This time, however, instead of using a table format to control the appearance of the table, we follow the naming convention described above. For the title, we create a style named “Cell0,0Object.” This style includes attributes to make the title larger, bold, and centered. For the body, we create a style named “Cell1,0Object” and define the report template and theme.

 

Since we now have a nested report, we must adjust our selection and editor models. Recall that TemplateReportSelectionModel chooses a selection model based on the name of the template used to create the report element that the report pane is trying to select. In our example, we do not want the user to be able to select any part of the outer table. Only the cells and rows of the nested table are selectable. Thus, we use a TemplateReportSelectionModel to register our original selection model with the template name “Census Template.” This name was provided in the constructor of TableReportTemplate.

 

  ReportSelectionModel selectionModel = new TemplateReportSelectionModel( 
    "Census Template", compositeSelectionModel );

 

On a similar note, our element editors are registered for columns 1, 2, 3, and 4. Since this only applies to our nested table, we use TemplateReportEditorModel to register our original editor model with “Census Template.”

 

  ReportEditorModel editorModel = new TemplateReportEditorModel( 
    "Census Template", tierEditorModel );

Cell Straddling

Tables can support cell straddling, or merged cells. In other words, a cell can span multiple rows and/or columns. The class TableReportTemplate handles cell straddling by allowing the data model to return a sentinel value to indicate if a cell belongs to the cell of the preview row or column. Specifically, when the data model returns the constant TableReportTemplate.STRADDLE_PREVIOUS_COLUMN for the value at row i, column j, the template recognizes that the cell element at row i, column j-1 should occupy this space as well. Similarly, when the data model returns the constant TableReportTemplate.STRADDLE_PREVIOUS_ROW, the template ensures that the cell in the previous row covers multiple rows. In our example, we want to move the word “Census,” which is common to the header of three of the columns, to a header row of its own.

 

  Object[][] data = new Object[][] {
    { "Family", "Census", TableReportTemplate.STRADDLE_PREVIOUS_COLUMN,
        TableReportTemplate.STRADDLE_PREVIOUS_COLUMN, "Notes" },
    { TableReportTemplate.STRADDLE_PREVIOUS_ROW, "1850", "1860", "1870",
        TableReportTemplate.STRADDLE_PREVIOUS_ROW },
    { "Smith", Boolean.TRUE, Boolean.FALSE, Boolean.FALSE, "" },
    { "Johnson", Boolean.FALSE, Boolean.TRUE, Boolean.TRUE, 
      "In 1860 spelled Johnssen" },
    { "Russell", Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, "" },
    { "Grant", Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, 
      "Not found in Springfield" },
    { "Baker", Boolean.TRUE, Boolean.FALSE, Boolean.TRUE, "" },
  };
 

With the addition of a new header row, we must inform the table template that there are two header rows instead of one.

 

  TableReportTemplate template = new TableReportTemplate( 
    "Census Template", 2, 1, 0, 0 );
 

In addition, we must change the way the header rows are colored. Row coloring does not suffice if cells span multiple rows and there are horizontal grid lines or space between the rows. We actually want to color the entire cell yellow.

Putting It Together

Adding cell straddling and a title to our application, we get the code

 

import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import sos.reports.*;
 
public class TutorialExample4
{
  public static void main(String[] args)
  {
    SwingUtilities.invokeLater( new Runnable() {
      public void run()
      {
        Object[][] data = new Object[][] {
          { "Family", "Census", TableReportTemplate.STRADDLE_PREVIOUS_COLUMN,
              TableReportTemplate.STRADDLE_PREVIOUS_COLUMN, "Notes" },
          { TableReportTemplate.STRADDLE_PREVIOUS_ROW, "1850", "1860", "1870",
              TableReportTemplate.STRADDLE_PREVIOUS_ROW },
          { "Smith", Boolean.TRUE, Boolean.FALSE, Boolean.FALSE, "" },
          { "Johnson", Boolean.FALSE, Boolean.TRUE, Boolean.TRUE, 
            "In 1860 spelled Johnssen" },
          { "Russell", Boolean.TRUE, Boolean.TRUE, Boolean.TRUE, "" },
          { "Grant", Boolean.FALSE, Boolean.FALSE, Boolean.FALSE, 
            "Not found in Springfield" },
          { "Baker", Boolean.TRUE, Boolean.FALSE, Boolean.TRUE, "" },
        };
 
        // create the table format
        DefaultTableFormat defaultTableFormat = new DefaultTableFormat();
        
        // the table's attributes
        MutableAttributeSet tableAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setHorizontalGridThickness( tableAttributes, 1.0 );
        ReportStyleConstants.setVerticalGridThickness( tableAttributes, 1.0 );
        StyleConstants.setFontFamily( tableAttributes, "San Serif" );
        ReportStyleConstants.setBorderThickness( tableAttributes, 1.0 );
        ReportStyleConstants.setWrapped( tableAttributes, false );
        defaultTableFormat.setTableAttributes( tableAttributes );
        
        // use a checkbox renderer for columns 2, 3, and 4
        MutableAttributeSet bodyAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setRenderer( bodyAttributes, 
          new CheckBoxElementRenderer() );
        defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
          1, DefaultTableFormat.BODY );
        defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
          2, DefaultTableFormat.BODY );
        defaultTableFormat.setColumnCellAttributes( bodyAttributes, 
          3, DefaultTableFormat.BODY );
        
        // do not let a row or column be wider than its preferred size
        MutableAttributeSet tierAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setFillProportion( tierAttributes, 0 );
        defaultTableFormat.setTierAttributes( tierAttributes );
        
        // the last column can wrap and should take up any extra space
        MutableAttributeSet lastColumnAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setWrapped( lastColumnAttributes, true );
        ReportStyleConstants.setFillProportion( lastColumnAttributes, 1 );
        defaultTableFormat.setColumnAttributes( lastColumnAttributes, -1 );
        
        // text in the header row and column is bold
        MutableAttributeSet headerAttributes = new SimpleAttributeSet();
        StyleConstants.setBold( headerAttributes, true );
 
        // the header row is yellow
        MutableAttributeSet headerRowCellAttributes = new SimpleAttributeSet();
        ReportStyleConstants.setBackgroundFill( headerRowCellAttributes, 
          Fill.YELLOW );
        ReportStyleConstants.setVerticalAlignment( headerRowCellAttributes,
          StyleConstants.ALIGN_CENTER );
        ReportStyleConstants.setHorizontalAlignment( headerRowCellAttributes,
          StyleConstants.ALIGN_CENTER );
        defaultTableFormat.setColumnCellAttributes( headerRowCellAttributes,
          DefaultTableFormat.HEADER );
        
        defaultTableFormat.setRowAttributes( headerAttributes, 
          DefaultTableFormat.HEADER );
        defaultTableFormat.setColumnAttributes( headerAttributes, 
          DefaultTableFormat.HEADER );
 
        StyleContext styleContext = new StyleContext();
        Style tableStyle = styleContext.addStyle( "Table", null );
        ReportStyleConstants.setTableFormat( tableStyle, defaultTableFormat );
        Theme theme = new DefaultTheme( "Default Table Report Template Theme", 
          styleContext );
        
        TableReportTemplate template = new TableReportTemplate( 
          "Census Template", 2, 1, 0, 0 );
        Report report = makeTitledReport( "Census Checklist", data, 
          template, theme );
 
        JReportPane reportPane = new JReportPane( report );
 
        // allow cells to be selected
        ReportSelectionModel cellSelectionModel = new CellReportSelectionModel();
        ReportSelectionModel rowSelectionModel = new TierReportSelectionModel( 
          TableElement.ROW );
        CompositeReportSelectionModel compositeSelectionModel = 
          new CompositeReportSelectionModel();
        compositeSelectionModel.addSelectionModel( cellSelectionModel );
        compositeSelectionModel.addSelectionModel( rowSelectionModel );
        ReportSelectionModel selectionModel = new TemplateReportSelectionModel(
          "Census Template", compositeSelectionModel );
        reportPane.setSelectionModel( selectionModel );
 
        // cells with checkboxes can be clicked
        JCheckBox checkBox = new JCheckBox();
        checkBox.setOpaque( false );
        checkBox.setHorizontalAlignment( SwingConstants.CENTER );
       ElementEditor checkBoxEditor = new DefaultElementEditor( checkBox );
        
        // the notes cells can be edited
        JTextField textField = new JTextField();
        ElementEditor textFieldEditor = new DefaultElementEditor( textField );
        
        // the editor to use depends on the column
        TierReportEditorModel tierEditorModel = new TierReportEditorModel( 
          TableElement.COLUMN );
        tierEditorModel.setTierEditor( 1, checkBoxEditor );
        tierEditorModel.setTierEditor( 2, checkBoxEditor );
        tierEditorModel.setTierEditor( 3, checkBoxEditor );
        tierEditorModel.setTierEditor( 4, textFieldEditor );
        
        ReportEditorModel editorModel = new TemplateReportEditorModel(
          "Census Template", tierEditorModel );
        
        // allow cells to be edited
        reportPane.setEditorModel( editorModel );
        
        JScrollPane scrollPane = new JScrollPane( reportPane );
        JFrame frame = new JFrame( "Census Report" );
        frame.getContentPane().add( scrollPane );
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
        frame.setSize( 500, 300 );
        frame.show();
      }
    } );
  }
  
  public static Report makeTitledReport( String title, Object body,
   ReportTemplate bodyTemplate, Theme bodyTheme )
  {
    StyleContext styleContext = new StyleContext();
    
    Style titleStyle = styleContext.addStyle( "Cell0,0Object", null );
    StyleConstants.setFontSize( titleStyle, 16 );
    StyleConstants.setBold( titleStyle, true );
    StyleConstants.setAlignment( titleStyle, StyleConstants.ALIGN_CENTER );
    StyleConstants.setSpaceBelow( titleStyle, 6.0f );
 
    Style rowStyle = styleContext.addStyle( "Row", null );
    ReportStyleConstants.setFillProportion( rowStyle, 0 );
    StyleConstants.setFontFamily( titleStyle, "San Serif" );
    
    // the content resides in cell 1,0
    //
    // use a nested report template and theme; default font Arial
    //
    Style bodyStyle = styleContext.addStyle( "Cell1,0Object", null );
    ReportStyleConstants.setReportTemplate( bodyStyle, bodyTemplate );
    ReportStyleConstants.setTheme( bodyStyle, bodyTheme );
    
    Theme theme = new DefaultTheme( styleContext );
    
    Object[] content = new Object[] { title, body };
    ReportTemplate template = new TableReportTemplate();
    Report report = template.createReport( content, theme );
    return report;
  }
}

 

Here is a snapshot of our final report. It illustrates straddling cells and row selection:

 

Miscellaneous Features

The following topics are not illustrated in our simple example, but they are useful.

Printing Reports

To print a report, you can use one of the following JReportPane methods

                                                    

  Pageable getFittedPageable( PageFormat pageFormat );
 
  Pageable getFittedPageable( PageFormat pageFormat,
    JReportPane.HeaderFooterFactory headerFooterFactory,
    Component underlaidWatermark,
    Component overlaidWatermark );
 
  Pageable getPageable( PageFormat pageFormat );
 
  Pageable getPageable( PageFormat pageFormat,
    JReportPane.HeaderFooterFactory headerFooterFactory,
    Component underlaidWatermark,
    Component overlaidWatermark );

 

to acquire a java.awt.print.Pageable object that has broken the report into a number of pages. All pages must be the same size. The first two methods scale the report so that it fits in the width of the page (if possible), while the latter two do no scaling. The second and fourth methods allow you to specify page headers, page footers, and watermark images. Once you have the Pageable object, you can pass it to the standard print framework for printing.

 

The Report Library does not include the ability to preview pages before printing them; however, the Print Preview Library, which is sold separately, can be used for this purpose. For more information, see the Print Preview Library’s homepage.

Exporting Reports     

The library does not come with built-in support for exporting reports. However, it is nearly trivial to generate a PDF file with the iText library, which is freely available here. The following method, export, can be used to redirect all rendering to a PDF output stream:

 

import com.lowagie.text.*;
import com.lowagie.text.pdf.*;
import java.awt.*;
import java.awt.print.*;
import java.io.*;
import sos.reports.*;
 
/**
 * A sample method that demonstrates how one may use iText,
 * available at http://www.lowagie.com/iText/,
 * to export a report as pdf. This method was tested against
 * version 1.02b.
 *
 * @author Side of Software
 */
public class PdfExportExample
{
  public static void export( JReportPane reportPane, OutputStream outputStream )
  {
    try
    {
      // create a new document
      Document document = new Document();
 
      // the document is in the PDF format
      PdfWriter writer = PdfWriter.getInstance( document, outputStream );
      
      // open the document for writing
      document.open();
 
     // use the default page format (alternative: can pass it as a parameter)
      PageFormat format = new PageFormat();
      int width = (int)format.getWidth();
      int height = (int)format.getHeight();
      
      // create the pageable object
      Pageable pagedReport = reportPane.getFittedPageable( format );
 
      DefaultFontMapper mapper = new DefaultFontMapper();
 
      // for each page
      for( int i = 0; i < pagedReport.getNumberOfPages(); i++ )
      {
        // subsequent pages must be created explicitly
        if( i > 0 )
          document.newPage();
 
        // use a graphics object that converts to pdf
        PdfContentByte cb = writer.getDirectContent();
        PdfTemplate tp = cb.createTemplate( width, height );
        Graphics2D g2 = tp.createGraphics( width, height, mapper);
        
        // fetch the page to render
        Printable printable = pagedReport.getPrintable( i );
        try
        {
          // render the page using the pdf graphics
          printable.print( g2, format, 0 );
          g2.dispose(); // crucial!
          cb.addTemplate( tp, 0, 0 );
        }
        catch( PrinterException pe )
        {
          // handle the print errors
          pe.printStackTrace();
        }
      }
      
      // close the document
      document.close();
    }
    catch( DocumentException de )
    {
      // handle exception here
      de.printStackTrace();
    }
  }
}

More Examples

Throughout these web pages, you will find a number of sample reports. The following table provides a list of the examples and links to the source code.

 

Sample Report

Illustrations

Source Code

Table formats, colors, print preview, watermarks, headers, footers, cell selection, row selection, column selection, custom renderers, text editing, checkbox editing, nested reports, cell straddling, grid lines

ReportDemo

Nested reports, custom renderers

ChartExample

Nested reports, cell straddling, colors, grid lines

ScheduleExample

Custom renderers, rotated text, grid lines

AngledTextExample

Nested report, colors, cell straddling, cell selection, row selection, custom renderers, text editing, checkbox editing, table formats, grid lines

TutorialExample4

 

 

 

Home  |  Contact Us  |  Privacy Policy

 

Copyright © 2016 Side of Software, a branch of Natavision. All rights reserved.