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    }