Alert info can go here.
Get Support: +866 314 7300

BDL

Architecture

A schematic diagram of Genero Report Writer for BDL

architecture

Scalability

A schematic diagram illustrating the distributed nature of Genero Report Writer for BDL

Java scalability

Highlights

Detailed feature overview

  • Easy to use, WYSIWYG editor,
  • Schema based, type-safe editing
  • Avoids run-time errors caused by failing string conversions,
  • Compile-time impact analysis,
  • Lists issues in a report design caused by changes to the schema. This feature is of  particular importance for end users that have customized or created their own report designs. Upon receipt of a software update they can check their reports against the new schemas shipped with the update to detect and fix any issues before making the update operational,
  • Batch upgrades.  A command line tool is available to perform batch upgrades of report design files from one version of the software to another,
  • Batch updates and software-assisted issue resolution in the report designer. A command line tool is available to update the report design files in cases where the schemas may have changed (e.g. during an application update and the end user’s site). The update algorithm is optimal in the sense that it finds a minimal patch so that in the general case no manual intervention is required. In the rare case that parts of a schema have been removed or renamed users are assisted in resolving the issues in form of issue list that is persistently stored with the report design file until all issues have been resolved.
  • Data stream and document formatting are separate,
  • Multiple presentations per data source,
  • Defines a clear interface between application vendors who are responsible for providing the data sources and their associated schemas and users who customize or add presentations to them.
  • Process level interface as opposed to accessing the database directly,
  • Less sensitive to database changes. Process level messages shield users from details of the database and allow application vendors to change the database schema without breaking customized or self made reports,
  • Prevents replication of business rules in report designs. By accessing “cooked” data, users are shielded from the need to know details on how things are stored in the database. It avoids the replication of central business logic in the report designs (e.g. rules about which address is actually used as a billing address of an invoice when there are multiple choices in the database),
  • Support for structured data as opposed to flat record sets returned by SQL queries. The data behind sophisticated reports such as invoices or delivery can rarely be represented as a flat list of records. The natural representation is a structured message allowing for arbitrary nested lists of different records. Using “sub reports” as in many ad hoc reporting tools is not an adequate solution since it adds unnecessary complexity and typically reverses the network communication causing performance problems due to network latency,
  • Novel, serial, graphical matching and transformation language. Conceptually similar to XSL-T, the RTL transformation language is limited in the type of transformations it can do (e.g. no recursion), but can be represented graphically. It works for documents of arbitrary size.
  • Single pass, unidirectional processing pipe. Since network latency can be regarded as a resource that does not scale it is important to avoid reversing the communication direction during a job. The engine can be used in a distributed environment with very high roundtrip times and low bandwidth without noticeable decrease in performance,
  • Serial processing of arbitrarily sized documents.
    • Thousands of pages, low and constant memory,
    • Distributed processing and multiple server option. The multi tier option allows to offload the report rendering to one or more separate machines leaving only the data source message creation for the server.
  • Pixel exact positioning,
  • Relative positioning vertical and horizontal,
  • Content propagation vertical and horizontal,
  • Multicolumn layout,
  • Tabular layout,
  • Pivot tables,
  •  Sub-reports,
  • Sophisticated headers and footers,
  • Sophisticated page breaking,
  • Label printing on varying media. The report design contains the description of a single label abstracting from the actual layout on the media removing the need to change the report design for different label printers or different label sheets,
  • Running totals in headers and footers,
  • Page n of m in headers and footers while retaining serial processing,
  • Self-layouting business graphs,
  • Large number of natively implemented bar codes,
  • Bitmap and SVG images. The lean scalable vector graphics (also those of other origin such as bar codes and business graphics) are preserved in all formats that support vector drawing such as PDF, browser or printer output. In all other formats they are rendered into bitmaps.
  • Design time API
    • API for schema introspection,
    • API for report design file introspection and generation,
    • API for template based report design generation,
    • API for pivot table configuration.
  • Run time API
    • Data level API,
      • Selection of source,
      • Configuration of localization,
      • Recommend JAXB for schema generation and marshalling,
    • Document level API,
      • Selection of design,
      • Configuration of page characteristics,
      • Configuration of label layout,
    • Device level API
      • Selection of device (e.g. PDF, server printer, client printer, ..),
      • Device specific configuration (e.g. Paper tray selection of printer).
  • Browser based report viewer
    • Low band-width,
    • 100% pixel exact rendering,
    • Uses cacheable WOFF fonts and SVG vector graphics,
    • Streaming capable for immediate rendering of huge reports,
    • Fast, random navigation in huge documents,
    • Bookmarkable and shareable reports
  • Native report viewer
    • Very fast processing, low, nearly fixed memory consumption,
    • 100% pixel exact rendering using embedded fonts,
    • Streaming capable for immediate rendering of huge reports,
    • Fast, random navigation in huge documents,
    • Support for silent printing
      • Viewer not visible,
      • Selecting printer via server API,
      • Configuration of printer via server API (e.g. selecting paper source),
  • Server side printing
    • Serial processing if supported by the operating system,
    • Programmatic control via IPP properties
  • File generation
    • PDF,
    • RTF (MS-Word),
    • XLS/XLSX (MS-Excel),
    • Postscript,
    • SVG
  • Single design for multiple languages. Primitives are provided to translate strings and format data in locale specific manner individually per document at runtime. Layouts can be defined that handle the resulting differences in text size well.
  • Asian language and alternative writing modes support.
  • Efficient handling of Asian fonts.
  • Support for self written document templates,
  • Type-safe unrolling of field lists and placeholders,
  • Support for complexly structured data sources.
  • Human readable and version controllable file formats,
  • Type safe report design editing,
  • Compile time impact analysis after changed to data sources,
  • Sophisticated report design upgrade algorithm (Similar to diff/patch).
    • 100 % tolerant to addition of fields and structures,
    • Good tolerance to removal,
    • Reports conflicts and allows for manual correction.
  • Graphical data source generator,
  • Creation of code for XML generation,
  • Creation of schema,
  • Type-safe unrolling of field lists and placeholders,
  • Support for complexly structured data sources.
  • DB2
  • Informix
  • MS SQL Server
  • MySQL
  • Oracle
  • PostrgreSQL

