Chapter 34 Crystal Report Modification and Creation APIs

In this chapter

Introduction 856

Deploying RAS Environments 856

Installing the RAS SDK 856

RAS Exception Handling 856

The RAS SDK in Action 857

Introduction

This chapter covers the capability of the Report Application Server (RAS) SDK to create and modify reports. Topics include

  • RAS environments
  • Loading report files
  • RAS component locations
  • Installing the RAS SDK
  • Exception handling
  • Programming with the RAS SDK

Deploying RAS Environments

The RAS APIS are available in two different environments—as a service of the BusinessObjects Enterprise framework or as part of Crystal Reports Server. For the remainder of the chapter, any reference to functionality being provided in BusinessObjects Enterprise automatically implies that the same functionality is available in Crystal Reports Server, unless otherwise noted.

Using RAS in a BusinessObjects Enterprise (or Crystal Reports Server) Environment

BusinessObjects Enterprise provides a framework for delivering enterprise reporting. RAS adds the capability for users to modify reports stored in BusinessObjects Enterprise. In this scenario, the BusinessObjects Enterprise framework manages the RAS. Multiple instances of the RAS can be added and BusinessObjects Enterprise will load balance between them.

Installing the RAS SDK

As mentioned previously, the RAS SDK JAR files can be found in the jar folder by default. Copy the RAS and BusinessObjects Enterprise .jar files to the appropriate folder on the application server being used. If you are using Apache Tomcat, for example, move the .jar files to the Web application’s WEB-INFlib folder. Configuring a Web server to access the SDK JAR files might take additional steps, detailed in the installation help files provided with the RAS.

RAS Exception Handling

Options for displaying and logging exception information can also be specified. These tasks can be performed by modifying the web.xml file (located by default in the WEB-INF directory of your Web application) as follows.

Displaying Exceptions

Three options exist for displaying exception information to the user. Setting the crystal_exception_info parameter to one of the following values determines how exceptions are handled:

  • short—The exception information is displayed without the accompanying stack trace.
  • long—The exception information is displayed with the accompanying stack trace.
  • disable—The exception information is not displayed; the user must handle the exception.

The following code shows an example of the exception display configuration:

image

The crystal_exception_info parameter is short by default. Modifying exception.css specifies the style and formatting of short messages.

Logging Exceptions

The option to turn exception logging either on or off can be set with the crystal_exception_log_file parameter. The exception information output to the log file will be in the long format regardless of the setting of the crystal_exception_info parameter. The following code shows an example of the exception logging configuration:

image

When setting the parameter to the desired path of the log file, by default, exceptions are not logged.

The RAS SDK in Action

This section covers the common programming tasks associated with the RAS SDK. Although the SDK provides many capabilities, some of the following tasks are common to most programming exercises and are central to the SDK.

Initializing a RAS Session

Initiating a session with the RAS is the first step in programming with the RAS SDK. In this step, a specific RAS can be specified for use; otherwise, the system selects one from the RASs listed in the clientSDKOptions.xml file using a round-robin method. Initializing a RAS session by specifying a machine name at runtime is shown in the following code:

image

All ReportClientDocument objects created from the same ReportAppSession communicate with the same RAS.

Opening a Report

A report can be opened first by creating a new ReportClientDocument object and specifying the ReportAppServer. Then the open method can be used to open a report. This method takes two parameters:

  • The absolute path and filename of the report
  • A flag indicating how the file will be opened

See the OpenReportOptions class for valid report options.

Caution

Reports are loaded from the report folder found at Program FilesCrystal DecisionsReport Application Server 10Reports by default.

The following code opens a report:

image

The previous chapter explained how to view reports using RAS. Creating and modifying those reports using the RAS SDK will be the focus of the remainder of this chapter.

Adding Fields to the Report

A report can be modified after creating and opening a ReportClientDocument by using the report’s controllers. The only way to modify reports and ensure that the changes are synchronized with the server is to use controllers. Although the report’s fields can be accessed directly through the DataDefinition property, any changes made will not be committed. This section explains how to add a field to a report.

