From 9bd0d43f500285a5b9040cb95b27457d6ae3eadf Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Wed, 14 Mar 2018 08:09:29 +0100 Subject: [PATCH] [Major] Better generics support in Search API, includin ExpressionBuilder --- .../li/strolch/search/ExpressionBuilder.java | 55 ++++++++ .../li/strolch/search/PredicatesSupport.java | 29 +++++ .../li/strolch/search/SearchExpressions.java | 47 +++++-- .../li/strolch/search/SearchNavigator.java | 4 +- .../li/strolch/search/SearchPredicates.java | 45 +++++-- .../java/li/strolch/search/SearchResult.java | 15 --- .../java/li/strolch/search/StrolchSearch.java | 117 +++--------------- .../li/strolch/search/StrolchSearchTest.java | 33 +++-- 8 files changed, 189 insertions(+), 156 deletions(-) create mode 100644 li.strolch.agent/src/main/java/li/strolch/search/ExpressionBuilder.java create mode 100644 li.strolch.agent/src/main/java/li/strolch/search/PredicatesSupport.java diff --git a/li.strolch.agent/src/main/java/li/strolch/search/ExpressionBuilder.java b/li.strolch.agent/src/main/java/li/strolch/search/ExpressionBuilder.java new file mode 100644 index 000000000..f2f2e4903 --- /dev/null +++ b/li.strolch.agent/src/main/java/li/strolch/search/ExpressionBuilder.java @@ -0,0 +1,55 @@ +package li.strolch.search; + +import java.util.Date; + +import li.strolch.model.StrolchRootElement; +import li.strolch.utils.collections.DateRange; + +public interface ExpressionBuilder { + + Object extract(StrolchRootElement element); + + default SearchExpression isEqualTo(Object right) { + return element -> PredicatesSupport.isEqualTo(right, false).matches(extract(element)); + } + + default SearchExpression isNotEqualTo(Object right) { + return element -> PredicatesSupport.isEqualTo(right, false).not().matches(extract(element)); + } + + default SearchExpression isEqualToIgnoreCase(Object right) { + return element -> PredicatesSupport.isEqualTo(right, true).matches(extract(element)); + } + + default SearchExpression isNotEqualToIgnoreCase(Object right) { + return element -> PredicatesSupport.isEqualTo(right, true).not().matches(extract(element)); + } + + default SearchExpression startsWith(Object right) { + return element -> PredicatesSupport.startsWith(right, false).matches(extract(element)); + } + + default SearchExpression startsWithIgnoreCase(Object right) { + return element -> PredicatesSupport.startsWith(right, true).matches(extract(element)); + } + + default SearchExpression endsWith(Object right) { + return element -> PredicatesSupport.endsWith(right, false).matches(extract(element)); + } + + default SearchExpression endsWithIgnoreCase(Object right) { + return element -> PredicatesSupport.endsWith(right, true).matches(extract(element)); + } + + default SearchExpression contains(Object right) { + return element -> PredicatesSupport.contains(right, false).matches(extract(element)); + } + + default SearchExpression containsIgnoreCase(Object right) { + return element -> PredicatesSupport.contains(right, true).matches(extract(element)); + } + + default SearchExpression inRange(DateRange range) { + return element -> range.contains((Date) extract(element)); + } +} diff --git a/li.strolch.agent/src/main/java/li/strolch/search/PredicatesSupport.java b/li.strolch.agent/src/main/java/li/strolch/search/PredicatesSupport.java new file mode 100644 index 000000000..6731f8cc8 --- /dev/null +++ b/li.strolch.agent/src/main/java/li/strolch/search/PredicatesSupport.java @@ -0,0 +1,29 @@ +package li.strolch.search; + +import java.util.Date; + +import li.strolch.utils.ObjectHelper; +import li.strolch.utils.collections.DateRange; + +public class PredicatesSupport { + + public static SearchPredicate isEqualTo(Object right, boolean ignoreCase) { + return left -> ObjectHelper.equals(left, right, ignoreCase); + } + + public static SearchPredicate startsWith(Object right, boolean ignoreCase) { + return left -> ObjectHelper.startsWith(left, right, ignoreCase); + } + + public static SearchPredicate endsWith(Object right, boolean ignoreCase) { + return left -> ObjectHelper.endsWith(left, right, ignoreCase); + } + + public static SearchPredicate contains(Object right, boolean ignoreCase) { + return left -> ObjectHelper.contains(left, right, ignoreCase); + } + + public static SearchPredicate inRange(DateRange range) { + return left -> range.contains((Date) left); + } +} diff --git a/li.strolch.agent/src/main/java/li/strolch/search/SearchExpressions.java b/li.strolch.agent/src/main/java/li/strolch/search/SearchExpressions.java index 8654903fe..e8a7f7a5b 100644 --- a/li.strolch.agent/src/main/java/li/strolch/search/SearchExpressions.java +++ b/li.strolch.agent/src/main/java/li/strolch/search/SearchExpressions.java @@ -2,45 +2,70 @@ package li.strolch.search; import li.strolch.model.Order; import li.strolch.model.ParameterBag; +import li.strolch.model.StrolchElement; import li.strolch.model.activity.Activity; import li.strolch.model.parameter.Parameter; -public class SearchExpressions { +public interface SearchExpressions { - public static SearchExpression id(SearchPredicate predicate) { + default SearchExpression not(SearchExpression expression) { + return element -> !expression.matches(element); + } + + default ExpressionBuilder id() { + return StrolchElement::getId; + } + + default SearchExpression id(SearchPredicate predicate) { return element -> predicate.matches(element.getId()); } - public static SearchExpression name(SearchPredicate predicate) { + default ExpressionBuilder name() { + return StrolchElement::getName; + } + + default SearchExpression name(SearchPredicate predicate) { return element -> predicate.matches(element.getName()); } - public static SearchExpression date(SearchPredicate predicate) { + default ExpressionBuilder date() { + return element -> ((Order) element).getDate(); + } + + default SearchExpression date(SearchPredicate predicate) { return element -> predicate.matches(((Order) element).getDate()); } - public static SearchExpression state(SearchPredicate predicate) { + default ExpressionBuilder state() { return element -> { if (element instanceof Order) - return predicate.matches(((Order) element).getState()); + return ((Order) element).getState(); if (element instanceof Activity) - return predicate.matches(((Activity) element).getState()); + return ((Activity) element).getState(); throw new IllegalArgumentException(element.getObjectType() + " does not have a state!"); }; } - public static SearchExpression param(String bagId, String paramId, SearchPredicate predicate) { + default SearchExpression state(SearchPredicate predicate) { + return element -> predicate.matches(state().extract(element)); + } + + default ExpressionBuilder param(String bagId, String paramId) { return element -> { ParameterBag bag = element.getParameterBag(bagId); if (bag == null) - return false; + return null; Parameter param = bag.getParameter(paramId); - return param != null && predicate.matches(param.getValue()); + return param == null ? null : param.getValue(); }; } - public static SearchExpression paramNull(String bagId, String paramId) { + default SearchExpression param(String bagId, String paramId, SearchPredicate predicate) { + return element -> predicate.matches(param(bagId, paramId).extract(element)); + } + + default SearchExpression paramNull(String bagId, String paramId) { return element -> { ParameterBag bag = element.getParameterBag(bagId); if (bag == null) diff --git a/li.strolch.agent/src/main/java/li/strolch/search/SearchNavigator.java b/li.strolch.agent/src/main/java/li/strolch/search/SearchNavigator.java index 6078c33e6..4ae38eb90 100644 --- a/li.strolch.agent/src/main/java/li/strolch/search/SearchNavigator.java +++ b/li.strolch.agent/src/main/java/li/strolch/search/SearchNavigator.java @@ -5,7 +5,7 @@ import java.util.stream.Stream; import li.strolch.model.StrolchRootElement; import li.strolch.persistence.api.StrolchTransaction; -public interface SearchNavigator { +public interface SearchNavigator { - Stream navigate(StrolchTransaction tx); + Stream navigate(StrolchTransaction tx); } diff --git a/li.strolch.agent/src/main/java/li/strolch/search/SearchPredicates.java b/li.strolch.agent/src/main/java/li/strolch/search/SearchPredicates.java index 5372dbc04..5dd7543dc 100644 --- a/li.strolch.agent/src/main/java/li/strolch/search/SearchPredicates.java +++ b/li.strolch.agent/src/main/java/li/strolch/search/SearchPredicates.java @@ -2,28 +2,51 @@ package li.strolch.search; import java.util.Date; -import li.strolch.utils.ObjectHelper; import li.strolch.utils.collections.DateRange; -public class SearchPredicates { +public interface SearchPredicates { - public static SearchPredicate isEqualTo(Object right, boolean ignoreCase) { - return left -> ObjectHelper.equals(left, right, ignoreCase); + default SearchPredicate isEqualTo(Object right) { + return PredicatesSupport.isEqualTo(right, false); } - public static SearchPredicate startsWith(Object right, boolean ignoreCase) { - return left -> ObjectHelper.startsWith(left, right, ignoreCase); + default SearchPredicate isNotEqualTo(Object right) { + return PredicatesSupport.isEqualTo(right, false).not(); } - public static SearchPredicate endsWith(Object right, boolean ignoreCase) { - return left -> ObjectHelper.endsWith(left, right, ignoreCase); + default SearchPredicate isEqualToIgnoreCase(Object right) { + return PredicatesSupport.isEqualTo(right, true); } - public static SearchPredicate contains(Object right, boolean ignoreCase) { - return left -> ObjectHelper.contains(left, right, ignoreCase); + default SearchPredicate isNotEqualToIgnoreCase(Object right) { + return PredicatesSupport.isEqualTo(right, true).not(); } - public static SearchPredicate inRange(DateRange range) { + default SearchPredicate startsWith(Object right) { + return PredicatesSupport.startsWith(right, false); + } + + default SearchPredicate startsWithIgnoreCase(Object right) { + return PredicatesSupport.startsWith(right, true); + } + + default SearchPredicate endsWith(Object right) { + return PredicatesSupport.endsWith(right, false); + } + + default SearchPredicate endsWithIgnoreCase(Object right) { + return PredicatesSupport.endsWith(right, true); + } + + default SearchPredicate contains(Object right) { + return PredicatesSupport.contains(right, false); + } + + default SearchPredicate containsIgnoreCase(Object right) { + return PredicatesSupport.contains(right, true); + } + + default SearchPredicate inRange(DateRange range) { return left -> range.contains((Date) left); } } diff --git a/li.strolch.agent/src/main/java/li/strolch/search/SearchResult.java b/li.strolch.agent/src/main/java/li/strolch/search/SearchResult.java index a414ee80a..e8c11b516 100644 --- a/li.strolch.agent/src/main/java/li/strolch/search/SearchResult.java +++ b/li.strolch.agent/src/main/java/li/strolch/search/SearchResult.java @@ -8,9 +8,6 @@ import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; -import li.strolch.model.Order; -import li.strolch.model.Resource; -import li.strolch.model.activity.Activity; import li.strolch.utils.collections.Paging; public class SearchResult { @@ -25,18 +22,6 @@ public class SearchResult { return this.stream; } - public RootElementSearchResult asResources() { - return new RootElementSearchResult<>(this.stream.map(e -> (Resource) e)); - } - - public RootElementSearchResult asOrders() { - return new RootElementSearchResult<>(this.stream.map(e -> (Order) e)); - } - - public RootElementSearchResult asActivities() { - return new RootElementSearchResult<>(this.stream.map(e -> (Activity) e)); - } - public SearchResult map(Function mapper) { return new SearchResult(this.stream.map(mapper)); } diff --git a/li.strolch.agent/src/main/java/li/strolch/search/StrolchSearch.java b/li.strolch.agent/src/main/java/li/strolch/search/StrolchSearch.java index ef1b82e0d..a7e89f69c 100644 --- a/li.strolch.agent/src/main/java/li/strolch/search/StrolchSearch.java +++ b/li.strolch.agent/src/main/java/li/strolch/search/StrolchSearch.java @@ -9,15 +9,15 @@ import li.strolch.persistence.api.StrolchTransaction; import li.strolch.privilege.base.PrivilegeException; import li.strolch.privilege.model.PrivilegeContext; import li.strolch.privilege.model.Restrictable; -import li.strolch.utils.collections.DateRange; import li.strolch.utils.dbc.DBC; import li.strolch.utils.helper.ExceptionHelper; -public abstract class StrolchSearch implements Restrictable { +public abstract class StrolchSearch + implements SearchExpressions, SearchPredicates, Restrictable { private String privilegeValue; - private SearchNavigator navigator; + private SearchNavigator navigator; private SearchExpression expression; public StrolchSearch() { @@ -35,7 +35,7 @@ public abstract class StrolchSearch implements Restrictable { return this; } - public final RootElementSearchResult search(StrolchTransaction tx) { + public RootElementSearchResult search(StrolchTransaction tx) { try { PrivilegeContext privilegeContext = tx.getContainer().getPrivilegeHandler().validate(tx.getCertificate()); privilegeContext.validateAction(this); @@ -45,42 +45,37 @@ public abstract class StrolchSearch implements Restrictable { } // first prepare - prepare(); + define(); // then validate status DBC.PRE.assertNotNull("navigation not set! Call one of resources(), orders() or activities()", this.navigator); DBC.PRE.assertNotNull("expression not set! Call where()!", this.expression); - @SuppressWarnings("unchecked") - Stream stream = (Stream) this.navigator.navigate(tx) - .filter(e -> this.expression.matches(e)); - return new RootElementSearchResult(stream); + Stream stream = this.navigator.navigate(tx).filter(e -> this.expression.matches(e)); + return new RootElementSearchResult(stream); } - protected abstract StrolchSearch prepare(); + protected abstract void define(); - // Navigator - - protected final StrolchSearch resources(String... types) { - this.navigator = tx -> tx.streamResources(types); + @SuppressWarnings("unchecked") + protected StrolchSearch resources(String... types) { + this.navigator = tx -> (Stream) tx.streamResources(types); return this; } - protected final StrolchSearch orders(String... types) { - this.navigator = tx -> tx.streamOrders(types); + @SuppressWarnings("unchecked") + protected StrolchSearch orders(String... types) { + this.navigator = tx -> (Stream) tx.streamOrders(types); return this; } - protected final StrolchSearch activities(String... types) { - this.navigator = tx -> tx.streamActivities(types); + @SuppressWarnings("unchecked") + protected StrolchSearch activities(String... types) { + this.navigator = tx -> (Stream) tx.streamActivities(types); return this; } - // - // Expressions - // - - protected final StrolchSearch where(SearchExpression searchExpression) { + protected StrolchSearch where(SearchExpression searchExpression) { if (this.expression == null) this.expression = searchExpression; else @@ -88,82 +83,6 @@ public abstract class StrolchSearch implements Restrictable { return this; } - protected final SearchExpression id(SearchPredicate predicate) { - return SearchExpressions.id(predicate); - } - - protected final SearchExpression name(SearchPredicate predicate) { - return SearchExpressions.name(predicate); - } - - protected final SearchExpression date(SearchPredicate predicate) { - return SearchExpressions.date(predicate); - } - - protected final SearchExpression state(SearchPredicate predicate) { - return SearchExpressions.state(predicate); - } - - protected final SearchExpression param(String bagId, String paramId, SearchPredicate predicate) { - return SearchExpressions.param(bagId, paramId, predicate); - } - - protected final SearchExpression paramNull(String bag, String param) { - return SearchExpressions.paramNull(bag, param); - } - - protected final SearchExpression not(SearchExpression expression) { - return element -> !expression.matches(element); - } - - // - // Predicates - // - - protected final SearchPredicate isEqualTo(Object value) { - return SearchPredicates.isEqualTo(value, false); - } - - protected final SearchPredicate isEqualToIgnoreCase(Object value) { - return SearchPredicates.isEqualTo(value, true); - } - - protected final SearchPredicate isNotEqualTo(Object value) { - return SearchPredicates.isEqualTo(value, false).not(); - } - - protected final SearchPredicate isNotEqualToIgnoreCase(Object value) { - return SearchPredicates.isEqualTo(value, true).not(); - } - - protected final SearchPredicate startsWith(Object value) { - return SearchPredicates.startsWith(value, false); - } - - protected final SearchPredicate startsWithIgnoreCase(Object value) { - return SearchPredicates.startsWith(value, true); - } - - protected final SearchPredicate endsWith(Object value) { - return SearchPredicates.endsWith(value, false); - } - - protected final SearchPredicate endsWithIgnoreCase(Object value) { - return SearchPredicates.endsWith(value, true); - } - - protected final SearchPredicate contains(Object value) { - return SearchPredicates.contains(value, false); - } - - protected final SearchPredicate containsIgnoreCase(Object value) { - return SearchPredicates.contains(value, true); - } - - protected final SearchPredicate inRange(DateRange range) { - return SearchPredicates.inRange(range); - } - /** * @see li.strolch.privilege.model.Restrictable#getPrivilegeName() */ diff --git a/li.strolch.agent/src/test/java/li/strolch/search/StrolchSearchTest.java b/li.strolch.agent/src/test/java/li/strolch/search/StrolchSearchTest.java index e4f3dd6d5..4a1457d46 100644 --- a/li.strolch.agent/src/test/java/li/strolch/search/StrolchSearchTest.java +++ b/li.strolch.agent/src/test/java/li/strolch/search/StrolchSearchTest.java @@ -30,7 +30,7 @@ import org.slf4j.LoggerFactory; public class StrolchSearchTest { - public static final Logger logger = LoggerFactory.getLogger(ParallelTests.class); + public static final Logger logger = LoggerFactory.getLogger(StrolchSearchTest.class); private static final String TARGET_PATH = "target/" + StrolchSearchTest.class.getSimpleName(); private static final String SOURCE_PATH = "src/test/resources/transienttest"; @@ -108,7 +108,6 @@ public class StrolchSearchTest { // do search, returns SearchResult .search(tx) // transform, either: - .asResources() // asOrders(), asActivities() or map to something entirely different: .map(a -> a.accept(toJsonVisitor)) // finishing as toList, toSet, toPaging .toList(); @@ -122,15 +121,15 @@ public class StrolchSearchTest { StrolchRealm realm = runtimeMock.getAgent().getContainer().getRealm(cert); try (StrolchTransaction tx = realm.openTx(cert, ParallelTests.class)) { - List result = new StrolchSearch() { + List result = new StrolchSearch() { @Override - public StrolchSearch prepare() { + public void define() { DateRange dateRange = new DateRange() // .from(ISO8601FormatFactory.getInstance().parseDate("2012-01-01T00:00:00.000+01:00"), true) .to(ISO8601FormatFactory.getInstance().parseDate("2013-01-01T00:00:00.000+01:00"), true); - return orders() // + orders() // .where(date(isEqualTo(new Date(1384929777699L))) // .or(state(isEqualTo(State.CREATED)) // .and(param(BAG_ID, PARAM_STRING_ID, isEqualTo("Strolch"))) // @@ -138,7 +137,6 @@ public class StrolchSearchTest { } } // .search(tx) // - .asOrders() // .toList(); assertEquals(7, result.size()); @@ -150,14 +148,14 @@ public class StrolchSearchTest { StrolchRealm realm = runtimeMock.getAgent().getContainer().getRealm(cert); try (StrolchTransaction tx = realm.openTx(cert, ParallelTests.class)) { - StrolchSearch search = new StrolchSearch() { + StrolchSearch search = new StrolchSearch() { @Override - public StrolchSearch prepare() { - return orders("SortingType").where(state(isEqualTo(State.CREATED))); + public void define() { + orders("SortingType").where(state(isEqualTo(State.CREATED))); } }; - List result; + List result; result = search // .search(tx) // @@ -180,24 +178,23 @@ public class StrolchSearchTest { StrolchRealm realm = runtimeMock.getAgent().getContainer().getRealm(cert); try (StrolchTransaction tx = realm.openTx(cert, ParallelTests.class)) { - Map states = new StrolchSearch() { + Map states = new StrolchSearch() { @Override - public StrolchSearch prepare() { - return activities() // - .where(state(isEqualTo(State.PLANNING)) // + public void define() { + activities() // + .where(state().isEqualTo(State.PLANNING) // .and(name(isEqualTo("Activity"))) // ); } } // .search(tx) // - .asActivities() // .toMap(Activity::getId, Activity::getState); assertEquals(1, states.size()); } } - public class BallSearch extends StrolchSearch { + public class BallSearch extends StrolchSearch { private String id; private String status; @@ -210,8 +207,8 @@ public class StrolchSearchTest { } @Override - public StrolchSearch prepare() { - return resources("Ball") // + public void define() { + resources("Ball") // .where(id(isEqualTo(this.id)) // .or(param("parameters", "status", isEqualTo(this.status)) // .and(not(param("parameters", "color", isEqualTo(this.color)))) //