From d25e7577f71ebf67b031a1d963cf64198496d903 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Tue, 30 Aug 2016 11:44:55 +0200 Subject: [PATCH] [New] QueryParser can now handle parameters incl. without prefixes --- .../model/query/parser/QueryParser.java | 220 ++++++++++++++---- .../li/strolch/rest/endpoint/ModelQuery.java | 6 +- .../model/query/parser/QueryParserTest.java | 108 +++++++-- 3 files changed, 270 insertions(+), 64 deletions(-) diff --git a/li.strolch.rest/src/main/java/li/strolch/model/query/parser/QueryParser.java b/li.strolch.rest/src/main/java/li/strolch/model/query/parser/QueryParser.java index 49de40c77..5f0b713a0 100644 --- a/li.strolch.rest/src/main/java/li/strolch/model/query/parser/QueryParser.java +++ b/li.strolch.rest/src/main/java/li/strolch/model/query/parser/QueryParser.java @@ -1,9 +1,14 @@ package li.strolch.model.query.parser; +import static org.petitparser.parser.primitive.CharacterParser.digit; +import static org.petitparser.parser.primitive.CharacterParser.letter; +import static org.petitparser.parser.primitive.CharacterParser.of; import static org.petitparser.parser.primitive.CharacterParser.whitespace; import static org.petitparser.parser.primitive.CharacterParser.word; import static org.petitparser.parser.primitive.StringParser.ofIgnoringCase; +import java.util.Set; + import org.petitparser.context.Result; import org.petitparser.parser.Parser; import org.petitparser.tools.CompositeParser; @@ -16,6 +21,7 @@ import li.strolch.model.query.IdSelection; import li.strolch.model.query.NameSelection; import li.strolch.model.query.OrSelection; import li.strolch.model.query.OrderQuery; +import li.strolch.model.query.ParameterSelection; import li.strolch.model.query.ResourceQuery; import li.strolch.model.query.StrolchElementQuery; import li.strolch.model.query.StrolchTypeNavigation; @@ -23,81 +29,170 @@ import li.strolch.model.visitor.NoStrategyActivityVisitor; import li.strolch.model.visitor.NoStrategyOrderVisitor; import li.strolch.model.visitor.NoStrategyResourceVisitor; import li.strolch.utils.StringMatchMode; +import li.strolch.utils.collections.MapOfSets; -public class QueryParser extends CompositeParser { +public abstract class QueryParser extends CompositeParser { private StrolchElementQuery query; private OrSelection or; private IdSelection idSelection; - /** - * Use static helper methods instead of constructors - * - * @param resourceQuery - */ - private QueryParser(StrolchElementQuery query) { - // don't allow public construction + public QueryParser(StrolchElementQuery query) { this.query = query; } - private OrSelection or() { + protected OrSelection or() { if (this.or == null) this.or = query.or(); return or; } - @Override - protected void initialize() { + private Parser[] charParsers() { + return new Parser[] { of('@'), of('.'), of('-'), of('_'), of('+'), of(':') }; + } + + protected Parser key(String key) { + return ofIgnoringCase(key + ":").seq(word().or(charParsers()).star().flatten()).pick(1); + } + + public abstract MapOfSets getBagParamSet(); + + public abstract boolean withPrefix(); + + protected void defs() { // [id:] [name:] [type:] [param::] [value] - Parser id = ofIgnoringCase("id:").seq(word().star().flatten()).pick(1); - Parser name = ofIgnoringCase("name:").seq(word().star().flatten()).pick(1); - Parser type = ofIgnoringCase("type:").seq(word().star().flatten()).pick(1); + Parser parsers = null; - def("id", id); - def("name", name); - def("type", type); + if (withPrefix()) { + def("id", key("id")); + def("name", key("name")); + def("type", key("type")); - Parser query = whitespace().optional().seq(ref("type").or(ref("id")).or(ref("name")).or(whitespace())).star(); + for (String bagId : getBagParamSet().keySet()) { + Set set = getBagParamSet().getSet(bagId); + for (String paramId : set) { + + def(paramId, key(paramId)); + + if (parsers == null) { + parsers = ref(paramId); + } else { + parsers = parsers.or(ref(paramId)); + } + } + } + + if (parsers == null) + parsers = ref("id").or(ref("name")).or(ref("type")); + else + parsers = parsers.or(ref("id")).or(ref("name")).or(ref("type")); + + } else { + def("other", letter().or(digit()).seq(word().or(charParsers()).star().flatten()).flatten()); + parsers = ref("other"); + } + + parsers = parsers.or(whitespace()); + + Parser query = whitespace().optional().seq(parsers).star(); def("query", query); def("start", ref("query")); + } - action("id", (String s) -> { - String trimmed = s.trim(); - if (trimmed.isEmpty()) - return null; + protected void actions() { - if (this.idSelection == null) { - this.idSelection = new IdSelection(trimmed, StringMatchMode.ci()); - or().with(this.idSelection); - } else { - this.idSelection.with(trimmed); + if (withPrefix()) { + for (String bagId : getBagParamSet().keySet()) { + Set set = getBagParamSet().getSet(bagId); + for (String paramId : set) { + action(paramId, (String s) -> { + String trimmed = s.trim(); + if (!trimmed.isEmpty()) + or().with( + ParameterSelection.anyTypeSelection(bagId, paramId, trimmed, StringMatchMode.ci())); + return null; + }); + } } - return null; - }); - action("name", (String s) -> { - String trimmed = s.trim(); - if (!trimmed.isEmpty()) - or().with(new NameSelection(trimmed, StringMatchMode.ci())); - return null; - }); - action("type", (String s) -> { - String trimmed = s.trim(); - if (!trimmed.isEmpty()) - this.query.setNavigation(new StrolchTypeNavigation(trimmed)); - return null; - }); + action("id", (String s) -> { + String trimmed = s.trim(); + if (trimmed.isEmpty()) + return null; + + if (this.idSelection == null) { + this.idSelection = new IdSelection(trimmed, StringMatchMode.ci()); + or().with(this.idSelection); + } else { + this.idSelection.with(trimmed); + } + + return null; + }); + action("name", (String s) -> { + String trimmed = s.trim(); + if (!trimmed.isEmpty()) + or().with(new NameSelection(trimmed, StringMatchMode.ci())); + return null; + }); + action("type", (String s) -> { + String trimmed = s.trim(); + if (!trimmed.isEmpty()) + this.query.setNavigation(new StrolchTypeNavigation(trimmed)); + return null; + }); + + } else { + + action("other", (String s) -> { + + for (String bagId : getBagParamSet().keySet()) { + Set set = getBagParamSet().getSet(bagId); + for (String paramId : set) { + String trimmed = s.trim(); + if (!trimmed.isEmpty()) + or().with( + ParameterSelection.anyTypeSelection(bagId, paramId, trimmed, StringMatchMode.ci())); + } + } + + return null; + + }); + } action("start", o -> this.query); } - public static ResourceQuery parseToResourceQuery(String queryString, boolean withAny) { - QueryParser parser = new QueryParser(new ResourceQuery<>()); + @Override + protected void initialize() { + defs(); + actions(); + } + + public static ResourceQuery parseToResourceQuery(String queryString, boolean withPrefix, + boolean withAny) { + return parseToResourceQuery(new MapOfSets<>(), withPrefix, queryString, withAny); + } + + public static ResourceQuery parseToResourceQuery(MapOfSets bagParamSet, + boolean withPrefix, String queryString, boolean withAny) { + QueryParser parser = new QueryParser(new ResourceQuery<>()) { + @Override + public MapOfSets getBagParamSet() { + return bagParamSet; + } + + @Override + public boolean withPrefix() { + return withPrefix; + } + }; Result result = parser.parse(queryString); ResourceQuery query = result.get(); query.setResourceVisitor(new NoStrategyResourceVisitor()); @@ -109,8 +204,23 @@ public class QueryParser extends CompositeParser { return query; } - public static OrderQuery parseToOrderQuery(String queryString, boolean withAny) { - QueryParser parser = new QueryParser(new OrderQuery<>()); + public static OrderQuery parseToOrderQuery(String queryString, boolean withPrefix, boolean withAny) { + return parseToOrderQuery(new MapOfSets<>(), withPrefix, queryString, withAny); + } + + public static OrderQuery parseToOrderQuery(MapOfSets bagParamSet, boolean withPrefix, + String queryString, boolean withAny) { + QueryParser parser = new QueryParser(new OrderQuery<>()) { + @Override + public MapOfSets getBagParamSet() { + return bagParamSet; + } + + @Override + public boolean withPrefix() { + return withPrefix; + } + }; Result result = parser.parse(queryString); OrderQuery query = result.get(); query.setOrderVisitor(new NoStrategyOrderVisitor()); @@ -122,8 +232,24 @@ public class QueryParser extends CompositeParser { return query; } - public static ActivityQuery parseToActivityQuery(String queryString, boolean withAny) { - QueryParser parser = new QueryParser(new ActivityQuery<>()); + public static ActivityQuery parseToActivityQuery(String queryString, boolean withPrefix, + boolean withAny) { + return parseToActivityQuery(new MapOfSets<>(), withPrefix, queryString, withAny); + } + + public static ActivityQuery parseToActivityQuery(MapOfSets bagParamSet, + boolean withPrefix, String queryString, boolean withAny) { + QueryParser parser = new QueryParser(new ActivityQuery<>()) { + @Override + public MapOfSets getBagParamSet() { + return bagParamSet; + } + + @Override + public boolean withPrefix() { + return withPrefix; + } + }; Result result = parser.parse(queryString); ActivityQuery query = result.get(); query.setActivityVisitor(new NoStrategyActivityVisitor()); diff --git a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ModelQuery.java b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ModelQuery.java index 6a30c3fd1..19085abba 100644 --- a/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ModelQuery.java +++ b/li.strolch.rest/src/main/java/li/strolch/rest/endpoint/ModelQuery.java @@ -74,7 +74,7 @@ public class ModelQuery { List resources = new ArrayList<>(); // parse the query string - ResourceQuery query = QueryParser.parseToResourceQuery(queryData.getQuery(), true); + ResourceQuery query = QueryParser.parseToResourceQuery(queryData.getQuery(), true, true); // query the data long dataSetSize = 0L; @@ -132,7 +132,7 @@ public class ModelQuery { List orders = new ArrayList<>(); // parse the query string - OrderQuery query = QueryParser.parseToOrderQuery(queryData.getQuery(), true); + OrderQuery query = QueryParser.parseToOrderQuery(queryData.getQuery(), true, true); // query the data long dataSetSize = 0L; @@ -191,7 +191,7 @@ public class ModelQuery { List activities = new ArrayList<>(); // parse the query string - ActivityQuery query = QueryParser.parseToActivityQuery(queryData.getQuery(), true); + ActivityQuery query = QueryParser.parseToActivityQuery(queryData.getQuery(), true, true); // query the data long dataSetSize = 0L; diff --git a/li.strolch.rest/src/test/java/li/strolch/model/query/parser/QueryParserTest.java b/li.strolch.rest/src/test/java/li/strolch/model/query/parser/QueryParserTest.java index 41d52f78e..f1f028f5b 100644 --- a/li.strolch.rest/src/test/java/li/strolch/model/query/parser/QueryParserTest.java +++ b/li.strolch.rest/src/test/java/li/strolch/model/query/parser/QueryParserTest.java @@ -12,57 +12,59 @@ import li.strolch.model.Resource; import li.strolch.model.query.IdSelection; import li.strolch.model.query.NameSelection; import li.strolch.model.query.OrSelection; +import li.strolch.model.query.ParameterSelection.AnyTypeParameterSelection; import li.strolch.model.query.ResourceQuery; import li.strolch.model.query.Selection; import li.strolch.model.query.StrolchTypeNavigation; +import li.strolch.utils.collections.MapOfSets; public class QueryParserTest { @Test public void shouldIgnoreGibberish() { - ResourceQuery query = QueryParser.parseToResourceQuery("sdf dfg3 !sdf", false); + ResourceQuery query = QueryParser.parseToResourceQuery("sdf dfg3 !sdf", true, false); assertFalse(query.hasNavigation()); assertFalse(query.hasSelection()); } @Test public void shouldParseEmpty() { - ResourceQuery query = QueryParser.parseToResourceQuery("", false); + ResourceQuery query = QueryParser.parseToResourceQuery("", true, false); assertFalse(query.hasNavigation()); assertFalse(query.hasSelection()); } @Test public void shouldParseEmptyId() { - ResourceQuery query = QueryParser.parseToResourceQuery("id:", false); + ResourceQuery query = QueryParser.parseToResourceQuery("id:", true, false); assertFalse(query.hasNavigation()); assertFalse(query.hasSelection()); } @Test public void shouldParseEmptyName() { - ResourceQuery query = QueryParser.parseToResourceQuery("name:", false); + ResourceQuery query = QueryParser.parseToResourceQuery("name:", true, false); assertFalse(query.hasNavigation()); assertFalse(query.hasSelection()); } @Test public void shouldParseEmptyType() { - ResourceQuery query = QueryParser.parseToResourceQuery("type:", false); + ResourceQuery query = QueryParser.parseToResourceQuery("type:", true, false); assertFalse(query.hasNavigation()); assertFalse(query.hasSelection()); } @Test public void shouldParseEmptyIdNameType() { - ResourceQuery query = QueryParser.parseToResourceQuery("id: name: type:", false); + ResourceQuery query = QueryParser.parseToResourceQuery("id: name: type:", true, false); assertFalse(query.hasNavigation()); assertFalse(query.hasSelection()); } @Test public void shouldParseId() { - ResourceQuery query = QueryParser.parseToResourceQuery("id:asd", false); + ResourceQuery query = QueryParser.parseToResourceQuery("id:asd", true, false); OrSelection or = (OrSelection) query.getSelection(); List selections = or.getSelections(); assertEquals(1, selections.size()); @@ -76,7 +78,7 @@ public class QueryParserTest { @Test public void shouldParseIds() { - ResourceQuery query = QueryParser.parseToResourceQuery("id:asd id:bbb", false); + ResourceQuery query = QueryParser.parseToResourceQuery("id:asd id:bbb", true, false); OrSelection or = (OrSelection) query.getSelection(); List selections = or.getSelections(); assertEquals(1, selections.size()); @@ -91,7 +93,7 @@ public class QueryParserTest { @Test public void shouldParseName() { - ResourceQuery query = QueryParser.parseToResourceQuery("name:asd", false); + ResourceQuery query = QueryParser.parseToResourceQuery("name:asd", true, false); OrSelection or = (OrSelection) query.getSelection(); List selections = or.getSelections(); assertEquals(1, selections.size()); @@ -102,7 +104,7 @@ public class QueryParserTest { @Test public void shouldParseNames() { - ResourceQuery query = QueryParser.parseToResourceQuery("name:asd name:bbb", false); + ResourceQuery query = QueryParser.parseToResourceQuery("name:asd name:bbb", true, false); OrSelection or = (OrSelection) query.getSelection(); List selections = or.getSelections(); assertEquals(2, selections.size()); @@ -116,7 +118,7 @@ public class QueryParserTest { @Test public void shouldParseType() { - ResourceQuery query = QueryParser.parseToResourceQuery("type:asd", false); + ResourceQuery query = QueryParser.parseToResourceQuery("type:asd", true, false); assertFalse(query.hasSelection()); assertTrue(query.hasNavigation()); StrolchTypeNavigation navigation = (StrolchTypeNavigation) query.getNavigation(); @@ -125,7 +127,7 @@ public class QueryParserTest { @Test public void shouldReplaceMultipleType() { - ResourceQuery query = QueryParser.parseToResourceQuery("type:asd type:fff", false); + ResourceQuery query = QueryParser.parseToResourceQuery("type:asd type:fff", true, false); assertFalse(query.hasSelection()); assertTrue(query.hasNavigation()); StrolchTypeNavigation navigation = (StrolchTypeNavigation) query.getNavigation(); @@ -134,7 +136,8 @@ public class QueryParserTest { @Test public void shouldParseIdNameType() { - ResourceQuery query = QueryParser.parseToResourceQuery("id:foo name:bar type:asd", false); + ResourceQuery query = QueryParser + .parseToResourceQuery("id:foo name:bar type:asd date:1970-01-01T01:00:00.000+01:00", true, false); OrSelection or = (OrSelection) query.getSelection(); List selections = or.getSelections(); assertEquals(2, selections.size()); @@ -145,7 +148,8 @@ public class QueryParserTest { @Test public void shouldParseWithWhitespace() { - ResourceQuery query = QueryParser.parseToResourceQuery(" id:foo name:bar type:asd \t ", false); + ResourceQuery query = QueryParser.parseToResourceQuery(" id:foo name:bar type:asd \t ", true, + false); OrSelection or = (OrSelection) query.getSelection(); List selections = or.getSelections(); assertEquals(2, selections.size()); @@ -153,4 +157,80 @@ public class QueryParserTest { assertEquals(NameSelection.class, selections.get(1).getClass()); assertTrue(query.hasNavigation()); } + + @Test + public void shouldParserWithParameters() { + MapOfSets maps = new MapOfSets<>(); + maps.addElement("parameters", "email"); + maps.addElement("parameters", "date"); + + ResourceQuery query = QueryParser.parseToResourceQuery(maps, true, + "type:asd id:asd email:bla@dsfdfg.ch date:1970-01-01T01:00:00.000+01:00", false); + assertTrue(query.hasNavigation()); + OrSelection or = (OrSelection) query.getSelection(); + List selections = or.getSelections(); + assertEquals(3, selections.size()); + assertEquals(IdSelection.class, selections.get(0).getClass()); + assertEquals(AnyTypeParameterSelection.class, selections.get(1).getClass()); + assertEquals(AnyTypeParameterSelection.class, selections.get(2).getClass()); + + AnyTypeParameterSelection sel = (AnyTypeParameterSelection) selections.get(1); + assertEquals("parameters", sel.getBagKey()); + assertEquals("email", sel.getParamKey()); + assertEquals("bla@dsfdfg.ch", sel.getValue()); + + sel = (AnyTypeParameterSelection) selections.get(2); + assertEquals("parameters", sel.getBagKey()); + assertEquals("date", sel.getParamKey()); + assertEquals("1970-01-01T01:00:00.000+01:00", sel.getValue()); + } + + @Test + public void shouldParserWithParametersAndNoPrefix() { + MapOfSets maps = new MapOfSets<>(); + maps.addElement("parameters", "email"); + maps.addElement("parameters", "date"); + + ResourceQuery query = QueryParser.parseToResourceQuery(maps, false, + "asd bla@dsfdfg.ch 1970-01-01T01:00:00.000+01:00", false); + assertFalse(query.hasNavigation()); + OrSelection or = (OrSelection) query.getSelection(); + List selections = or.getSelections(); + assertEquals(6, selections.size()); + for (Selection selection : selections) { + assertEquals(AnyTypeParameterSelection.class, selection.getClass()); + } + + AnyTypeParameterSelection sel; + + sel = (AnyTypeParameterSelection) selections.get(0); + assertEquals("parameters", sel.getBagKey()); + assertEquals("date", sel.getParamKey()); + assertEquals("asd", sel.getValue()); + + sel = (AnyTypeParameterSelection) selections.get(1); + assertEquals("parameters", sel.getBagKey()); + assertEquals("email", sel.getParamKey()); + assertEquals("asd", sel.getValue()); + + sel = (AnyTypeParameterSelection) selections.get(2); + assertEquals("parameters", sel.getBagKey()); + assertEquals("date", sel.getParamKey()); + assertEquals("bla@dsfdfg.ch", sel.getValue()); + + sel = (AnyTypeParameterSelection) selections.get(3); + assertEquals("parameters", sel.getBagKey()); + assertEquals("email", sel.getParamKey()); + assertEquals("bla@dsfdfg.ch", sel.getValue()); + + sel = (AnyTypeParameterSelection) selections.get(4); + assertEquals("parameters", sel.getBagKey()); + assertEquals("date", sel.getParamKey()); + assertEquals("1970-01-01T01:00:00.000+01:00", sel.getValue()); + + sel = (AnyTypeParameterSelection) selections.get(5); + assertEquals("parameters", sel.getBagKey()); + assertEquals("email", sel.getParamKey()); + assertEquals("1970-01-01T01:00:00.000+01:00", sel.getValue()); + } }