001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * SimpleSQLReportDataFactory.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.modules.misc.datafactory.sql;
030:
031: import java.io.Serializable;
032: import java.sql.CallableStatement;
033: import java.sql.Connection;
034: import java.sql.PreparedStatement;
035: import java.sql.ResultSet;
036: import java.sql.SQLException;
037: import java.sql.Types;
038: import java.util.HashMap;
039: import javax.swing.table.TableModel;
040:
041: import org.jfree.report.DataFactory;
042: import org.jfree.report.DataRow;
043: import org.jfree.report.JFreeReportBoot;
044: import org.jfree.report.ReportDataFactoryException;
045: import org.jfree.report.modules.misc.tablemodel.ResultSetTableModelFactory;
046: import org.jfree.util.Configuration;
047:
048: /**
049: * Creation-Date: 19.02.2006, 17:37:33
050: *
051: * @author Thomas Morgner
052: */
053: public class SimpleSQLReportDataFactory implements DataFactory,
054: Cloneable, Serializable {
055: private static class PreparedStatementCarrier {
056: private PreparedStatement preparedStatement;
057: private String[] parameters;
058:
059: protected PreparedStatementCarrier(
060: final PreparedStatement preparedStatement,
061: final String[] parameters) {
062: this .preparedStatement = preparedStatement;
063: this .parameters = parameters;
064: }
065:
066: public PreparedStatement getPreparedStatement() {
067: return preparedStatement;
068: }
069:
070: public String[] getParameters() {
071: return parameters;
072: }
073: }
074:
075: private transient HashMap preparedStatements;
076: private transient Connection connection;
077: private ConnectionProvider connectionProvider;
078:
079: private boolean labelMapping;
080: private static final String COLUMN_NAME_MAPPING_KEY = "org.jfree.report.modules.data.sql.ColumnNameMapping"; //$NON-NLS-1$
081:
082: public SimpleSQLReportDataFactory(final Connection connection) {
083: this (new StaticConnectionProvider(connection));
084: }
085:
086: public SimpleSQLReportDataFactory(
087: final ConnectionProvider connectionProvider) {
088: if (connectionProvider == null) {
089: throw new NullPointerException();
090: }
091: this .connectionProvider = connectionProvider;
092: final Configuration globalConfig = JFreeReportBoot
093: .getInstance().getGlobalConfig();
094: this .labelMapping = "Label".equals(globalConfig.getConfigProperty //$NON-NLS-1$
095: (
096: SimpleSQLReportDataFactory.COLUMN_NAME_MAPPING_KEY,
097: "Label")); //$NON-NLS-1$
098: }
099:
100: public boolean isLabelMapping() {
101: return labelMapping;
102: }
103:
104: public void setLabelMapping(final boolean labelMapping) {
105: this .labelMapping = labelMapping;
106: }
107:
108: private synchronized Connection getConnection() throws SQLException {
109: if (connection == null) {
110: connection = connectionProvider.getConnection();
111: }
112: return connection;
113: }
114:
115: private int getBestResultSetType() throws SQLException {
116: final Connection connection = getConnection();
117: final boolean supportsScrollInsensitive = connection
118: .getMetaData().supportsResultSetType(
119: ResultSet.TYPE_SCROLL_INSENSITIVE);
120: final boolean supportsScrollSensitive = connection
121: .getMetaData().supportsResultSetType(
122: ResultSet.TYPE_SCROLL_SENSITIVE);
123:
124: if (supportsScrollInsensitive) {
125: return ResultSet.TYPE_SCROLL_INSENSITIVE;
126: }
127: if (supportsScrollSensitive) {
128: return ResultSet.TYPE_SCROLL_SENSITIVE;
129: }
130: return ResultSet.TYPE_FORWARD_ONLY;
131: }
132:
133: /**
134: * Queries a datasource. The string 'query' defines the name of the query. The Parameterset given here may contain
135: * more data than actually needed.
136: * <p/>
137: * The dataset may change between two calls, do not assume anything!
138: *
139: * @param query
140: * @param parameters
141: * @return
142: */
143: public synchronized TableModel queryData(final String query,
144: final DataRow parameters) throws ReportDataFactoryException {
145: try {
146: if (preparedStatements == null) {
147: preparedStatements = new HashMap();
148: }
149:
150: PreparedStatementCarrier pstmtCarrier = (PreparedStatementCarrier) preparedStatements
151: .get(query);
152: final boolean callableStatementQuery = isCallableStatementQuery(query);
153: if (pstmtCarrier == null) {
154: final SQLParameterLookupParser parser = new SQLParameterLookupParser();
155: final String translatedQuery = parser
156: .translateAndLookup(query);
157: final PreparedStatement pstmt;
158: if (callableStatementQuery
159: || isCallableStatement(query)) {
160: pstmt = getConnection().prepareCall(
161: translatedQuery, getBestResultSetType(),
162: ResultSet.CONCUR_READ_ONLY);
163: } else {
164: pstmt = getConnection().prepareStatement(
165: translatedQuery, getBestResultSetType(),
166: ResultSet.CONCUR_READ_ONLY);
167: }
168: pstmtCarrier = new PreparedStatementCarrier(pstmt,
169: parser.getFields());
170: preparedStatements.put(query, pstmtCarrier);
171: }
172:
173: final PreparedStatement pstmt = pstmtCarrier
174: .getPreparedStatement();
175: pstmt.clearParameters();
176: if (callableStatementQuery) {
177: final CallableStatement callableStatement = (CallableStatement) pstmt;
178: callableStatement.registerOutParameter(0, Types.OTHER);
179: }
180: final String[] params = pstmtCarrier.getParameters();
181: for (int i = 0; i < params.length; i++) {
182: final String param = params[i];
183: final Object pvalue = parameters.get(param);
184: if (pvalue == null) {
185: // this should work, but some driver are known to die here.
186: // they should be fed with setNull(..) instead; something
187: // we cant do as JDK1.2's JDBC does not define it.
188: pstmt.setObject(i + 1, null);
189: } else {
190: pstmt.setObject(i + 1, pvalue);
191: }
192: }
193: final ResultSet res = pstmt.executeQuery();
194: return ResultSetTableModelFactory.getInstance()
195: .createTableModel(res);
196: } catch (Exception e) {
197: throw new ReportDataFactoryException(
198: "Failed at query: " + query, e); //$NON-NLS-1$
199: }
200: }
201:
202: private boolean isCallableStatement(final String query) {
203: int state = 0;
204: final char[] chars = query.toCharArray();
205: final int length = query.length();
206: for (int i = 0; i < length; i++) {
207: final char c = chars[i];
208: if (Character.isWhitespace(c)) {
209: if (state == 5) {
210: return true;
211: }
212: } else if ('{' == c && state == 0) {
213: state = 1;
214: } else if (('c' == c || 'C' == c) && state == 1) {
215: state = 2;
216: } else if (('a' == c || 'A' == c) && state == 2) {
217: state = 3;
218: } else if (('l' == c || 'L' == c) && state == 4) {
219: state = 4;
220: } else if (('l' == c || 'L' == c) && state == 5) {
221: state = 5;
222: } else {
223: if (state == 5) {
224: return true;
225: }
226: return false;
227: }
228: }
229: return false;
230: }
231:
232: private boolean isCallableStatementQuery(final String query) {
233: int state = 0;
234: final char[] chars = query.toCharArray();
235: final int length = query.length();
236: for (int i = 0; i < length; i++) {
237: final char c = chars[i];
238: if (Character.isWhitespace(c)) {
239: if (state == 7) {
240: return true;
241: }
242: } else if ('{' == c && state == 0) {
243: state = 1;
244: } else if ('?' == c && state == 1) {
245: state = 2;
246: } else if ('=' == c && state == 2) {
247: state = 3;
248: } else if (('c' == c || 'C' == c) && state == 3) {
249: state = 4;
250: } else if (('a' == c || 'A' == c) && state == 4) {
251: state = 5;
252: } else if (('l' == c || 'L' == c) && state == 5) {
253: state = 6;
254: } else if (('l' == c || 'L' == c) && state == 6) {
255: state = 7;
256: } else {
257: if (state == 7) {
258: return true;
259: }
260: return false;
261: }
262: }
263: return false;
264: }
265:
266: public void open() {
267:
268: }
269:
270: public synchronized void close() {
271: if (connection == null) {
272: return;
273: }
274:
275: try {
276: connection.close();
277: } catch (SQLException e) {
278: // we tried our very best ..
279: }
280: connection = null;
281: }
282:
283: /**
284: * Derives a freshly initialized report data factory, which is independend of the original data factory. Opening or
285: * Closing one data factory must not affect the other factories.
286: *
287: * @return
288: */
289: public DataFactory derive() {
290: try {
291: return (DataFactory) clone();
292: } catch (CloneNotSupportedException e) {
293: // this should not happen ..
294: throw new IllegalStateException("Clone failed?"); //$NON-NLS-1$
295: }
296: }
297:
298: public Object clone() throws CloneNotSupportedException {
299: final SimpleSQLReportDataFactory dataFactory = (SimpleSQLReportDataFactory) super
300: .clone();
301: dataFactory.connection = null;
302: dataFactory.preparedStatements = null;
303: return dataFactory;
304: }
305:
306: public ConnectionProvider getConnectionProvider() {
307: return connectionProvider;
308: }
309:
310: }
|