Identifying the Field to Add

A field is usually selected by name. The DatabaseController can be used to retrieve the object that represents this field given a database field’s name or its table’s name. Another method of accessing a table’s fields is using the ReportClientDocument’s Database property. Here you use the DatabaseController.

The DatabaseController contains a collection of database tables that are available to the report and might be accessed using the getDatabaseController method of ReportClientDocument. Each table contains a collection of DBField objects.

Caution

All tables and fields that are listed by DatabaseController.getDatabase() are not retrieved when the report is refreshed; that is, they are available for report design but might not actually be part of the report’s data definition.

A method called findFieldByName is shown in the following sample code snippet. This method returns a field given its fully qualified field name in the form: <TableAlias>.<FieldName>. The table alias is used as a qualifier and it is assumed that a period is used to separate the table alias from the field name.

image

This method uses the following key methods:

  • Tables.findByAlias finds the index of a particular table when given its alias. Given the index, the desired Table object can be retrieved from the collection.
  • Fields.find finds the index of a field in a table’s Fields collection when given the name of the field.
Adding a Field to the Report Document

After you obtain the Field object that you want to add, the field can be added to the report so that it is processed and displayed when the report is run. This is done via the DataDefController, which is used to modify the report’s data definition and contains a subcontroller called the ResultFieldController. This subcontroller is used for modifying fields that have been placed on the report and that are processed at runtime. The fields that are shown on the report belong to the ResultFields collection. A new database field will be added to the ResultFields collection in this step.

Caution

The ResultFields collection can contain other types of field objects such as parameter fields, formula fields, and summary fields in addition to DBField objects. Like DBFields, the ResultFieldController can add these fields to a report. Unlike DBFields, only the DatabaseDefController’s DataDefinition property, and not the DatabaseDefController’s Database property, can retrieve these fields.

A field being added to the ResultFields collection is shown by the following code:

image

The parameter 1 indicates that the field is to be placed at the end of the collection. As a result of this code, the new field displays on the report and is processed when the report is refreshed.

Determining All Fields Used in the Report

The fields that have been added to a report are stored in the ResultFields collection and can be retrieved using the following sample method:

image

With the full name of the field, you can use a DatabaseController to retrieve the DBField object.

Removing a Field from the Report

When you’ve found the field you want to remove, use the ResultFieldController to remove it as follows:

image

In this code, fieldToDelete is a DBField object. After the field is removed from the result fields using this method, the report ceases to display the field.

Creating a New Report

A new report can be created by first creating an empty ReportClientDocument as shown:

image

Caution

Because the newDocument method of ReportClientDocument is provided for deployments that use an unmanaged RAS to access report (.rpt) files, it should not be deployed when using a BusinessObjects Enterprise RAS. Instead, when deploying with BusinessObjects Enterprise, the IReportAppFactory.newDocument method should be used as in the previous code.

Because the report is not actually created until tables are added, after creating an empty ReportClientDocument, details such as the new report’s tables and the fields used to link them should be added.

Retrieving a Report’s Tables

However, before adding the tables to the new report, the table objects must first be retrieved from the source report. This can be accomplished in two ways: using the DatabaseController object and using the Database object, both of which are available from the ReportClientDocument object. The ensuing code iterates through all the tables in an open report and prints the tables’ aliases:

image

Adding Tables to the Report

Because controllers are the only objects that can modify the report’s object model, a controller must be used to add tables to a report. The following code retrieves the report’s DatabaseController and adds a table.

image

The addTable method of the DatabaseController adds a table to the report. The addTable method takes two parameters:

  • The Table object you want to add
  • A TableLinks object that defines how the table being added is linked with other tables

Linking Tables

Tables must be linked after they have been added to the report. To link two tables, first create a new TableLink object, set the properties of the TableLink, and then add the TableLink to the report definition.

Linking two tables using an equal join is illustrated by the following code:

image

