Report Scriptlets

W poprzednich rozdziałach widzieliśmy, że dane wyświetlane w raporcie są zwykle pobierane z parametrów raportu i pól raportu. Dane te mogą być przetwarzane przy użyciu zmiennych raportu i ich wyrażeń. Istnieją sytuacje, w których złożonej funkcjonalności nie można łatwo osiągnąć za pomocą wyrażeń raportu lub zmiennych. Przykładami mogą być złożone operacje na ciągach znaków, tworzenie map lub list obiektów w pamięci lub manipulowanie datami za pomocą zewnętrznych interfejsów Java. W takich sytuacjach JasperReports zapewnia nam prosty i skuteczny sposób na zrobienie tegoScriptlets.

Skryptlety to sekwencje kodu Java, które są wykonywane za każdym razem, gdy wystąpi zdarzenie raportu. Na wartości zmiennych raportu można wpływać za pomocą skryptletów.

Deklaracja Scriptlet

Możemy zadeklarować skryptlet na dwa sposoby -

  • Korzystanie z <scriptlet> element. Ten element ma atrybut nazwy i atrybut klasy . Klasa atrybut powinien podać nazwę klasy, która rozciąga JRAbstractScriptlet klasę. Klasa musi być dostępna w ścieżce klas w czasie wypełniania raportu i musi mieć pusty konstruktor, aby silnik mógł tworzyć jej instancję w locie.

  • Korzystanie z atrybutu scriptletClass elementu <jasperReport> w szablonie raportu (JRXML). Ustawiając ten atrybut na w pełni kwalifikowaną nazwę scriptletu (łącznie z całą nazwą pakietu), wskazujemy, że chcemy użyć scriptletu. Instancja skryptletu, utworzona za pomocą tego atrybutu, działa jak pierwszy skryptlet na liście skryptletów i ma wstępnie zdefiniowaną nazwę REPORT.

Klasa Scriptlet

Skryptlet to klasa java, która musi rozszerzać jedną z następujących klas -

  • net.sf.jasperreports.engine.JRAbstractScriptlet- Ta klasa zawiera kilka abstrakcyjnych metod, które muszą zostać przesłonięte w każdej implementacji. Te metody są wywoływane automatycznie przez JasperReports w odpowiednim momencie. Programista musi zaimplementować wszystkie metody abstrakcyjne.

  • net.sf.jasperreports.engine.JRDefaultScriptlet- Ta klasa zawiera domyślne puste implementacje każdej metody w JRAbstractScriptlet. Deweloper jest zobowiązany do wdrożenia tylko tych metod, których potrzebuje w swoim projekcie.

Poniższa tabela zawiera listę metod w powyższej klasie. Metody te zostaną wywołane przez silnik raportujący w odpowiednim momencie, podczas fazy wypełniania raportu.

S.NO Metoda i opis
1

public void beforeReportInit()

Wywoływane przed inicjalizacją raportu.

2

public void afterReportInit()

Wywoływane po inicjalizacji raportu.

3

public void beforePageInit()

Wywoływane przed zainicjowaniem każdej strony.

4

public void afterPageInit()

Wywoływane po zainicjowaniu każdej strony.

5

public void beforeColumnInit()

Wywoływane przed zainicjowaniem każdej kolumny.

6

public void afterColumnInit()

Wywoływane po zainicjowaniu każdej kolumny.

7

public void beforeGroupInit(String groupName)

Wywoływana przed zainicjowaniem grupy określonej w parametrze.

8

public void afterGroupInit(String groupName)

Wywoływane po zainicjowaniu grupy określonej w parametrze.

9

public void beforeDetailEval()

Wywoływane przed oceną każdego rekordu w sekcji szczegółowej raportu.

10

public void afterDetailEval()

Wywoływane po ocenie każdego rekordu w szczegółowej sekcji raportu.

W raporcie można określić dowolną liczbę skryptletów. Jeśli dla raportu nie określono skryptu, mechanizm nadal tworzy pojedynczą instancję JRDefaultScriptlet i rejestruje ją przy użyciu wbudowanego parametru REPORT_SCRIPTLET.

Do naszych skryptletów możemy dodać dodatkowe metody, których potrzebujemy. Raporty mogą wywoływać te metody przy użyciu wbudowanego parametru REPORT_SCRIPTLET.

Global Scriptlets

Możemy kojarzyć skryptlety w inny sposób z raportami, czyli przez globalne deklarowanie skryptletów. To sprawia, że ​​skryptlety mają zastosowanie do wszystkich raportów wypełnianych w danym wdrożeniu JasperReports. Ułatwia to fakt, że skryptlety można dodawać do JasperReports jako rozszerzenia. Punkt rozszerzenia skryptletu jest reprezentowany przez interfejs net.sf.jasperreports.engine.scriptlets.ScriptletFactory . JasperReports załaduje wszystkie fabryki skryptów dostępne przez rozszerzenia w czasie wykonywania. Następnie zapyta każdego z nich o listę instancji skryptów, które chcą zastosować do bieżącego raportu, który jest uruchamiany. Pytając o listę instancji skryptów, silnik podaje pewne informacje kontekstowe, z których fabryka może skorzystać w celu podjęcia decyzji, które skrypty faktycznie mają zastosowanie do bieżącego raportu.

Report Governors

