2 * Copyright (C) 2012 Openismus GmbH
4 * This file is part of GWT-Glom.
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.
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
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/>.
20 package org.glom.web.server;
22 import java.io.ByteArrayOutputStream;
23 import java.sql.Connection;
24 import java.util.HashMap;
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.JRDesignGroup;
35 import net.sf.jasperreports.engine.design.JRDesignQuery;
36 import net.sf.jasperreports.engine.design.JRDesignSection;
37 import net.sf.jasperreports.engine.design.JRDesignStaticText;
38 import net.sf.jasperreports.engine.design.JRDesignTextField;
39 import net.sf.jasperreports.engine.design.JasperDesign;
40 import net.sf.jasperreports.engine.export.JRHtmlExporterParameter;
41 import net.sf.jasperreports.engine.export.JRXhtmlExporter;
43 import org.apache.commons.lang.StringUtils;
44 import org.glom.libglom.Document;
45 import org.glom.libglom.Glom;
46 import org.glom.libglom.LayoutFieldVector;
47 import org.glom.libglom.LayoutGroup;
48 import org.glom.libglom.LayoutItemVector;
49 import org.glom.libglom.LayoutItem_Field;
50 import org.glom.libglom.LayoutItem_GroupBy;
51 import org.glom.libglom.Relationship;
52 import org.glom.libglom.SortClause;
53 import org.glom.libglom.SortFieldPair;
54 import org.glom.libglom.SqlBuilder;
55 import org.glom.libglom.SqlExpr;
56 import org.glom.libglom.Value;
59 * @author Murray Cumming <murrayc@openimus.com>
62 public class ReportGenerator {
64 final int height = 30;
65 LayoutFieldVector fieldsToGet = new LayoutFieldVector();
66 SortClause sortClause = new SortClause();
71 * @param configuredDoc
75 public String generateReport(final Document document, final String tableName, final String reportName,
76 final Connection connection, final org.glom.libglom.LayoutGroup layout_group) {
78 final JasperDesign design = new JasperDesign();
79 design.setName(reportName); // TODO: Actually, we want the title.
81 final JRDesignBand titleBand = new JRDesignBand();
82 titleBand.setHeight(height);
83 final JRDesignStaticText staticTitle = new JRDesignStaticText();
84 staticTitle.setText("debug: test report title text");
85 titleBand.addElement(staticTitle);
86 design.setTitle(titleBand);
88 final JRDesignBand detailBand = new JRDesignBand();
89 detailBand.setHeight(height + 20);
91 fieldsToGet = new LayoutFieldVector();
93 addToReport(layout_group, design, detailBand, x);
95 ((JRDesignSection) design.getDetailSection()).addBand(detailBand);
97 // Later versions of libglom actually return an empty SqlExpr when quickFindValue is empty,
99 final String quickFind = ""; // TODO
101 if (StringUtils.isEmpty(quickFind)) {
102 whereClause = new SqlExpr();
104 final Value quickFindValue = new Value(quickFind);
105 whereClause = Glom.get_find_where_clause_quick(document, tableName, quickFindValue);
108 final Relationship extraJoin = new Relationship(); // Ignored.
109 final SqlBuilder builder = Glom.build_sql_select_with_where_clause(tableName, fieldsToGet, whereClause,
110 extraJoin, sortClause);
111 final String sqlQuery = Glom.sqlbuilder_get_full_query(builder);
113 final JRDesignQuery query = new JRDesignQuery();
114 query.setText(sqlQuery); // TODO: Extra sort clause to sort the rows within the groups.
115 design.setQuery(query);
119 report = JasperCompileManager.compileReport(design);
120 } catch (final JRException ex) {
121 ex.printStackTrace();
122 return "Failed to Generate HTML: compileReport() failed.";
127 final HashMap<String, Object> parameters = new HashMap<String, Object>();
128 parameters.put("ReportTitle", reportName); // TODO: Use the title, not the name.
129 print = JasperFillManager.fillReport(report, parameters, connection);
130 } catch (final JRException ex) {
131 ex.printStackTrace();
132 return "Failed to Generate HTML: fillReport() failed.";
135 final ByteArrayOutputStream output = new ByteArrayOutputStream();
137 // We use this because there is no JasperExportManager.exportReportToHtmlStream() method.
138 // JasperExportManager.exportReportToXmlStream(print, output);
140 final JRXhtmlExporter exporter = new JRXhtmlExporter();
141 exporter.setParameter(JRHtmlExporterParameter.JASPER_PRINT, print);
142 exporter.setParameter(JRHtmlExporterParameter.OUTPUT_STREAM, output);
144 // Use points instead of pixels for sizes, because pixels are silly
146 exporter.setParameter(JRHtmlExporterParameter.SIZE_UNIT, "pt");
148 exporter.exportReport();
149 } catch (final JRException ex) {
150 ex.printStackTrace();
151 return "Failed to Generate HTML: exportReport() failed.";
154 // System.out.print(output.toString() + "\n");
155 return output.toString();
159 * @param layout_group
165 private int addToReport(final org.glom.libglom.LayoutGroup layout_group, final JasperDesign design,
166 final JRDesignBand parentBand, int x) {
167 final LayoutItemVector layoutItemsVec = layout_group.get_items();
168 final int numItems = Utils.safeLongToInt(layoutItemsVec.size());
169 for (int i = 0; i < numItems; i++) {
170 final org.glom.libglom.LayoutItem libglomLayoutItem = layoutItemsVec.get(i);
172 final LayoutGroup libglomLayoutGroup = LayoutGroup.cast_dynamic(libglomLayoutItem);
173 final LayoutItem_Field libglomLayoutItemField = LayoutItem_Field.cast_dynamic(libglomLayoutItem);
174 if (libglomLayoutItemField != null) {
175 x = addFieldToBand(design, parentBand, x, libglomLayoutItemField);
176 } else if (libglomLayoutGroup != null) {
177 final LayoutItem_GroupBy libglomGroupBy = LayoutItem_GroupBy.cast_dynamic(libglomLayoutGroup);
178 if (libglomGroupBy != null) {
179 final LayoutItem_Field fieldGroupBy = libglomGroupBy.get_field_group_by();
180 if (fieldGroupBy == null)
183 final String fieldName = addField(design, fieldGroupBy);
185 // We must sort by the group field,
186 // so that JasperReports can start a new group when its value changes.
187 // Note that this is not like a SQL GROUP BY.
188 final SortFieldPair pair = new SortFieldPair();
189 pair.setFirst(fieldGroupBy);
190 pair.setSecond(true); // Ascending.
191 sortClause.add(pair);
193 final JRDesignGroup group = new JRDesignGroup();
194 group.setName(fieldName);
196 final JRDesignExpression expression = new JRDesignExpression();
197 expression.setText("$F{" + fieldName + "}");
198 group.setExpression(expression);
200 final JRDesignBand headerBand = new JRDesignBand();
201 headerBand.setHeight(height);
202 ((JRDesignSection) group.getGroupHeaderSection()).addBand(headerBand);
204 final JRDesignBand footerBand = new JRDesignBand();
205 footerBand.setHeight(height);
206 ((JRDesignSection) group.getGroupFooterSection()).addBand(footerBand);
209 design.addGroup(group);
210 } catch (final JRException e) {
211 // TODO Auto-generated catch block
215 // Show the group-by field:
216 final int groupX = addFieldToBand(design, headerBand, x, fieldGroupBy);
218 // Show the secondary fields:
219 final LayoutGroup groupSecondaries = libglomGroupBy.get_group_secondary_fields();
220 if (groupSecondaries != null)
221 addToReport(groupSecondaries, design, headerBand, groupX);
224 // Recurse into sub-groups:
225 x = addToReport(libglomLayoutGroup, design, parentBand, x);
236 * @param libglomLayoutItemField
239 private int addFieldToBand(final JasperDesign design, final JRDesignBand parentBand, int x,
240 final LayoutItem_Field libglomLayoutItemField) {
241 final String fieldName = addField(design, libglomLayoutItemField);
243 // Tell the JasperDesign to show an instance of the field:
244 final JRDesignTextField textField = new JRDesignTextField();
246 // Make sure this field starts at the right of the previous field,
247 // because JasperReports uses absolute positioning.
251 // An arbitrary width, because we must specify _some_ width:
252 final int width = 100; // Points, as specified later.
253 textField.setWidth(width); // No data will be shown without this.
256 // This only stretches vertically, but that is better than
258 textField.setStretchWithOverflow(true);
259 textField.setHeight(height); // We must specify _some_ height.
261 // TODO: Where is this format documented?
262 final JRDesignExpression expression = new JRDesignExpression();
263 expression.setText("$F{" + fieldName + "}");
265 textField.setExpression(expression);
266 parentBand.addElement(textField);
272 * @param libglomLayoutItemField
275 private String addField(final JasperDesign design, final LayoutItem_Field libglomLayoutItemField) {
276 fieldsToGet.add(libglomLayoutItemField);
278 final String fieldName = libglomLayoutItemField.get_name();
279 // System.out.print("fieldName=" + fieldName + "\n");
281 // Tell the JasperDesign about the database field that will be in the SQL query,
283 final JRDesignField field = new JRDesignField();
284 field.setName(fieldName); // TODO: Related fields.
285 field.setValueClass(getClassTypeForGlomType(libglomLayoutItemField));
288 design.addField(field);
289 } catch (final JRException e2) {
290 // TODO Auto-generated catch block
291 e2.printStackTrace();
297 * @param libglomLayoutItemField
300 private Class<?> getClassTypeForGlomType(final LayoutItem_Field libglomLayoutItemField) {
301 // Choose a suitable java class type for the SQL field:
302 Class<?> klass = null;
303 switch (libglomLayoutItemField.get_glom_type()) {
305 klass = java.lang.String.class;
308 klass = java.lang.Boolean.class;
311 klass = java.lang.Double.class;
314 klass = java.util.Date.class;
317 klass = java.sql.Time.class;
320 klass = java.sql.Blob.class; // TODO: This does not work.