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