001    package com.mockrunner.mock.jdbc;
002    
003    import java.sql.BatchUpdateException;
004    import java.sql.Connection;
005    import java.sql.ResultSet;
006    import java.sql.SQLException;
007    import java.sql.SQLWarning;
008    import java.sql.Statement;
009    import java.util.ArrayList;
010    import java.util.List;
011    
012    import com.mockrunner.base.NestedApplicationException;
013    import com.mockrunner.jdbc.AbstractResultSetHandler;
014    import com.mockrunner.jdbc.SQLUtil;
015    import com.mockrunner.util.common.ArrayUtil;
016    
017    /**
018     * Mock implementation of <code>Statement</code>.
019     */
020    public class MockStatement implements Statement
021    {
022        private AbstractResultSetHandler resultSetHandler;
023        private ResultSet[] currentResultSets = null;
024        private int[] currentUpdateCounts = null;
025        private int currentResultSetIndex = 0;
026        private int currentUpdateCountIndex = 0;
027        private List batches = new ArrayList();
028        private String cursorName = "";
029        private int querySeconds = 0;
030        private int maxRows = 0;
031        private int maxFieldSize = 0;
032        private int fetchDirection = ResultSet.FETCH_FORWARD;
033        private int fetchSize = 0;
034        private int resultSetType = ResultSet.TYPE_FORWARD_ONLY;
035        private int resultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
036        private int resultSetHoldability = ResultSet.HOLD_CURSORS_OVER_COMMIT;
037        private MockResultSet lastGeneratedKeys = null;
038        private boolean closed = false;
039        private boolean poolable = false;
040        private Connection connection;
041        
042        public MockStatement(Connection connection)
043        {
044            this.connection = connection;
045            this.resultSetType = ResultSet.TYPE_FORWARD_ONLY;
046            this.resultSetConcurrency = ResultSet.CONCUR_READ_ONLY;
047            try
048            {
049                this.resultSetHoldability = connection.getMetaData().getResultSetHoldability();
050            }
051            catch(SQLException exc)
052            {
053                throw new NestedApplicationException(exc);
054            }
055        }
056        
057        public MockStatement(Connection connection, int resultSetType, int resultSetConcurrency)
058        {
059            this.connection = connection;
060            this.resultSetType = resultSetType;
061            this.resultSetConcurrency = resultSetConcurrency;
062            try
063            {
064                this.resultSetHoldability = connection.getMetaData().getResultSetHoldability();
065            }
066            catch(SQLException exc)
067            {
068                throw new NestedApplicationException(exc);
069            } 
070        }
071        
072        public MockStatement(Connection connection, int resultSetType, int resultSetConcurrency, int resultSetHoldability)
073        {
074            this.connection = connection;
075            this.resultSetType = resultSetType;
076            this.resultSetConcurrency = resultSetConcurrency;
077            this.resultSetHoldability = resultSetHoldability;
078        }
079        
080        public boolean isClosed()
081        {
082            return closed;
083        }
084        
085        public void setResultSetHandler(AbstractResultSetHandler resultSetHandler)
086        {
087            this.resultSetHandler = resultSetHandler;
088        }
089        
090        protected void setResultSets(ResultSet[] resultSets)
091        {
092            closeCurrentResultSets();
093            this.currentUpdateCounts = null;
094            this.currentResultSets = resultSets;
095            this.currentResultSetIndex = 0;
096            this.currentUpdateCountIndex = 0;
097        }
098        
099        protected void setUpdateCounts(int[] updateCounts)
100        {
101            closeCurrentResultSets();
102            this.currentResultSets = null;
103            this.currentUpdateCounts = updateCounts;
104            this.currentResultSetIndex = 0;
105            this.currentUpdateCountIndex = 0;
106        }
107        
108        public String getCursorName()
109        {
110            return cursorName;
111        }
112        
113        public ResultSet executeQuery(String sql) throws SQLException
114        {
115            SQLException exception = resultSetHandler.getSQLException(sql);
116            if(null != exception)
117            {
118                throw exception;
119            }
120            resultSetHandler.addExecutedStatement(sql);
121            if(resultSetHandler.hasMultipleResultSets(sql))
122            {
123                MockResultSet[] results = resultSetHandler.getResultSets(sql);
124                if(null != results) return cloneAndSetMultipleResultSets(results);
125            }
126            else
127            {
128                MockResultSet result = resultSetHandler.getResultSet(sql);
129                if(null != result) return cloneAndSetSingleResultSet(result);
130            }
131            if(resultSetHandler.hasMultipleGlobalResultSets())
132            {
133                return cloneAndSetMultipleResultSets(resultSetHandler.getGlobalResultSets());
134            }
135            return cloneAndSetSingleResultSet(resultSetHandler.getGlobalResultSet());
136        }
137        
138        private MockResultSet cloneAndSetSingleResultSet(MockResultSet result)
139        {
140            result = cloneResultSet(result);
141            if(null != result)
142            {
143                resultSetHandler.addReturnedResultSet(result);
144            }
145            setResultSets(new MockResultSet[] {result});
146            setLastGeneratedKeysResultSet(null);
147            return result;
148        }
149        
150        private MockResultSet cloneAndSetMultipleResultSets(MockResultSet[] results)
151        {
152            results = cloneResultSets(results);
153            if(null != results)
154            {
155                resultSetHandler.addReturnedResultSets(results);
156            }
157            setResultSets(results);
158            setLastGeneratedKeysResultSet(null);
159            if(null != results && results.length > 0)
160            {
161                return results[0];
162            }
163            return null;
164        }
165        
166        private void closeCurrentResultSets()
167        {
168            if(null != currentResultSets)
169            {
170                for(int ii = 0; ii < currentResultSets.length; ii++)
171                {
172                    try
173                    {
174                        if(null != currentResultSets[ii])
175                        {
176                            currentResultSets[ii].close();
177                        }
178                    } 
179                    catch(SQLException exc)
180                    {
181                        throw new NestedApplicationException(exc);
182                    }
183                }
184            }
185        }
186    
187        public int executeUpdate(String sql) throws SQLException
188        {
189            SQLException exception = resultSetHandler.getSQLException(sql);
190            if(null != exception)
191            {
192                throw exception;
193            }
194            resultSetHandler.addExecutedStatement(sql);
195            if(resultSetHandler.hasMultipleUpdateCounts(sql))
196            {
197                Integer[] returnValues = resultSetHandler.getUpdateCounts(sql);
198                if(null != returnValues)
199                {
200                    return setMultipleUpdateCounts((int[])ArrayUtil.convertToPrimitiveArray(returnValues));
201                }
202            }
203            else
204            {
205                Integer returnValue = resultSetHandler.getUpdateCount(sql);
206                if(null != returnValue)
207                {
208                    return setSingleUpdateCount(returnValue.intValue());
209                }
210            }
211            if(resultSetHandler.hasMultipleGlobalUpdateCounts())
212            {
213                return setMultipleUpdateCounts(resultSetHandler.getGlobalUpdateCounts());
214            }
215            return setSingleUpdateCount(resultSetHandler.getGlobalUpdateCount());
216        }
217        
218        private int setSingleUpdateCount(int updateCount)
219        {
220            setUpdateCounts(new int[] {updateCount});
221            setLastGeneratedKeysResultSet(null);
222            return updateCount;
223        }
224        
225        private int setMultipleUpdateCounts(int[] updateCounts)
226        {
227            setUpdateCounts(updateCounts);
228            setLastGeneratedKeysResultSet(null);
229            if(null != updateCounts && updateCounts.length > 0)
230            {
231                return updateCounts[0];
232            }
233            return 0;
234        }
235        
236        public boolean execute(String sql) throws SQLException
237        {
238            boolean callExecuteQuery = isQuery(sql);
239            if(callExecuteQuery)
240            {
241                executeQuery(sql);
242            }
243            else
244            {
245                executeUpdate(sql);
246            }
247            return callExecuteQuery;
248        }
249        
250        public int[] executeBatch() throws SQLException
251        {
252            int[] results = new int[batches.size()];
253            SQLException exception = null;
254            for(int ii = 0; ii < results.length; ii++)
255            {
256                String nextSQL = (String)batches.get(ii);
257                if(isQuery(nextSQL))
258                {
259                    exception = prepareFailedResult(results, ii, "SQL " + batches.get(ii) + " in the list of batches returned a ResultSet.", null);
260                }
261                else
262                {
263                    try
264                    {
265                        results[ii] = executeUpdate(nextSQL);
266                    } 
267                    catch(SQLException exc)
268                    {
269                        exception = prepareFailedResult(results, ii, null, exc);
270                    }
271                }
272                if(null != exception && !resultSetHandler.getContinueProcessingOnBatchFailure())
273                {
274                    throw exception;
275                }
276            }
277            if(null != exception)
278            {
279                throw new BatchUpdateException(exception.getMessage(), exception.getSQLState(), exception.getErrorCode(), results);
280            }
281            return results;
282        }
283        
284        protected SQLException prepareFailedResult(int[] actualResults, int index, String message, SQLException caughtException)
285        {
286            actualResults[index] = -3;
287            if(caughtException instanceof BatchUpdateException)
288            {
289                return caughtException;
290            }
291            else
292            {
293                int[] partialResults = (int[])ArrayUtil.truncateArray(actualResults, index);
294                if(null == caughtException)
295                {
296                    return new BatchUpdateException(message, partialResults);
297                }
298                else
299                {
300                    return new BatchUpdateException(caughtException.getMessage(), caughtException.getSQLState(), caughtException.getErrorCode(), partialResults);
301                }
302            }
303        }
304        
305        public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException
306        {
307            int updateCount = executeUpdate(sql);
308            setGeneratedKeysResultSet(sql, autoGeneratedKeys);
309            return updateCount;
310        }
311    
312        public int executeUpdate(String sql, int[] columnIndexes) throws SQLException
313        {
314            return executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
315        }
316    
317        public int executeUpdate(String sql, String[] columnNames) throws SQLException
318        {
319            return executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
320        }
321    
322        public boolean execute(String sql, int autoGeneratedKeys) throws SQLException
323        {
324            boolean isQuery = execute(sql);
325            setGeneratedKeysResultSet(sql, autoGeneratedKeys);
326            return isQuery;
327        }
328    
329        public boolean execute(String sql, int[] columnIndexes) throws SQLException
330        {
331            return execute(sql, Statement.RETURN_GENERATED_KEYS);
332        }
333    
334        public boolean execute(String sql, String[] columnNames) throws SQLException
335        {
336            return execute(sql, Statement.RETURN_GENERATED_KEYS);
337        }
338        
339        private void setGeneratedKeysResultSet(String sql, int autoGeneratedKeys) throws SQLException
340        {
341            if(Statement.RETURN_GENERATED_KEYS != autoGeneratedKeys && Statement.NO_GENERATED_KEYS != autoGeneratedKeys)
342            {
343                throw new SQLException("autoGeneratedKeys must be either Statement.RETURN_GENERATED_KEYS or Statement.NO_GENERATED_KEYS");
344            }
345            if(Statement.RETURN_GENERATED_KEYS == autoGeneratedKeys)
346            {
347                setLastGeneratedKeysResultSet(determineGeneratedKeysResultSet(sql));
348            }
349            else
350            {
351                setLastGeneratedKeysResultSet(null);
352            }
353        }
354        
355        protected void setLastGeneratedKeysResultSet(MockResultSet generatedKeys)
356        {
357            lastGeneratedKeys = generatedKeys;
358        }
359    
360        protected MockResultSet determineGeneratedKeysResultSet(String sql)
361        {
362            MockResultSet generatedKeys = resultSetHandler.getGeneratedKeys(sql);
363            if(null != generatedKeys) return generatedKeys;
364            return resultSetHandler.getGlobalGeneratedKeys();
365        }
366    
367        public void close() throws SQLException
368        {
369            closed = true;
370        }
371    
372        public int getMaxFieldSize() throws SQLException
373        {
374            return maxFieldSize;
375        }
376    
377        public void setMaxFieldSize(int maxFieldSize) throws SQLException
378        {
379            this.maxFieldSize = maxFieldSize;
380        }
381    
382        public int getMaxRows() throws SQLException
383        {
384            return maxRows;
385        }
386    
387        public void setMaxRows(int maxRows) throws SQLException
388        {
389            this.maxRows = maxRows;
390        }
391    
392        public void setEscapeProcessing(boolean enable) throws SQLException
393        {
394    
395        }
396    
397        public int getQueryTimeout() throws SQLException
398        {
399            return querySeconds;
400        }
401    
402        public void setQueryTimeout(int querySeconds) throws SQLException
403        {
404            this.querySeconds = querySeconds;
405        }
406    
407        public void cancel() throws SQLException
408        {
409    
410        }
411    
412        public SQLWarning getWarnings() throws SQLException
413        {
414            return null;
415        }
416    
417        public void clearWarnings() throws SQLException
418        {
419    
420        }
421    
422        public void setCursorName(String cursorName) throws SQLException
423        {
424            this.cursorName = cursorName;
425        }
426    
427        protected boolean isQuery(String sql)
428        {
429            boolean isQuery;
430            Boolean returnsResultSet = resultSetHandler.getReturnsResultSet(sql);
431            if(null != returnsResultSet)
432            {
433                isQuery = returnsResultSet.booleanValue();
434            }
435            else
436            {
437                isQuery = SQLUtil.isSelect(sql);
438            }
439            return isQuery;
440        }
441    
442        public ResultSet getResultSet() throws SQLException
443        {
444            if(null == currentResultSets) return null;
445            if(currentResultSetIndex >= currentResultSets.length) return null;
446            return currentResultSets[currentResultSetIndex];
447        }
448    
449        public int getUpdateCount() throws SQLException
450        {
451            if(null == currentUpdateCounts) return -1;
452            if(currentUpdateCountIndex >= currentUpdateCounts.length) return -1;
453            return currentUpdateCounts[currentUpdateCountIndex];
454        }
455        
456        public boolean getMoreResults(int current) throws SQLException
457        {
458            return getMoreResults(current != Statement.KEEP_CURRENT_RESULT);
459        }
460    
461        public boolean getMoreResults() throws SQLException
462        {
463            return getMoreResults(true);
464        }
465        
466        private boolean getMoreResults(boolean doCloseCurrentResult) throws SQLException
467        {
468            if(null != currentResultSets)
469            {
470                if(currentResultSetIndex < currentResultSets.length)
471                {
472                    if(null != currentResultSets[currentResultSetIndex] && doCloseCurrentResult)
473                    {
474                        currentResultSets[currentResultSetIndex].close();
475                    }
476                    currentResultSetIndex++;
477                }
478                return (currentResultSetIndex < currentResultSets.length);
479            }
480            else if(null != currentUpdateCounts)
481            {
482                if(currentUpdateCountIndex < currentUpdateCounts.length)
483                {
484                    currentUpdateCountIndex++;
485                }
486            }
487            return false;
488        }
489    
490        public void setFetchDirection(int fetchDirection) throws SQLException
491        {
492            this.fetchDirection = fetchDirection;
493        }
494    
495        public int getFetchDirection() throws SQLException
496        {
497            return fetchDirection;
498        }
499    
500        public void setFetchSize(int fetchSize) throws SQLException
501        {   
502            this.fetchSize = fetchSize;
503        }
504    
505        public int getFetchSize() throws SQLException
506        {
507            return fetchSize;
508        }
509    
510        public void addBatch(String sql) throws SQLException
511        {
512            batches.add(sql);
513        }
514    
515        public void clearBatch() throws SQLException
516        {
517            batches.clear();
518        }
519    
520        public Connection getConnection() throws SQLException
521        {
522            return connection;
523        }
524    
525        public ResultSet getGeneratedKeys() throws SQLException
526        {
527            if(null == lastGeneratedKeys)
528            {
529                MockResultSet resultSet = new MockResultSet("Last statement did not generate any keys");
530                resultSet.setStatement(this);
531                return resultSet;
532            }
533            return cloneResultSet(lastGeneratedKeys);
534        }
535        
536        public int getResultSetType() throws SQLException
537        {
538            return resultSetType;
539        }
540        
541        public int getResultSetConcurrency() throws SQLException
542        {
543            return resultSetConcurrency;
544        }
545        
546        public int getResultSetHoldability() throws SQLException
547        {
548            return resultSetHoldability;
549        }
550        
551        public boolean isPoolable() throws SQLException
552        {
553            return poolable;
554        }
555    
556        public void setPoolable(boolean poolable) throws SQLException
557        {
558            this.poolable = poolable;
559        }
560    
561        public boolean isWrapperFor(Class iface) throws SQLException
562        {
563            return false;
564        }
565    
566        public Object unwrap(Class iface) throws SQLException
567        {
568            throw new SQLException("No object found for " + iface);
569        }
570        
571        protected MockResultSet cloneResultSet(MockResultSet resultSet)
572        {
573            if(null == resultSet) return null;
574            MockResultSet clone = (MockResultSet)resultSet.clone();
575            clone.setStatement(this);
576            return clone;
577        }
578        
579        protected MockResultSet[] cloneResultSets(MockResultSet[] resultSets)
580        {
581            if(null == resultSets) return null;
582            MockResultSet[] clonedResultsSets = new MockResultSet[resultSets.length];
583            for(int ii = 0; ii < resultSets.length; ii++)
584            {
585                if(null != resultSets[ii])
586                {
587                    clonedResultsSets[ii] = (MockResultSet)resultSets[ii].clone();
588                    clonedResultsSets[ii].setStatement(this);
589                }
590            }
591            return clonedResultsSets;
592        }
593    }