2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 package org
.libreoffice
.report
.pentaho
;
20 import org
.libreoffice
.report
.DataSourceFactory
;
21 import org
.libreoffice
.report
.ImageService
;
22 import org
.libreoffice
.report
.InputRepository
;
23 import org
.libreoffice
.report
.JobDefinitionException
;
24 import org
.libreoffice
.report
.JobProperties
;
25 import org
.libreoffice
.report
.OutputRepository
;
26 import org
.libreoffice
.report
.ParameterMap
;
27 import org
.libreoffice
.report
.ReportEngineParameterNames
;
28 import org
.libreoffice
.report
.ReportExecutionException
;
29 import org
.libreoffice
.report
.ReportJob
;
30 import org
.libreoffice
.report
.ReportJobDefinition
;
31 import org
.libreoffice
.report
.SDBCReportDataFactory
;
32 import org
.libreoffice
.report
.pentaho
.loader
.InputRepositoryLoader
;
33 import org
.libreoffice
.report
.pentaho
.model
.OfficeDetailSection
;
34 import org
.libreoffice
.report
.pentaho
.model
.OfficeDocument
;
35 import org
.libreoffice
.report
.pentaho
.model
.OfficeGroup
;
36 import org
.libreoffice
.report
.pentaho
.model
.OfficeReport
;
37 import org
.libreoffice
.report
.pentaho
.output
.chart
.ChartRawReportProcessor
;
38 import org
.libreoffice
.report
.pentaho
.output
.spreadsheet
.SpreadsheetRawReportProcessor
;
39 import org
.libreoffice
.report
.pentaho
.output
.text
.TextRawReportProcessor
;
41 import java
.io
.IOException
;
43 import java
.util
.ArrayList
;
44 import java
.util
.List
;
45 import java
.util
.logging
.Logger
;
47 import org
.jfree
.report
.expressions
.Expression
;
48 import org
.jfree
.report
.expressions
.FormulaExpression
;
49 import org
.jfree
.report
.flow
.DefaultReportJob
;
50 import org
.jfree
.report
.flow
.ReportProcessor
;
51 import org
.jfree
.report
.flow
.raw
.XmlPrintReportProcessor
;
52 import org
.jfree
.report
.structure
.Node
;
53 import org
.jfree
.report
.structure
.Section
;
54 import org
.jfree
.report
.util
.ReportParameters
;
56 import org
.pentaho
.reporting
.libraries
.formula
.lvalues
.ContextLookup
;
57 import org
.pentaho
.reporting
.libraries
.formula
.lvalues
.FormulaFunction
;
58 import org
.pentaho
.reporting
.libraries
.formula
.lvalues
.LValue
;
59 import org
.pentaho
.reporting
.libraries
.formula
.lvalues
.Term
;
60 import org
.pentaho
.reporting
.libraries
.formula
.parser
.FormulaParser
;
61 import org
.pentaho
.reporting
.libraries
.formula
.parser
.ParseException
;
62 import org
.pentaho
.reporting
.libraries
.resourceloader
.Resource
;
63 import org
.pentaho
.reporting
.libraries
.resourceloader
.ResourceException
;
64 import org
.pentaho
.reporting
.libraries
.resourceloader
.ResourceManager
;
68 * ToDo: Allow interrupting of jobs and report the report progress
70 public class PentahoReportJob
implements ReportJob
73 private static final Logger LOGGER
= Logger
.getLogger(PentahoReportJob
.class.getName());
74 private final DataSourceFactory dataSourceFactory
;
75 private final OutputRepository outputRepository
;
76 private final JobProperties jobProperties
;
77 private OfficeDocument report
;
78 private final ResourceManager resourceManager
;
79 private final String outputName
;
80 private final ImageService imageService
;
81 private final InputRepository inputRepository
;
82 private final List masterValues
;
83 private final List detailColumns
;
85 public PentahoReportJob(final ReportJobDefinition definition
)
86 throws JobDefinitionException
88 if (definition
== null)
90 throw new NullPointerException();
93 this.jobProperties
= definition
.getProcessingParameters().copy();
95 this.dataSourceFactory
= (DataSourceFactory
) jobProperties
.getProperty(ReportEngineParameterNames
.INPUT_DATASOURCE_FACTORY
);
96 if (this.dataSourceFactory
== null)
98 throw new JobDefinitionException("DataSourceFactory must not be null.");
101 this.outputRepository
= (OutputRepository
) jobProperties
.getProperty(ReportEngineParameterNames
.OUTPUT_REPOSITORY
);
102 if (this.outputRepository
== null)
104 throw new JobDefinitionException("OutputRepository must not be null.");
107 this.inputRepository
=
108 (InputRepository
) jobProperties
.getProperty(ReportEngineParameterNames
.INPUT_REPOSITORY
);
109 if (inputRepository
== null)
111 throw new JobDefinitionException("InputRepository must not be null.");
114 this.outputName
= (String
) jobProperties
.getProperty(ReportEngineParameterNames
.OUTPUT_NAME
);
115 if (outputName
== null)
117 throw new JobDefinitionException("OutputName must not be null");
120 this.imageService
= (ImageService
) jobProperties
.getProperty(ReportEngineParameterNames
.IMAGE_SERVICE
);
121 if (imageService
== null)
123 throw new JobDefinitionException("A valid image-service implementation must be given.");
126 this.masterValues
= (ArrayList
<?
>) jobProperties
.getProperty(ReportEngineParameterNames
.INPUT_MASTER_VALUES
);
127 this.detailColumns
= (ArrayList
<?
>) jobProperties
.getProperty(ReportEngineParameterNames
.INPUT_DETAIL_COLUMNS
);
129 this.resourceManager
= new ResourceManager();
130 this.resourceManager
.registerDefaults();
131 this.resourceManager
.registerLoader(new InputRepositoryLoader(inputRepository
));
135 this.report
= parseReport(definition
);
137 catch (ResourceException e
)
139 throw new JobDefinitionException("Failed to parse the report.", e
);
143 private OfficeDocument
parseReport(final ReportJobDefinition definition
)
144 throws ResourceException
, JobDefinitionException
146 final String reportResource
= (String
) this.jobProperties
.getProperty(ReportEngineParameterNames
.INPUT_NAME
);
147 if (reportResource
== null)
149 throw new JobDefinitionException("Report definition name must be given");
152 final Resource res
= resourceManager
.createDirectly("sun:oo://" + reportResource
, OfficeDocument
.class);
153 final OfficeDocument tempReport
= (OfficeDocument
) res
.getResource();
154 tempReport
.setDataFactory(new StarReportDataFactory(dataSourceFactory
));
155 tempReport
.setJobProperties(definition
.getProcessingParameters().copy());
156 final ReportParameters inputParameters
= tempReport
.getInputParameters();
158 final ParameterMap queryParameters
= definition
.getQueryParameters();
159 final String
[] paramKeys
= queryParameters
.keys();
160 for (int i
= 0; i
< paramKeys
.length
; i
++)
162 final String key
= paramKeys
[i
];
163 inputParameters
.put(key
, queryParameters
.get(key
));
169 private void collectGroupExpressions(final Node
[] nodes
, final List
<Object
[]> expressions
, final FormulaParser parser
, final Expression reportFunctions
[])
171 for (int i
= 0; i
< nodes
.length
; i
++)
173 final Node node
= nodes
[i
];
174 if (node
instanceof OfficeGroup
)
176 final OfficeGroup group
= (OfficeGroup
) node
;
177 final FormulaExpression exp
= (FormulaExpression
) group
.getGroupingExpression();
185 final String expression
= exp
.getFormulaExpression();
186 if (expression
== null)
190 final FormulaFunction function
= (FormulaFunction
) parser
.parse(expression
);
191 final LValue
[] parameters
= function
.getChildValues();
192 if (parameters
.length
> 0)
194 String name
= parameters
[0].toString();
195 if (parameters
[0] instanceof ContextLookup
)
197 final ContextLookup context
= (ContextLookup
) parameters
[0];
198 name
= context
.getName();
200 for (int j
= 0; j
< reportFunctions
.length
; j
++)
202 if (reportFunctions
[j
] instanceof FormulaExpression
)
204 final FormulaExpression reportExp
= (FormulaExpression
) reportFunctions
[j
];
206 if (reportExp
.getName().equals(name
))
208 LValue val
= parser
.parse(reportExp
.getFormulaExpression());
209 while( !(val
instanceof ContextLookup
))
211 if (val
instanceof Term
)
213 val
= ((Term
)val
).getHeadValue();
215 else if (val
instanceof FormulaFunction
)
217 final FormulaFunction reportFunction
= (FormulaFunction
) val
;
218 val
= reportFunction
.getChildValues()[0];
221 final ContextLookup context
= (ContextLookup
) val
;
222 name
= context
.getName();
228 final Object
[] pair
= new Object
[2];
230 pair
[1] = group
.getAttribute(OfficeNamespaces
.OOREPORT_NS
, "sort-ascending");
231 expressions
.add(pair
);
234 catch (ParseException ex
)
236 LOGGER
.severe("ReportProcessing failed: " + ex
);
239 else if (node
instanceof OfficeDetailSection
)
243 if (node
instanceof Section
)
245 final Section section
= (Section
) node
;
246 collectGroupExpressions(section
.getNodeArray(), expressions
, parser
, reportFunctions
);
251 private void collectSortExpressions(final Node
[] nodes
, final List
<Object
[]> expressions
, final FormulaParser parser
, final Expression reportFunctions
[])
253 for (int i
= 0; i
< nodes
.length
; i
++)
255 final Node node
= nodes
[i
];
256 if (node
instanceof OfficeGroup
)
258 final OfficeGroup group
= (OfficeGroup
) node
;
259 final String exp
= group
.getSortingExpression();
265 final Object
[] pair
= new Object
[2];
267 pair
[1] = group
.getAttribute(OfficeNamespaces
.OOREPORT_NS
, "sort-ascending");
268 expressions
.add(pair
);
270 else if (node
instanceof OfficeDetailSection
)
274 if (node
instanceof Section
)
276 final Section section
= (Section
) node
;
277 collectSortExpressions(section
.getNodeArray(), expressions
, parser
, reportFunctions
);
282 private void setMetaDataProperties(DefaultReportJob job
)
284 job
.getConfiguration().setConfigProperty(ReportEngineParameterNames
.AUTHOR
, (String
) jobProperties
.getProperty(ReportEngineParameterNames
.AUTHOR
));
285 job
.getConfiguration().setConfigProperty(ReportEngineParameterNames
.TITLE
, (String
) jobProperties
.getProperty(ReportEngineParameterNames
.TITLE
));
289 * Although we might want to run the job as soon as it has been created, sometimes it is
290 * wiser to let the user add some listeners first. If we execute at once, the user
291 * either has to deal with threading code or won't receive any progress information in
292 * single threaded environments.
294 public void execute()
295 throws ReportExecutionException
, IOException
297 final DefaultReportJob job
= new DefaultReportJob(report
);
298 setMetaDataProperties(job
);
299 final String contentType
= (String
) jobProperties
.getProperty(ReportEngineParameterNames
.CONTENT_TYPE
);
300 //noinspection OverlyBroadCatchBlock
303 final ReportParameters parameters
= job
.getParameters();
305 if (masterValues
!= null && detailColumns
!= null)
307 parameters
.put(SDBCReportDataFactory
.MASTER_VALUES
, masterValues
);
308 parameters
.put(SDBCReportDataFactory
.DETAIL_COLUMNS
, detailColumns
);
311 final Node
[] nodes
= report
.getNodeArray();
313 final FormulaParser parser
= new FormulaParser();
314 final OfficeReport officeReport
= (OfficeReport
) ((Section
) nodes
[0]).getNode(0);
315 final Section reportBody
= (Section
) officeReport
.getBodySection();
316 final ArrayList
<Object
[]> sortExpressions
= new ArrayList
<Object
[]>();
317 collectSortExpressions(reportBody
.getNodeArray(), sortExpressions
, parser
, officeReport
.getExpressions());
318 parameters
.put(SDBCReportDataFactory
.SORT_EXPRESSIONS
, sortExpressions
);
319 final ArrayList
<Object
[]> groupExpressions
= new ArrayList
<Object
[]>();
320 collectGroupExpressions(reportBody
.getNodeArray(), groupExpressions
, parser
, officeReport
.getExpressions());
321 parameters
.put(SDBCReportDataFactory
.GROUP_EXPRESSIONS
, groupExpressions
);
322 final String command
= (String
) officeReport
.getAttribute(OfficeNamespaces
.OOREPORT_NS
, "command");
323 final String commandType
= (String
) officeReport
.getAttribute(OfficeNamespaces
.OOREPORT_NS
, SDBCReportDataFactory
.COMMAND_TYPE
);
324 final String escapeProcessing
= (String
) officeReport
.getAttribute(OfficeNamespaces
.OOREPORT_NS
, SDBCReportDataFactory
.ESCAPE_PROCESSING
);
325 report
.setQuery(command
);
326 parameters
.put(SDBCReportDataFactory
.COMMAND_TYPE
, commandType
);
327 parameters
.put(SDBCReportDataFactory
.ESCAPE_PROCESSING
, !("false".equals(escapeProcessing
)));
329 final String filter
= (String
) officeReport
.getAttribute(OfficeNamespaces
.OOREPORT_NS
, "filter");
330 parameters
.put(SDBCReportDataFactory
.UNO_FILTER
, filter
);
332 parameters
.put(ReportEngineParameterNames
.MAXROWS
, report
.getJobProperties().getProperty(ReportEngineParameterNames
.MAXROWS
));
334 final long startTime
= System
.currentTimeMillis();
335 final ReportProcessor rp
= getProcessorForContentType(contentType
);
336 rp
.processReport(job
);
338 final long endTime
= System
.currentTimeMillis();
339 LOGGER
.config("Report processing time: " + (endTime
- startTime
));
341 catch (final Exception e
)
343 String message
= e
.getMessage();
344 if (message
== null || message
.length() == 0)
346 message
= "Failed to process the report";
348 throw new ReportExecutionException(message
, e
);
353 private ReportProcessor
getProcessorForContentType(final String mimeType
)
354 throws ReportExecutionException
356 final ReportProcessor ret
;
358 if (PentahoReportEngineMetaData
.OPENDOCUMENT_SPREADSHEET
.equals(mimeType
))
360 ret
= new SpreadsheetRawReportProcessor(inputRepository
, outputRepository
, outputName
, imageService
, dataSourceFactory
);
362 else if (PentahoReportEngineMetaData
.OPENDOCUMENT_TEXT
.equals(mimeType
))
364 ret
= new TextRawReportProcessor(inputRepository
, outputRepository
, outputName
, imageService
, dataSourceFactory
);
366 else if (PentahoReportEngineMetaData
.OPENDOCUMENT_CHART
.equals(mimeType
))
368 ret
= new ChartRawReportProcessor(inputRepository
, outputRepository
, outputName
, imageService
, dataSourceFactory
);
370 else if (PentahoReportEngineMetaData
.DEBUG
.equals(mimeType
))
372 ret
= new XmlPrintReportProcessor(System
.out
, "ISO-8859-1");
376 throw new ReportExecutionException("Invalid mime-type");