2012-10-28 21:27:34 -04:00
|
|
|
package com.fsck.k9.search;
|
|
|
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import com.fsck.k9.Account;
|
|
|
|
import com.fsck.k9.mail.MessagingException;
|
2013-07-30 16:36:47 -04:00
|
|
|
import com.fsck.k9.mail.Folder;
|
2012-10-28 21:27:34 -04:00
|
|
|
import com.fsck.k9.mail.store.LocalStore;
|
|
|
|
import com.fsck.k9.mail.store.LocalStore.LocalFolder;
|
2012-12-07 06:03:04 -05:00
|
|
|
import com.fsck.k9.search.SearchSpecification.Attribute;
|
2012-10-28 21:27:34 -04:00
|
|
|
import com.fsck.k9.search.SearchSpecification.SearchCondition;
|
|
|
|
import com.fsck.k9.search.SearchSpecification.Searchfield;
|
|
|
|
|
|
|
|
|
|
|
|
public class SqlQueryBuilder {
|
|
|
|
public static void buildWhereClause(Account account, ConditionsTreeNode node,
|
|
|
|
StringBuilder query, List<String> selectionArgs) {
|
|
|
|
buildWhereClauseInternal(account, node, query, selectionArgs);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void buildWhereClauseInternal(Account account, ConditionsTreeNode node,
|
|
|
|
StringBuilder query, List<String> selectionArgs) {
|
|
|
|
if (node == null) {
|
2012-12-07 06:55:32 -05:00
|
|
|
query.append("1");
|
2012-10-28 21:27:34 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (node.mLeft == null && node.mRight == null) {
|
|
|
|
SearchCondition condition = node.mCondition;
|
|
|
|
switch (condition.field) {
|
|
|
|
case FOLDER: {
|
2012-10-30 20:45:44 -04:00
|
|
|
String folderName = condition.value;
|
2012-10-28 21:27:34 -04:00
|
|
|
long folderId = getFolderId(account, folderName);
|
2012-12-07 06:03:04 -05:00
|
|
|
if (condition.attribute == Attribute.EQUALS) {
|
|
|
|
query.append("folder_id = ?");
|
|
|
|
} else {
|
|
|
|
query.append("folder_id != ?");
|
|
|
|
}
|
2012-10-28 21:27:34 -04:00
|
|
|
selectionArgs.add(Long.toString(folderId));
|
|
|
|
break;
|
|
|
|
}
|
2012-12-07 06:55:32 -05:00
|
|
|
case SEARCHABLE: {
|
|
|
|
switch (account.getSearchableFolders()) {
|
|
|
|
case ALL: {
|
2013-04-23 14:14:29 -04:00
|
|
|
// Create temporary LocalSearch object so we can use...
|
|
|
|
LocalSearch tempSearch = new LocalSearch();
|
|
|
|
// ...the helper methods in Account to create the necessary conditions
|
|
|
|
// to exclude "unwanted" folders.
|
|
|
|
account.excludeUnwantedFolders(tempSearch);
|
|
|
|
|
|
|
|
buildWhereClauseInternal(account, tempSearch.getConditions(), query,
|
|
|
|
selectionArgs);
|
2012-12-07 06:55:32 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DISPLAYABLE: {
|
|
|
|
// Create temporary LocalSearch object so we can use...
|
|
|
|
LocalSearch tempSearch = new LocalSearch();
|
|
|
|
// ...the helper methods in Account to create the necessary conditions
|
|
|
|
// to limit the selection to displayable, non-special folders.
|
|
|
|
account.excludeSpecialFolders(tempSearch);
|
|
|
|
account.limitToDisplayableFolders(tempSearch);
|
|
|
|
|
|
|
|
buildWhereClauseInternal(account, tempSearch.getConditions(), query,
|
|
|
|
selectionArgs);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NONE: {
|
|
|
|
// Dummy condition, never select
|
|
|
|
query.append("0");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2012-10-28 21:27:34 -04:00
|
|
|
default: {
|
|
|
|
appendCondition(condition, query, selectionArgs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
query.append("(");
|
|
|
|
buildWhereClauseInternal(account, node.mLeft, query, selectionArgs);
|
|
|
|
query.append(") ");
|
|
|
|
query.append(node.mValue.name());
|
|
|
|
query.append(" (");
|
|
|
|
buildWhereClauseInternal(account, node.mRight, query, selectionArgs);
|
|
|
|
query.append(")");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void appendCondition(SearchCondition condition, StringBuilder query,
|
|
|
|
List<String> selectionArgs) {
|
|
|
|
query.append(getColumnName(condition));
|
|
|
|
appendExprRight(condition, query, selectionArgs);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static long getFolderId(Account account, String folderName) {
|
|
|
|
long folderId = 0;
|
|
|
|
try {
|
|
|
|
LocalStore localStore = account.getLocalStore();
|
|
|
|
LocalFolder folder = localStore.getFolder(folderName);
|
2013-07-30 16:36:47 -04:00
|
|
|
folder.open(Folder.OPEN_MODE_RO);
|
2012-10-28 21:27:34 -04:00
|
|
|
folderId = folder.getId();
|
|
|
|
} catch (MessagingException e) {
|
|
|
|
//FIXME
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
|
|
|
|
return folderId;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static String getColumnName(SearchCondition condition) {
|
|
|
|
String columnName = null;
|
|
|
|
switch (condition.field) {
|
|
|
|
case ATTACHMENT_COUNT: {
|
|
|
|
columnName = "attachment_count";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case BCC: {
|
|
|
|
columnName = "bcc_list";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case CC: {
|
|
|
|
columnName = "cc_list";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DATE: {
|
|
|
|
columnName = "date";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case DELETED: {
|
|
|
|
columnName = "deleted";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case FLAG: {
|
|
|
|
columnName = "flags";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case ID: {
|
|
|
|
columnName = "id";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case MESSAGE_CONTENTS: {
|
|
|
|
columnName = "text_content";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case REPLY_TO: {
|
|
|
|
columnName = "reply_to_list";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SENDER: {
|
|
|
|
columnName = "sender_list";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SUBJECT: {
|
|
|
|
columnName = "subject";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TO: {
|
|
|
|
columnName = "to_list";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case UID: {
|
|
|
|
columnName = "uid";
|
|
|
|
break;
|
|
|
|
}
|
2012-10-30 20:45:44 -04:00
|
|
|
case INTEGRATE: {
|
|
|
|
columnName = "integrate";
|
|
|
|
break;
|
|
|
|
}
|
2012-12-03 23:13:58 -05:00
|
|
|
case READ: {
|
|
|
|
columnName = "read";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case FLAGGED: {
|
|
|
|
columnName = "flagged";
|
|
|
|
break;
|
|
|
|
}
|
2012-12-07 06:03:04 -05:00
|
|
|
case DISPLAY_CLASS: {
|
|
|
|
columnName = "display_class";
|
|
|
|
break;
|
|
|
|
}
|
2013-03-07 19:15:26 -05:00
|
|
|
case THREAD_ID: {
|
|
|
|
columnName = "threads.root";
|
|
|
|
break;
|
|
|
|
}
|
2012-12-07 06:55:32 -05:00
|
|
|
case FOLDER:
|
|
|
|
case SEARCHABLE: {
|
|
|
|
// Special cases handled in buildWhereClauseInternal()
|
|
|
|
break;
|
|
|
|
}
|
2012-10-28 21:27:34 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (columnName == null) {
|
|
|
|
throw new RuntimeException("Unhandled case");
|
|
|
|
}
|
|
|
|
|
|
|
|
return columnName;
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void appendExprRight(SearchCondition condition, StringBuilder query,
|
|
|
|
List<String> selectionArgs) {
|
|
|
|
String value = condition.value;
|
|
|
|
Searchfield field = condition.field;
|
|
|
|
|
|
|
|
query.append(" ");
|
|
|
|
String selectionArg = null;
|
|
|
|
switch (condition.attribute) {
|
|
|
|
case NOT_CONTAINS:
|
|
|
|
query.append("NOT ");
|
|
|
|
//$FALL-THROUGH$
|
|
|
|
case CONTAINS: {
|
|
|
|
query.append("LIKE ?");
|
|
|
|
selectionArg = "%" + value + "%";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NOT_STARTSWITH:
|
|
|
|
query.append("NOT ");
|
|
|
|
//$FALL-THROUGH$
|
|
|
|
case STARTSWITH: {
|
|
|
|
query.append("LIKE ?");
|
|
|
|
selectionArg = "%" + value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NOT_ENDSWITH:
|
|
|
|
query.append("NOT ");
|
|
|
|
//$FALL-THROUGH$
|
|
|
|
case ENDSWITH: {
|
|
|
|
query.append("LIKE ?");
|
|
|
|
selectionArg = value + "%";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case NOT_EQUALS: {
|
|
|
|
if (isNumberColumn(field)) {
|
|
|
|
query.append("!= ?");
|
|
|
|
} else {
|
|
|
|
query.append("NOT LIKE ?");
|
|
|
|
}
|
|
|
|
selectionArg = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case EQUALS: {
|
|
|
|
if (isNumberColumn(field)) {
|
2012-10-30 20:45:44 -04:00
|
|
|
query.append("= ?");
|
2012-10-28 21:27:34 -04:00
|
|
|
} else {
|
|
|
|
query.append("LIKE ?");
|
|
|
|
}
|
|
|
|
selectionArg = value;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectionArg == null) {
|
|
|
|
throw new RuntimeException("Unhandled case");
|
|
|
|
}
|
|
|
|
|
|
|
|
selectionArgs.add(selectionArg);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean isNumberColumn(Searchfield field) {
|
|
|
|
switch (field) {
|
|
|
|
case ATTACHMENT_COUNT:
|
|
|
|
case DATE:
|
|
|
|
case DELETED:
|
|
|
|
case FOLDER:
|
|
|
|
case ID:
|
2012-10-30 20:45:44 -04:00
|
|
|
case INTEGRATE:
|
2013-01-10 21:40:35 -05:00
|
|
|
case THREAD_ID:
|
2012-12-07 06:03:04 -05:00
|
|
|
case READ:
|
|
|
|
case FLAGGED: {
|
2012-10-28 21:27:34 -04:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
default: {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-11-02 20:52:45 -04:00
|
|
|
|
|
|
|
public static String addPrefixToSelection(String[] columnNames, String prefix, String selection) {
|
|
|
|
String result = selection;
|
|
|
|
for (String columnName : columnNames) {
|
2013-01-10 21:40:35 -05:00
|
|
|
result = result.replaceAll("(?<=^|[^\\.])\\b" + columnName + "\\b", prefix + columnName);
|
2012-11-02 20:52:45 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2012-10-28 21:27:34 -04:00
|
|
|
}
|