Governors to tylko rozszerzenie globalnych skryptletów, które pozwalają nam rozwiązać problem wchodzenia silnika raportów w nieskończoną pętlę w czasie wykonywania, podczas generowania raportów. Nieprawidłowe szablony raportów nie mogą zostać wykryte w czasie projektowania, ponieważ przez większość czasu warunki wejścia w nieskończone pętle zależą od rzeczywistych danych wprowadzanych do silnika w czasie wykonywania. Report Governors pomagają w podjęciu decyzji, czy dany raport wszedł w nieskończoną pętlę i mogą go zatrzymać. Zapobiega to wyczerpaniu zasobów maszyny, na której jest uruchamiany raport.

JasperReports ma dwa proste regulatory raportów, które zatrzymywałyby wykonanie raportu na podstawie określonej maksymalnej liczby stron lub określonego limitu czasu. Oni są -

  • net.sf.jasperreports.governors.MaxPagesGovernor- Jest to globalny skryptlet, który szuka dwóch właściwości konfiguracyjnych, aby zdecydować, czy ma zastosowanie do aktualnie uruchamianego raportu. Właściwości konfiguracyjne to -

    • net.sf.jasperreports.governor.max.pages.enabled=[true|false]

    • net.sf.jasperreports.governor.max.pages=[integer]

  • net.sf.jasperreports.governors.TimeoutGovernor- Jest to również globalny skryptlet, który szuka następujących dwóch właściwości konfiguracyjnych, aby zdecydować, czy ma zastosowanie, czy nie.

    Właściwości konfiguracyjne to -

    • net.sf.jasperreports.governor.timeout.enabled=[true|false]

    • net.sf.jasperreports.governor.timeout=[milliseconds]

Właściwości obu menedżerów można ustawić globalnie, w pliku jasperreports.properties lub na poziomie raportu, jako niestandardowe właściwości raportu. Jest to przydatne, ponieważ różne raporty mogą mieć różny szacowany rozmiar lub limity czasu, a także dlatego, że możesz chcieć włączyć zarządców dla wszystkich raportów, wyłączając je dla niektórych lub odwrotnie.

Przykład

Napiszmy klasę scriptlet (MyScriptlet). Zawartość pliku C: \ tools \ jasperreports-5.0.1 \ test \ src \ com \ tutorialspoint \ MyScriptlet.java jest następująca -

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.";
   }

}

Szczegóły powyższej klasy skryptletów są następujące -

  • W metodzie afterReportInit ustawiamy wartość zmiennej"someVar" this.setVariableValue ("someVar", new String ("Wartość tej zmiennej została zmodyfikowana przez skryptlet.")).

  • Na końcu klasy została wywołana dodatkowa metoda 'hello'został zdefiniowany. To jest przykład metody, którą można dodać do Scriptlet, która w rzeczywistości zwraca wartość, zamiast ustawiać zmienną.

Następnie dodamy odwołanie do klasy scriptlet w naszym istniejącym szablonie raportu (rozdział Projekty raportów ). Zmieniony szablon raportu (jasper_report_template.jrxml) jest następujący. Zapisz go w katalogu 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>

Szczegóły poprawionego szablonu raportu podano poniżej -

  • Odwołaliśmy się do klasy MyScriptlet w atrybucie scriptletClass elementu <jasperReport>.

  • Scriptlets mogą tylko uzyskiwać dostęp do pól i parametrów raportu, ale nie mogą ich modyfikować. Jednak skryptlety mogą modyfikować wartości zmiennych raportu. Można to osiągnąć, wywołując metodę setVariableValue (). Ta metoda jest zdefiniowana w klasie JRAbstractScriptlet, która jest zawsze klasą nadrzędną każdego scriptletu. Tutaj zdefiniowaliśmy zmienną someVar , która zostanie zmodyfikowana przez MyScriptlet tak, aby miała wartość Ta wartość została zmodyfikowana przez skryptlet .

  • Powyższy szablon raportu ma wywołanie metody w paśmie Summary, które ilustruje, jak pisać nowe metody (w skryptletach) i używać ich w szablonie raportu. ($P{REPORT_SCRIPTLET}.hello())

Kody java do wypełniania raportów pozostają niezmienione. Zawartość plikuC:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint\JasperReportFill.java są jak podano poniżej -

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();
      }
   }
}

Zawartość pliku POJO C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint\DataBean.java są jak podano poniżej -

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;
   }
}

Zawartość pliku C:\tools\jasperreports-5.0.1\test\src\com\tutorialspoint\DataBeanList.java są jak podano poniżej -

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;
   }
}

Generowanie raportów

Skompilujemy i uruchomimy powyższy plik przy użyciu naszego zwykłego procesu budowania ANT. Zawartość pliku build.xml (zapisanego w katalogu C: \ tools \ jasperreports-5.0.1 \ test) jest taka, jak podano poniżej.

Plik importu - baseBuild.xml jest pobierany z rozdziału Konfiguracja środowiska i powinien być umieszczony w tym samym katalogu, co plik 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>

Następnie otwórzmy okno wiersza poleceń i przejdźmy do katalogu, w którym znajduje się plik build.xml. Na koniec wykonaj polecenieant -Dmain-class=com.tutorialspoint.JasperReportFill (domyślnym celem jest viewFullReport) jako -

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

W wyniku powyższej kompilacji otworzy się okno JasperViewer, jak pokazano na poniższym ekranie -

Tutaj widzimy dwa komunikaty wyświetlane z klasy MyScriptlet -

  • W sekcji tytułowej - wartość tej zmiennej została zmodyfikowana przez skryptlet
  • Na dole - witam! Jestem obiektem skryptu raportu.