001    package com.mockrunner.jdbc;
002    
003    import java.sql.SQLException;
004    import java.util.ArrayList;
005    import java.util.Collections;
006    import java.util.List;
007    import java.util.Map;
008    import java.util.TreeMap;
009    
010    import com.mockrunner.mock.jdbc.MockResultSet;
011    import com.mockrunner.util.common.ArrayUtil;
012    
013    /**
014     * Abstract base class for all <code>ResultSet</code> handlers.
015     * Used to coordinate <code>ResultSet</code> objects for a
016     * statement. You can use this class to prepare <code>ResultSet</code>
017     * objects and update count values that are returned by the
018     * <code>execute</code> method of a statement, if the current
019     * SQL string matches.
020     * Furthermore it can be used to create <code>ResultSet</code> objects.
021     * Please note that the <code>ResultSet</code> objects you create and
022     * prepare with this handler are cloned when executing statements.
023     * So you cannot rely on object identity. You have to use the id
024     * of the <code>ResultSet</code> to identify it.
025     * The <code>ResultSet</code> objects returned by {@link #getReturnedResultSets}
026     * are actually the instances the executed statements returned.
027     */
028    public abstract class AbstractResultSetHandler
029    {
030        private boolean caseSensitive = false;
031        private boolean exactMatch = false;
032        private boolean useRegularExpressions = false;
033        private boolean continueProcessingOnBatchFailure = false;
034        private Object globalResultSets;
035        private Map resultSetsForStatement = new TreeMap();
036        private Object globalUpdateCounts;
037        private Map updateCountForStatement = new TreeMap();
038        private MockResultSet globalGeneratedKeys;
039        private Map generatedKeysForStatement = new TreeMap();
040        private Map returnsResultSetMap = new TreeMap();
041        private Map throwsSQLException = new TreeMap();
042        private List executedStatements = new ArrayList();
043        private List returnedResultSets = new ArrayList();
044        
045        /**
046         * Creates a new <code>ResultSet</code> with a
047         * random id.
048         * @return the new <code>ResultSet</code>
049         */
050        public MockResultSet createResultSet()
051        {
052            return new MockResultSet(String.valueOf(Math.random()));
053        }
054        
055        /**
056         * Creates a new <code>ResultSet</code> with the specified id.
057         * @param id the id
058         * @return the new <code>ResultSet</code>
059         */
060        public MockResultSet createResultSet(String id)
061        {
062            return new MockResultSet(id);
063        }
064        
065        /**
066         * Returns a new <code>ResultSet</code> created by the specified factory.
067         * Creates a random id.
068         * @param factory the {@link ResultSetFactory}
069         * @return the new <code>ResultSet</code>
070         */
071        public MockResultSet createResultSet(ResultSetFactory factory)
072        {
073            return factory.create(String.valueOf(Math.random()));
074        }
075        
076        /**
077         * Returns a new <code>ResultSet</code> created by the specified factory.
078         * @param id the id
079         * @param factory the {@link ResultSetFactory}
080         * @return the new <code>ResultSet</code>
081         */
082        public MockResultSet createResultSet(String id, ResultSetFactory factory)
083        {
084            return factory.create(id);
085        }
086        
087        /**
088         * Set if specified SQL strings should be handled case sensitive.
089         * Defaults to to <code>false</code>, i.e. <i>INSERT</i> is the same
090         * as <i>insert</i>.
091         * Please note that this method controls SQL statement
092         * matching for prepared results and update counts, i.e. what
093         * statements the tested application has to execute to receive
094         * a specified result. Unlike {@link JDBCTestModule#setCaseSensitive(boolean)}
095         * it does not control the statement matching of {@link JDBCTestModule}
096         * methods.
097         * @param caseSensitive enable or disable case sensitivity
098         */
099        public void setCaseSensitive(boolean caseSensitive)
100        {
101            this.caseSensitive = caseSensitive;
102        }
103    
104        /**
105         * Set if specified SQL statements must match exactly.
106         * Defaults to <code>false</code>, i.e. the SQL string
107         * does not need to match exactly. If the original statement 
108         * is <i>insert into mytable values(?, ?, ?)</i>
109         * the string <i>insert into mytable</i> will match this statement.
110         * Usually <code>false</code> is the best choice, so
111         * prepared <code>ResultSet</code> objects do not have
112         * to match exactly the current statements SQL string.
113         * The current SQL string just has to contain the SQL string
114         * for the prepared prepared <code>ResultSet</code>.
115         * Please note that this method controls SQL statement
116         * matching for prepared results and update counts, i.e. what
117         * statements the tested application has to execute to receive
118         * a specified result. Unlike {@link JDBCTestModule#setExactMatch(boolean)}
119         * it does not control the statement matching of {@link JDBCTestModule}
120         * methods.
121         * @param exactMatch enable or disable exact matching
122         */
123        public void setExactMatch(boolean exactMatch)
124        {
125            this.exactMatch = exactMatch;
126        }
127        
128        /**
129         * Set if regular expressions should be used when matching
130         * SQL statements. Irrelevant if <code>exactMatch</code> is
131         * <code>true</code>. Default is <code>false</code>, i.e. you
132         * cannot use regular expressions and matching is based
133         * on string comparison.
134         * Please note that this method controls SQL statement
135         * matching for prepared results and update counts, i.e. what
136         * statements the tested application has to execute to receive
137         * a specified result. Unlike {@link JDBCTestModule#setUseRegularExpressions(boolean)}
138         * it does not control the statement matching of {@link JDBCTestModule}
139         * methods.
140         * @param useRegularExpressions should regular expressions be used
141         */
142        public void setUseRegularExpressions(boolean useRegularExpressions)
143        {
144            this.useRegularExpressions = useRegularExpressions;
145        }
146        
147        /**
148         * Set if batch processing should be continued if one of the commands
149         * in the batch fails. This behaviour is driver dependend. The default is
150         * <code>false</code>, i.e. if a command fails with an exception,
151         * batch processing will not continue and the remaining commands
152         * will not be executed.
153         * @param continueProcessingOnBatchFailure should batch processing be continued
154         */
155        public void setContinueProcessingOnBatchFailure(boolean continueProcessingOnBatchFailure)
156        {
157            this.continueProcessingOnBatchFailure = continueProcessingOnBatchFailure;
158        }
159        
160        /**
161         * Collects all SQL strings that were executed.
162         * @param sql the SQL string
163         */
164        public void addExecutedStatement(String sql)
165        {
166                    executedStatements.add(sql);
167        }
168        
169        /**
170         * Collects all <code>ResultSet</code> objects that were returned by
171         * a <code>Statement</code>, <code>PreparedStatement</code> or
172         * <code>CallableStatement</code>.
173         * @param resultSet the <code>ResultSet</code>
174         */
175        public void addReturnedResultSet(MockResultSet resultSet)
176        {
177            if(null == resultSet) return;
178            returnedResultSets.add(resultSet);
179        }
180        
181        /**
182         * Collects all <code>ResultSet[]</code> objects that were returned by
183         * a <code>Statement</code>, <code>PreparedStatement</code> or
184         * <code>CallableStatement</code>. Called if a statement returns
185         * multiple result sets.
186         * @param resultSets the <code>ResultSet[]</code>
187         */
188        public void addReturnedResultSets(MockResultSet[] resultSets)
189        {
190            if(null == resultSets) return;
191            returnedResultSets.add(resultSets);
192        }
193        
194        /**
195         * Returns the <code>List</code> of all executed SQL strings.
196         * @return the <code>List</code> of executed SQL strings
197         */
198        public List getExecutedStatements()
199        {
200            return Collections.unmodifiableList(executedStatements);
201        }
202        
203        /**
204         * Returns the <code>List</code> of all returned <code>ResultSet</code> 
205         * or <code>ResultSet[]</code> objects. The <code>List</code> contains
206         * arrays of result sets, if a query returned multiple result sets.
207         * If a query returned multiple result sets, the list will always contain
208         * the full array of <code>ResultSet</code> objects that were prepared, even
209         * if {@link com.mockrunner.mock.jdbc.MockStatement#getMoreResults()} was
210         * not called for all the result sets.
211         * @return the <code>List</code> of returned <code>ResultSet</code> or <code>ResultSet[]</code> objects
212         */
213        public List getReturnedResultSets()
214        {
215            return Collections.unmodifiableList(returnedResultSets);
216        }
217        
218        /**
219         * Clears all prepared <code>ResultSet</code> objects.
220         */
221        public void clearResultSets()
222        {
223            resultSetsForStatement.clear();
224        }
225        
226        /**
227         * Clears all prepared update counts.
228         */
229        public void clearUpdateCounts()
230        {
231            updateCountForStatement.clear();
232        }
233        
234        /**
235         * Clears the definitions if statements return
236         * <code>ResultSet</code> objects or update counts.
237         */
238        public void clearReturnsResultSet()
239        {
240            returnsResultSetMap.clear();
241        }
242        
243        /**
244         * Clears the list of statements that should throw an exception.
245         */
246        public void clearThrowsSQLException()
247        {
248            throwsSQLException.clear();
249        }
250        
251        /**
252         * Clears the list of statements that return generated keys.
253         */
254        public void clearGeneratedKeys()
255        {
256            generatedKeysForStatement.clear();
257        }
258        
259        /**
260         * Clears the prepared global <code>ResultSet</code>.
261         */
262        public void clearGlobalResultSet()
263        {
264            this.globalResultSets = null;
265        }
266        
267        /**
268         * Clears the prepared global generated keys <code>ResultSet</code>.
269         */
270        public void clearGlobalGeneratedKeys()
271        {
272            this.globalGeneratedKeys = null;
273        }
274        
275        /**
276         * Clears the prepared global update count.
277         */
278        public void clearGlobalUpdateCount()
279        {
280            this.globalUpdateCounts = null;
281        }
282        
283        /**
284         * Returns the <code>Map</code> of all <code>ResultSet</code>
285         * objects, that were added with {@link #prepareResultSet(String, MockResultSet)}.
286         * The SQL strings map to the corresponding <code>ResultSet</code>.
287         * @return the <code>Map</code> of <code>ResultSet</code> objects
288         */
289        public Map getResultSetMap()
290        {
291            return Collections.unmodifiableMap(resultSetsForStatement);
292        }
293        
294        /**
295         * Returns the <code>Map</code> of all update counts, that were added 
296         * with {@link #prepareUpdateCount(String, int)}.
297         * The SQL strings map to the corresponding update count as
298         * <code>Integer</code> object.
299         * @return the <code>Map</code> of <code>ResultSet</code> objects
300         */
301        public Map getUpdateCountMap()
302        {
303            return Collections.unmodifiableMap(updateCountForStatement);
304        }
305        
306        /**
307         * Returns the <code>Map</code> of all generated keys <code>ResultSet</code>
308         * objects, that were added with {@link #prepareGeneratedKeys(String, MockResultSet)}.
309         * The SQL strings map to the corresponding generated keys <code>ResultSet</code>.
310         * @return the <code>Map</code> of generated keys <code>ResultSet</code> objects
311         */
312        public Map getGeneratedKeysMap()
313        {
314            return Collections.unmodifiableMap(generatedKeysForStatement);
315        }
316        
317        /**
318         * Returns the first <code>ResultSet</code> that matches the
319         * specified SQL string. If the specified SQL string was
320         * prepared to return multiple result sets, the first one will
321         * be returned.
322         * Please note that you can modify the match parameters with {@link #setCaseSensitive},
323         * {@link #setExactMatch} and {@link #setUseRegularExpressions}.
324         * @param sql the SQL string
325         * @return the corresponding {@link MockResultSet}
326         */
327        public MockResultSet getResultSet(String sql)
328        {
329            Object resultSets = getMatchingResultSets(sql);
330            if(null == resultSets) return null;
331            if(resultSets instanceof MockResultSet)
332            {
333                return (MockResultSet)resultSets;
334            }
335            else if(resultSets instanceof MockResultSet[])
336            {
337                MockResultSet[] actualResults = (MockResultSet[])resultSets;
338                if(actualResults.length > 0)
339                {
340                    return actualResults[0];
341                }
342            }
343            return null;
344        }
345        
346        /**
347         * Returns the first <code>ResultSet[]</code> that matches the
348         * specified SQL string. If the specified SQL string was
349         * prepared to return one single result set, this <code>ResultSet</code>
350         * will be wrapped in an array with one element.
351         * Please note that you can modify the match parameters with {@link #setCaseSensitive},
352         * {@link #setExactMatch} and {@link #setUseRegularExpressions}.
353         * @param sql the SQL string
354         * @return the corresponding <code>MockResultSet[]</code>
355         */
356        public MockResultSet[] getResultSets(String sql)
357        {
358            Object resultSets = getMatchingResultSets(sql);
359            if(null == resultSets) return null;
360            if(resultSets instanceof MockResultSet)
361            {
362                return new MockResultSet[] {(MockResultSet)resultSets};
363            }
364            else if(resultSets instanceof MockResultSet[])
365            {
366                return (MockResultSet[])resultSets;
367            }
368            return null;
369        }
370    
371        /**
372         * Returns the if the specified SQL string returns multiple result sets.
373         * Please note that you can modify the match parameters with {@link #setCaseSensitive},
374         * {@link #setExactMatch} and {@link #setUseRegularExpressions}.
375         * @param sql the SQL string
376         * @return <code>true</code> if the query returns multiple result sets,
377         *         <code>false</code> otherwise
378         */
379        public boolean hasMultipleResultSets(String sql)
380        {
381            Object resultSets = getMatchingResultSets(sql);
382            return (resultSets instanceof MockResultSet[]);
383        }
384        
385        private Object getMatchingResultSets(String sql)
386        {
387            SQLStatementMatcher matcher = new SQLStatementMatcher(getCaseSensitive(), getExactMatch(), getUseRegularExpressions());
388            List list = matcher.getMatchingObjects(resultSetsForStatement, sql, true, true);
389            if(null != list && list.size() > 0)
390            {
391                return list.get(0);
392            }
393            return null;
394        }
395        
396        /**
397         * Returns the global <code>ResultSet</code>. 
398         * If an array of global result sets was prepared, the first one will
399         * be returned.
400         * @return the global {@link MockResultSet}
401         */
402        public MockResultSet getGlobalResultSet()
403        {
404            if(null == globalResultSets) return null;
405            if(globalResultSets instanceof MockResultSet)
406            {
407                return (MockResultSet)globalResultSets;
408            }
409            MockResultSet[] resultSets = getGlobalResultSets();
410            if(null != resultSets && resultSets.length > 0)
411            {
412                return resultSets[0];
413            }
414            return null;
415        }
416        
417        /**
418         * Returns the global <code>ResultSet[]</code>. 
419         * If one single <code>ResultSet</code> was prepared, this <code>ResultSet</code>
420         * will be wrapped in an array with one element.
421         * @return the global <code>MockResultSet[]</code>
422         */
423        public MockResultSet[] getGlobalResultSets()
424        {
425            if(null == globalResultSets) return null;
426            if(globalResultSets instanceof MockResultSet[])
427            {
428                return (MockResultSet[])globalResultSets;
429            }
430            return new MockResultSet[] {(MockResultSet)globalResultSets};
431        }
432        
433        /**
434         * Returns if multiple global result sets have been prepared, i.e. if
435         * an array of global result sets was prepared.
436         * @return <code>true</code> if an array of global result sets was prepared,
437         *         <code>false</code> otherwise
438         */
439        public boolean hasMultipleGlobalResultSets()
440        {
441            return (globalResultSets instanceof MockResultSet[]);
442        }
443        
444        /**
445         * Returns the first update count that matches the
446         * specified SQL string. If the specified SQL string was
447         * prepared to return multiple update counts, the first one will
448         * be returned.
449         * Please note that you can modify the match parameters with {@link #setCaseSensitive},
450         * {@link #setExactMatch} and {@link #setUseRegularExpressions}.
451         * @param sql the SQL string
452         * @return the corresponding update count
453         */
454        public Integer getUpdateCount(String sql)
455        {
456            Object updateCounts = getMatchingUpdateCounts(sql);
457            if(null == updateCounts) return null;
458            if(updateCounts instanceof Integer)
459            {
460                return (Integer)updateCounts;
461            }
462            else if(updateCounts instanceof Integer[])
463            {
464                Integer[] actualUpdateCounts = (Integer[])updateCounts;
465                if(actualUpdateCounts.length > 0)
466                {
467                    return actualUpdateCounts[0];
468                }
469            }
470            return null;
471        }
472        
473        /**
474         * Returns the first update count array that matches the
475         * specified SQL string. If the specified SQL string was
476         * prepared to return one update count, this value
477         * will be wrapped in an array with one element.
478         * Please note that you can modify the match parameters with {@link #setCaseSensitive},
479         * {@link #setExactMatch} and {@link #setUseRegularExpressions}.
480         * @param sql the SQL string
481         * @return the corresponding update count array
482         */
483        public Integer[] getUpdateCounts(String sql)
484        {
485            Object updateCounts = getMatchingUpdateCounts(sql);
486            if(null == updateCounts) return null;
487            if(updateCounts instanceof Integer)
488            {
489                return new Integer[] {(Integer)updateCounts};
490            }
491            else if(updateCounts instanceof Integer[])
492            {
493                return (Integer[])updateCounts;
494            }
495            return null;
496        }
497        
498        /**
499         * Returns the if the specified SQL string returns multiple update counts.
500         * Please note that you can modify the match parameters with {@link #setCaseSensitive},
501         * {@link #setExactMatch} and {@link #setUseRegularExpressions}.
502         * @param sql the SQL string
503         * @return <code>true</code> if the SQL string returns multiple update counts,
504         *         <code>false</code> otherwise
505         */
506        public boolean hasMultipleUpdateCounts(String sql)
507        {
508            Object updateCounts = getMatchingUpdateCounts(sql);
509            return (updateCounts instanceof Integer[]);
510        }
511        
512        private Object getMatchingUpdateCounts(String sql)
513        {
514            SQLStatementMatcher matcher = new SQLStatementMatcher(getCaseSensitive(), getExactMatch(), getUseRegularExpressions());
515            List list = matcher.getMatchingObjects(updateCountForStatement, sql, true, true);
516            if(null != list && list.size() > 0)
517            {
518                return list.get(0);
519            }
520            return null;
521        }
522        
523        /**
524         * Returns the global update count for <code>executeUpdate</code> calls.
525         * If an array of global update counts was prepared, the first one will
526         * be returned.
527         * @return the global update count
528         */
529        public int getGlobalUpdateCount()
530        {
531            if(null == globalUpdateCounts) return 0;
532            if(globalUpdateCounts instanceof Integer)
533            {
534                return ((Integer)globalUpdateCounts).intValue();
535            }
536            int[] updateCounts = getGlobalUpdateCounts();
537            if(null != updateCounts && updateCounts.length > 0)
538            {
539                return updateCounts[0];
540            }
541            return 0;
542        }
543        
544        /**
545         * Returns the array of global update counts.
546         * If one single update count value was prepared, this value
547         * will be wrapped in an array with one element.
548         * @return the array of global update counts
549         */
550        public int[] getGlobalUpdateCounts()
551        {
552            if(null == globalUpdateCounts) return null;
553            if(globalUpdateCounts instanceof int[])
554            {
555                return (int[])globalUpdateCounts;
556            }
557            return new int[] {((Integer)globalUpdateCounts).intValue()};
558        }
559        
560        /**
561         * Returns if multiple global update counts have been prepared, i.e. if
562         * an array of global update counts was prepared.
563         * @return <code>true</code> if an array of global update counts was prepared,
564         *         <code>false</code> otherwise
565         */
566        public boolean hasMultipleGlobalUpdateCounts()
567        {
568            return (globalUpdateCounts instanceof int[]);
569        }
570        
571        /**
572         * Returns the first generated keys <code>ResultSet</code> that 
573         * matches the specified SQL string. Please note that you can modify
574         * the match parameters with {@link #setCaseSensitive},
575         * {@link #setExactMatch} and {@link #setUseRegularExpressions}.
576         * @param sql the SQL string
577         * @return the corresponding generated keys {@link MockResultSet}
578         */
579        public MockResultSet getGeneratedKeys(String sql)
580        {
581            SQLStatementMatcher matcher = new SQLStatementMatcher(getCaseSensitive(), getExactMatch(), getUseRegularExpressions());
582            List list = matcher.getMatchingObjects(generatedKeysForStatement, sql, true, true);
583            if(null != list && list.size() > 0)
584            {
585                return (MockResultSet)list.get(0);
586            }
587            return null;
588        }
589        
590        /**
591         * Returns the global generated keys <code>ResultSet</code>.
592         * @return the global generated keys {@link MockResultSet}
593         */
594        public MockResultSet getGlobalGeneratedKeys()
595        {
596            return globalGeneratedKeys;
597        }
598        
599        /**
600         * Returns if the specified SQL string is a <i>select</i> that returns
601         * a <code>ResultSet</code>.
602         * Usually you do not have to specify this.
603         * It is assumed that an SQL string returns a <code>ResultSet</code> 
604         * if it contains the string <i>select</i> (case insensitive).
605         * Please note that you can modify the match parameters with 
606         * {@link #setCaseSensitive}, {@link #setExactMatch} and 
607         * {@link #setUseRegularExpressions}.
608         * @param sql the SQL string
609         * @return <code>true</code> if the SQL string returns a <code>ResultSet</code>
610         */
611        public Boolean getReturnsResultSet(String sql)
612        {
613            SQLStatementMatcher matcher = new SQLStatementMatcher(getCaseSensitive(), getExactMatch(), getUseRegularExpressions());
614            List list = matcher.getMatchingObjects(returnsResultSetMap, sql, true, true);
615            if(null != list && list.size() > 0)
616            {
617                return (Boolean)list.get(0);
618            }
619            return null;
620        }
621        
622        /**
623         * Returns if the specified SQL string should raise an exception.
624         * This can be used to simulate database exceptions.
625         * Please note that you can modify the match parameters with 
626         * {@link #setCaseSensitive}, {@link #setExactMatch} and 
627         * {@link #setUseRegularExpressions}.
628         * @param sql the SQL string
629         * @return <code>true</code> if the specified SQL string should raise an exception,
630         *         <code>false</code> otherwise
631         */
632        public boolean getThrowsSQLException(String sql)
633        {
634            return (getSQLException(sql) != null);
635        }
636        
637        /**
638         * Returns the <code>SQLException</code> the specified SQL string
639         * should throw. Returns <code>null</code> if the specified SQL string
640         * should not throw an exception.
641         * This can be used to simulate database exceptions.
642         * Please note that you can modify the match parameters with 
643         * {@link #setCaseSensitive}, {@link #setExactMatch} and 
644         * {@link #setUseRegularExpressions}.
645         * @param sql the SQL string
646         * @return the <code>SQLException</code> or <code>null</code>
647         */
648        public SQLException getSQLException(String sql)
649        {
650            SQLStatementMatcher matcher = new SQLStatementMatcher(getCaseSensitive(), getExactMatch(), getUseRegularExpressions());
651            List list = matcher.getMatchingObjects(throwsSQLException, sql, true, true);
652            if(null == list || list.size() == 0) return null;
653            return (SQLException)list.get(0);
654        }
655        
656        /**
657         * Prepare a <code>ResultSet</code> for a specified SQL string.
658         * Please note that you can modify the match parameters with 
659         * {@link #setCaseSensitive}, {@link #setExactMatch} and 
660         * {@link #setUseRegularExpressions}.
661         * @param sql the SQL string
662         * @param resultSet the corresponding {@link MockResultSet}
663         */
664        public void prepareResultSet(String sql, MockResultSet resultSet)
665        {
666            resultSetsForStatement.put(sql, resultSet);
667        }
668        
669        /**
670         * Prepare an array of <code>ResultSet</code> objects for a specified SQL string.
671         * This method can be used for queries that return multiple result sets.
672         * Please note that you can modify the match parameters with 
673         * {@link #setCaseSensitive}, {@link #setExactMatch} and 
674         * {@link #setUseRegularExpressions}.
675         * @param sql the SQL string
676         * @param resultSets the corresponding <code>MockResultSet[]</code>
677         */
678        public void prepareResultSets(String sql, MockResultSet[] resultSets)
679        {
680            resultSetsForStatement.put(sql, resultSets.clone());
681        }
682    
683        /**
684         * Prepare the global <code>ResultSet</code>.
685         * @param resultSet the {@link MockResultSet}
686         */
687        public void prepareGlobalResultSet(MockResultSet resultSet)
688        {
689            this.globalResultSets = resultSet;
690        }
691        
692        /**
693         * Prepare an array of global <code>ResultSet</code> objects.
694         * @param resultSets the corresponding <code>MockResultSet[]</code>
695         */
696        public void prepareGlobalResultSets(MockResultSet[] resultSets)
697        {
698            this.globalResultSets = (MockResultSet[])resultSets.clone();
699        }
700        
701        /**
702         * Prepare the update count for <code>executeUpdate</code> calls 
703         * for a specified SQL string. Please note that you can modify
704         * the match parameters with {@link #setCaseSensitive},
705         * {@link #setExactMatch} and {@link #setUseRegularExpressions}.
706         * @param sql the SQL string
707         * @param updateCount the update count
708         */
709        public void prepareUpdateCount(String sql, int updateCount)
710        {
711            updateCountForStatement.put(sql, new Integer(updateCount));
712        }
713        
714        /**
715         * Prepare an array update count values for <code>executeUpdate</code> calls 
716         * for a specified SQL string. This method can be used if multiple update counts
717         * are returned.
718         * Please note that you can modify the match parameters with {@link #setCaseSensitive},
719         * {@link #setExactMatch} and {@link #setUseRegularExpressions}.
720         * @param sql the SQL string
721         * @param updateCounts the update count array
722         */
723        public void prepareUpdateCounts(String sql, int[] updateCounts)
724        {
725            updateCountForStatement.put(sql, ArrayUtil.convertToObjectArray(updateCounts));
726        }
727        
728        /**
729         * Prepare the global update count for <code>executeUpdate</code> calls.
730         * @param updateCount the update count
731         */
732        public void prepareGlobalUpdateCount(int updateCount)
733        {
734            this.globalUpdateCounts = new Integer(updateCount);
735        }
736        
737        /**
738         * Prepare an array of global update count values for <code>executeUpdate</code> calls.
739         * @param updateCounts the update count array
740         */
741        public void prepareGlobalUpdateCounts(int[] updateCounts)
742        {
743            this.globalUpdateCounts = (int[])updateCounts.clone();
744        }
745        
746        /**
747         * Prepare the generated keys <code>ResultSet</code> 
748         * for a specified SQL string. Please note that you can modify
749         * the match parameters with {@link #setCaseSensitive},
750         * {@link #setExactMatch} and {@link #setUseRegularExpressions}.
751         * @param sql the SQL string
752         * @param generatedKeysResult the generated keys {@link MockResultSet}
753         */
754        public void prepareGeneratedKeys(String sql, MockResultSet generatedKeysResult)
755        {
756            generatedKeysForStatement.put(sql, generatedKeysResult);
757        }
758        
759        /**
760         * Prepare the global generated keys <code>ResultSet</code>.
761         * @param generatedKeysResult the generated keys {@link MockResultSet}
762         */
763        public void prepareGlobalGeneratedKeys(MockResultSet generatedKeysResult)
764        {
765            this.globalGeneratedKeys = generatedKeysResult;
766        }
767        
768        /**
769         * Prepare if the specified SQL string is a <i>select</i> that returns
770         * a <code>ResultSet</code>. Usually you do not have to specify this.
771         * It is assumed that an SQL string returns a <code>ResultSet</code> 
772         * if it contains the string <i>select</i> (case insensitive).
773         * Please note that you can modify the match parameters with 
774         * {@link #setCaseSensitive}, {@link #setExactMatch} and 
775         * {@link #setUseRegularExpressions}.
776         * @param sql the SQL string
777         * @param returnsResultSet specify if the SQL string returns a <code>ResultSet</code>
778         */
779        public void prepareReturnsResultSet(String sql, boolean returnsResultSet)
780        {
781            returnsResultSetMap.put(sql, new Boolean(returnsResultSet));
782        }
783        
784        /**
785         * Prepare that the specified SQL string should raise an exception.
786         * This can be used to simulate database exceptions. This method
787         * creates an <code>SQLException</code> and will throw this exception.
788         * With {@link #prepareThrowsSQLException(String, SQLException)} you
789         * can specify the exception.
790         * Please note that you can modify the match parameters with 
791         * {@link #setCaseSensitive}, {@link #setExactMatch} and 
792         * {@link #setUseRegularExpressions}.
793         * @param sql the SQL string
794         */
795        public void prepareThrowsSQLException(String sql)
796        {
797            throwsSQLException.put(sql, new SQLException("Statement " + sql + " was specified to throw an exception"));
798        }
799        
800        /**
801         * Prepare that the specified SQL string should raise an exception.
802         * This can be used to simulate database exceptions. This method
803         * takes an exception object that will be thrown.
804         * Please note that you can modify the match parameters with 
805         * {@link #setCaseSensitive}, {@link #setExactMatch} and 
806         * {@link #setUseRegularExpressions}.
807         * @param sql the SQL string
808         * @param exc the <code>SQLException</code> that should be thrown
809         */
810        public void prepareThrowsSQLException(String sql, SQLException exc)
811        {
812            throwsSQLException.put(sql, exc);
813        }
814        
815        /**
816         * Returns if specified SQL strings should be handled case sensitive.
817         * @return is case sensitivity enabled or disabled
818         */
819        protected boolean getCaseSensitive()
820        {
821            return caseSensitive;
822        }
823        
824        /**
825         * Returns if specified SQL statements must match exactly.
826         * @return is exact matching enabled or disabled
827         */
828        protected boolean getExactMatch()
829        {
830            return exactMatch;
831        }
832        
833        /**
834         * Returns if regular expression matching is enabled
835         * @return if regular expression matching is enabled
836         */
837        protected boolean getUseRegularExpressions()
838        {
839            return useRegularExpressions;
840        }
841    
842        /**
843         * Returns if batch processing should be continued if one of 
844         * the commands in the batch fails.
845         * @return if batch processing should be continued
846         */
847        public boolean getContinueProcessingOnBatchFailure()
848        {
849            return continueProcessingOnBatchFailure;
850        }
851    }