Actually show some data with JasperReports.
[online-glom:gwt-glom.git] / src / main / java / org / glom / web / server / ReportGenerator.java
1 /*
2  * Copyright (C) 2012 Openismus GmbH
3  *
4  * This file is part of GWT-Glom.
5  *
6  * GWT-Glom is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or (at your
9  * option) any later version.
10  *
11  * GWT-Glom is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
14  * for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with GWT-Glom.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package org.glom.web.server;
21
22 import java.io.ByteArrayOutputStream;
23 import java.sql.Connection;
24 import java.util.HashMap;
25
26 import net.sf.jasperreports.engine.JRException;
27 import net.sf.jasperreports.engine.JasperCompileManager;
28 import net.sf.jasperreports.engine.JasperFillManager;
29 import net.sf.jasperreports.engine.JasperPrint;
30 import net.sf.jasperreports.engine.JasperReport;
31 import net.sf.jasperreports.engine.design.JRDesignBand;
32 import net.sf.jasperreports.engine.design.JRDesignExpression;
33 import net.sf.jasperreports.engine.design.JRDesignField;
34 import net.sf.jasperreports.engine.design.JRDesignQuery;
35 import net.sf.jasperreports.engine.design.JRDesignSection;
36 import net.sf.jasperreports.engine.design.JRDesignStaticText;
37 import net.sf.jasperreports.engine.design.JRDesignTextField;
38 import net.sf.jasperreports.engine.design.JasperDesign;
39 import net.sf.jasperreports.engine.export.JRHtmlExporterParameter;
40 import net.sf.jasperreports.engine.export.JRXhtmlExporter;
41
42 import org.apache.commons.lang.StringUtils;
43 import org.glom.libglom.Document;
44 import org.glom.libglom.Glom;
45 import org.glom.libglom.LayoutFieldVector;
46 import org.glom.libglom.LayoutGroup;
47 import org.glom.libglom.LayoutItemVector;
48 import org.glom.libglom.LayoutItem_Field;
49 import org.glom.libglom.Relationship;
50 import org.glom.libglom.SortClause;
51 import org.glom.libglom.SqlBuilder;
52 import org.glom.libglom.SqlExpr;
53 import org.glom.libglom.Value;
54
55 /**
56  * @author Murray Cumming <murrayc@openimus.com>
57  * 
58  */
59 public class ReportGenerator {
60
61         final int height = 30;
62         LayoutFieldVector fieldsToGet = new LayoutFieldVector();
63
64         /**
65          * @param tableName
66          * @param reportName
67          * @param configuredDoc
68          * @param layout_group
69          * @return
70          */
71         public String generateReport(final Document document, final String tableName, final String reportName,
72                         final Connection connection, final org.glom.libglom.LayoutGroup layout_group) {
73
74                 final JasperDesign design = new JasperDesign();
75                 design.setName(reportName); // TODO: Actually, we want the title.
76
77                 final JRDesignBand titleBand = new JRDesignBand();
78                 titleBand.setHeight(height);
79                 final JRDesignStaticText staticTitle = new JRDesignStaticText();
80                 staticTitle.setText("debug: test report title text");
81                 titleBand.addElement(staticTitle);
82                 design.setTitle(titleBand);
83
84                 final JRDesignBand detailBand = new JRDesignBand();
85                 detailBand.setHeight(height + 20);
86
87                 fieldsToGet = new LayoutFieldVector();
88                 final int x = 0;
89                 addToReport(layout_group, design, detailBand, x);
90
91                 ((JRDesignSection) design.getDetailSection()).addBand(detailBand);
92
93                 // Later versions of libglom actually return an empty SqlExpr when quickFindValue is empty,
94                 // but let's be sure:
95                 final String quickFind = ""; // TODO
96                 final SortClause sortClause = new SortClause(); // TODO
97                 SqlExpr whereClause;
98                 if (StringUtils.isEmpty(quickFind)) {
99                         whereClause = new SqlExpr();
100                 } else {
101                         final Value quickFindValue = new Value(quickFind);
102                         whereClause = Glom.get_find_where_clause_quick(document, tableName, quickFindValue);
103                 }
104
105                 final Relationship extraJoin = new Relationship(); // Ignored.
106                 final SqlBuilder builder = Glom.build_sql_select_with_where_clause(tableName, fieldsToGet, whereClause,
107                                 extraJoin, sortClause);
108                 final String sqlQuery = Glom.sqlbuilder_get_full_query(builder);
109
110                 final JRDesignQuery query = new JRDesignQuery();
111                 query.setText(sqlQuery); // TODO: quickfind and sort clause.
112                 design.setQuery(query);
113
114                 JasperReport report;
115                 try {
116                         report = JasperCompileManager.compileReport(design);
117                 } catch (final JRException ex) {
118                         ex.printStackTrace();
119                         return "Failed to Generate HTML: compileReport() failed.";
120                 }
121
122                 JasperPrint print;
123                 try {
124                         final HashMap<String, Object> parameters = new HashMap<String, Object>();
125                         parameters.put("ReportTitle", reportName); // TODO: Use the title, not the name.
126                         print = JasperFillManager.fillReport(report, parameters, connection);
127                 } catch (final JRException ex) {
128                         ex.printStackTrace();
129                         return "Failed to Generate HTML: fillReport() failed.";
130                 }
131
132                 final ByteArrayOutputStream output = new ByteArrayOutputStream();
133
134                 // We use this because there is no JasperExportManager.exportReportToHtmlStream() method.
135                 // JasperExportManager.exportReportToXmlStream(print, output);
136                 try {
137                         final JRXhtmlExporter exporter = new JRXhtmlExporter();
138                         exporter.setParameter(JRHtmlExporterParameter.JASPER_PRINT, print);
139                         exporter.setParameter(JRHtmlExporterParameter.OUTPUT_STREAM, output);
140
141                         // Use points instead of pixels for sizes, because pixels are silly
142                         // in HTML:
143                         exporter.setParameter(JRHtmlExporterParameter.SIZE_UNIT, "pt");
144
145                         exporter.exportReport();
146                 } catch (final JRException ex) {
147                         ex.printStackTrace();
148                         return "Failed to Generate HTML: exportReport() failed.";
149                 }
150
151                 // System.out.print(output.toString() + "\n");
152                 return output.toString();
153         }
154
155         /**
156          * @param layout_group
157          * @param design
158          * @param height
159          * @param detailBand
160          * @param x
161          */
162         private int addToReport(final org.glom.libglom.LayoutGroup layout_group, final JasperDesign design,
163                         final JRDesignBand detailBand, int x) {
164                 final LayoutItemVector layoutItemsVec = layout_group.get_items();
165                 final int numItems = Utils.safeLongToInt(layoutItemsVec.size());
166                 for (int i = 0; i < numItems; i++) {
167                         final org.glom.libglom.LayoutItem libglomLayoutItem = layoutItemsVec.get(i);
168
169                         final LayoutGroup libglomLayoutGroup = LayoutGroup.cast_dynamic(libglomLayoutItem);
170                         final LayoutItem_Field libglomLayoutItemField = LayoutItem_Field.cast_dynamic(libglomLayoutItem);
171                         if (libglomLayoutItemField != null) {
172                                 fieldsToGet.add(libglomLayoutItemField);
173
174                                 final String fieldName = libglomLayoutItemField.get_name();
175                                 // System.out.print("fieldName=" + fieldName + "\n");
176
177                                 // Tell the JasperDesign about the database field that will be in the SQL query,
178                                 // specified later:
179                                 final JRDesignField field = new JRDesignField();
180                                 field.setName(fieldName); // TODO: Related fields.
181
182                                 // Choose a suitable java class type for the SQL field:
183                                 Class<?> klass = null;
184                                 switch (libglomLayoutItemField.get_glom_type()) {
185                                 case TYPE_TEXT:
186                                         klass = java.lang.String.class;
187                                         break;
188                                 case TYPE_BOOLEAN:
189                                         klass = java.lang.Boolean.class;
190                                         break;
191                                 case TYPE_NUMERIC:
192                                         klass = java.lang.Double.class;
193                                         break;
194                                 case TYPE_DATE:
195                                         klass = java.util.Date.class;
196                                         break;
197                                 case TYPE_TIME:
198                                         klass = java.sql.Time.class;
199                                         break;
200                                 case TYPE_IMAGE:
201                                         klass = java.sql.Blob.class; // TODO: This does not work.
202                                         break;
203                                 }
204                                 field.setValueClass(klass);
205
206                                 try {
207                                         design.addField(field);
208                                 } catch (final JRException e2) {
209                                         // TODO Auto-generated catch block
210                                         e2.printStackTrace();
211                                 }
212
213                                 // Tell the JasperDesign to show an instance of the field:
214                                 final JRDesignTextField textField = new JRDesignTextField();
215
216                                 // Make sure this field starts at the right of the previous field,
217                                 // because JasperReports uses absolute positioning.
218                                 textField.setY(0);
219                                 textField.setX(x);
220
221                                 // An arbitrary width, because we must specify _some_ width:
222                                 final int width = 100; // Points, as specified later.
223                                 textField.setWidth(width); // No data will be shown without this.
224                                 x += width;
225
226                                 // This only stretches vertically, but that is better than
227                                 // nothing.
228                                 textField.setStretchWithOverflow(true);
229                                 textField.setHeight(height); // We must specify _some_ height.
230
231                                 // TODO: Where is this format documented?
232                                 final JRDesignExpression expression = new JRDesignExpression();
233                                 expression.setText("$F{" + fieldName + "}");
234
235                                 textField.setExpression(expression);
236                                 detailBand.addElement(textField);
237                         } else if (libglomLayoutGroup != null) {
238                                 // Recurse into sub-groups:
239                                 x = addToReport(libglomLayoutGroup, design, detailBand, x);
240                         }
241                 }
242
243                 return x;
244         }
245 }