Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up LocalSearch #8823

Merged
merged 2 commits into from
Feb 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -151,30 +151,11 @@ private static void appendExprRight(SearchCondition condition, StringBuilder que
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("!= ?");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,12 @@
package app.k9mail.legacy.search;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Stack;
import java.util.Set;

import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable;

import app.k9mail.legacy.search.api.SearchAttribute;
import app.k9mail.legacy.search.api.SearchCondition;
import app.k9mail.legacy.search.api.SearchField;


/**
Expand All @@ -39,75 +33,10 @@ public enum Operator {
public Operator mValue;
public SearchCondition mCondition;

/*
* Used for storing and retrieving the tree to/from the database.
* The algorithm is called "modified preorder tree traversal".
*/
public int mLeftMPTTMarker;
public int mRightMPTTMarker;


///////////////////////////////////////////////////////////////
// Static Helpers to restore a tree from a database cursor
///////////////////////////////////////////////////////////////
/**
* Builds a condition tree starting from a database cursor. The cursor
* should point to rows representing the nodes of the tree.
*
* @param cursor Cursor pointing to the first of a bunch or rows. Each rows
* should contains 1 tree node.
* @return A condition tree.
*/
public static ConditionsTreeNode buildTreeFromDB(Cursor cursor) {
Stack<ConditionsTreeNode> stack = new Stack<>();
ConditionsTreeNode tmp = null;

// root node
if (cursor.moveToFirst()) {
tmp = buildNodeFromRow(cursor);
stack.push(tmp);
}

// other nodes
while (cursor.moveToNext()) {
tmp = buildNodeFromRow(cursor);
if (tmp.mRightMPTTMarker < stack.peek().mRightMPTTMarker) {
stack.peek().mLeft = tmp;
stack.push(tmp);
} else {
while (stack.peek().mRightMPTTMarker < tmp.mRightMPTTMarker) {
stack.pop();
}
stack.peek().mRight = tmp;
}
}
return tmp;
}

/**
* Converts a single database row to a single condition node.
*
* @param cursor Cursor pointing to the row we want to convert.
* @return A single ConditionsTreeNode
*/
private static ConditionsTreeNode buildNodeFromRow(Cursor cursor) {
ConditionsTreeNode result = null;
SearchCondition condition = null;

Operator tmpValue = ConditionsTreeNode.Operator.valueOf(cursor.getString(5));

if (tmpValue == Operator.CONDITION) {
condition = new SearchCondition(SearchField.valueOf(cursor.getString(0)),
SearchAttribute.valueOf(cursor.getString(2)), cursor.getString(1));
}

result = new ConditionsTreeNode(condition);
result.mValue = tmpValue;
result.mLeftMPTTMarker = cursor.getInt(3);
result.mRightMPTTMarker = cursor.getInt(4);

return result;
}


///////////////////////////////////////////////////////////////
Expand All @@ -126,35 +55,6 @@ public ConditionsTreeNode(ConditionsTreeNode parent, Operator op) {
}


/* package */ ConditionsTreeNode cloneTree() {
if (mParent != null) {
throw new IllegalStateException("Can't call cloneTree() for a non-root node");
}

ConditionsTreeNode copy = new ConditionsTreeNode(mCondition.clone());

copy.mLeftMPTTMarker = mLeftMPTTMarker;
copy.mRightMPTTMarker = mRightMPTTMarker;

copy.mLeft = (mLeft == null) ? null : mLeft.cloneNode(copy);
copy.mRight = (mRight == null) ? null : mRight.cloneNode(copy);

return copy;
}

private ConditionsTreeNode cloneNode(ConditionsTreeNode parent) {
ConditionsTreeNode copy = new ConditionsTreeNode(parent, mValue);

copy.mCondition = mCondition.clone();
copy.mLeftMPTTMarker = mLeftMPTTMarker;
copy.mRightMPTTMarker = mRightMPTTMarker;

copy.mLeft = (mLeft == null) ? null : mLeft.cloneNode(copy);
copy.mRight = (mRight == null) ? null : mRight.cloneNode(copy);

return copy;
}

///////////////////////////////////////////////////////////////
// Public modifiers
///////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -206,17 +106,6 @@ public ConditionsTreeNode or(SearchCondition condition) {
return or(tmp);
}

/**
* This applies the MPTT labeling to the subtree of which this node
* is the root node.
*
* For a description on MPTT see:
* http://www.sitepoint.com/hierarchical-data-database-2/
*/
public void applyMPTTLabel() {
applyMPTTLabel(1);
}


///////////////////////////////////////////////////////////////
// Public accessors
Expand All @@ -238,34 +127,6 @@ public Set<ConditionsTreeNode> getLeafSet() {
return getLeafSet(leafSet);
}

