2012-10-12 07:22:54 -04:00
|
|
|
package com.fsck.k9.search;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
import android.os.Parcel;
|
|
|
|
import android.os.Parcelable;
|
|
|
|
|
|
|
|
import com.fsck.k9.mail.Flag;
|
|
|
|
|
|
|
|
/**
|
2012-10-16 16:42:51 -04:00
|
|
|
* This class represents a local search.
|
2012-10-17 14:52:03 -04:00
|
|
|
*
|
2012-10-12 07:22:54 -04:00
|
|
|
* Removing conditions could be done through matching there unique id in the leafset and then
|
|
|
|
* removing them from the tree.
|
2012-10-16 16:42:51 -04:00
|
|
|
*
|
2012-10-12 07:22:54 -04:00
|
|
|
* TODO implement a complete addAllowedFolder method
|
|
|
|
* TODO conflicting conditions check on add
|
|
|
|
* TODO duplicate condition checking?
|
|
|
|
* TODO assign each node a unique id that's used to retrieve it from the leaveset and remove.
|
2012-10-16 16:42:51 -04:00
|
|
|
*
|
2012-10-12 07:22:54 -04:00
|
|
|
*/
|
|
|
|
|
|
|
|
public class LocalSearch implements SearchSpecification {
|
|
|
|
|
|
|
|
private String mName;
|
2012-10-16 16:42:51 -04:00
|
|
|
private boolean mPredefined;
|
|
|
|
|
|
|
|
// since the uuid isn't in the message table it's not in the tree neither
|
|
|
|
private HashSet<String> mAccountUuids = new HashSet<String>();
|
|
|
|
private ConditionsTreeNode mConditions = null;
|
2012-10-12 07:22:54 -04:00
|
|
|
private HashSet<ConditionsTreeNode> mLeafSet = new HashSet<ConditionsTreeNode>();
|
2012-10-16 16:42:51 -04:00
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
// Constructors
|
|
|
|
///////////////////////////////////////////////////////////////
|
2012-10-12 07:22:54 -04:00
|
|
|
/**
|
2012-10-16 16:42:51 -04:00
|
|
|
* Use this only if the search won't be saved. Saved searches need
|
2012-10-12 07:22:54 -04:00
|
|
|
* a name!
|
|
|
|
*/
|
2012-10-17 14:52:03 -04:00
|
|
|
public LocalSearch() {}
|
2012-10-16 16:42:51 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
2012-10-12 07:22:54 -04:00
|
|
|
* @param name
|
|
|
|
*/
|
|
|
|
public LocalSearch(String name) {
|
|
|
|
this.mName = name;
|
|
|
|
}
|
2012-10-16 16:42:51 -04:00
|
|
|
|
2012-10-12 07:22:54 -04:00
|
|
|
/**
|
2012-10-16 16:42:51 -04:00
|
|
|
* Use this constructor when you know what you'r doing. Normally it's only used
|
2012-10-12 07:22:54 -04:00
|
|
|
* when restoring these search objects from the database.
|
2012-10-16 16:42:51 -04:00
|
|
|
*
|
2012-10-12 07:22:54 -04:00
|
|
|
* @param name Name of the search
|
|
|
|
* @param searchConditions SearchConditions, may contains flags and folders
|
|
|
|
* @param accounts Relative Account's uuid's
|
|
|
|
* @param predefined Is this a predefined search or a user created one?
|
|
|
|
*/
|
2012-10-16 16:42:51 -04:00
|
|
|
protected LocalSearch(String name, ConditionsTreeNode searchConditions,
|
|
|
|
String accounts, boolean predefined) {
|
|
|
|
this(name);
|
|
|
|
mConditions = searchConditions;
|
|
|
|
mPredefined = predefined;
|
|
|
|
mLeafSet = new HashSet<ConditionsTreeNode>();
|
|
|
|
if (mConditions != null) {
|
|
|
|
mLeafSet.addAll(mConditions.getLeafSet());
|
|
|
|
}
|
|
|
|
|
|
|
|
// initialize accounts
|
|
|
|
if (accounts != null) {
|
|
|
|
for (String account : accounts.split(",")) {
|
|
|
|
mAccountUuids.add(account);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// impossible but still not unrecoverable
|
|
|
|
}
|
2012-10-12 07:22:54 -04:00
|
|
|
}
|
2012-10-16 16:42:51 -04:00
|
|
|
|
2012-10-26 22:48:37 -04:00
|
|
|
@Override
|
|
|
|
public LocalSearch clone() {
|
|
|
|
ConditionsTreeNode conditions = (mConditions == null) ? null : mConditions.cloneTree();
|
|
|
|
|
|
|
|
LocalSearch copy = new LocalSearch(mName, conditions, null, mPredefined);
|
|
|
|
copy.mAccountUuids = new HashSet<String>(mAccountUuids);
|
|
|
|
|
|
|
|
return copy;
|
|
|
|
}
|
2012-10-16 16:42:51 -04:00
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
// Public manipulation methods
|
|
|
|
///////////////////////////////////////////////////////////////
|
2012-10-12 07:22:54 -04:00
|
|
|
/**
|
|
|
|
* Sets the name of the saved search. If one existed it will
|
|
|
|
* be overwritten.
|
|
|
|
*
|
|
|
|
* @param name Name to be set.
|
|
|
|
*/
|
|
|
|
public void setName(String name) {
|
|
|
|
this.mName = name;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-10-16 16:42:51 -04:00
|
|
|
* Add a new account to the search. When no accounts are
|
2012-10-12 07:22:54 -04:00
|
|
|
* added manually we search all accounts on the device.
|
2012-10-16 16:42:51 -04:00
|
|
|
*
|
2012-10-12 07:22:54 -04:00
|
|
|
* @param uuid Uuid of the account to be added.
|
|
|
|
*/
|
|
|
|
public void addAccountUuid(String uuid) {
|
2012-10-16 16:42:51 -04:00
|
|
|
if (uuid.equals(ALL_ACCOUNTS)) {
|
|
|
|
mAccountUuids.clear();
|
|
|
|
}
|
|
|
|
mAccountUuids.add(uuid);
|
2012-10-12 07:22:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds all the account uuids in the provided array to
|
|
|
|
* be matched by the seach.
|
2012-10-16 16:42:51 -04:00
|
|
|
*
|
2012-10-12 07:22:54 -04:00
|
|
|
* @param accountUuids
|
|
|
|
*/
|
2012-10-16 16:42:51 -04:00
|
|
|
public void addAccountUuids(String[] accountUuids) {
|
|
|
|
for (String acc : accountUuids) {
|
|
|
|
addAccountUuid(acc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-12 07:22:54 -04:00
|
|
|
/**
|
|
|
|
* Removes an account UUID from the current search.
|
2012-10-16 16:42:51 -04:00
|
|
|
*
|
2012-10-12 07:22:54 -04:00
|
|
|
* @param uuid Account UUID to remove.
|
|
|
|
* @return True if removed, false otherwise.
|
|
|
|
*/
|
|
|
|
public boolean removeAccountUuid(String uuid) {
|
2012-10-16 16:42:51 -04:00
|
|
|
return mAccountUuids.remove(uuid);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds the provided node as the second argument of an AND
|
|
|
|
* clause to this node.
|
|
|
|
*
|
|
|
|
* @param field Message table field to match against.
|
|
|
|
* @param string Value to look for.
|
|
|
|
* @param contains Attribute to use when matching.
|
|
|
|
*
|
|
|
|
* @throws IllegalConditionException
|
|
|
|
*/
|
2012-10-17 14:52:03 -04:00
|
|
|
public void and(Searchfield field, String value, Attribute attribute) {
|
2012-10-16 16:42:51 -04:00
|
|
|
and(new SearchCondition(field, attribute, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds the provided condition as the second argument of an AND
|
|
|
|
* clause to this node.
|
|
|
|
*
|
|
|
|
* @param condition Condition to 'AND' with.
|
|
|
|
* @return New top AND node, new root.
|
|
|
|
*/
|
|
|
|
public ConditionsTreeNode and(SearchCondition condition) {
|
|
|
|
try {
|
|
|
|
ConditionsTreeNode tmp = new ConditionsTreeNode(condition);
|
|
|
|
return and(tmp);
|
|
|
|
} catch (Exception e) {
|
|
|
|
// impossible
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds the provided node as the second argument of an AND
|
|
|
|
* clause to this node.
|
|
|
|
*
|
|
|
|
* @param node Node to 'AND' with.
|
|
|
|
* @return New top AND node, new root.
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
public ConditionsTreeNode and(ConditionsTreeNode node) throws Exception {
|
|
|
|
mLeafSet.addAll(node.getLeafSet());
|
|
|
|
|
|
|
|
if (mConditions == null) {
|
|
|
|
mConditions = node;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2012-10-22 21:01:50 -04:00
|
|
|
mConditions = mConditions.and(node);
|
|
|
|
return mConditions;
|
2012-10-16 16:42:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds the provided condition as the second argument of an OR
|
|
|
|
* clause to this node.
|
|
|
|
*
|
|
|
|
* @param condition Condition to 'OR' with.
|
|
|
|
* @return New top OR node, new root.
|
|
|
|
*/
|
|
|
|
public ConditionsTreeNode or(SearchCondition condition) {
|
|
|
|
try {
|
|
|
|
ConditionsTreeNode tmp = new ConditionsTreeNode(condition);
|
|
|
|
return or(tmp);
|
|
|
|
} catch (Exception e) {
|
|
|
|
// impossible
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds the provided node as the second argument of an OR
|
|
|
|
* clause to this node.
|
|
|
|
*
|
|
|
|
* @param node Node to 'OR' with.
|
|
|
|
* @return New top OR node, new root.
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
|
|
|
public ConditionsTreeNode or(ConditionsTreeNode node) throws Exception {
|
|
|
|
mLeafSet.addAll(node.getLeafSet());
|
|
|
|
|
|
|
|
if (mConditions == null) {
|
|
|
|
mConditions = node;
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
2012-10-22 21:01:50 -04:00
|
|
|
mConditions = mConditions.or(node);
|
|
|
|
return mConditions;
|
2012-10-12 07:22:54 -04:00
|
|
|
}
|
2012-10-16 16:42:51 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Add all the flags to this node as required flags. The
|
|
|
|
* provided flags will be combined using AND with the root.
|
|
|
|
*
|
|
|
|
* @param requiredFlags Array of required flags.
|
|
|
|
*/
|
|
|
|
public void allRequiredFlags(Flag[] requiredFlags) {
|
|
|
|
if (requiredFlags != null) {
|
|
|
|
for (Flag f : requiredFlags) {
|
2012-10-17 14:52:03 -04:00
|
|
|
and(new SearchCondition(Searchfield.FLAG, Attribute.CONTAINS, f.name()));
|
2012-10-16 16:42:51 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add all the flags to this node as forbidden flags. The
|
|
|
|
* provided flags will be combined using AND with the root.
|
|
|
|
*
|
|
|
|
* @param forbiddenFlags Array of forbidden flags.
|
|
|
|
*/
|
|
|
|
public void allForbiddenFlags(Flag[] forbiddenFlags) {
|
|
|
|
if (forbiddenFlags != null) {
|
|
|
|
for (Flag f : forbiddenFlags) {
|
2012-10-17 14:52:03 -04:00
|
|
|
and(new SearchCondition(Searchfield.FLAG, Attribute.NOT_CONTAINS, f.name()));
|
2012-10-16 16:42:51 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO
|
|
|
|
* FOR NOW: And the folder with the root.
|
|
|
|
*
|
|
|
|
* Add the folder as another folder to search in. The folder
|
|
|
|
* will be added AND to the root if no 'folder subtree' was found.
|
|
|
|
* Otherwise the folder will be added OR to that tree.
|
|
|
|
*
|
|
|
|
* @param name Name of the folder to add.
|
|
|
|
*/
|
|
|
|
public void addAllowedFolder(String name) {
|
|
|
|
/*
|
|
|
|
* TODO find folder sub-tree
|
|
|
|
* - do and on root of it & rest of search
|
|
|
|
* - do or between folder nodes
|
|
|
|
*/
|
2012-10-17 14:52:03 -04:00
|
|
|
mConditions = and(new SearchCondition(Searchfield.FOLDER, Attribute.EQUALS, name));
|
2012-10-16 16:42:51 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* TODO make this more advanced!
|
|
|
|
* This is a temporarely solution that does NOT WORK for
|
|
|
|
* real searches because of possible extra conditions to a folder requirement.
|
|
|
|
*/
|
|
|
|
public List<String> getFolderNames() {
|
|
|
|
ArrayList<String> results = new ArrayList<String>();
|
|
|
|
for (ConditionsTreeNode node : mLeafSet) {
|
2012-10-17 14:52:03 -04:00
|
|
|
if (node.mCondition.field == Searchfield.FOLDER &&
|
|
|
|
node.mCondition.attribute == Attribute.EQUALS) {
|
2012-10-16 16:42:51 -04:00
|
|
|
results.add(node.mCondition.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return results;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the leafset of the related condition tree.
|
|
|
|
*
|
|
|
|
* @return All the leaf conditions as a set.
|
|
|
|
*/
|
|
|
|
public Set<ConditionsTreeNode> getLeafSet() {
|
|
|
|
return mLeafSet;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
// Public accesor methods
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
/**
|
|
|
|
* TODO THIS HAS TO GO!!!!
|
|
|
|
* very dirty fix for remotesearch support atm
|
|
|
|
*/
|
|
|
|
public String getRemoteSearchArguments() {
|
2012-10-21 13:18:57 -04:00
|
|
|
Set<ConditionsTreeNode> leafSet = getLeafSet();
|
|
|
|
if (leafSet == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ConditionsTreeNode node : leafSet) {
|
2012-10-17 14:52:03 -04:00
|
|
|
if (node.getCondition().field == Searchfield.SUBJECT ||
|
|
|
|
node.getCondition().field == Searchfield.SENDER ) {
|
2012-10-16 16:42:51 -04:00
|
|
|
return node.getCondition().value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2012-10-12 07:22:54 -04:00
|
|
|
/**
|
|
|
|
* Returns the name of the saved search.
|
|
|
|
*
|
|
|
|
* @return Name of the search.
|
|
|
|
*/
|
2012-10-17 14:52:03 -04:00
|
|
|
@Override
|
2012-10-12 07:22:54 -04:00
|
|
|
public String getName() {
|
2012-10-17 14:52:03 -04:00
|
|
|
return (mName == null) ? "" : mName;
|
2012-10-12 07:22:54 -04:00
|
|
|
}
|
2012-10-16 16:42:51 -04:00
|
|
|
|
2012-10-12 07:22:54 -04:00
|
|
|
/**
|
|
|
|
* Checks if this search was hard coded and shipped with K-9
|
2012-10-16 16:42:51 -04:00
|
|
|
*
|
2012-10-12 07:22:54 -04:00
|
|
|
* @return True is search was shipped with K-9
|
|
|
|
*/
|
2012-10-16 16:42:51 -04:00
|
|
|
public boolean isPredefined() {
|
|
|
|
return mPredefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns all the account uuids that this search will try to
|
|
|
|
* match against.
|
|
|
|
*
|
|
|
|
* @return Array of account uuids.
|
|
|
|
*/
|
2012-10-12 07:22:54 -04:00
|
|
|
@Override
|
|
|
|
public String[] getAccountUuids() {
|
|
|
|
if (mAccountUuids.size() == 0) {
|
2012-10-17 14:52:03 -04:00
|
|
|
return new String[] { SearchSpecification.ALL_ACCOUNTS };
|
2012-10-12 07:22:54 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
String[] tmp = new String[mAccountUuids.size()];
|
|
|
|
mAccountUuids.toArray(tmp);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the condition tree.
|
|
|
|
*
|
|
|
|
* @return The root node of the related conditions tree.
|
|
|
|
*/
|
2012-10-16 16:42:51 -04:00
|
|
|
@Override
|
|
|
|
public ConditionsTreeNode getConditions() {
|
|
|
|
return mConditions;
|
|
|
|
}
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
// Parcelable
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
@Override
|
|
|
|
public int describeContents() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void writeToParcel(Parcel dest, int flags) {
|
|
|
|
dest.writeString(mName);
|
|
|
|
dest.writeByte((byte) (mPredefined ? 1 : 0));
|
|
|
|
dest.writeStringList(new ArrayList<String>(mAccountUuids));
|
|
|
|
dest.writeParcelable(mConditions, flags);
|
|
|
|
}
|
|
|
|
|
2012-10-17 14:52:03 -04:00
|
|
|
public static final Parcelable.Creator<LocalSearch> CREATOR =
|
|
|
|
new Parcelable.Creator<LocalSearch>() {
|
|
|
|
|
|
|
|
@Override
|
2012-10-16 16:42:51 -04:00
|
|
|
public LocalSearch createFromParcel(Parcel in) {
|
|
|
|
return new LocalSearch(in);
|
|
|
|
}
|
|
|
|
|
2012-10-17 14:52:03 -04:00
|
|
|
@Override
|
2012-10-16 16:42:51 -04:00
|
|
|
public LocalSearch[] newArray(int size) {
|
|
|
|
return new LocalSearch[size];
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
public LocalSearch(Parcel in) {
|
|
|
|
mName = in.readString();
|
2012-10-17 14:52:03 -04:00
|
|
|
mPredefined = (in.readByte() == 1);
|
2012-10-16 16:42:51 -04:00
|
|
|
mAccountUuids.addAll(in.createStringArrayList());
|
|
|
|
mConditions = in.readParcelable(LocalSearch.class.getClassLoader());
|
2012-10-21 13:17:01 -04:00
|
|
|
mLeafSet = (mConditions == null) ? null : mConditions.getLeafSet();
|
2012-10-16 16:42:51 -04:00
|
|
|
}
|
2012-10-12 07:22:54 -04:00
|
|
|
}
|