Getting Started

Code examples

In this example we use a plain BDL object as a data source as shown in the source code below:

REPORT report_all_orders( orderline )
DEFINE
orderline OrderType,
lineitemprice LIKE lineitem.unitprice,
overalltotal LIKE orders.totalprice,
usertotal LIKE orders.totalprice,
ordertotal LIKE orders.totalprice

ORDER EXTERNAL BY orderline.orders.userid, orderline.orders.orderid, orderline.lineitem.linenum

FORMAT
FIRST PAGE HEADER
LET overalltotal = 0
PRINT controlBlock.*

BEFORE GROUP OF orderline.orders.userid
DISPLAY "USER " || orderline.orders.userid
LET usertotal = 0

BEFORE GROUP OF orderline.orders.orderid
DISPLAY " ORDER " || orderline.orders.orderid
LET ordertotal = 0

ON EVERY ROW
DISPLAY " EVERY ROW " || orderline.lineitem.linenum
LET lineitemprice = orderline.lineitem.unitprice * orderline.lineitem.quantity
LET overalltotal = overalltotal + lineitemprice
LET usertotal = usertotal + lineitemprice
LET ordertotal = ordertotal + lineitemprice
PRINT orderline.*, lineitemprice, overalltotal, usertotal, ordertotal

END REPORT

Graphical reports are designed based on a schema that matches the data to be serialized.
The “fglcomp” command line tool can be used to generate the schema:

$fglcomp –build-rdd OrderReport.4gl
$ls -ltr
$OrderReport.4gl
$OrderReport.rdd

Design the report using Report Designer. Choose your report template, associate the triggers and populate the report with data picked from the data view.

The image below shows “SalesList.4rp” created in the Report Designer:

