001: /*
002: * Copyright 2006-2007 Pentaho Corporation. All rights reserved.
003: * This software was developed by Pentaho Corporation and is provided under the terms
004: * of the Mozilla Public License, Version 1.1, or any later version. You may not use
005: * this file except in compliance with the license. If you need a copy of the license,
006: * please go to http://www.mozilla.org/MPL/MPL-1.1.txt.
007: *
008: * Software distributed under the Mozilla Public License is distributed on an "AS IS"
009: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. Please refer to
010: * the license for the specific language governing your rights and limitations.
011: *
012: * Additional Contributor(s): Martin Schmid gridvision engineering GmbH
013: */
014: package org.pentaho.reportdesigner.crm.report.reportexporter.jfreereport;
015:
016: import org.apache.commons.lang.StringUtils;
017: import org.gjt.xpp.XmlPullNode;
018: import org.gjt.xpp.XmlPullParser;
019: import org.gjt.xpp.XmlPullParserFactory;
020: import org.jetbrains.annotations.NonNls;
021: import org.jetbrains.annotations.NotNull;
022: import org.jetbrains.annotations.Nullable;
023: import org.jfree.report.AbstractReportDefinition;
024: import org.jfree.report.DataFactory;
025: import org.jfree.report.DataRow;
026: import org.jfree.report.JFreeReport;
027: import org.jfree.report.JFreeReportBoot;
028: import org.jfree.report.ReportDataFactoryException;
029: import org.jfree.report.TableDataFactory;
030: import org.jfree.report.modules.gui.base.PreviewFrame;
031: import org.jfree.report.modules.parser.ext.factory.base.ArrayClassFactory;
032: import org.jfree.report.modules.parser.ext.factory.base.URLClassFactory;
033: import org.jfree.report.modules.parser.ext.factory.datasource.DefaultDataSourceFactory;
034: import org.jfree.report.modules.parser.ext.factory.elements.DefaultElementFactory;
035: import org.jfree.report.modules.parser.ext.factory.objects.BandLayoutClassFactory;
036: import org.jfree.report.modules.parser.ext.factory.objects.DefaultClassFactory;
037: import org.jfree.report.modules.parser.ext.factory.stylekey.DefaultStyleKeyFactory;
038: import org.jfree.report.modules.parser.ext.factory.stylekey.PageableLayoutStyleKeyFactory;
039: import org.jfree.report.modules.parser.ext.factory.templates.DefaultTemplateCollection;
040: import org.jfree.report.modules.parser.extwriter.ReportWriter;
041: import org.jfree.report.util.ReportConfiguration;
042: import org.jfree.xmlns.parser.AbstractXmlResourceFactory;
043: import org.pentaho.reportdesigner.crm.report.ReportDialog;
044: import org.pentaho.reportdesigner.crm.report.ReportDialogConstants;
045: import org.pentaho.reportdesigner.crm.report.datasetplugin.multidataset.MultiDataSetReportElement;
046: import org.pentaho.reportdesigner.crm.report.model.Report;
047: import org.pentaho.reportdesigner.crm.report.model.ReportElement;
048: import org.pentaho.reportdesigner.crm.report.model.StaticImageReportElement;
049: import org.pentaho.reportdesigner.crm.report.model.SubReport;
050: import org.pentaho.reportdesigner.crm.report.model.dataset.DataSetsReportElement;
051: import org.pentaho.reportdesigner.crm.report.reportelementinfo.ReportElementInfoFactory;
052: import org.pentaho.reportdesigner.crm.report.reportexporter.ReportCreationException;
053: import org.pentaho.reportdesigner.crm.report.reportexporter.ReportExporter;
054: import org.pentaho.reportdesigner.crm.report.settings.WorkspaceSettings;
055: import org.pentaho.reportdesigner.lib.client.util.IOUtil;
056: import org.pentaho.reportdesigner.lib.common.xml.XMLConstants;
057: import org.pentaho.reportdesigner.lib.common.xml.XMLContext;
058: import org.pentaho.reportdesigner.lib.common.xml.XMLWriter;
059:
060: import javax.swing.table.TableModel;
061: import java.awt.event.WindowAdapter;
062: import java.awt.event.WindowEvent;
063: import java.io.BufferedReader;
064: import java.io.ByteArrayInputStream;
065: import java.io.ByteArrayOutputStream;
066: import java.io.File;
067: import java.io.FileInputStream;
068: import java.io.FileOutputStream;
069: import java.io.InputStream;
070: import java.io.InputStreamReader;
071: import java.io.OutputStreamWriter;
072: import java.io.Writer;
073: import java.lang.reflect.Field;
074: import java.util.ArrayList;
075: import java.util.logging.Level;
076: import java.util.logging.Logger;
077:
078: /**
079: * User: Martin
080: * Date: 28.10.2005
081: * Time: 08:37:52
082: */
083: public class JFreeReportFileExporter extends ReportExporter {
084: @NonNls
085: @NotNull
086: private static final Logger LOG = Logger
087: .getLogger(JFreeReportFileExporter.class.getName());
088:
089: @Nullable
090: private File reportFile;
091: @Nullable
092: private File xactionFile;
093: @Nullable
094: private String reportDescription;
095: private boolean exportXActionFile;
096: @Nullable
097: private String reportNameString;
098: @Nullable
099: private String type;
100: private boolean useJNDIName;
101:
102: public JFreeReportFileExporter(@Nullable
103: File file, @Nullable
104: File xactionFile, boolean exportXActionFile, @Nullable
105: String reportNameString, @Nullable
106: String reportDescription, @Nullable
107: String type, boolean useJNDIName) {
108: this .reportFile = file;
109: this .xactionFile = xactionFile;
110: this .exportXActionFile = exportXActionFile;
111: this .reportNameString = reportNameString;
112: this .reportDescription = reportDescription;
113: this .type = type;
114: this .useJNDIName = useJNDIName;
115: }
116:
117: public void createReport(@NotNull
118: ReportDialog reportDialog, @NotNull
119: Report report, @NotNull
120: TableModel tableModel) throws ReportCreationException {
121: JFreeReportVisitor reportVisitor = new JFreeReportVisitor();
122: report.accept(null, reportVisitor);
123: JFreeReport jFreeReport = reportVisitor.getJFreeReport();
124:
125: if (jFreeReport.getDataFactory() instanceof NullDataFactory) {
126: jFreeReport.setDataFactory(new TableDataFactory(
127: ReportDialogConstants.DEFAULT_DATA_FACTORY,
128: tableModel));
129: }
130:
131: JFreeReportBoot.getInstance().start();
132:
133: final PreviewFrame preview = new PreviewFrame(jFreeReport);
134: preview.setIconImage(reportDialog.getIconImage());
135: preview.setTitle(reportDialog.getTitle());
136:
137: if (!WorkspaceSettings.getInstance().restoreFrameBounds(
138: preview, "PreviewFrame")) {
139: preview.setSize(800, 600);
140: }
141:
142: preview.addWindowListener(new WindowAdapter() {
143: public void windowClosing(@NotNull
144: WindowEvent e) {
145: WorkspaceSettings.getInstance().storeFrameBounds(
146: preview, "PreviewFrame");
147: }
148: });
149:
150: preview.setVisible(true);
151: }
152:
153: public void exportReport(boolean isSubReport, @NotNull
154: Report report) throws PublishException {
155: if (report == null) {
156: throw new IllegalArgumentException(
157: "report must not be null");
158: }
159:
160: try {
161: //create a copy, so we can modify names, some paths and so on without affecting the original report displayed in the designer
162: Report newReport = createDeepCopy(report);
163: newReport.setName(reportNameString);
164:
165: File tempDirectory = reportFile.getParentFile();//NON-NLS
166: tempDirectory.mkdirs();
167:
168: // write the images out first, because we will be updating the image elements
169: // this must be done before the report is written (or it will not be updated when saved)
170: byte[] buffer = new byte[8192];
171: try {
172: ArrayList<StaticImageReportElement> imageElements = new ArrayList<StaticImageReportElement>();
173: getImageElements(newReport, imageElements);
174: for (int i = 0; i < imageElements.size(); i++) {
175: // roll through all images
176: StaticImageReportElement imageElement = imageElements
177: .get(i);
178: // for all file urls, update them to the new location and write their contents as well t0 the "tempDirectory" from above
179: if (!"http".equals(imageElement.getUrl()
180: .getProtocol()))//NON-NLS
181: {
182: InputStream is = imageElement.getUrl()
183: .openStream();
184: FileOutputStream fos = null;
185: File destFile = new File(
186: tempDirectory,
187: reportNameString
188: + "_staticImage"
189: + i
190: + imageElement
191: .getUrl()
192: .getFile()
193: .substring(
194: imageElement
195: .getUrl()
196: .getFile()
197: .lastIndexOf(
198: '.')));//NON-NLS
199: try {
200: //noinspection IOResourceOpenedButNotSafelyClosed
201: fos = new FileOutputStream(destFile);
202: int len;
203: while ((len = is.read(buffer)) != -1) {
204: fos.write(buffer, 0, len);
205: }
206: } finally {
207: IOUtil.closeStream(is);
208: IOUtil.closeStream(fos);
209: }
210: imageElement.setUrl(destFile.toURI().toURL());
211: }
212: }
213: } catch (Throwable e) {
214: if (LOG.isLoggable(Level.FINE))
215: LOG
216: .log(
217: Level.FINE,
218: "JFreeReportPublishOnServerExporter.exportReport ",
219: e);
220: }
221:
222: JFreeReportVisitor reportVisitor = new JFreeReportVisitor();
223:
224: newReport.accept(null, reportVisitor);
225:
226: JFreeReport jFreeReport = reportVisitor.getJFreeReport();
227:
228: final ReportConfiguration config = new ReportConfiguration(
229: jFreeReport.getReportConfiguration());
230: config.setConfigProperty(
231: AbstractXmlResourceFactory.CONTENTBASE_KEY,
232: new File(".").toURI().toURL().toExternalForm());
233: jFreeReport.getReportConfiguration().setConfigProperty(
234: "org.jfree.report.NoPrinterAvailable",
235: Boolean.TRUE.toString());
236:
237: // MB - 6/9/07 - Hack Alert PRD-144
238: //
239: // There is a bug in the JFreeReport writer for the XML that causes an
240: // empty <data-factory/> tag to be written to the exported XML. As a
241: // result, the report cannot be read in and parsed by the platform. So,
242: // I put in the following hack which needs to be removed when the JFreeReport
243: // engine has been properly fixed.
244: //
245: hackReport(jFreeReport);
246:
247: if (newReport.isUseMaxCharBounds()) {
248: config
249: .setConfigProperty(
250: "org.jfree.report.layout.fontrenderer.UseMaxCharBounds",
251: Boolean.TRUE.toString());//NON-NLS
252: }
253:
254: final ReportWriter writer = new ReportWriter(jFreeReport,
255: XMLConstants.ENCODING, config);
256: writer.addClassFactoryFactory(new URLClassFactory());
257: writer.addClassFactoryFactory(new DefaultClassFactory());
258: writer.addClassFactoryFactory(new BandLayoutClassFactory());
259: writer.addClassFactoryFactory(new ArrayClassFactory());
260:
261: writer.addStyleKeyFactory(new DefaultStyleKeyFactory());
262: writer
263: .addStyleKeyFactory(new PageableLayoutStyleKeyFactory());
264: writer
265: .addTemplateCollection(new DefaultTemplateCollection());
266: writer.addElementFactory(new DefaultElementFactory());
267: writer.addDataSourceFactory(new DefaultDataSourceFactory());
268:
269: //export the JFreeReport xml
270: Writer outputStreamWriter = null;
271: try {
272: //noinspection IOResourceOpenedButNotSafelyClosed
273: outputStreamWriter = new OutputStreamWriter(
274: new FileOutputStream(reportFile),
275: XMLConstants.ENCODING);
276: writer.write(outputStreamWriter);
277: } finally {
278: IOUtil.closeStream(outputStreamWriter);
279: }
280:
281: //export the xaction xml
282: try {
283: if (exportXActionFile) {
284: //noinspection IOResourceOpenedButNotSafelyClosed
285: outputStreamWriter = new OutputStreamWriter(
286: new FileOutputStream(xactionFile),
287: XMLConstants.ENCODING);
288: }
289: DataSetsReportElement dataSetsReportElement = newReport
290: .getDataSetsReportElement();
291: String xactionName = xactionFile != null ? xactionFile
292: .getName() : "";
293: String xactionContent = XActionHelper.getXActionFile(
294: xactionName, tempDirectory.getName(), newReport
295: .getName(), reportDescription, type,
296: reportFile.getName(), useJNDIName,
297: dataSetsReportElement);
298:
299: if (exportXActionFile) {
300: outputStreamWriter.write(xactionContent);
301: }
302: } finally {
303: IOUtil.closeStream(outputStreamWriter);
304: }
305:
306: //prepare the XQuery data file
307: //prepare the mondrian definition file
308: //prepare the local image files
309:
310: ArrayList<File> filesToCopyToServer = new ArrayList<File>();
311:
312: // export all mondrian cube files and xquery data files
313: for (ReportElement reportElement : newReport
314: .getDataSetsReportElement().getChildren()) {
315: if (reportElement instanceof MultiDataSetReportElement) {
316: MultiDataSetReportElement multiDataSetReportElement = (MultiDataSetReportElement) reportElement;
317: if (!StringUtils.isEmpty(multiDataSetReportElement
318: .getMondrianCubeDefinitionFile())) {
319: filesToCopyToServer
320: .add(new File(
321: multiDataSetReportElement
322: .getMondrianCubeDefinitionFile()));
323: }
324: if (!StringUtils.isEmpty(multiDataSetReportElement
325: .getXQueryDataFile())) {
326: filesToCopyToServer.add(new File(
327: multiDataSetReportElement
328: .getXQueryDataFile()));
329: }
330: }
331: }
332:
333: //copy everything to the location
334: for (File fileToCopy : filesToCopyToServer) {
335: File destFile = new File(tempDirectory, fileToCopy
336: .getName());
337: FileInputStream fis = null;
338: FileOutputStream fos = null;
339: try {
340: //noinspection IOResourceOpenedButNotSafelyClosed
341: fis = new FileInputStream(fileToCopy);
342: //noinspection IOResourceOpenedButNotSafelyClosed
343: fos = new FileOutputStream(destFile);
344:
345: int len;
346: while ((len = fis.read(buffer)) != -1) {
347: fos.write(buffer, 0, len);
348: }
349: } finally {
350: IOUtil.closeStream(fis);
351: IOUtil.closeStream(fos);
352: }
353: }
354: } catch (Exception e) {
355: throw new PublishException(e.getMessage(), e);
356: }
357: }
358:
359: @NotNull
360: private Report createDeepCopy(@NotNull
361: Report report) throws Exception {
362: //this IO stuff needs no native resources, so don't bother closing streams
363: ByteArrayOutputStream baos = new ByteArrayOutputStream(
364: 50 * 1024);//50 KB
365: XMLWriter xmlWriter = new XMLWriter(baos, true);
366: xmlWriter.writeDefaultProlog();
367:
368: XMLContext xmlContext = new XMLContext();
369:
370: if (report instanceof SubReport) {
371: xmlWriter.startElement(XMLConstants.SUBREPORT);
372: report.externalizeObject(xmlWriter, xmlContext);
373: xmlWriter.closeElement(XMLConstants.SUBREPORT);
374: } else {
375: xmlWriter.startElement(XMLConstants.REPORT);
376: report.externalizeObject(xmlWriter, xmlContext);
377: xmlWriter.closeElement(XMLConstants.REPORT);
378: }
379:
380: xmlWriter.close();
381:
382: XmlPullParserFactory xmlPullParserFactory = XmlPullParserFactory
383: .newInstance();
384: XmlPullParser xmlPullParser = xmlPullParserFactory
385: .newPullParser();
386: //noinspection IOResourceOpenedButNotSafelyClosed
387: BufferedReader bufferedReader = new BufferedReader(
388: new InputStreamReader(new ByteArrayInputStream(baos
389: .toByteArray()), XMLConstants.ENCODING));
390: xmlPullParser.setInput(bufferedReader);
391: xmlPullParser.next(); // get first start tag
392: XmlPullNode node = xmlPullParserFactory
393: .newPullNode(xmlPullParser);
394:
395: Report newReport = ReportElementInfoFactory.getInstance()
396: .getReportReportElementInfo().createReportElement();
397:
398: if (XMLConstants.REPORT.equals(node.getRawName())) {
399: newReport.readObject(node, xmlContext);
400: }
401:
402: node.resetPullNode();
403:
404: return newReport;
405: }
406:
407: /*
408: * MB - 6/9/07
409: *
410: * This method is temporary, and clearly a hack. The goal is to satisfy a bug
411: * in the writer for the report spec - essentially, the two conditions that
412: * must be satisfied fort the writer to not write out an empty data-factory
413: * tag. The bug is in org.jfree.report.modules.parser.reportwriter.DataFactoryWriter,
414: * and it has the following flawed logic:
415: *
416: * Line 119:
417: * if (query == null && dataFactoryClass == null)
418: *
419: * should be
420: *
421: * if (query == null || dataFactoryClass == null)
422: *
423: * As soon as this gets fixed, the hack can be removed. Until then, I have to be sure
424: * that the dataFactoryClass is null AND the query is null. The setter for dataFactory
425: * now prevents a null set (unlike 0.8.8_01). But, I can satisfy this part of the bug by
426: * setting a DataFactory that doesn't have a public constructor.
427: *
428: * The next part of the hack is that the report.query must be null. Well, the API prevents
429: * me from calling report.setQuery(null). The only way to satisfy the problem is to set the
430: * field to null. I use reflection below to fix this - first, I get the query field, then
431: * I force it to be a public variable (setAccessible), and then I set the value to null.
432: *
433: * Once these steps are done, then when the report gets written out as XML, the bug will
434: * be worked around.
435: *
436: */
437: private void hackReport(@NotNull
438: JFreeReport report) {
439: try {
440: // First part of the hack - set the data factory to a class that
441: // has no public constructor.
442: report.setDataFactory(new PlatformDataFactory());
443:
444: // Second part of the hack - I need to make sure the query
445: // field is null so that an empty data-factory tag doesn't get
446: // written to the xml file. This is such a hack, but it shouldn't
447: // last for very long.
448: Class<?> c2 = AbstractReportDefinition.class;
449: Field query = c2.getDeclaredField("query"); //$NON-NLS-1$
450: query.setAccessible(true);
451: query.set(report, null);
452:
453: } catch (Exception ex) {
454: if (LOG.isLoggable(Level.FINE))
455: LOG.log(Level.FINE,
456: "JFreeReportFileExporter.hackReport ", ex);
457: }
458:
459: }
460:
461: public static class PlatformDataFactory implements DataFactory {
462: @Nullable
463: public TableModel queryData(@Nullable
464: final String query, @Nullable
465: final DataRow parameters) throws ReportDataFactoryException {
466: return null;
467: }
468:
469: @Nullable
470: public DataFactory derive() throws ReportDataFactoryException {
471: return null;
472: }
473:
474: public void open() {
475: }
476:
477: public void close() {
478:
479: }
480:
481: private PlatformDataFactory() {
482: super();
483: }
484: }
485: }
|