These newly linked tables can be used as the report’s data source. However there have been no visible objects added to the report, so when the report is refreshed, it will be blank.

Adding Groups

To add a group, you must know which field is being grouped on. For information on working with fields, see the “Adding a Field to the Report Document” section earlier in this chapter. Because not all fields can be used to define a group, use the canGroupOn method of GroupController to check whether a field can be used for grouping. If canGroupOn returns true, the field is an acceptable field to use for grouping. The next example demonstrates a function that adds a new group to a report:

image

Here the group was added to the end of the Groups collection by setting the index to -1, which means that the new group becomes the innermost group. When a new group is added, a new sorting definition is also added which will sort the records according to the group’s condition field and group options. An additional reflection of adding the new group is the group name field appearing on the group’s header. Fields added to the group header are not added to the ResultFields collection. When the group is removed, the group name field is also removed.

Adding Sorting to the Report

Using the SortController adds a new sorting definition to a report. The SortController can add any kind of sorting definition, including a Top N sort. Adding a Top N sort requires that a summary has first been added.

Next you demonstrate how to add a sort to the report by taking a Fields collection and adding a sorting definition based on each field in the collection:

image

When the new Sort object is added, it is added to the end of the collection, indicated by the -1 argument, which designates that the records will be sorted on this field after all other sorting definitions. The SortDirection class indicates the direction of the sort. The static objects SortDirection.ascendingOrder and SortDirection.descendingOrder are the only values that can be used for a normal sort. The other values are used for a Top N or Bottom N sort. See Adding a Top N sorting definition in the SDK documentation for additional details.

Adding Summaries to the Report

The SummaryFieldController adds a new summary field. To determine if a field can produce a summary, the SummaryFieldController’s method canSummarizeOn is called. Here you add a summary to a group:

image

After creating a summary field, set the following properties before adding it:

  • SummarizedFieldThe field used to calculate the summary.
  • GroupThe group for which the summary will be calculated.
  • OperationThe operation used to calculate the summary. One of the static objects defined in the SummaryOperation class.

Working with Filters

Filters are used in record selection and group selection. The filter is initially a string written in Crystal formula syntax. The record selection formula is then parsed into an array of FilterItems stored in the Filter object’s FilterItems property. The string is broken up into data components and operator components that act on the data. These components are stored as FieldRangeFilterItem and OperatorFilterItem objects respectively, which are stored in the FilterItems collection in the same order that they appear in the formula string. Re-ordering the objects in the array changes the functionality of the formula. In summary, the FieldRangeFilterItem is an expression that is joined with other expressions using an OperatorFilterItem.

For instance, consider a simple record selection formula such as

{Customer.Name} = "Bashka Futbol" and {Customer.Country} = "USA".

This results in only the records that have a name equal to “Bashka Futbol” and a country of the USA. The result is stored in the FreeEditingText property. After this string is parsed, the FieldRangeItems collection contains two FieldRangeFilterItem objects because there are two data items used to filter the records. The OperatorFilterItem is used to indicate how two primitive expressions are combined, so it is now equal to and.

The FieldRangeFilterItem contains three properties:

  • OperationThis property indicates the operation performed in the primitive expression; in both cases, it is the equals operator.
  • RangeFieldThe RangeField property indicates the comparator field used in the expression. Because not all fields are suitable to filter records and groups, use the canFilterOn method in the RecordFilterController and the GroupFilterController to determine whether a field can be used for a particular filter.
  • ValuesThe Values property indicates the comparison values in the expression. In this example, it is the strings “USA” and “Bashka Futbol”. This property has one ConstantValue object that stores “USA”.

After the file is opened, and the filters parsed, the FreeEditingText property that stores these strings is cleared and the FilterItems populated. Conversely, if the formula is too complex, the FilterItems collection property remains empty and the FreeEditingText property populated. When altering a filter, you have two options: modify the FreeEditingText property or the FilterItems property.

