mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-17 11:18:01 +00:00
Refactor logic for advanced search to make it re-usable for desktop
This commit is contained in:
@@ -273,4 +273,9 @@ public class GuiDesktop implements IGuiBase {
|
||||
return match;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runBackgroundTask(String message, Runnable task) {
|
||||
//TODO: Show loading overlay
|
||||
FThreads.invokeInBackgroundThread(task);
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import forge.interfaces.IGuiGame;
|
||||
import forge.item.PaperCard;
|
||||
import forge.match.HostedMatch;
|
||||
import forge.properties.ForgeConstants;
|
||||
import forge.screens.LoadingOverlay;
|
||||
import forge.screens.match.MatchController;
|
||||
import forge.screens.quest.QuestMenu;
|
||||
import forge.screens.settings.GuiDownloader;
|
||||
@@ -279,4 +280,9 @@ public class GuiMobile implements IGuiBase {
|
||||
public HostedMatch hostMatch() {
|
||||
return MatchController.hostMatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runBackgroundTask(String message, Runnable task) {
|
||||
LoadingOverlay.runBackgroundTask(message, task);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
package forge.itemmanager.filters;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont.HAlignment;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.Forge;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.interfaces.IButton;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.itemmanager.AdvancedSearch;
|
||||
import forge.itemmanager.ItemManager;
|
||||
import forge.itemmanager.AdvancedSearch.FilterOption;
|
||||
import forge.menu.FTooltip;
|
||||
import forge.screens.FScreen;
|
||||
import forge.screens.LoadingOverlay;
|
||||
import forge.toolbox.FContainer;
|
||||
import forge.toolbox.FDisplayObject;
|
||||
import forge.toolbox.FEvent;
|
||||
@@ -29,13 +23,14 @@ import forge.util.Callback;
|
||||
|
||||
|
||||
public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T> {
|
||||
private final List<Object> expression = new ArrayList<Object>();
|
||||
private final AdvancedSearch.Model<T> model;
|
||||
|
||||
private FiltersLabel label;
|
||||
private EditScreen editScreen;
|
||||
|
||||
public AdvancedSearchFilter(ItemManager<? super T> itemManager0) {
|
||||
super(itemManager0);
|
||||
model = new AdvancedSearch.Model<T>();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,76 +41,14 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
||||
|
||||
@Override
|
||||
protected final Predicate<T> buildPredicate() {
|
||||
if (expression.isEmpty()) {
|
||||
if (model.isEmpty()) {
|
||||
return Predicates.alwaysTrue();
|
||||
}
|
||||
return getPredicate();
|
||||
return model.getPredicate();
|
||||
}
|
||||
|
||||
public Predicate<T> getPredicate() {
|
||||
return getPredicatePiece(new ExpressionIterator());
|
||||
}
|
||||
|
||||
private class ExpressionIterator {
|
||||
private int index;
|
||||
private boolean hasNext() {
|
||||
return index < expression.size();
|
||||
}
|
||||
private ExpressionIterator next() {
|
||||
index++;
|
||||
return this;
|
||||
}
|
||||
private Object get() {
|
||||
return expression.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Predicate<T> getPredicatePiece(ExpressionIterator iterator) {
|
||||
Predicate<T> pred = null;
|
||||
Predicate<T> predPiece = null;
|
||||
Operator operator = null;
|
||||
boolean applyNot = false;
|
||||
|
||||
for (; iterator.hasNext(); iterator.next()) {
|
||||
Object piece = iterator.get();
|
||||
if (piece.equals(Operator.OPEN_PAREN)) {
|
||||
predPiece = getPredicatePiece(iterator.next());
|
||||
}
|
||||
else if (piece.equals(Operator.CLOSE_PAREN)) {
|
||||
return pred;
|
||||
}
|
||||
else if (piece.equals(Operator.AND)) {
|
||||
operator = Operator.AND;
|
||||
continue;
|
||||
}
|
||||
else if (piece.equals(Operator.OR)) {
|
||||
operator = Operator.OR;
|
||||
continue;
|
||||
}
|
||||
else if (piece.equals(Operator.NOT)) {
|
||||
applyNot = !applyNot;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
predPiece = ((AdvancedSearch.Filter<T>) piece).getPredicate();
|
||||
}
|
||||
if (applyNot) {
|
||||
predPiece = Predicates.not(predPiece);
|
||||
applyNot = false;
|
||||
}
|
||||
if (pred == null) {
|
||||
pred = predPiece;
|
||||
}
|
||||
else if (operator == Operator.AND) {
|
||||
pred = Predicates.and(pred, predPiece);
|
||||
}
|
||||
else if (operator == Operator.OR) {
|
||||
pred = Predicates.or(pred, predPiece);
|
||||
}
|
||||
operator = null;
|
||||
}
|
||||
return pred;
|
||||
public Predicate<? super T> getPredicate() {
|
||||
return model.getPredicate();
|
||||
}
|
||||
|
||||
public void edit() {
|
||||
@@ -125,80 +58,21 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
||||
Forge.openScreen(editScreen);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void updateLabel() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Filter: ");
|
||||
if (expression.isEmpty()) {
|
||||
builder.append("(none)");
|
||||
}
|
||||
else {
|
||||
int prevFilterEndIdx = -1;
|
||||
AdvancedSearch.Filter<T> filter, prevFilter = null;
|
||||
for (Object piece : expression) {
|
||||
if (piece instanceof AdvancedSearch.Filter) {
|
||||
filter = (AdvancedSearch.Filter<T>)piece;
|
||||
if (filter.canMergeCaptionWith(prevFilter)) {
|
||||
//convert boolean operators between filters to lowercase
|
||||
builder.replace(prevFilterEndIdx, builder.length(), builder.substring(prevFilterEndIdx).toLowerCase());
|
||||
//append only values for filter
|
||||
builder.append(filter.extractValuesFromCaption());
|
||||
}
|
||||
else {
|
||||
builder.append(filter);
|
||||
}
|
||||
prevFilter = filter;
|
||||
prevFilterEndIdx = builder.length();
|
||||
}
|
||||
else {
|
||||
if (piece.equals(Operator.OPEN_PAREN) || piece.equals(Operator.CLOSE_PAREN)) {
|
||||
prevFilter = null; //prevent merging filters with parentheses in between
|
||||
}
|
||||
builder.append(piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
label.setText(builder.toString());
|
||||
}
|
||||
|
||||
private String getTooltip() {
|
||||
if (expression.isEmpty()) { return ""; }
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Filter:\n");
|
||||
|
||||
String indent = "";
|
||||
|
||||
for (Object piece : expression) {
|
||||
if (piece.equals(Operator.CLOSE_PAREN) && !indent.isEmpty()) {
|
||||
indent = indent.substring(2); //trim an indent level when a close paren is hit
|
||||
}
|
||||
builder.append("\n" + indent + piece.toString().trim());
|
||||
if (piece.equals(Operator.OPEN_PAREN)) {
|
||||
indent += " "; //add an indent level when an open paren is hit
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return expression.isEmpty();
|
||||
return model.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
expression.clear();
|
||||
model.reset();
|
||||
editScreen = null;
|
||||
if (label != null) {
|
||||
updateLabel();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void buildWidget(Widget widget) {
|
||||
label = new FiltersLabel();
|
||||
updateLabel();
|
||||
model.setLabel(label);
|
||||
widget.add(label);
|
||||
widget.setVisible(!isEmpty());
|
||||
}
|
||||
@@ -215,7 +89,7 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
||||
|
||||
@Override
|
||||
public boolean tap(float x, float y, int count) {
|
||||
FTooltip tooltip = new FTooltip(getTooltip());
|
||||
FTooltip tooltip = new FTooltip(model.getTooltip());
|
||||
tooltip.show(this, x, getHeight());
|
||||
return true;
|
||||
}
|
||||
@@ -241,48 +115,16 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
||||
|
||||
private EditScreen() {
|
||||
super("Advanced Search");
|
||||
scroller.add(new Filter());
|
||||
Filter filter = new Filter();
|
||||
model.addFilterControl(filter);
|
||||
scroller.add(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void onClose(Callback<Boolean> canCloseCallback) {
|
||||
//build expression when closing screen
|
||||
expression.clear();
|
||||
|
||||
for (FDisplayObject child : scroller.getChildren()) {
|
||||
Filter filter = (Filter)child;
|
||||
if (filter.filter == null) { continue; } //skip any blank filters
|
||||
|
||||
if (filter.btnNotBeforeParen.isSelected()) {
|
||||
expression.add(Operator.NOT);
|
||||
}
|
||||
if (filter.btnOpenParen.isSelected()) {
|
||||
expression.add(Operator.OPEN_PAREN);
|
||||
}
|
||||
if (filter.btnNotAfterParen.isSelected()) {
|
||||
expression.add(Operator.NOT);
|
||||
}
|
||||
|
||||
expression.add(filter.filter);
|
||||
|
||||
if (filter.btnCloseParen.isSelected()) {
|
||||
expression.add(Operator.CLOSE_PAREN);
|
||||
}
|
||||
if (filter.btnAnd.isSelected()) {
|
||||
expression.add(Operator.AND);
|
||||
}
|
||||
else if (filter.btnOr.isSelected()) {
|
||||
expression.add(Operator.OR);
|
||||
}
|
||||
}
|
||||
|
||||
if (label != null) {
|
||||
updateLabel();
|
||||
}
|
||||
|
||||
//update expression when screen closed
|
||||
model.updateExpression();
|
||||
itemManager.applyNewOrModifiedFilter(AdvancedSearchFilter.this);
|
||||
|
||||
super.onClose(canCloseCallback);
|
||||
}
|
||||
|
||||
@@ -293,7 +135,9 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
||||
|
||||
private void addNewFilter(Filter fromFilter) {
|
||||
if (scroller.getChildAt(scroller.getChildCount() - 1) == fromFilter) {
|
||||
scroller.add(new Filter());
|
||||
Filter filter = new Filter();
|
||||
model.addFilterControl(filter);
|
||||
scroller.add(filter);
|
||||
scroller.revalidate();
|
||||
scroller.scrollToBottom();
|
||||
}
|
||||
@@ -304,14 +148,13 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
||||
int index = scroller.indexOf(fromFilter);
|
||||
if (index < scroller.getChildCount() - 1) {
|
||||
Filter nextFilter = (Filter)scroller.getChildAt(index + 1);
|
||||
fromFilter.btnAnd.setSelected(nextFilter.btnAnd.isSelected());
|
||||
fromFilter.btnOr.setSelected(nextFilter.btnOr.isSelected());
|
||||
model.removeFilterControl(nextFilter);
|
||||
scroller.remove(nextFilter);
|
||||
scroller.revalidate();
|
||||
}
|
||||
}
|
||||
|
||||
private class Filter extends FContainer {
|
||||
private class Filter extends FContainer implements AdvancedSearch.IFilterControl<T> {
|
||||
private final FLabel btnNotBeforeParen, btnOpenParen, btnNotAfterParen;
|
||||
private final FLabel btnFilter;
|
||||
private final FLabel btnCloseParen, btnAnd, btnOr;
|
||||
@@ -321,41 +164,7 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
||||
btnNotBeforeParen = add(new FLabel.Builder().align(HAlignment.CENTER).text("NOT").selectable().build());
|
||||
btnOpenParen = add(new FLabel.Builder().align(HAlignment.CENTER).text("(").selectable().build());
|
||||
btnNotAfterParen = add(new FLabel.Builder().align(HAlignment.CENTER).text("NOT").selectable().build());
|
||||
|
||||
final String emptyFilterText = "Select Filter...";
|
||||
btnFilter = add(new FLabel.ButtonBuilder().text(emptyFilterText).parseSymbols(true).command(new FEventHandler() {
|
||||
@Override
|
||||
public void handleEvent(FEvent e) {
|
||||
FThreads.invokeInBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final AdvancedSearch.Filter<T> newFilter = AdvancedSearch.getFilter(itemManager.getGenericType(), filter);
|
||||
if (filter != newFilter) {
|
||||
FThreads.invokeInEdtLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
filter = newFilter;
|
||||
if (filter != null) {
|
||||
btnFilter.setText(filter.toString());
|
||||
}
|
||||
else {
|
||||
btnFilter.setText(emptyFilterText);
|
||||
}
|
||||
if (filter.getOption() == FilterOption.CARD_KEYWORDS) {
|
||||
//the first time the user selects keywords, preload keywords for all cards
|
||||
Runnable preloadTask = Keyword.getPreloadTask();
|
||||
if (preloadTask != null) {
|
||||
LoadingOverlay.runBackgroundTask("Loading keywords...", preloadTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}).build());
|
||||
|
||||
btnFilter = add(new FLabel.ButtonBuilder().parseSymbols(true).build());
|
||||
btnCloseParen = add(new FLabel.Builder().align(HAlignment.CENTER).selectable().text(")").build());
|
||||
btnAnd = add(new FLabel.Builder().align(HAlignment.CENTER).text("AND").selectable().command(new FEventHandler() {
|
||||
@Override
|
||||
@@ -409,24 +218,47 @@ public class AdvancedSearchFilter<T extends InventoryItem> extends ItemFilter<T>
|
||||
x += dx;
|
||||
btnOr.setBounds(x, y, buttonWidth, buttonHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum Operator {
|
||||
AND(" AND "),
|
||||
OR(" OR "),
|
||||
NOT("NOT "),
|
||||
OPEN_PAREN("("),
|
||||
CLOSE_PAREN(")");
|
||||
|
||||
private final String token;
|
||||
|
||||
private Operator(String token0) {
|
||||
token = token0;
|
||||
@Override
|
||||
public IButton getBtnNotBeforeParen() {
|
||||
return btnNotBeforeParen;
|
||||
}
|
||||
@Override
|
||||
public IButton getBtnOpenParen() {
|
||||
return btnOpenParen;
|
||||
}
|
||||
@Override
|
||||
public IButton getBtnNotAfterParen() {
|
||||
return btnNotAfterParen;
|
||||
}
|
||||
@Override
|
||||
public IButton getBtnFilter() {
|
||||
return btnFilter;
|
||||
}
|
||||
@Override
|
||||
public IButton getBtnCloseParen() {
|
||||
return btnCloseParen;
|
||||
}
|
||||
@Override
|
||||
public IButton getBtnAnd() {
|
||||
return btnAnd;
|
||||
}
|
||||
@Override
|
||||
public IButton getBtnOr() {
|
||||
return btnOr;
|
||||
}
|
||||
@Override
|
||||
public AdvancedSearch.Filter<T> getFilter() {
|
||||
return filter;
|
||||
}
|
||||
@Override
|
||||
public void setFilter(AdvancedSearch.Filter<T> filter0) {
|
||||
filter = filter0;
|
||||
}
|
||||
@Override
|
||||
public Class<? super T> getGenericType() {
|
||||
return itemManager.getGenericType();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,4 +51,5 @@ public interface IGuiBase {
|
||||
void showBazaar();
|
||||
IGuiGame getNewGuiGame();
|
||||
HostedMatch hostMatch();
|
||||
void runBackgroundTask(String message, Runnable task);
|
||||
}
|
||||
@@ -5,7 +5,11 @@ import java.util.Map.Entry;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Predicates;
|
||||
|
||||
import forge.FThreads;
|
||||
import forge.GuiBase;
|
||||
import forge.UiCommand;
|
||||
import forge.card.CardEdition;
|
||||
import forge.card.CardRarity;
|
||||
import forge.card.CardRules;
|
||||
@@ -18,6 +22,7 @@ import forge.deck.DeckProxy;
|
||||
import forge.deck.DeckSection;
|
||||
import forge.game.GameFormat;
|
||||
import forge.game.keyword.Keyword;
|
||||
import forge.interfaces.IButton;
|
||||
import forge.item.InventoryItem;
|
||||
import forge.item.PaperCard;
|
||||
import forge.model.FModel;
|
||||
@@ -908,4 +913,262 @@ public class AdvancedSearch {
|
||||
return caption;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IFilterControl<T extends InventoryItem> {
|
||||
IButton getBtnNotBeforeParen();
|
||||
IButton getBtnOpenParen();
|
||||
IButton getBtnNotAfterParen();
|
||||
IButton getBtnFilter();
|
||||
IButton getBtnCloseParen();
|
||||
IButton getBtnAnd();
|
||||
IButton getBtnOr();
|
||||
Filter<T> getFilter();
|
||||
void setFilter(Filter<T> filter0);
|
||||
Class<? super T> getGenericType();
|
||||
}
|
||||
|
||||
public static class Model<T extends InventoryItem> {
|
||||
private final List<Object> expression = new ArrayList<Object>();
|
||||
private final List<IFilterControl<T>> controls = new ArrayList<IFilterControl<T>>();
|
||||
private IButton label;
|
||||
|
||||
public Model() {
|
||||
}
|
||||
|
||||
public Predicate<T> getPredicate() {
|
||||
return getPredicatePiece(new ExpressionIterator());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Predicate<T> getPredicatePiece(ExpressionIterator iterator) {
|
||||
Predicate<T> pred = null;
|
||||
Predicate<T> predPiece = null;
|
||||
Operator operator = null;
|
||||
boolean applyNot = false;
|
||||
|
||||
for (; iterator.hasNext(); iterator.next()) {
|
||||
Object piece = iterator.get();
|
||||
if (piece.equals(Operator.OPEN_PAREN)) {
|
||||
predPiece = getPredicatePiece(iterator.next());
|
||||
}
|
||||
else if (piece.equals(Operator.CLOSE_PAREN)) {
|
||||
return pred;
|
||||
}
|
||||
else if (piece.equals(Operator.AND)) {
|
||||
operator = Operator.AND;
|
||||
continue;
|
||||
}
|
||||
else if (piece.equals(Operator.OR)) {
|
||||
operator = Operator.OR;
|
||||
continue;
|
||||
}
|
||||
else if (piece.equals(Operator.NOT)) {
|
||||
applyNot = !applyNot;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
predPiece = ((AdvancedSearch.Filter<T>) piece).getPredicate();
|
||||
}
|
||||
if (applyNot) {
|
||||
predPiece = Predicates.not(predPiece);
|
||||
applyNot = false;
|
||||
}
|
||||
if (pred == null) {
|
||||
pred = predPiece;
|
||||
}
|
||||
else if (operator == Operator.AND) {
|
||||
pred = Predicates.and(pred, predPiece);
|
||||
}
|
||||
else if (operator == Operator.OR) {
|
||||
pred = Predicates.or(pred, predPiece);
|
||||
}
|
||||
operator = null;
|
||||
}
|
||||
return pred;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return expression.isEmpty();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
expression.clear();
|
||||
updateLabel();
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public void addFilterControl(final IFilterControl<T> control) {
|
||||
final String emptyFilterText = "Select Filter...";
|
||||
control.getBtnFilter().setText(emptyFilterText);
|
||||
control.getBtnFilter().setCommand(new UiCommand() {
|
||||
@Override
|
||||
public void run() {
|
||||
FThreads.invokeInBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final Filter<T> filter = getFilter(control.getGenericType(), control.getFilter());
|
||||
if (control.getFilter() != filter) {
|
||||
FThreads.invokeInEdtLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
control.setFilter(filter);
|
||||
if (filter != null) {
|
||||
control.getBtnFilter().setText(filter.toString());
|
||||
}
|
||||
else {
|
||||
control.getBtnFilter().setText(emptyFilterText);
|
||||
}
|
||||
if (filter.getOption() == FilterOption.CARD_KEYWORDS) {
|
||||
//the first time the user selects keywords, preload keywords for all cards
|
||||
Runnable preloadTask = Keyword.getPreloadTask();
|
||||
if (preloadTask != null) {
|
||||
GuiBase.getInterface().runBackgroundTask("Loading keywords...", preloadTask);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
controls.add(control);
|
||||
}
|
||||
public void removeFilterControl(IFilterControl<T> control) {
|
||||
int index = controls.indexOf(control);
|
||||
if (index > 0) {
|
||||
IFilterControl<T> prevControl = controls.get(index - 1);
|
||||
prevControl.getBtnAnd().setSelected(control.getBtnAnd().isSelected());
|
||||
prevControl.getBtnOr().setSelected(control.getBtnOr().isSelected());
|
||||
}
|
||||
controls.remove(index);
|
||||
}
|
||||
|
||||
public void setLabel(IButton label0) {
|
||||
label = label0;
|
||||
updateLabel();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void updateLabel() {
|
||||
if (label == null) { return; }
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Filter: ");
|
||||
if (expression.isEmpty()) {
|
||||
builder.append("(none)");
|
||||
}
|
||||
else {
|
||||
int prevFilterEndIdx = -1;
|
||||
AdvancedSearch.Filter<T> filter, prevFilter = null;
|
||||
for (Object piece : expression) {
|
||||
if (piece instanceof AdvancedSearch.Filter) {
|
||||
filter = (AdvancedSearch.Filter<T>)piece;
|
||||
if (filter.canMergeCaptionWith(prevFilter)) {
|
||||
//convert boolean operators between filters to lowercase
|
||||
builder.replace(prevFilterEndIdx, builder.length(), builder.substring(prevFilterEndIdx).toLowerCase());
|
||||
//append only values for filter
|
||||
builder.append(filter.extractValuesFromCaption());
|
||||
}
|
||||
else {
|
||||
builder.append(filter);
|
||||
}
|
||||
prevFilter = filter;
|
||||
prevFilterEndIdx = builder.length();
|
||||
}
|
||||
else {
|
||||
if (piece.equals(Operator.OPEN_PAREN) || piece.equals(Operator.CLOSE_PAREN)) {
|
||||
prevFilter = null; //prevent merging filters with parentheses in between
|
||||
}
|
||||
builder.append(piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
label.setText(builder.toString());
|
||||
}
|
||||
|
||||
public String getTooltip() {
|
||||
if (expression.isEmpty()) { return ""; }
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("Filter:\n");
|
||||
|
||||
String indent = "";
|
||||
|
||||
for (Object piece : expression) {
|
||||
if (piece.equals(Operator.CLOSE_PAREN) && !indent.isEmpty()) {
|
||||
indent = indent.substring(2); //trim an indent level when a close paren is hit
|
||||
}
|
||||
builder.append("\n" + indent + piece.toString().trim());
|
||||
if (piece.equals(Operator.OPEN_PAREN)) {
|
||||
indent += " "; //add an indent level when an open paren is hit
|
||||
}
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void updateExpression() {
|
||||
expression.clear();
|
||||
|
||||
for (IFilterControl<T> control : controls) {
|
||||
if (control.getFilter() == null) { continue; } //skip any blank filters
|
||||
|
||||
if (control.getBtnNotBeforeParen().isSelected()) {
|
||||
expression.add(Operator.NOT);
|
||||
}
|
||||
if (control.getBtnOpenParen().isSelected()) {
|
||||
expression.add(Operator.OPEN_PAREN);
|
||||
}
|
||||
if (control.getBtnNotAfterParen().isSelected()) {
|
||||
expression.add(Operator.NOT);
|
||||
}
|
||||
|
||||
expression.add(control.getFilter());
|
||||
|
||||
if (control.getBtnCloseParen().isSelected()) {
|
||||
expression.add(Operator.CLOSE_PAREN);
|
||||
}
|
||||
if (control.getBtnAnd().isSelected()) {
|
||||
expression.add(Operator.AND);
|
||||
}
|
||||
else if (control.getBtnOr().isSelected()) {
|
||||
expression.add(Operator.OR);
|
||||
}
|
||||
}
|
||||
|
||||
updateLabel();
|
||||
}
|
||||
|
||||
private class ExpressionIterator {
|
||||
private int index;
|
||||
private boolean hasNext() {
|
||||
return index < expression.size();
|
||||
}
|
||||
private ExpressionIterator next() {
|
||||
index++;
|
||||
return this;
|
||||
}
|
||||
private Object get() {
|
||||
return expression.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
private enum Operator {
|
||||
AND(" AND "),
|
||||
OR(" OR "),
|
||||
NOT("NOT "),
|
||||
OPEN_PAREN("("),
|
||||
CLOSE_PAREN(")");
|
||||
|
||||
private final String token;
|
||||
|
||||
private Operator(String token0) {
|
||||
token = token0;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user