Select the report design

IF num_args()>1 THEN
LET filename=arg_val(1)
ELSE
LET filename=”SalesList.4rp”
END IF

IF NOT fgl_report_loadCurrentSettings(fileName) THEN
EXIT PROGRAM
END IF

Configure the output
Select PDF output

CALL fgl_report_selectDevice(“PDF”)

Serialize model

START REPORT report_all_orders TO XML HANDLER handler
FOREACH c_order INTO orderline.*
OUTPUT TO REPORT report_all_orders(orderline.*)
IF fgl_report_getErrorStatus() THEN
DISPLAY "FGL: STOPPING REPORT, msg=\"",fgl_report_getErrorString(),"\""
EXIT FOREACH
END IF
END FOREACH

FINISH REPORT report_all_orders

Find below the complete listing of the BDLprogram:

# FOURJS_START_COPYRIGHT(U,2003)
# Property of Four Js*
# (c) Copyright Four Js 2003, 2024. All Rights Reserved.
# * Trademark of Four Js Development Tools Europe Ltd
# in the United States and elsewhere
#
# Four Js and its suppliers do not warrant or guarantee that these samples are
# accurate and suitable for your purposes.
# Their inclusion is purely for information purposes only.
# FOURJS_END_COPYRIGHT

IMPORT FGL greruntime
IMPORT FGL gredesigntime
IMPORT FGL pivotdialog
IMPORT FGL fieldselectiondialog


SCHEMA officestore

GLOBALS "globals.4gl"

TYPE OrderType RECORD
orders RECORD LIKE orders.*,
account RECORD LIKE account.*,
country RECORD LIKE country.*,
lineitem RECORD LIKE lineitem.*,
product RECORD LIKE product.*,
category RECORD LIKE category.*,
item RECORD LIKE item.*
END RECORD

-- set this constant to false if you prefer to run from the database
-- In this case you will need an office store database up and running
CONSTANT runFromFile INTEGER = FALSE
CONSTANT dataFile STRING = "OrderReport.unl"


DEFINE controlBlock PivotControlBlock

MAIN

DEFINE
r_output STRING,
r_filename STRING

LET r_filename="OrderReport"
LET r_output="Genero Report Viewer"
OPEN FORM f_configuration FROM "Configuration"
DISPLAY FORM f_configuration
INPUT BY NAME r_filename, r_output ATTRIBUTES (UNBUFFERED, WITHOUT DEFAULTS, CANCEL=FALSE, ACCEPT=FALSE)
BEFORE INPUT
CALL dialog.setActionActive("saveondisk",r_output != "Genero Report Viewer" OR isGDC())
ON ACTION preview
CALL runReport(r_filename, r_output,"preview")
ON ACTION saveOnDisk
CALL runReport(r_filename, r_output,"saveOnDisk")
ON ACTION close
EXIT INPUT
ON CHANGE r_output
CALL dialog.setActionActive("preview",r_output != "Image")
CALL dialog.setActionActive("saveondisk",r_output != "Genero Report Viewer" OR isGDC())
END INPUT
END MAIN
FUNCTION getWebRootDirectory()
RETURN base.Application.getProgramDir()||"../HTTPDaemon"
END FUNCTION
FUNCTION getDocumentDirectory()
RETURN getWebRootDirectory()||"/reports/documents"
END FUNCTION

FUNCTION getFontsDirectory()
RETURN getWebRootDirectory()||"/reports/documents/fonts"
END FUNCTION
FUNCTION isGDC()
DEFINE fename STRING
CALL ui.interface.frontcall("standard", "feinfo", ["fename"],[fename])
RETURN fename == "Genero Desktop Client"
END FUNCTION
FUNCTION runReport(filename, output, action)
DEFINE
output STRING,
action STRING,
filename STRING,
preview INTEGER,
handler om.SaxDocumentHandler
IF requiresDesign(output) AND NOT haveDesign(filename) THEN
ERROR "This output option is not available with generic reports"
RETURN
END IF
IF output=="Genero Report Viewer" THEN
IF isGDC() THEN
LET output="SVG"
ELSE
LET output="Browser"
END IF
END IF
LET preview = FALSE
IF action IS NOT NULL THEN
IF action == "preview" THEN
LET preview = TRUE
END IF
END IF
INITIALIZE handler TO NULL
IF filename IS NOT NULL AND output IS NOT NULL THEN
LET handler = configureReport(filename || '.4rp', output, preview)
END IF
IF handler IS NULL THEN
RETURN
END IF

