Side Of Software

 

 

Home

 

Contact Us

 

 

 Products

 

Dated Collections

 

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