• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.xtremelabs.robolectric.shadows;
2 
3 import android.database.sqlite.SQLiteCursor;
4 import com.xtremelabs.robolectric.internal.Implementation;
5 import com.xtremelabs.robolectric.internal.Implements;
6 
7 import java.sql.Clob;
8 import java.sql.Connection;
9 import java.sql.ResultSet;
10 import java.sql.ResultSetMetaData;
11 import java.sql.SQLException;
12 import java.sql.Statement;
13 import java.util.HashMap;
14 import java.util.Map;
15 
16 /**
17  * Simulates an Android Cursor object, by wrapping a JDBC ResultSet.
18  */
19 @Implements(SQLiteCursor.class)
20 public class ShadowSQLiteCursor extends ShadowAbstractCursor {
21 
22     private ResultSet resultSet;
23 
24 
25     /**
26      * Stores the column names so they are retrievable after the resultSet has closed
27      */
cacheColumnNames(ResultSet rs)28     private void cacheColumnNames(ResultSet rs) {
29     	try {
30             ResultSetMetaData metaData = rs.getMetaData();
31             int columnCount = metaData.getColumnCount();
32             columnNameArray = new String[columnCount];
33             for (int columnIndex = 1; columnIndex <= columnCount; columnIndex++) {
34                 String cName = metaData.getColumnName(columnIndex).toLowerCase();
35                 this.columnNames.put(cName, columnIndex-1);
36                 this.columnNameArray[columnIndex-1]=cName;
37             }
38         } catch (SQLException e) {
39             throw new RuntimeException("SQL exception in cacheColumnNames", e);
40         }
41     }
42 
43 
44 
45 
getColIndex(String columnName)46     private Integer getColIndex(String columnName) {
47         if (columnName == null) {
48             return -1;
49         }
50 
51         Integer i  = this.columnNames.get(columnName.toLowerCase());
52         if (i==null) return -1;
53         return i;
54     }
55 
56     @Implementation
getColumnIndex(String columnName)57     public int getColumnIndex(String columnName) {
58     	return getColIndex(columnName);
59     }
60 
61     @Implementation
getColumnIndexOrThrow(String columnName)62     public int getColumnIndexOrThrow(String columnName) {
63     	Integer columnIndex = getColIndex(columnName);
64         if (columnIndex == -1) {
65             throw new IllegalArgumentException("Column index does not exist");
66         }
67         return columnIndex;
68     }
69 
70     @Implementation
71     @Override
moveToLast()72     public final boolean moveToLast() {
73         return super.moveToLast();
74     }
75 
76     @Implementation
77     @Override
moveToFirst()78     public final boolean moveToFirst() {
79         return super.moveToFirst();
80     }
81 
82     @Implementation
83     @Override
moveToNext()84     public boolean moveToNext() {
85         return super.moveToNext();
86     }
87 
88     @Implementation
89     @Override
moveToPrevious()90     public boolean moveToPrevious() {
91         return super.moveToPrevious();
92     }
93 
94     @Implementation
95     @Override
moveToPosition(int pos)96     public boolean moveToPosition(int pos) {
97     	return super.moveToPosition(pos);
98     }
99 
100     @Implementation
getBlob(int columnIndex)101     public byte[] getBlob(int columnIndex) {
102     	checkPosition();
103         return (byte[]) this.currentRow.get(getColumnNames()[columnIndex]);
104     }
105 
106     @Implementation
getString(int columnIndex)107     public String getString(int columnIndex) {
108         checkPosition();
109         Object value = this.currentRow.get(getColumnNames()[columnIndex]);
110         if (value instanceof Clob) {
111             try {
112                 return ((Clob) value).getSubString(1, (int)((Clob) value).length());
113             } catch (SQLException x) {
114                 throw new RuntimeException(x);
115             }
116         } else {
117             return (String)value;
118         }
119     }
120 
121 	@Implementation
getShort(int columnIndex)122 	public short getShort(int columnIndex) {
123 		checkPosition();
124 		Object o =this.currentRow.get(getColumnNames()[columnIndex]);
125     	if (o==null) return 0;
126         return new Short(o.toString());
127 	}
128 
129     @Implementation
getInt(int columnIndex)130     public int getInt(int columnIndex) {
131     	checkPosition();
132     	Object o =this.currentRow.get(getColumnNames()[columnIndex]);
133     	if (o==null) return 0;
134         return new Integer(o.toString());
135     }
136 
137     @Implementation
getLong(int columnIndex)138     public long getLong(int columnIndex) {
139     	checkPosition();
140     	Object o =this.currentRow.get(getColumnNames()[columnIndex]);
141     	if (o==null) return 0;
142         return new Long(o.toString());
143     }
144 
145     @Implementation
getFloat(int columnIndex)146     public float getFloat(int columnIndex) {
147     	checkPosition();
148     	Object o =this.currentRow.get(getColumnNames()[columnIndex]);
149     	if (o==null) return 0;
150         return new Float(o.toString());
151 
152     }
153 
154     @Implementation
getDouble(int columnIndex)155     public double getDouble(int columnIndex) {
156     	checkPosition();
157     	Object o =this.currentRow.get(getColumnNames()[columnIndex]);
158     	if (o==null) return 0;
159     	return new Double(o.toString());
160     }
161 
checkPosition()162     private void checkPosition() {
163         if (-1 == currentRowNumber || getCount() == currentRowNumber) {
164             throw new IndexOutOfBoundsException(currentRowNumber + " " + getCount());
165         }
166     }
167 
168     @Implementation
close()169     public void close() {
170         if (resultSet == null) {
171             return;
172         }
173 
174         try {
175             resultSet.close();
176             resultSet = null;
177             rows = null;
178             currentRow = null;
179         } catch (SQLException e) {
180             throw new RuntimeException("SQL exception in close", e);
181         }
182     }
183 
184     @Implementation
isClosed()185     public boolean isClosed() {
186         return (resultSet == null);
187     }
188 
189     @Implementation
isNull(int columnIndex)190     public boolean isNull(int columnIndex) {
191         Object o = this.currentRow.get(getColumnNames()[columnIndex]);
192         return o == null;
193     }
194 
195     /**
196      * Allows test cases access to the underlying JDBC ResultSet, for use in
197      * assertions.
198      *
199      * @return the result set
200      */
getResultSet()201     public ResultSet getResultSet() {
202         return resultSet;
203     }
204 
205     /**
206      * Allows test cases access to the underlying JDBC ResultSetMetaData, for use in
207      * assertions. Available even if cl
208      *
209      * @return the result set
210      */
getResultSetMetaData()211     public ResultSet getResultSetMetaData() {
212         return resultSet;
213     }
214 
215     /**
216      * loads a row's values
217      * @param rs
218      * @return
219      * @throws SQLException
220      */
fillRowValues(ResultSet rs)221     private Map<String,Object> fillRowValues(ResultSet rs) throws SQLException {
222     	Map<String,Object> row = new HashMap<String,Object>();
223     	for (String s : getColumnNames()) {
224 			  row.put(s, rs.getObject(s));
225     	}
226     	return row;
227     }
fillRows(String sql, Connection connection)228     private void fillRows(String sql, Connection connection) throws SQLException {
229     	//ResultSets in SQLite\Android are only TYPE_FORWARD_ONLY. Android caches results in the WindowedCursor to allow moveToPrevious() to function.
230     	//Robolectric will have to cache the results too. In the rows map.
231     	Statement statement = connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
232         ResultSet rs = statement.executeQuery(sql);
233         int count = 0;
234         if (rs.next()) {
235         	     do {
236         	    	Map<String,Object> row = fillRowValues(rs);
237          	    	rows.put(count, row);
238         	    	count++;
239         	     } while (rs.next());
240         	 } else {
241         		 rs.close();
242         	 }
243 
244         rowCount = count;
245 
246     }
247 
setResultSet(ResultSet result, String sql)248     public void setResultSet(ResultSet result, String sql) {
249         this.resultSet = result;
250         rowCount = 0;
251 
252         //Cache all rows.  Caching rows should be thought of as a simple replacement for ShadowCursorWindow
253         if (resultSet != null) {
254         	cacheColumnNames(resultSet);
255         	try {
256         		fillRows(sql, result.getStatement().getConnection());
257 			} catch (SQLException e) {
258 			    throw new RuntimeException("SQL exception in setResultSet", e);
259 			}
260         }
261     }
262 }
263