IF filename =="MasterReport" OR filename =="MasterReportInlined" THEN
CALL runTwice(handler)
ELSE
IF runFromFile THEN
DISPLAY "Running report from file \"", dataFile.trim(), "\""
CALL runReportFromFile(handler)
ELSE
DISPLAY "Running report from database"
CALL runReportFromDatabase(handler)
END IF
END IF
END FUNCTION
FUNCTION runReportFromDatabase(handler)
DEFINE
orderline OrderType,
handler om.SaxDocumentHandler

DATABASE officestore
DECLARE c_order CURSOR FOR
SELECT orders.*,
account.*,
country.*,
lineitem.*,
product.*,
category.*,
item.*
FROM orders, account, lineitem, product, category, item, country
WHERE
orders.orderid = lineitem.orderid
AND orders.userid = account.userid
AND lineitem.itemid = item.itemid
AND item.productid = product.productid
AND product.catid = category.catid
AND country.code = orders.billcountry
ORDER BY orders.userid, orders.orderid, lineitem.linenum

START REPORT report_all_orders TO XML HANDLER handler
FOREACH c_order INTO orderline.*
OUTPUT TO REPORT report_all_orders(orderline.*)
IF fgl_report_getErrorStatus() THEN
DISPLAY "FGL: STOPPING REPORT, msg=\"",fgl_report_getErrorString(),"\""
EXIT FOREACH
END IF
END FOREACH
FINISH REPORT report_all_orders
DISPLAY "Produced "||fgl_report_getTotalNumberOfPages()||" page(s)"

CLOSE c_order
END FUNCTION

FUNCTION runReportFromFile(handler)
DEFINE
orderline OrderType,
handler om.SaxDocumentHandler,
ch base.channel

LET ch = base.Channel.create()
CALL ch.openFile(dataFile,"r")

START REPORT report_all_orders TO XML HANDLER handler
WHILE ch.read([orderline.*])
OUTPUT TO REPORT report_all_orders(orderline.*)
IF fgl_report_getErrorStatus() THEN
DISPLAY "FGL: STOPPING REPORT, msg=\"",fgl_report_getErrorString(),"\""
EXIT WHILE
END IF
END WHILE
FINISH REPORT report_all_orders
DISPLAY "Produced "||fgl_report_getTotalNumberOfPages()||" page(s)"

CALL ch.close()
END FUNCTION

FUNCTION runTwice(handler)
DEFINE
handler om.SaxDocumentHandler
START REPORT report_all_orders_twice TO XML HANDLER HANDLER
OUTPUT TO REPORT report_all_orders_twice()
FINISH REPORT report_all_orders_twice
DISPLAY "Produced "||fgl_report_getTotalNumberOfPages()||" page(s)"
END FUNCTION

FUNCTION promptForFieldsToPrint(rddFile,reportName)
DEFINE rddFile,reportName STRING
DEFINE left DYNAMIC ARRAY OF STRING
DEFINE right DYNAMIC ARRAY OF STRING
DEFINE retval,i,leftFieldCount,rightFieldCount INTEGER
DEFINE fieldNames DYNAMIC ARRAY OF STRING
DEFINE fieldNamesString STRING

CALL rdd_getEveryRowFields(rddFile,reportName) RETURNING fieldNames