If you use only one property to modify the filter, the other will not be automatically updated, however. For instance, you would modify the FreeEditingText property, but this will not necessarily be parsed again to repopulate the FilterItems. You should use only one of these properties per session.

Use a controller to ensure that modifications are saved. The GroupFilterController and the RecordFilterController modify the group formula and record formula respectively.

Creating a FieldRangeFilterItem

A FieldRangeFilterItem contains a primitive comparison expression. Its most relevant properties are

  • Operation
  • RangeField
  • Values

The Operation and RangeField properties usually contain a constant. However, the Values property stores either ConstantValue objects, which don’t need evaluation (such as 1, 5, or “Stringiethingie”), and ExpressionValue objects, which do need evaluation (such as “WeekToDateSinceSun,” 4/2, and so on).

The following section of code defines the expression {Customer.ID > 2}. Note how it creates a new ConstantValue object for the number 2 and adds it to the Values collection:

image

All fields cannot be used in a filter formula (for example, you can’t use BLOB fields). Use the canFilterOn method, which is located in either the RecordFilterController or the GroupFilterController, to verify that a field can be filtered on. You must also verify that the constant data type is the same as the field. In the previous example, constantValue must not be a variant and corresponds to the data type used in the comparison.

Creating a OperatorFilterItem

The following example assumes the same expression as defined in the preceding example, but concatenates to the filter using the OR operator. Assume the filter would look like this: {Customer.ID} > 2 OR {Customer.name} = "Arsel". To the code above you would add:

image

The filterItems parameter is a FilterItems collection. It stores FilterItem objects. In the two examples, both a FieldRangeFilterItem object and an OperatorFilterItem object were added to this collection. Both of these objects inherit from FilterItem, making this possible.

Adding a Filter to the Report

After defining the filter, you add it to the report. Filters can be used in two places: group selection and record selection. The GroupFilterController and RecordFilterController, which can be accessed via the DataDefController object, modify their respective filters.

You can also obtain the filters from the GroupFilter and RecordFilter properties in the DataDefinition, although they can only be modified with a controller.

FilterController provides these methods for modifying a filter:

  • addItemThis method adds an expression or an operator to the existing filter.
  • modifyThis method replaces the current filter with a new or modified one.
  • modifyItemThis method modifies a filter element.
  • moveItemThis method moves the filter element around the filter array.
  • removeItemThis method deletes a filter element.

In the following code, the modify method is used because a new filter has already been defined. Assume that there is a ReportClientDocument object and that you have opened a report already:

image

Working with Parameters

Parameters enable end users to enter information to define the report behavior. Parameters have specific data types just like any other field: string, number, date, and so on. Parameters also are divided into two basic types: discrete and ranged. A discrete parameter value is one that represents a singular value such as 9, “Nur”, 1863, True, and so on. Ranged values represent a particular span of values from one point to another such as [9..95], [4..6], [“Alpha”, “Omega”]. The lower bound value of the range must be smaller than the upper bound. Some parameters support more than one value: They effectively contain an array containing many values.

Parameters have default values and the user can be forced to select from them. You can also provide default parameters but allow users to enter their own values. Default values are stored in the ParameterField.DefaultValues property. Selected values are stored in the ParameterField.CurrentValues property.

Parameters support many more features than those covered here. For a complete list of features, see the ParameterField class in the SDK documentation.

Reading Parameters and Their Values

The parameters are exposed in the SDK by the DataDefinition’s ParameterFields class. The ParameterFields class inherits from the Fields class. For example the name of a parameter is obtained using this method:

image

The getDisplayName method is used for UI purposes and so is not a unique identifier. The getFormulaForm method can be used to retrieve a unique identifier. getDisplayName and getFormulaForm are not documented under the ParameterField class because they are inherited from Field.

Because parameter values might be either discrete or ranged, and default values might only be discrete, there are two different objects to represent these: ParameterFieldDiscreteValue and ParameterFieldRangeValue. Both of these objects inherit from ParameterField. You must understand the type of the parameter to know what kind of parameter values it contains. For example, the following code determines if the parameter is of a ranged or discrete type:

