001 package com.mockrunner.mock.jdbc;
002
003 import java.io.InputStream;
004 import java.io.Reader;
005 import java.math.BigDecimal;
006 import java.net.URL;
007 import java.sql.Array;
008 import java.sql.BatchUpdateException;
009 import java.sql.Blob;
010 import java.sql.Clob;
011 import java.sql.Connection;
012 import java.sql.Date;
013 import java.sql.ParameterMetaData;
014 import java.sql.PreparedStatement;
015 import java.sql.Ref;
016 import java.sql.ResultSet;
017 import java.sql.ResultSetMetaData;
018 import java.sql.SQLException;
019 import java.sql.Time;
020 import java.sql.Timestamp;
021 import java.util.ArrayList;
022 import java.util.Calendar;
023 import java.util.Collections;
024 import java.util.HashMap;
025 import java.util.Iterator;
026 import java.util.List;
027 import java.util.Map;
028
029 import com.mockrunner.jdbc.AbstractParameterResultSetHandler;
030 import com.mockrunner.jdbc.ParameterUtil;
031 import com.mockrunner.util.common.ArrayUtil;
032 import com.mockrunner.util.common.StringUtil;
033
034 /**
035 * Mock implementation of <code>PreparedStatement</code>.
036 */
037 public class MockPreparedStatement extends MockStatement implements PreparedStatement
038 {
039 private AbstractParameterResultSetHandler resultSetHandler;
040 private Map paramObjects = new HashMap();
041 private List batchParameters = new ArrayList();
042 private String sql;
043 private MockParameterMetaData parameterMetaData;
044 private boolean returnGeneratedKeys = false;
045
046 public MockPreparedStatement(Connection connection, String sql)
047 {
048 this(connection, sql, false);
049 }
050
051 public MockPreparedStatement(Connection connection, String sql, boolean returnGeneratedKeys)
052 {
053 super(connection);
054 this.sql = sql;
055 this.returnGeneratedKeys = returnGeneratedKeys;
056 prepareParameterMetaData();
057 }
058
059 public MockPreparedStatement(Connection connection, String sql, int resultSetType, int resultSetConcurrency)
060 {
061 super(connection, resultSetType, resultSetConcurrency);
062 this.sql = sql;
063 prepareParameterMetaData();
064 }
065
066 public MockPreparedStatement(Connection connection, String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
067 {
068 super(connection, resultSetType, resultSetConcurrency, resultSetHoldability);
069 this.sql = sql;
070 prepareParameterMetaData();
071 }
072
073 public void setPreparedStatementResultSetHandler(AbstractParameterResultSetHandler resultSetHandler)
074 {
075 super.setResultSetHandler(resultSetHandler);
076 this.resultSetHandler = resultSetHandler;
077 }
078
079 private void prepareParameterMetaData()
080 {
081 int number = StringUtil.countMatches(sql, "?");
082 parameterMetaData = new MockParameterMetaData();
083 parameterMetaData.setParameterCount(number);
084 }
085
086 public String getSQL()
087 {
088 return sql;
089 }
090
091 public Map getIndexedParameterMap()
092 {
093 return Collections.unmodifiableMap(paramObjects);
094 }
095
096 public Map getParameterMap()
097 {
098 return getIndexedParameterMap();
099 }
100
101 public Object getParameter(int index)
102 {
103 return paramObjects.get(new Integer(index));
104 }
105
106 public void setObject(int index, Object object) throws SQLException
107 {
108 paramObjects.put(new Integer(index), object);
109 }
110
111 public void setObject(int parameterIndex, Object object, int targetSqlType, int scale) throws SQLException
112 {
113 setObject(parameterIndex, object);
114 }
115
116 public void setObject(int parameterIndex, Object object, int targetSqlType) throws SQLException
117 {
118 setObject(parameterIndex, object);
119 }
120
121 public void addBatch() throws SQLException
122 {
123 batchParameters.add(new HashMap(paramObjects));
124 }
125
126 public void clearParameters() throws SQLException
127 {
128 paramObjects.clear();
129 }
130
131 public boolean execute() throws SQLException
132 {
133 boolean callExecuteQuery = isQuery(getSQL());
134 if(callExecuteQuery)
135 {
136 executeQuery();
137 }
138 else
139 {
140 executeUpdate();
141 }
142 return callExecuteQuery;
143 }
144
145 public ResultSet executeQuery() throws SQLException
146 {
147 return executeQuery(paramObjects);
148 }
149
150 protected ResultSet executeQuery(Map params) throws SQLException
151 {
152 SQLException exception = resultSetHandler.getSQLException(sql, params);
153 if(null != exception)
154 {
155 throw exception;
156 }
157 exception = resultSetHandler.getSQLException(sql);
158 if(null != exception)
159 {
160 throw exception;
161 }
162 resultSetHandler.addParameterMapForExecutedStatement(getSQL(), getParameterMapCopy(params));
163 if(resultSetHandler.hasMultipleResultSets(getSQL(), params))
164 {
165 MockResultSet[] results = resultSetHandler.getResultSets(getSQL(), params);
166 if(null != results)
167 {
168 resultSetHandler.addExecutedStatement(getSQL());
169 return cloneAndSetMultipleResultSets(results, params);
170 }
171 }
172 else
173 {
174 MockResultSet result = resultSetHandler.getResultSet(getSQL(), params);
175 if(null != result)
176 {
177 resultSetHandler.addExecutedStatement(getSQL());
178 return cloneAndSetSingleResultSet(result, params);
179 }
180 }
181 ResultSet superResultSet = super.executeQuery(getSQL());
182 setGeneratedKeysResultSet(sql, params);
183 return superResultSet;
184 }
185
186 private MockResultSet cloneAndSetSingleResultSet(MockResultSet result, Map params)
187 {
188 result = cloneResultSet(result);
189 if(null != result)
190 {
191 resultSetHandler.addReturnedResultSet(result);
192 }
193 setResultSets(new MockResultSet[] {result});
194 setGeneratedKeysResultSet(sql, params);
195 return result;
196 }
197
198 private MockResultSet cloneAndSetMultipleResultSets(MockResultSet[] results, Map params)
199 {
200 results = cloneResultSets(results);
201 if(null != results)
202 {
203 resultSetHandler.addReturnedResultSets(results);
204 }
205 setResultSets(results);
206 setGeneratedKeysResultSet(sql, params);
207 if(null != results && results.length > 0)
208 {
209 return results[0];
210 }
211 return null;
212 }
213
214 public int executeUpdate() throws SQLException
215 {
216 return executeUpdate(paramObjects);
217 }
218
219 protected int executeUpdate(Map params) throws SQLException
220 {
221 SQLException exception = resultSetHandler.getSQLException(sql, params);
222 if(null != exception)
223 {
224 throw exception;
225 }
226 exception = resultSetHandler.getSQLException(sql);
227 if(null != exception)
228 {
229 throw exception;
230 }
231 resultSetHandler.addParameterMapForExecutedStatement(getSQL(), getParameterMapCopy(params));
232 if(resultSetHandler.hasMultipleUpdateCounts(getSQL(), params))
233 {
234 Integer[] updateCounts = resultSetHandler.getUpdateCounts(getSQL(), params);
235 if(null != updateCounts)
236 {
237 resultSetHandler.addExecutedStatement(getSQL());
238 return setMultipleUpdateCounts((int[])ArrayUtil.convertToPrimitiveArray(updateCounts), params);
239 }
240 }
241 else
242 {
243 Integer updateCount = resultSetHandler.getUpdateCount(getSQL(), params);
244 if(null != updateCount)
245 {
246 resultSetHandler.addExecutedStatement(getSQL());
247 return setSingleUpdateCount(updateCount.intValue(), params);
248 }
249 }
250 int superUpdateCount = super.executeUpdate(getSQL());
251 setGeneratedKeysResultSet(sql, params);
252 return superUpdateCount;
253 }
254
255 private int setSingleUpdateCount(int updateCount, Map params)
256 {
257 setUpdateCounts(new int[] {updateCount});
258 setGeneratedKeysResultSet(sql, params);
259 return updateCount;
260 }
261
262 private int setMultipleUpdateCounts(int[] updateCounts, Map params)
263 {
264 setUpdateCounts(updateCounts);
265 setGeneratedKeysResultSet(sql, params);
266 if(null != updateCounts && updateCounts.length > 0)
267 {
268 return updateCounts[0];
269 }
270 return 0;
271 }
272
273 public int[] executeBatch() throws SQLException
274 {
275 return executeBatch(this.batchParameters);
276 }
277
278 protected int[] executeBatch(List batchParams) throws SQLException
279 {
280 int[] results = new int[batchParams.size()];
281 SQLException exception = null;
282 for(int ii = 0; ii < results.length; ii++)
283 {
284 if(isQuery(getSQL()))
285 {
286 exception = prepareFailedResult(results, ii, "SQL " + getSQL() + " in the list of batches returned a ResultSet.", null);
287 }
288 else
289 {
290 try
291 {
292 Map currentParameters = (Map)batchParams.get(ii);
293 results[ii] = executeUpdate(currentParameters);
294 }
295 catch(SQLException exc)
296 {
297 exception = prepareFailedResult(results, ii, null, exc);
298 }
299 }
300 if(null != exception && !resultSetHandler.getContinueProcessingOnBatchFailure())
301 {
302 throw exception;
303 }
304 }
305 if(null != exception)
306 {
307 throw new BatchUpdateException(exception.getMessage(), exception.getSQLState(), exception.getErrorCode(), results);
308 }
309 return results;
310 }
311
312 private void setGeneratedKeysResultSet(String sql, Map params)
313 {
314 MockResultSet generatedKeys = resultSetHandler.getGeneratedKeys(sql, params);
315 if(returnGeneratedKeys)
316 {
317 if(null != generatedKeys)
318 {
319 setLastGeneratedKeysResultSet(generatedKeys);
320 }
321 else
322 {
323 setLastGeneratedKeysResultSet(determineGeneratedKeysResultSet(sql));
324 }
325 }
326 else
327 {
328 setLastGeneratedKeysResultSet(null);
329 }
330 }
331
332 public ResultSetMetaData getMetaData() throws SQLException
333 {
334 return new MockResultSetMetaData();
335 }
336
337 public ParameterMetaData getParameterMetaData() throws SQLException
338 {
339 return parameterMetaData;
340 }
341
342 public void setArray(int parameterIndex, Array array) throws SQLException
343 {
344 setObject(parameterIndex, array);
345 }
346
347 public void setAsciiStream(int parameterIndex, InputStream stream, int length) throws SQLException
348 {
349 setObject(parameterIndex, stream);
350 }
351
352 public void setBigDecimal(int parameterIndex, BigDecimal bigDecimal) throws SQLException
353 {
354 setObject(parameterIndex, bigDecimal);
355 }
356
357 public void setBinaryStream(int parameterIndex, InputStream stream, int length) throws SQLException
358 {
359 setObject(parameterIndex, stream);
360 }
361
362 public void setBlob(int parameterIndex, Blob blob) throws SQLException
363 {
364 setObject(parameterIndex, blob);
365 }
366
367 public void setBoolean(int parameterIndex, boolean bool) throws SQLException
368 {
369 setObject(parameterIndex, new Boolean(bool));
370 }
371
372 public void setByte(int parameterIndex, byte byteValue) throws SQLException
373 {
374 setObject(parameterIndex, new Byte(byteValue));
375 }
376
377 public void setBytes(int parameterIndex, byte[] byteArray) throws SQLException
378 {
379 setObject(parameterIndex, byteArray);
380 }
381
382 public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException
383 {
384 setObject(parameterIndex, reader);
385 }
386
387 public void setClob(int parameterIndex, Clob clob) throws SQLException
388 {
389 setObject(parameterIndex, clob);
390 }
391
392 public void setDate(int parameterIndex, Date date, Calendar calendar) throws SQLException
393 {
394 setObject(parameterIndex, date);
395 }
396
397 public void setDate(int parameterIndex, Date date) throws SQLException
398 {
399 setObject(parameterIndex, date);
400 }
401
402 public void setDouble(int parameterIndex, double doubleValue) throws SQLException
403 {
404 setObject(parameterIndex, new Double(doubleValue));
405 }
406
407 public void setFloat(int parameterIndex, float floatValue) throws SQLException
408 {
409 setObject(parameterIndex, new Float(floatValue));
410 }
411
412 public void setInt(int parameterIndex, int intValue) throws SQLException
413 {
414 setObject(parameterIndex, new Integer(intValue));
415 }
416
417 public void setLong(int parameterIndex, long longValue) throws SQLException
418 {
419 setObject(parameterIndex, new Long(longValue));
420 }
421
422 public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException
423 {
424 setObject(parameterIndex, null);
425 }
426
427 public void setNull(int parameterIndex, int sqlType) throws SQLException
428 {
429 setObject(parameterIndex, null);
430 }
431
432 public void setRef(int parameterIndex, Ref ref) throws SQLException
433 {
434 setObject(parameterIndex, ref);
435 }
436
437 public void setShort(int parameterIndex, short shortValue) throws SQLException
438 {
439 setObject(parameterIndex, new Short(shortValue));
440 }
441
442 public void setString(int parameterIndex, String string) throws SQLException
443 {
444 setObject(parameterIndex, string);
445 }
446
447 public void setTime(int parameterIndex, Time time, Calendar calendar) throws SQLException
448 {
449 setObject(parameterIndex, time);
450 }
451
452 public void setTime(int parameterIndex, Time time) throws SQLException
453 {
454 setObject(parameterIndex, time);
455 }
456
457 public void setTimestamp(int parameterIndex, Timestamp timeStamp, Calendar cal) throws SQLException
458 {
459 setObject(parameterIndex, timeStamp);
460 }
461
462 public void setTimestamp(int parameterIndex, Timestamp timeStamp) throws SQLException
463 {
464 setObject(parameterIndex, timeStamp);
465 }
466
467 public void setUnicodeStream(int parameterIndex, InputStream stream, int length) throws SQLException
468 {
469 setObject(parameterIndex, stream);
470 }
471
472 public void setURL(int parameterIndex, URL url) throws SQLException
473 {
474 setObject(parameterIndex, url);
475 }
476
477 private Map getParameterMapCopy(Map actualParameters)
478 {
479 Map copyParameters = new HashMap();
480 Iterator keys = actualParameters.keySet().iterator();
481 while(keys.hasNext())
482 {
483 Object key = keys.next();
484 Object actualParameter = actualParameters.get(key);
485 Object copyParameter = ParameterUtil.copyParameter(actualParameter);
486 copyParameters.put(key, copyParameter);
487 }
488 return copyParameters;
489 }
490 }