LET leftFieldCount=0
LET rightFieldCount=0
FOR i=1 TO fieldNames.getLength()
IF fieldNames[i] MATCHES "orderline.lineitem.*" OR fieldNames[i] MATCHES "orderline.category.*" THEN
LET rightFieldCount=rightFieldCount+1
LET right[rightFieldCount]=fieldNames[i]
ELSE
LET leftFieldCount=leftFieldCount+1
LET left[leftFieldCount]=fieldNames[i]
END IF
END FOR
LET retval=promptForFieldSelectionDialog(left,right)
IF retval THEN
IF right.getLength()<=0 THEN
RETURN TRUE,NULL
ELSE
LET fieldNamesString=right[1]
FOR i=2 TO right.getLength()
LET fieldNamesString=fieldNamesString||","||right[i]
END FOR
RETURN TRUE,fieldNamesString.toLowerCase()
END IF
ELSE
RETURN FALSE,NULL
END IF
END FUNCTION
FUNCTION requiresDesign(outputType)
DEFINE outputType STRING
RETURN outputType=="Normalized XML" OR
outputType=="Transformed XML" OR
outputType=="Document Model XML"
END FUNCTION
FUNCTION haveDesign(filename)
DEFINE filename STRING
RETURN filename.getIndexOf("Generic List",1)<=0
END FUNCTION
FUNCTION configureReport(filename, outputformat, preview)
DEFINE
filename STRING,
outputformat STRING,
preview INTEGER,
retval INTEGER,
fieldNames STRING,
renderingHints om.SaxAttributes,
GREDIR STRING

-- load the 4rp file
IF filename.getIndexOf("Generic List",1)>0 THEN
IF NOT fgl_report_loadCurrentSettings(NULL) THEN
EXIT PROGRAM
END IF
CALL promptForFieldsToPrint("OrderReport.rdd","report_all_orders") RETURNING retval,fieldNames
IF NOT retval THEN
RETURN NULL
END IF
IF filename.getIndexOf("New Generic List",1)>0 THEN
CALL fgl_report_setAutoformatType("NEW LIST")
IF filename!="New Generic List.4rp" THEN
LET renderingHints=om.saxAttributes.create()
LET GREDIR=fgl_getenv("GREDIR")
CALL renderingHints.addAttribute("fglAutoformatTemplateURL",GREDIR||"/templates/"||filename.subString(18,filename.getLength()-4))
CALL renderingHints.addAttribute("fglAutoformatOrganizationName","Four Js Development Tools")
CALL renderingHints.addAttribute("fglAutoformatKeyText"," ")
CALL renderingHints.addAttribute("fglAutoformatSelectionText","Sorted by Customers and Orders")
CALL renderingHints.addAttribute("fglAutoformatPrintingInformationText","Revenue List")
CALL fgl_report_setRenderingHints(renderingHints)
CALL fgl_report_setPageMargins("1.5cm","1.5cm","1.5cm","1.5cm")
END IF
ELSE
CALL fgl_report_setAutoformatType("FLAT LIST")
END IF
CALL fgl_report_configureAutoformatOutput(NULL,8,NULL,"Order List",fieldNames,NULL)
CALL fgl_report_configurePageSize("a4length","a4width")
CALL fgl_report_configureXLSDevice(NULL,NULL,FALSE,NULL,NULL,NULL,TRUE) #preserve spaces and merge pages in XLS output
CALL fgl_report_setTitle("Order List")
ELSE
CALL promptForPivotDialogIfAny(filename,"6,2","4,0,2") RETURNING retval, controlBlock.* #Zip code, Unitprice and Unitcost by Product category and Shipping area
IF NOT retval THEN
RETURN NULL
END IF
IF NOT fgl_report_loadCurrentSettings(filename) THEN
EXIT PROGRAM
END IF
END IF

-- change some parameters
IF filename == "OrderLabels.4rp" THEN
CALL fgl_report_selectLogicalPageMapping("labels")
CALL fgl_report_setPaperMargins("5mm", "5mm", "4mm", "4mm")
CALL fgl_report_configureLabelOutput("a4width", "a4length", NULL, NULL, 2, 6)
END IF
CALL fgl_report_selectDevice(outputformat)
CALL fgl_report_selectPreview(preview)
IF outputformat=="PDF" THEN
CALL fgl_report_setPDFAttachDocumentModelXML("filename.xml","filedescr")
END IF
IF outputformat=="Browser" AND preview THEN
CALL ui.Interface.frontCall( "standard", "launchurl", [fgl_report_getBrowserURL()], [] )
ELSE
IF outputformat=="TXT" THEN
CALL fgl_report_configureTXTDevice(NULL,NULL,NULL,NULL,NULL,"AtEnd",NULL)
END IF
END IF
IF outputformat=="XLS" THEN
CALL fgl_report_setXLSPageGroupingMode(TRUE)
END IF
IF outputformat=="XLSX" THEN
CALL fgl_report_setXLSXPageGroupingMode(TRUE)
END IF
-- use the report
RETURN fgl_report_commitCurrentSettings()

END FUNCTION

REPORT report_all_orders( orderline )
DEFINE
orderline OrderType,
lineitemprice LIKE lineitem.unitprice,
overalltotal LIKE orders.totalprice,
usertotal LIKE orders.totalprice,
ordertotal LIKE orders.totalprice

ORDER EXTERNAL BY orderline.orders.userid, orderline.orders.orderid, orderline.lineitem.linenum

FORMAT
FIRST PAGE HEADER
LET overalltotal = 0
PRINT controlBlock.*

BEFORE GROUP OF orderline.orders.userid
DISPLAY "USER " || orderline.orders.userid
LET usertotal = 0

BEFORE GROUP OF orderline.orders.orderid
DISPLAY " ORDER " || orderline.orders.orderid
LET ordertotal = 0



ON EVERY ROW
DISPLAY " EVERY ROW " || orderline.lineitem.linenum
LET lineitemprice = orderline.lineitem.unitprice * orderline.lineitem.quantity
LET overalltotal = overalltotal + lineitemprice
LET usertotal = usertotal + lineitemprice
LET ordertotal = ordertotal + lineitemprice
PRINT orderline.*, lineitemprice, overalltotal, usertotal, ordertotal

END REPORT

REPORT report_all_orders_twice()
DEFINE
orderline OrderType,
ch base.channel

FORMAT
ON EVERY ROW

LET ch = base.Channel.create()
CALL ch.openFile(dataFile,"r")
DISPLAY "MASTER ITERATION 1 "
START REPORT report_all_orders
WHILE ch.read([orderline.*])
OUTPUT TO REPORT report_all_orders(orderline.*)
IF fgl_report_getErrorStatus() THEN
DISPLAY "FGL: STOPPING REPORT, msg=\"",fgl_report_getErrorString(),"\""
EXIT WHILE
END IF
END WHILE
FINISH REPORT report_all_orders
CALL ch.close()

DISPLAY "MASTER ITERATION 2 "
LET ch = base.Channel.create()
CALL ch.openFile(dataFile,"r")

START REPORT report_all_orders
WHILE ch.read([orderline.*])
OUTPUT TO REPORT report_all_orders(orderline.*)
IF fgl_report_getErrorStatus() THEN
DISPLAY "FGL: STOPPING REPORT, msg=\"",fgl_report_getErrorString(),"\""
EXIT WHILE
END IF

END WHILE
FINISH REPORT report_all_orders
CALL ch.close()

END REPORT

Developer Tutorials

PDFs

Installing & Licensing

Open PDF

Creating The Report App

Open PDF

Creating A List Report

Open PDF

Grouping, Subtotals And Totals

Open PDF

Formatting Report Basics & Layouts

Open PDF

Headers and Footers

Open PDF

Creating a Forms-Based Report

Open PDF

Creating Labels

Open PDF

Charts, Graphs & Pivot Tables

Open PDF

Reporting Options #1

Open PDF

Reporting Options #2

Open PDF

Building Expressions

Open PDF

Barcodes

Open PDF

Tables

Open PDF

Pivot Tables

Open PDF

Miscellany

Open PDF