image

Check the parameter’s type before you try to print the parameter’s values. You must determine the type so you can retrieve the correct field. Trying to access the EndValue of a discrete value will cause a runtime error because no EndValue exists. The previous code example determines whether the parameter value is an instance of IParameterFieldRangeValue to determine what kind of values it will have. For parameters that support both discrete and ranged values, however, you must verify the type of the parameter by using getValueRangeKind method. The following code checks the parameter type and calls a secondary function to handle the correct type and build a table of parameters:

image

Changing Parameter Values

The ParameterFieldController, which can be found in the DataDefController, enables you to change parameters. To modify a parameter field in the report, you copy the field, modify the copy, and then have the controller modify the original based on changes made to the copy. For instance here you demonstrate this by changing a default discrete value:

image

Adding a Parameter

Use the ParameterFieldController to add new parameters. You do this the same way as adding any other fields to the report: A new field is created, its fields are set, and it is added using a controller. Here you define a new, discrete, string parameter and add it using the controller:

image

Adding a parameter using the Parameter field controller does not place the parameter on the report, so the user is not prompted for the parameter when the report is refreshed. To prompt the user, either use it in a filter, or add it by using the ResultFieldController.

Tips and Tricks for Parameter Handling

Handling parameters involves many important details. When using parameters keep the following points in mind:

  • Parameter values must match the type of the parameter.
  • Any values for the parameter should respect the parameter mask.
  • Ensure that you know what type of values you are reading: Are they discrete or ranged?
  • Set the bound type on a range value before adding it to the parameter.
  • Ensure that the upper bound of a range value is greater than the lower bound.

Failing these tests results in a runtime error.

Charting Overview

The ChartObject, which represents a chart in the RAS SDK, inherits variables and methods from the ReportObject. Remember that the report that you open is represented by the ReportClientDocument, not the ReportObject.

The ChartObject’s properties determine the chart’s appearance and where it shows on the report.

Here you focus on three ChartObject properties:

  • ChartDefinition indicates the chart type and the fields charted. The chart type can be a Group or Details type.
  • ChartStyle specifies the chart style type (such as a bar chart or a pie chart) and the text that appears on the chart (such as the chart title).
  • ChartReportArea is where the chart is located (for example, the report footer).

The following two sections show how you can use these ChartObject properties to create a chart. You must first specify the fields on which you want your chart to be based on. To do this, create a ChartDefinition object, which will then be added to the ChartObject with the ChartDefinition property.

Defining the Fields in a Chart

The ChartDefinition object determines the type of chart that appears in the report and sets the fields to be displayed. A simple, two-dimensional chart displays two types of fields:

  • ConditionFields—The fields that determine where to plot a point on the x-axis.
  • DataFields—The fields that determine what to plot on the y-axis.

Below the chart added is a Group type (see the ChartType class), so the ConditionFields and DataFields that are being charted on are group fields and summary fields respectively.

Adding ConditionFields

Add the first group field in the Groups collection to a Fields collection. This field is retrieved with the ChartDefinition’s getConditionFields method.

image

Note

Adding two groups as ConditionFields enables you to create a 3D chart. Because one value is required for the x values, the next value drives the z-axis.

Adding DataFields

After you have added ConditionFields to the ChartDefinition, add the DataFields. In a Group type chart, the DataFields are summaries for the group fields that you added as ConditionFields.

Adding DataFields is similar to how you added ConditionFields. For example, you use the name of the summary field that the user has selected to locate the desired field in the SummaryFields collection and add this field to a Fields collection. You then accessed the summary field with the ChartDefinition’s DataFields property:

image

Here you use the LongName of the summary field. The LongName contains the type of summary, for example, a sum or a count, and the group field that it applies to. For example:

