SQLite.java revision e33dc87ec6782df9a3711d06c687521ff2a97d86
1package com.xtremelabs.robolectric.util;
2
3import android.content.ContentValues;
4import android.database.sqlite.SQLiteException;
5
6import java.util.ArrayList;
7import java.util.Iterator;
8import java.util.List;
9import java.util.Map.Entry;
10
11/**
12 * SQL utility methods to support the database-related shadows, such as
13 * {@code ShadowSQLiteDatabase} and {@code ShadowSQLiteCursor}.
14 */
15public class SQLite {
16
17    /**
18     * Create a SQL INSERT string.  Returned values are then bound via
19     * JDBC to facilitate various data types.
20     *
21     * @param table  table name
22     * @param values column name/value pairs
23     * @return insert string
24     */
25    public static SQLStringAndBindings buildInsertString(String table, ContentValues values) {
26        StringBuilder sb = new StringBuilder();
27
28        sb.append("INSERT INTO ");
29        sb.append(table);
30        sb.append(" ");
31
32        SQLStringAndBindings columnsValueClause = buildColumnValuesClause(values);
33        sb.append(columnsValueClause.sql);
34        sb.append(";");
35
36        return new SQLStringAndBindings(sb.toString(), columnsValueClause.columnValues);
37    }
38
39    /**
40     * Create a SQL UPDATE string.  Returned values are then bound via
41     * JDBC to facilitate various data types.
42     *
43     * @param table       table name
44     * @param values      column name/value pairs
45     * @param whereClause SQL where clause fragment
46     * @param whereArgs   Array of substitutions for args in whereClause
47     * @return update string
48     */
49    public static SQLStringAndBindings buildUpdateString(String table, ContentValues values, String whereClause, String[] whereArgs) {
50        StringBuilder sb = new StringBuilder();
51
52        sb.append("UPDATE ");
53        sb.append(table);
54        sb.append(" SET ");
55
56        SQLStringAndBindings columnAssignmentsClause = buildColumnAssignmentsClause(values);
57        sb.append(columnAssignmentsClause.sql);
58
59        if (whereClause != null) {
60            String where = whereClause;
61            if (whereArgs != null) {
62                where = buildWhereClause(whereClause, whereArgs);
63            }
64            sb.append(" WHERE ");
65            sb.append(where);
66        }
67        sb.append(";");
68
69        return new SQLStringAndBindings(sb.toString(), columnAssignmentsClause.columnValues);
70    }
71
72    /**
73     * Create a SQL DELETE string.
74     *
75     * @param table       table name
76     * @param whereClause SQL where clause fragment
77     * @param whereArgs   Array of substitutions for args in whereClause
78     * @return delete string
79     */
80    public static String buildDeleteString(String table, String whereClause, String[] whereArgs) {
81        StringBuilder sb = new StringBuilder();
82
83        sb.append("DELETE FROM ");
84        sb.append(table);
85
86        if (whereClause != null) {
87            String where = whereClause;
88            if (whereArgs != null) {
89                where = buildWhereClause(whereClause, whereArgs);
90            }
91            sb.append(" WHERE ");
92            sb.append(where);
93        }
94        sb.append(";");
95
96        return sb.toString();
97    }
98
99    /**
100     * Build a WHERE clause used in SELECT, UPDATE and DELETE statements.
101     *
102     * @param selection     SQL where clause fragment
103     * @param selectionArgs Array of substitutions for args in selection
104     * @return where clause
105     */
106    public static String buildWhereClause(String selection, String[] selectionArgs) throws SQLiteException {
107        String whereClause = selection;
108        int argsNeeded = 0;
109        int args = 0;
110
111        for (String selectionArg : selectionArgs) {
112        	if (selectionArg!=null)args++;
113            whereClause = whereClause.replaceFirst("\\?", "'" + selectionArg + "'");
114        }
115
116        for (char c : selection.toCharArray()) {
117        	if (c=='?') argsNeeded++;
118        }
119
120        if (argsNeeded<args) throw new SQLiteException("bind or column index out of range: too many selectionArgs for the given sql statement!");
121
122
123        return whereClause;
124    }
125
126    /**
127     * Build the '(columns...) VALUES (values...)' clause used in INSERT
128     * statements.
129     *
130     * @param values column name/value pairs
131     * @return SQLStringAndBindings
132     */
133    public static SQLStringAndBindings buildColumnValuesClause(ContentValues values) {
134        StringBuilder clause = new StringBuilder("(");
135        List<Object> columnValues = new ArrayList<Object>(values.size());
136
137        Iterator<Entry<String, Object>> itemEntries = values.valueSet().iterator();
138        while (itemEntries.hasNext()) {
139            Entry<String, Object> entry = itemEntries.next();
140            clause.append(entry.getKey());
141            if (itemEntries.hasNext()) {
142                clause.append(", ");
143            }
144            columnValues.add(entry.getValue());
145        }
146
147        clause.append(") VALUES (");
148        for (int i = 0; i < values.size() - 1; i++) {
149            clause.append("?, ");
150        }
151        clause.append("?)");
152
153        return new SQLStringAndBindings(clause.toString(), columnValues);
154    }
155
156    /**
157     * Build the '(col1=?, col2=? ... )' clause used in UPDATE statements.
158     *
159     * @param values column name/value pairs
160     * @return SQLStringAndBindings
161     */
162    public static SQLStringAndBindings buildColumnAssignmentsClause(ContentValues values) {
163        StringBuilder clause = new StringBuilder();
164        List<Object> columnValues = new ArrayList<Object>(values.size());
165
166        Iterator<Entry<String, Object>> itemsEntries = values.valueSet().iterator();
167        while (itemsEntries.hasNext()) {
168            Entry<String, Object> entry = itemsEntries.next();
169            clause.append(entry.getKey());
170            clause.append("=?");
171            if (itemsEntries.hasNext()) {
172                clause.append(", ");
173            }
174            columnValues.add(entry.getValue());
175        }
176
177        return new SQLStringAndBindings(clause.toString(), columnValues);
178    }
179
180    /**
181     * Container for a SQL fragment and the objects which are to be
182     * bound to the arguments in the fragment.
183     */
184    public static class SQLStringAndBindings {
185        public String sql;
186        public List<Object> columnValues;
187
188        public SQLStringAndBindings(String sql, List<Object> columnValues) {
189            this.sql = sql;
190            this.columnValues = columnValues;
191        }
192    }
193
194}
195