Report Scriptlets
Como hemos visto en los capítulos anteriores, los datos que se muestran en el informe generalmente se obtienen de los parámetros del informe y los campos del informe. Estos datos se pueden procesar utilizando las variables del informe y sus expresiones. Hay situaciones en las que no se puede lograr fácilmente una funcionalidad compleja utilizando expresiones o variables de informe. Ejemplos de esto pueden ser manipulaciones complejas de cadenas, construcción de mapas o listas de objetos en la memoria o manipulaciones de fechas utilizando API de Java de terceros. Para tales situaciones, JasperReports nos proporciona un medio simple y poderoso de hacer esto conScriptlets.
Los scriptlets son secuencias de código Java que se ejecutan cada vez que ocurre un evento de informe. Los valores de las variables del informe pueden verse afectados mediante scriptlets.
Declaración de Scriptlet
Podemos declarar un scriptlet de dos formas:
Usando <scriptlet> elemento. Este elemento tiene atributo de nombre y atributo de clase . El atributo de clase debe especificar el nombre de la clase, que amplía la clase JRAbstractScriptlet . La clase debe estar disponible en la ruta de clase en el momento de completar el informe y debe tener un constructor vacío para que el motor pueda instanciarlo sobre la marcha.
Usando el atributo scriptletClass del elemento <jasperReport>, en la plantilla de informe (JRXML). Al establecer este atributo con el nombre completo de scriptlet (incluido el nombre del paquete completo), indicamos que queremos usar un scriptlet. La instancia de scriptlet, creada con este atributo, actúa como el primer scriptlet en la lista de scriptlets y tiene el nombre predefinido REPORT.
Clase scriptlet
Un scriptlet es una clase java, que debe extender cualquiera de las siguientes clases:
net.sf.jasperreports.engine.JRAbstractScriptlet- Esta clase contiene una serie de métodos abstractos que deben anularse en cada implementación. Estos métodos son llamados automáticamente por JasperReports en el momento apropiado. El desarrollador debe implementar todos los métodos abstractos.
net.sf.jasperreports.engine.JRDefaultScriptlet- Esta clase contiene implementaciones vacías predeterminadas de cada método en JRAbstractScriptlet. Un desarrollador solo está obligado a implementar los métodos que necesita para su proyecto.
La siguiente tabla enumera los métodos de la clase anterior. El motor de informes llamará a estos métodos en el momento adecuado, durante la fase de llenado del informe.
S.NO | Método y descripción |
---|---|
1 | public void beforeReportInit() Llamado antes de la inicialización del informe. |
2 | public void afterReportInit() Llamado después de la inicialización del informe. |
3 | public void beforePageInit() Se llama antes de que se inicialice cada página. |
4 | public void afterPageInit() Se llama después de que se inicializa cada página. |
5 | public void beforeColumnInit() Se llama antes de que se inicialice cada columna. |
6 | public void afterColumnInit() Se llama después de que se inicializa cada columna. |
7 | public void beforeGroupInit(String groupName) Se llama antes de que se inicialice el grupo especificado en el parámetro. |
8 | public void afterGroupInit(String groupName) Se llama después de que se inicializa el grupo especificado en el parámetro. |
9 | public void beforeDetailEval() Se llama antes de que se evalúe cada registro en la sección de detalles del informe. |
10 | public void afterDetailEval() Se llama después de evaluar cada registro de la sección de detalles del informe. |
Se puede especificar cualquier número de scriptlets por informe. Si no se especifica ningún scriptlet para un informe, el motor aún crea una única instancia de JRDefaultScriptlet y la registra con el parámetro incorporado REPORT_SCRIPTLET.
Podemos agregar cualquier método adicional que necesitemos a nuestros scriptlets. Los informes pueden llamar a estos métodos mediante el parámetro incorporado REPORT_SCRIPTLET.
Scriptlets globales
Podemos asociar scriptlets de otra manera a los informes, que es declarando los scriptlets globalmente. Esto hace que los scriptlets se apliquen a todos los informes que se completan en la implementación de JasperReports determinada. Esto se facilita por el hecho de que se pueden agregar scriptlets a JasperReports como extensiones. El punto de extensión de scriptlet está representado por la interfaz net.sf.jasperreports.engine.scriptlets.ScriptletFactory . JasperReports cargará todas las fábricas de scriptlet disponibles a través de extensiones en tiempo de ejecución. Luego, le pedirá a cada uno de ellos la lista de instancias de scriptlets que desean aplicar al informe actual que se está ejecutando. Cuando se solicita la lista de instancias de scriptlet, el motor proporciona información de contexto que la fábrica podría utilizar para decidir qué scriptlets se aplican realmente al informe actual.
Gobernadores de informes
Los gobernadores son solo una extensión de los scriptlets globales que nos permiten abordar el problema del motor de informes que entra en un bucle infinito en tiempo de ejecución, mientras se generan informes. Las plantillas de informes no válidas no se pueden detectar en el momento del diseño, porque la mayoría de las veces, las condiciones para ingresar a los bucles infinitos dependen de los datos reales que se introducen en el motor en tiempo de ejecución. Los gobernadores de informes ayudan a decidir si un determinado informe ha entrado en un ciclo infinito y pueden detenerlo. Esto evita el agotamiento de los recursos de la máquina que ejecuta el informe.
JasperReports tiene dos reguladores de informes simples que detendrían la ejecución de un informe en función de un número máximo especificado de páginas o un intervalo de tiempo de espera especificado. Ellos son -
net.sf.jasperreports.governors.MaxPagesGovernor- Este es un scriptlet global que busca dos propiedades de configuración para decidir si se aplica o no al informe que se está ejecutando actualmente. Las propiedades de configuración son:
net.sf.jasperreports.governor.max.pages.enabled=[true|false]
net.sf.jasperreports.governor.max.pages=[integer]
net.sf.jasperreports.governors.TimeoutGovernor- Este es también un scriptlet global que busca las siguientes dos propiedades de configuración para decidir si se aplica o no.
Las propiedades de configuración son:
net.sf.jasperreports.governor.timeout.enabled=[true|false]
net.sf.jasperreports.governor.timeout=[milliseconds]
Las propiedades de ambos reguladores se pueden establecer globalmente, en el archivo jasperreports.properties, o en el nivel de informe, como propiedades de informe personalizadas. Esto es útil porque diferentes informes pueden tener diferentes tamaños estimados o límites de tiempo de espera y también porque es posible que desee activar los reguladores para todos los informes, mientras que los desactiva para algunos, o viceversa.
Ejemplo
Escribamos una clase de scriptlet (MyScriptlet). El contenido del archivo C: \ tools \ jasperreports-5.0.1 \ test \ src \ com \ tutorialspoint \ MyScriptlet.java es el siguiente:
package com.tutorialspoint;
import net.sf.jasperreports.engine.JRDefaultScriptlet;
import net.sf.jasperreports.engine.JRScriptletException;
public class MyScriptlet extends JRDefaultScriptlet {
public void afterReportInit() throws JRScriptletException{
System.out.println("call afterReportInit()");
// this.setVariableValue("AllCountries", sbuffer.toString());
this.setVariableValue("someVar", new String("This variable value
was modified by the scriptlet."));
}
public String hello() throws JRScriptletException {
return "Hello! I'm the report's scriptlet object.";
}
}
Los detalles de la clase scriptlet anterior son los siguientes:
En el método afterReportInit , establecemos un valor para la variable"someVar" this.setVariableValue ("someVar", new String ("El valor de esta variable fue modificado por el scriptlet.")).
Al final de la clase, un método adicional llamado 'hello'ha sido definido. Este es un ejemplo de un método que se puede agregar al Scriptlet que realmente devuelve un valor, en lugar de establecer una variable.
A continuación, agregaremos la referencia de clase scriptlet en nuestra plantilla de informe existente ( Diseños de informe de capítulo ). La plantilla de informe revisada (jasper_report_template.jrxml) es la siguiente. Guárdelo en el directorio C: \ tools \ jasperreports-5.0.1 \ test -
<?xml version = "1.0"?>
<!DOCTYPE jasperReport PUBLIC
"//JasperReports//DTD Report Design//EN"
"http://jasperreports.sourceforge.net/dtds/jasperreport.dtd">
<jasperReport xmlns = "http://jasperreports.sourceforge.net/jasperreports"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation = "http://jasperreports.sourceforge.net/jasperreports
http://jasperreports.sourceforge.net/xsd/jasperreport.xsd"
name = "jasper_report_template" pageWidth = "595"
pageHeight = "842" columnWidth = "515"
leftMargin = "40" rightMargin = "40" topMargin = "50" bottomMargin = "50"
scriptletClass = "com.tutorialspoint.MyScriptlet">
<style name = "alternateStyle" fontName = "Arial" forecolor = "red">
<conditionalStyle>
<conditionExpression>
<![CDATA[new Boolean($V{countNumber}.intValue() % 2 == 0)]]>
</conditionExpression>
<style forecolor = "blue" isBold = "true"/>
</conditionalStyle>
</style>
<parameter name = "ReportTitle" class = "java.lang.String"/>
<parameter name = "Author" class = "java.lang.String"/>
<queryString>
<![CDATA[]]>
</queryString>
<field name = "country" class = "java.lang.String">
<fieldDescription>
<![CDATA[country]]>
</fieldDescription>
</field>
<field name = "name" class = "java.lang.String">
<fieldDescription>
<![CDATA[name]]>
</fieldDescription>
</field>
<variable name = "countNumber" class = "java.lang.Integer"
calculation = "Count">
<variableExpression><
![CDATA[Boolean.TRUE]]>
</variableExpression>
</variable>
<variable name = "someVar" class = "java.lang.String">
<initialValueExpression>
<![CDATA["This is the initial variable value."]]>
</initialValueExpression>
</variable>
<title>
<band height = "100">
<line>
<reportElement x = "0" y = "0" width = "515" height = "1"/>
</line>
<textField isBlankWhenNull = "true" bookmarkLevel = "1">
<reportElement x = "0" y = "10" width = "515" height = "30"/>
<textElement textAlignment = "Center">
<font size = "22"/>
</textElement>
<textFieldExpression class = "java.lang.String">
<![CDATA[$P{ReportTitle}]]>
</textFieldExpression>
<anchorNameExpression>
<![CDATA["Title"]]>
</anchorNameExpression>
</textField>
<textField isBlankWhenNull = "true">
<reportElement x = "0" y = "40" width = "515" height = "20"/>
<textElement textAlignment = "Center">
<font size = "10"/>
</textElement>
<textFieldExpression class = "java.lang.String">
<![CDATA[$P{Author}]]>
</textFieldExpression>
</textField>
<textField isBlankWhenNull = "true">
<reportElement x = "0" y = "50" width = "515"
height = "30" forecolor = "#993300"/>
<textElement textAlignment = "Center">
<font size = "10"/>
</textElement>
<textFieldExpression class = "java.lang.String">
<![CDATA[$V{someVar}]]>
</textFieldExpression>
</textField>
</band>
</title>
<columnHeader>
<band height = "23">
<staticText>
<reportElement mode = "Opaque" x = "0" y = "3"
width = "535" height = "15"
backcolor = "#70A9A9" />
<box>
<bottomPen lineWidth = "1.0" lineColor = "#CCCCCC" />
</box>
<textElement />
<text>
<![CDATA[]]>
</text>
</staticText>
<staticText>
<reportElement x = "414" y = "3" width = "121" height = "15" />
<textElement textAlignment = "Center" verticalAlignment = "Middle">
<font isBold = "true" />
</textElement>
<text><![CDATA[Country]]></text>
</staticText>
<staticText>
<reportElement x = "0" y = "3" width = "136" height = "15" />
<textElement textAlignment = "Center" verticalAlignment = "Middle">
<font isBold = "true" />
</textElement>
<text><![CDATA[Name]]></text>
</staticText>
</band>
</columnHeader>
<detail>
<band height = "16">
<staticText>
<reportElement mode = "Opaque" x = "0" y = "0"
width = "535" height = "14"
backcolor = "#E5ECF9" />
<box>
<bottomPen lineWidth = "0.25" lineColor = "#CCCCCC" />
</box>
<textElement />
<text>
<![CDATA[]]>
</text>
</staticText>
<textField>
<reportElement style = "alternateStyle" x="414" y = "0"
width = "121" height = "15" />
<textElement textAlignment = "Center" verticalAlignment = "Middle">
<font size = "9" />
</textElement>
<textFieldExpression class = "java.lang.String">
<![CDATA[$F{country}]]>
</textFieldExpression>
</textField>
<textField>
<reportElement x = "0" y = "0" width = "136" height = "15" />
<textElement textAlignment = "Center" verticalAlignment = "Middle" />
<textFieldExpression class = "java.lang.String">
<![CDATA[$F{name}]]>
</textFieldExpression>
</textField>
</band>
</detail>
<summary>
<band height = "45">
<textField isStretchWithOverflow = "true">
<reportElement x = "0" y = "10" width = "515" height = "15" />
<textElement textAlignment = "Center"/>
<textFieldExpression class = "java.lang.String">
<![CDATA["There are " + String.valueOf($V{REPORT_COUNT}) +
" records on this report."]]>
</textFieldExpression>
</textField>
<textField isStretchWithOverflow = "true">
<reportElement positionType = "Float" x = "0" y = "30" width = "515"
height = "15" forecolor = "# 993300" />
<textElement textAlignment = "Center">
<font size = "10"/>
</textElement>
<textFieldExpression class = "java.lang.String">
<![CDATA[$P{REPORT_SCRIPTLET}.hello()]]>
</textFieldExpression>
</textField>
</band>
</summary>
</jasperReport>
Los detalles de la plantilla de informe revisada se dan a continuación:
Hemos hecho referencia a la clase MyScriptlet en el atributo scriptletClass del elemento <jasperReport>.
Los scriptlets solo pueden acceder, pero no modificar, los campos y parámetros del informe. Sin embargo, los scriptlets pueden modificar los valores de las variables del informe. Esto se puede lograr llamando al método setVariableValue (). Este método se define en la clase JRAbstractScriptlet, que siempre es la clase padre de cualquier scriptlet. Aquí, hemos definido una variable someVar , que será modificada por MyScriptlet para tener el valor Este valor fue modificado por el scriptlet .
La plantilla de informe anterior tiene una llamada de método en la banda Resumen que ilustra cómo escribir nuevos métodos (en scriptlets) y usarlos en la plantilla de informe. ($P{REPORT_SCRIPTLET}.hello())
Los códigos Java para el llenado de informes permanecen sin cambios. El contenido del archivoC:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint\JasperReportFill.java son los que se indican a continuación:
package com.tutorialspoint;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource;
public class JasperReportFill {
@SuppressWarnings("unchecked")
public static void main(String[] args) {
String sourceFileName =
"C://tools/jasperreports-5.0.1/test/jasper_report_template.jasper";
DataBeanList DataBeanList = new DataBeanList();
ArrayList<DataBean> dataList = DataBeanList.getDataBeanList();
JRBeanCollectionDataSource beanColDataSource = new
JRBeanCollectionDataSource(dataList);
Map parameters = new HashMap();
/**
* Passing ReportTitle and Author as parameters
*/
parameters.put("ReportTitle", "List of Contacts");
parameters.put("Author", "Prepared By Manisha");
try {
JasperFillManager.fillReportToFile(
sourceFileName, parameters, beanColDataSource);
} catch (JRException e) {
e.printStackTrace();
}
}
}
El contenido del archivo POJO C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint\DataBean.java son los que se indican a continuación:
package com.tutorialspoint;
public class DataBean {
private String name;
private String country;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCountry() {
return country;
}
public void setCountry(String country) {
this.country = country;
}
}
El contenido del archivo C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint\DataBeanList.java son los que se indican a continuación:
package com.tutorialspoint;
import java.util.ArrayList;
public class DataBeanList {
public ArrayList<DataBean> getDataBeanList() {
ArrayList<DataBean> dataBeanList = new ArrayList<DataBean>();
dataBeanList.add(produce("Manisha", "India"));
dataBeanList.add(produce("Dennis Ritchie", "USA"));
dataBeanList.add(produce("V.Anand", "India"));
dataBeanList.add(produce("Shrinath", "California"));
return dataBeanList;
}
/**
* This method returns a DataBean object,
* with name and country set in it.
*/
private DataBean produce(String name, String country) {
DataBean dataBean = new DataBean();
dataBean.setName(name);
dataBean.setCountry(country);
return dataBean;
}
}
La generación del informe
Compilaremos y ejecutaremos el archivo anterior utilizando nuestro proceso de compilación ANT habitual. El contenido del archivo build.xml (guardado en el directorio C: \ tools \ jasperreports-5.0.1 \ test) se muestra a continuación.
El archivo de importación, baseBuild.xml, se obtiene del capítulo Configuración del entorno y debe colocarse en el mismo directorio que build.xml.
<?xml version = "1.0" encoding = "UTF-8"?>
<project name = "JasperReportTest" default = "viewFillReport" basedir = ".">
<import file = "baseBuild.xml" />
<target name = "viewFillReport" depends = "compile,compilereportdesing,run"
description = "Launches the report viewer to preview
the report stored in the .JRprint file.">
<java classname = "net.sf.jasperreports.view.JasperViewer" fork = "true">
<arg value = "-F${file.name}.JRprint" />
<classpath refid = "classpath" />
</java>
</target>
<target name = "compilereportdesing" description = "Compiles the JXML file and
produces the .jasper file.">
<taskdef name = "jrc" classname = "net.sf.jasperreports.ant.JRAntCompileTask">
<classpath refid = "classpath" />
</taskdef>
<jrc destdir = ".">
<src>
<fileset dir = ".">
<include name = "*.jrxml" />
</fileset>
</src>
<classpath refid = "classpath" />
</jrc>
</target>
</project>
A continuación, abramos la ventana de la línea de comandos y vayamos al directorio donde se coloca build.xml. Finalmente, ejecute el comandoant -Dmain-class=com.tutorialspoint.JasperReportFill (viewFullReport es el destino predeterminado) como -
C:\tools\jasperreports-5.0.1\test>ant -Dmain-class=com.tutorialspoint.JasperReportFill
Buildfile: C:\tools\jasperreports-5.0.1\test\build.xml
clean-sample:
[delete] Deleting directory C:\tools\jasperreports-5.0.1\test\classes
[delete] Deleting: C:\tools\jasperreports-5.0.1\test\jasper_report_template.jasper
[delete] Deleting: C:\tools\jasperreports-5.0.1\test\jasper_report_template.jrprint
compile:
[mkdir] Created dir: C:\tools\jasperreports-5.0.1\test\classes
[javac] C:\tools\jasperreports-5.0.1\test\baseBuild.xml:28:
warning: 'includeantruntime' was not set, defaulting to bu
[javac] Compiling 4 source files to C:\tools\jasperreports-5.0.1\test\classes
compilereportdesing:
[jrc] Compiling 1 report design files.
[jrc] log4j:WARN No appenders could be found for logger
(net.sf.jasperreports.engine.xml.JRXmlDigesterFactory).
[jrc] log4j:WARN Please initialize the log4j system properly.
[jrc] log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
[jrc] File : C:\tools\jasperreports-5.0.1\test\jasper_report_template.jrxml ... OK.
run:
[echo] Runnin class : com.tutorialspoint.JasperReportFill
[java] log4j:WARN No appenders could be found for logger
(net.sf.jasperreports.extensions.ExtensionsEnvironment).
[java] log4j:WARN Please initialize the log4j system properly.
[java] call afterReportInit()
[java] call afterReportInit()
viewFillReport:
[java] log4j:WARN No appenders could be found for logger
(net.sf.jasperreports.extensions.ExtensionsEnvironment).
[java] log4j:WARN Please initialize the log4j system properly.
BUILD SUCCESSFUL
Total time: 18 minutes 49 seconds
Como resultado de la compilación anterior, se abre una ventana de JasperViewer como se muestra en la pantalla que se muestra a continuación:
Aquí vemos que se muestran dos mensajes de la clase MyScriptlet:
- En la sección de título: el valor de esta variable fue modificado por el scriptlet
- En la parte inferior - ¡Hola! Soy el objeto scriptlet del informe.