Sum of (Customer.Last Year's Sales, Customer.Country)

In general you will want to use a field’s LongName instead of its ShortName or Name to avoid confusion as the ShortName or Name might be the same for several fields.

Creating a ChartObject

After the fields are defined, they are added to the ChartObject with the ChartDefinition property. The following code uses the ChartObject’s ChartStyle property and ChartReportArea to specify the chart style type, the chart title, and the location of the chart:

image

In this example, the first chart that you add will appear 50 points below the top of the report area in which the chart is located. (These fields are measured in twips, and 20 twips = 1 font point, so 1000/20 = 50 points.) Adding another chart to the same report area places it over the first chart because the formatting for report objects is absolute. The first chart remains hidden until the second chart is removed.

Adding a Chart to the Report

Now add the chart using the ReportObjectController’s add method. This method takes three parameters: the ChartObject, the section to place it in, and the position in the ReportObjectController collection where you want to add the chart. An option of 1 for the index adds the chart to the end of the array. Return the ReportObjectController by the ReportDefController’s getReportObjectController method:

image

Caution

If you want to modify an existing chart, you can use the clone method to copy the chart, make the desired changes, and then call the modifyObject method using the original chart and the newly modified chart as parameters.

Working with Subreports

A subreport is a report within a report. It can be a free-standing or linked report in the main report. A subreport can have most of the characteristics of a report, except the followings:

  • A subreport cannot stand on its own, but can be inserted as an object into a main report.
  • A subreport can be placed in any section of a main report and the entire subreport will be printed in that section.
  • A subreport cannot contain another subreport.
  • A subreport does not have Page Header or Page Footer sections.

With RAS SDK version XI existing subreports can be modified to the same level of details as in ReportClientDocument. There two classes and one interface that define most methods and properties about subreports, they are:

  • ISubreportClientDocument—Defined in package com.crystaldecisions.sdk.occa.report.application. Use this interface to access report data definition and controllers that are required to modify a subreport.
  • SubreportController—Defined in package com.crystaldecisions.sdk.occa.report.application. Use this class to import a report as a subreport, to add or remove subreport links, or to retrieve the names of all of the subreports in a report.
  • SubreportObject—Defined in package com.crystaldecisions.sdk.occa.report.definition. Use this class to access subreport properties such as layout and formatting of the subreport.

Importing a Subreport

From ReportClientDoccument object you call method getSubreportController() to get a SubreportController object. SubreportController class exposes two methods to import an existing report as a subreport with one setting intelligent default values for left, top, width and height of the subreport object and the other one using user-defined values for top, left, width and height.

image

In this example, reportURL is the URL of the report to import. The URL must be an absolute physical path accessible to the client RAS SDK. Web URLs and file paths managed by BusinessObjects Enterprise are not currently supported. If the reportURL is empty, a blank report will be imported. In this import subreport method intelligent default values for left and top are 0 in twips, that is, the relative left and top positions of the subreport to the section where it is imported are 0. The default width and height of the subreport are the width and height of the section where it is imported.

You can modify the subreport through the subRptClientDocument object by accessing the report data definition and controllers. The subreport object uses methods similar to those of the ReportClientDocument so that all controllers and properties are not different from the main document; therefore end user confusion is reduced.

Adding Subreport Links

A SubreportLink object specifies a link between a subreport and the enclosing main report. For example, you have customer data in a primary report and then use subreports to show the orders for each customer, to coordinate the data in the primary report with the data in the subreport so that the orders in each subreport match up with the correct customer you need to specify a field that is common to both the subreport and the primary report, such as Customer ID. A subreport link allows you to link the two common fields so that records from the primary report can be match up to those in the subreport. SubreportLinks object is a collection of SubreportLink objects.

image

Server Side Printing

RAS SDK version XI allows server-side printing. Server-side printing allows you to print reports from machines that run the RAS SDK application server.

To print a report from the machine that runs the RAS SDK application server you need to specify print options. Use the following code:

image

Then you get PrintOutputController from ReportClientDocument. PrintOutputController is used to print a report with RAS SDK server-side printing. It also defines methods to export reports to specific format, such as RTF, editableRTF, or PDF and to modify various formatting options.

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset