blob: ebfd5ada0a25ffe2fb8d2120dce54821628e9bdb [file] [log] [blame]
package com.xtremelabs.robolectric.util;
import android.content.ContentValues;
import android.database.sqlite.SQLiteException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
/**
* SQL utility methods to support the database-related shadows, such as
* {@code ShadowSQLiteDatabase} and {@code ShadowSQLiteCursor}.
*/
public class SQLite {
private static final String[] CONFLICT_VALUES = {"", "OR ROLLBACK ", "OR ABORT ", "OR FAIL ", "OR IGNORE ", "OR REPLACE "};
/**
* Create a SQL INSERT string. Returned values are then bound via
* JDBC to facilitate various data types.
*
* @param table table name
* @param values column name/value pairs
* @param conflictAlgorithm the conflict algorithm to use
* @return insert string
*/
public static SQLStringAndBindings buildInsertString(String table, ContentValues values, int conflictAlgorithm) throws SQLException {
StringBuilder sb = new StringBuilder();
sb.append("INSERT ");
sb.append(CONFLICT_VALUES[conflictAlgorithm]);
sb.append("INTO ");
sb.append(table);
sb.append(" ");
SQLStringAndBindings columnsValueClause = buildColumnValuesClause(values);
sb.append(columnsValueClause.sql);
sb.append(";");
String sql = DatabaseConfig.getScrubSQL(sb.toString());
return new SQLStringAndBindings(sql, columnsValueClause.columnValues);
}
/**
* Create a SQL UPDATE string. Returned values are then bound via
* JDBC to facilitate various data types.
*
* @param table table name
* @param values column name/value pairs
* @param whereClause SQL where clause fragment
* @param whereArgs Array of substitutions for args in whereClause
* @return update string
*/
public static SQLStringAndBindings buildUpdateString(String table, ContentValues values, String whereClause, String[] whereArgs) {
StringBuilder sb = new StringBuilder();
sb.append("UPDATE ");
sb.append(table);
sb.append(" SET ");
SQLStringAndBindings columnAssignmentsClause = buildColumnAssignmentsClause(values);
sb.append(columnAssignmentsClause.sql);
if (whereClause != null) {
String where = whereClause;
if (whereArgs != null) {
where = buildWhereClause(whereClause, whereArgs);
}
sb.append(" WHERE ");
sb.append(where);
}
sb.append(";");
return new SQLStringAndBindings(sb.toString(), columnAssignmentsClause.columnValues);
}
/**
* Create a SQL DELETE string.
*
* @param table table name
* @param whereClause SQL where clause fragment
* @param whereArgs Array of substitutions for args in whereClause
* @return delete string
*/
public static String buildDeleteString(String table, String whereClause, String[] whereArgs) {
StringBuilder sb = new StringBuilder();
sb.append("DELETE FROM ");
sb.append(table);
if (whereClause != null) {
String where = whereClause;
if (whereArgs != null) {
where = buildWhereClause(whereClause, whereArgs);
}
sb.append(" WHERE ");
sb.append(where);
}
sb.append(";");
return sb.toString();
}
/**
* Build a WHERE clause used in SELECT, UPDATE and DELETE statements.
*
* @param selection SQL where clause fragment
* @param selectionArgs Array of substitutions for args in selection
* @return where clause
*/
public static String buildWhereClause(String selection, String[] selectionArgs) throws SQLiteException {
String whereClause = selection;
int argsNeeded = 0;
int args = 0;
for (char c : selection.toCharArray()) {
if (c == '?') argsNeeded++;
}
if (selectionArgs != null) {
for (int x = 0; x < selectionArgs.length; x++) {
if (selectionArgs[x] == null) {
throw new IllegalArgumentException("the bind value at index " + x + " is null");
} else {
args++;
}
whereClause = whereClause.replaceFirst("\\?", "'" + selectionArgs[x] + "'");
}
}
if (argsNeeded != args) {
throw new SQLiteException("bind or column index out of range: count of selectionArgs does not match count of (?) placeholders for given sql statement!");
}
return whereClause;
}
/**
* Build the '(columns...) VALUES (values...)' clause used in INSERT
* statements.
*
* @param values column name/value pairs
* @return SQLStringAndBindings
*/
public static SQLStringAndBindings buildColumnValuesClause(ContentValues values) {
StringBuilder clause = new StringBuilder("(");
List<Object> columnValues = new ArrayList<Object>(values.size());
Iterator<Entry<String, Object>> itemEntries = values.valueSet().iterator();
while (itemEntries.hasNext()) {
Entry<String, Object> entry = itemEntries.next();
clause.append(entry.getKey());
if (itemEntries.hasNext()) {
clause.append(", ");
}
columnValues.add(entry.getValue());
}
clause.append(") VALUES (");
for (int i = 0; i < values.size() - 1; i++) {
clause.append("?, ");
}
clause.append("?)");
return new SQLStringAndBindings(clause.toString(), columnValues);
}
/**
* Build the '(col1=?, col2=? ... )' clause used in UPDATE statements.
*
* @param values column name/value pairs
* @return SQLStringAndBindings
*/
public static SQLStringAndBindings buildColumnAssignmentsClause(ContentValues values) {
StringBuilder clause = new StringBuilder();
List<Object> columnValues = new ArrayList<Object>(values.size());
Iterator<Entry<String, Object>> itemsEntries = values.valueSet().iterator();
while (itemsEntries.hasNext()) {
Entry<String, Object> entry = itemsEntries.next();
clause.append(entry.getKey());
clause.append("=?");
if (itemsEntries.hasNext()) {
clause.append(", ");
}
columnValues.add(entry.getValue());
}
return new SQLStringAndBindings(clause.toString(), columnValues);
}
/**
* Container for a SQL fragment and the objects which are to be
* bound to the arguments in the fragment.
*/
public static class SQLStringAndBindings {
public String sql;
public List<Object> columnValues;
public SQLStringAndBindings(String sql, List<Object> columnValues) {
this.sql = sql;
this.columnValues = columnValues;
}
}
}