/**
* Returns a list of all the nodes in the subtree of which this node
* is the root. The list contains the nodes in a pre traversal order.
*
* @return List of all nodes in subtree in preorder.
*/
public List<ConditionsTreeNode> preorder() {
List<ConditionsTreeNode> result = new ArrayList<>();
Stack<ConditionsTreeNode> stack = new Stack<>();
stack.push(this);

while (!stack.isEmpty()) {
ConditionsTreeNode current = stack.pop();

if (current.mLeft != null) {
stack.push(current.mLeft);
}

if (current.mRight != null) {
stack.push(current.mRight);
}

result.add(current);
}

return result;
}


///////////////////////////////////////////////////////////////
// Private class logic
Expand Down Expand Up @@ -346,29 +207,6 @@ private Set<ConditionsTreeNode> getLeafSet(Set<ConditionsTreeNode> leafSet) {
return leafSet;
}

/**
* This applies the MPTT labeling to the subtree of which this node
* is the root node.
*
* For a description on MPTT see:
* http://www.sitepoint.com/hierarchical-data-database-2/
*/
private int applyMPTTLabel(int label) {
mLeftMPTTMarker = label;

if (mLeft != null) {
label = mLeft.applyMPTTLabel(label += 1);
}

if (mRight != null) {
label = mRight.applyMPTTLabel(label += 1);
}

++label;
mRightMPTTMarker = label;
return label;
}


///////////////////////////////////////////////////////////////
// Parcelable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
public class LocalSearch implements SearchSpecification {

private String id;
private boolean mPredefined;
private boolean mManualSearch = false;

// since the uuid isn't in the message table it's not in the tree neither
Expand All @@ -48,43 +47,6 @@ public class LocalSearch implements SearchSpecification {
*/
public LocalSearch() {}

/**
* Use this constructor when you know what you're doing. Normally it's only used
* when restoring these search objects from the database.
*
* @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?
*/
protected LocalSearch(ConditionsTreeNode searchConditions, String accounts, boolean predefined) {
mConditions = searchConditions;
mPredefined = predefined;
mLeafSet = new HashSet<>();
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
}
}

@Override
public LocalSearch clone() {
ConditionsTreeNode conditions = (mConditions == null) ? null : mConditions.cloneTree();

LocalSearch copy = new LocalSearch(conditions, null, mPredefined);
copy.mManualSearch = mManualSearch;
copy.mAccountUuids = new HashSet<>(mAccountUuids);

return copy;
}

///////////////////////////////////////////////////////////////
// Public manipulation methods
///////////////////////////////////////////////////////////////
Expand All @@ -108,28 +70,6 @@ public void addAccountUuid(String uuid) {
mAccountUuids.add(uuid);
}

/**
* Adds all the account uuids in the provided array to
* be matched by the search.
*
* @param accountUuids
*/
public void addAccountUuids(String[] accountUuids) {
for (String acc : accountUuids) {
addAccountUuid(acc);
}
}

/**
* Removes an account UUID from the current search.
*
* @param uuid Account UUID to remove.
* @return True if removed, false otherwise.
*/
public boolean removeAccountUuid(String uuid) {
return mAccountUuids.remove(uuid);
}

/**
* Adds the provided node as the second argument of an AND
* clause to this node.
Expand Down Expand Up @@ -277,15 +217,6 @@ public String getId() {
return (id == null) ? "" : id;
}

/**
* Checks if this search was hard coded and shipped with K-9
*
* @return True is search was shipped with K-9
*/
public boolean isPredefined() {
return mPredefined;
}

public boolean isManualSearch() {
return mManualSearch;
}
Expand Down Expand Up @@ -335,7 +266,6 @@ public int describeContents() {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(id);
dest.writeByte((byte) (mPredefined ? 1 : 0));
dest.writeByte((byte) (mManualSearch ? 1 : 0));
dest.writeStringList(new ArrayList<>(mAccountUuids));
dest.writeParcelable(mConditions, flags);
Expand All @@ -357,7 +287,6 @@ public LocalSearch[] newArray(int size) {

public LocalSearch(Parcel in) {
id = in.readString();
mPredefined = (in.readByte() == 1);
mManualSearch = (in.readByte() == 1);
mAccountUuids.addAll(in.createStringArrayList());
mConditions = in.readParcelable(LocalSearch.class.getClassLoader());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,8 @@
package app.k9mail.legacy.search.api;


///////////////////////////////////////////////////////////////
// ATTRIBUTE enum
///////////////////////////////////////////////////////////////
public enum SearchAttribute {
CONTAINS,
NOT_CONTAINS,

EQUALS,
NOT_EQUALS,

STARTSWITH,
NOT_STARTSWITH,

ENDSWITH,
NOT_ENDSWITH
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,6 @@ private SearchCondition(Parcel in) {
this.field = SearchField.values()[in.readInt()];
}

@Override
public SearchCondition clone() {
return new SearchCondition(field, attribute, value);
}

public String toHumanString() {
return field.toString() + attribute.toString();
}

@Override
public boolean equals(Object o) {
if (o instanceof SearchCondition) {
Expand Down