Jasper Reports:如何编译子报告

问题描述:

我有一个独立的应用程序,它的一个职责是采用* .jrxml文件的路径并编译它。

I have a standalone application, and one of its duties is to take the path of a *.jrxml file and compile it.

我可以毫无问题地做到这一点直到带有子报表的报表出现,其中master的编译没有编译它的任何子项,导致后来在轨道下找不到子报表* .jasper文件。

I can do this without problem until a report with a subreport comes up, where the compilation of the master does not compile any of its children, resulting in a subreport *.jasper file not found later down the track.

有没有办法

1)设置JasperCompileManager自动获取子报告?

1) Set the JasperCompileManager to automatically pick up subreports?

2获取JasperDesign或JasperReport对象中包含的子报表的路径列表?

2) Get a list of paths to subreports contained within either a JasperDesign or JasperReport object?

我无法直接访问jrxml文件,因此修改报表以适应编译method不是一个选项,也不是应用任何标准命名方案来推断哪些子报表属于哪些报表。

I have no direct access to the jrxml files, so modifying the reports to suit the compile method is not an option, nor is applying any standard naming scheme to infer which subreports belong to which reports.

这里有一个类似的问题:

There is a similar problem here:

http://jasperforge.org/plugins/espforum/view.php?group_id=102&forumid=103&topicid=40683

其中JRVisitor用于生成JRSubreport对象列表,但是没有解释如何使用它来获取子报表的路径以便编译它并递归查找子报表的子报表,我无法弄清楚。 / p>

where a JRVisitor is used to produce a list of JRSubreport objects, however there is no explanation of how to use this to get a path to the subreport in order to compile it and recursively look for subreports of subreports, and I cant figure it out.

好的,所以它需要一些hackery,但我能够找到一些东西......

Ok, so it required a bit of hackery, but I was able to figure something out...

subreport.getExpression()。getText()返回主报表中子报表小部件的表达式字段,并且是一个看起来像这样的字符串

The subreport.getExpression().getText() returns the expression field of the subreport widget thing in the master report, and is a string that looks something like this

$P{SUBREPORT_DIR} + "/report_sub1.jasper"

所以我能够将它拆开以使用以下内容获取名称。它不理想,但应该坚持下去。

So I was able to pull it apart to get the name using the following. Its not ideal, but it should hold up.

JRElementsVisitor.visitReport(jasperReport, new JRVisitor(){

  // ** snip other overrides **

  @Override
  public void visitSubreport(JRSubreport subreport){
    String expression = subreport.getExpression().getText().replace(".jasper", ".jrxml");
    StringTokenizer st = new StringTokenizer(expression, "\"/");
    String subreportName = null;
    while(st.hasMoreTokens())
      subreportName = st.nextToken();
    compileReport(subreportName);
  }
}

编辑:

这是我的整个compileReport方法,演示如何递归编译子报告的子报告等。不完美,但对我的应用程序来说已经足够了。所有已编译的* .jasper文件都会保存回磁盘上与未编译的* .jrxml文件被拾取的位置相同,但这并不难改变。编译的主要回购如果要运行它或其他任何内容,则会传回rt对象。

Here is my whole compileReport method, demonstrating how to recursively compile subreports of subreports etc. Not perfect, but good enough for my app. All compiled *.jasper files are saved back onto disk in the same location as the uncompiled *.jrxml files were picked up, but this wouldn't be hard to change. The compiled main report object is passed back incase you want to run it or whatever.

请记住,此代码在此编辑时已有9个月的历史记录,以及更新版本的Jasper Reports现在可能有这种东西的inbuild函数。

Remember that this code is 9 months old at the time of this edit, and newer versions of Jasper Reports may now have an inbuild functions for this kind of thing.

private static final String reportsPath = "someplace/nice/";
private ArrayList<String>   completedSubReports = new ArrayList<String>(30);
private Throwable           subReportException  = null;

/**
 * Recursively compile report and subreports
 */
public JasperReport compileReport(String reportName) throws Throwable{
  JasperDesign jasperDesign = JRXmlLoader.load(reportsPath + reportName + ".jrxml");
  JasperReport jasperReport = JasperCompileManager.compileReport(jasperDesign);
  JRSaver.saveObject(jasperReport, reportsPath + reportName + ".jasper");
  toLog("Saving compiled report to: " + reportsPath + reportName + ".jasper");
  //Compile sub reports
  JRElementsVisitor.visitReport(jasperReport, new JRVisitor(){
    @Override
    public void visitBreak(JRBreak breakElement){}

    @Override
    public void visitChart(JRChart chart){}

    @Override
    public void visitCrosstab(JRCrosstab crosstab){}

    @Override
    public void visitElementGroup(JRElementGroup elementGroup){}

    @Override
    public void visitEllipse(JREllipse ellipse){}

    @Override
    public void visitFrame(JRFrame frame){}

    @Override
    public void visitImage(JRImage image){}

    @Override
    public void visitLine(JRLine line){}

    @Override
    public void visitRectangle(JRRectangle rectangle){}

    @Override
    public void visitStaticText(JRStaticText staticText){}

    @Override
    public void visitSubreport(JRSubreport subreport){
      try{
        String expression = subreport.getExpression().getText().replace(".jasper", "");
        StringTokenizer st = new StringTokenizer(expression, "\"/");
        String subReportName = null;
        while(st.hasMoreTokens())
          subReportName = st.nextToken();
        //Sometimes the same subreport can be used multiple times, but
        //there is no need to compile multiple times
        if(completedSubReports.contains(subReportName)) return;
        completedSubReports.add(subReportName);
        compileReport(subReportName);
      }
      catch(Throwable e){
        subReportException = e;
      }
    }
    @Override
    public void visitTextField(JRTextField textField){}

    @Override
    public void visitComponentElement(JRComponentElement componentElement){}

    @Override
    public void visitGenericElement(JRGenericElement element){}
  });
  if(subReportException != null) throw new RuntimeException(subReportException);
  return jasperReport;
}