From 5f71427d336bddb8b52d317818f525d10ba09e86 Mon Sep 17 00:00:00 2001 From: Robert von Burg Date: Tue, 5 Apr 2016 08:30:07 +0200 Subject: [PATCH] [New] Added a QueryParser to parse String queries to Strolch queries --- li.strolch.parent/pom.xml | 7 +- li.strolch.rest/pom.xml | 5 + .../model/query/parser/QueryParser.java | 112 ++++++++++++++++++ .../model/query/parser/QueryParserTest.java | 112 ++++++++++++++++++ pom.xml | 7 ++ 5 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 li.strolch.rest/src/main/java/li/strolch/model/query/parser/QueryParser.java create mode 100644 li.strolch.rest/src/test/java/li/strolch/model/query/parser/QueryParserTest.java diff --git a/li.strolch.parent/pom.xml b/li.strolch.parent/pom.xml index d780b6735..e76b17638 100644 --- a/li.strolch.parent/pom.xml +++ b/li.strolch.parent/pom.xml @@ -109,7 +109,7 @@ ${eitchnet.privilege.version} - + org.slf4j slf4j-api @@ -120,6 +120,11 @@ commons-cli 1.2 + + com.github.petitparser + java-petitparser + 2.0.0 + diff --git a/li.strolch.rest/pom.xml b/li.strolch.rest/pom.xml index 118d26bd0..6548b339a 100644 --- a/li.strolch.rest/pom.xml +++ b/li.strolch.rest/pom.xml @@ -97,6 +97,11 @@ jersey-media-moxy + + com.github.petitparser + java-petitparser + + li.strolch 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 new file mode 100644 index 000000000..03cb4f6fe --- /dev/null +++ b/li.strolch.rest/src/main/java/li/strolch/model/query/parser/QueryParser.java @@ -0,0 +1,112 @@ +package li.strolch.model.query.parser; + +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 org.petitparser.context.Result; +import org.petitparser.parser.Parser; +import org.petitparser.tools.CompositeParser; + +import ch.eitchnet.utils.StringMatchMode; +import li.strolch.model.Order; +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.OrderQuery; +import li.strolch.model.query.ResourceQuery; +import li.strolch.model.query.StrolchElementQuery; +import li.strolch.model.query.StrolchTypeNavigation; +import li.strolch.model.visitor.NoStrategyOrderVisitor; +import li.strolch.model.visitor.NoStrategyResourceVisitor; + +public 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 + this.query = query; + } + + private OrSelection or() { + if (this.or == null) + this.or = query.or(); + return or; + } + + @Override + protected void initialize() { + + // [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); + + def("id", id); + def("name", name); + def("type", type); + + Parser query = whitespace().optional().seq(ref("type").or(ref("id")).or(ref("name")).or(whitespace())).star(); + + def("query", query); + + def("start", ref("query")); + + action("id", (String s) -> { + if (this.idSelection == null) { + this.idSelection = new IdSelection(s.trim(), StringMatchMode.ci()); + or().with(this.idSelection); + } else { + this.idSelection.with(s.trim()); + } + return null; + }); + action("name", (String s) -> { + or().with(new NameSelection(s.trim(), StringMatchMode.ci())); + return null; + }); + action("type", (String s) -> { + this.query.setNavigation(new StrolchTypeNavigation(s.trim())); + return null; + }); + + action("start", o -> this.query); + } + + public static ResourceQuery parseToResourceQuery(String queryString, boolean withAny) { + QueryParser parser = new QueryParser(new ResourceQuery<>()); + Result result = parser.parse(queryString); + ResourceQuery query = result.get(); + query.setResourceVisitor(new NoStrategyResourceVisitor()); + + if (!query.hasSelection() && withAny) { + query.withAny(); + } + + return query; + } + + public static OrderQuery parseToOrderQuery(String queryString, boolean withAny) { + QueryParser parser = new QueryParser(new OrderQuery<>()); + Result result = parser.parse(queryString); + OrderQuery query = result.get(); + query.setOrderVisitor(new NoStrategyOrderVisitor()); + + if (!query.hasSelection() && withAny) { + query.withAny(); + } + + return query; + } +} 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 new file mode 100644 index 000000000..133eff982 --- /dev/null +++ b/li.strolch.rest/src/test/java/li/strolch/model/query/parser/QueryParserTest.java @@ -0,0 +1,112 @@ +package li.strolch.model.query.parser; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.List; + +import org.junit.Test; + +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.ResourceQuery; +import li.strolch.model.query.Selection; +import li.strolch.model.query.StrolchTypeNavigation; + +public class QueryParserTest { + + @Test + public void shouldParseEmpty() { + ResourceQuery query = QueryParser.parseToResourceQuery("", false); + assertFalse(query.hasNavigation()); + assertFalse(query.hasSelection()); + } + + @Test + public void shouldParseId() { + ResourceQuery query = QueryParser.parseToResourceQuery("id:asd", false); + OrSelection or = (OrSelection) query.getSelection(); + List selections = or.getSelections(); + assertEquals(1, selections.size()); + assertEquals(IdSelection.class, selections.get(0).getClass()); + IdSelection idSelection = (IdSelection) selections.get(0); + List ids = idSelection.getIds(); + assertEquals(1, ids.size()); + assertEquals("asd", ids.get(0)); + assertFalse(query.hasNavigation()); + } + + @Test + public void shouldParseIds() { + ResourceQuery query = QueryParser.parseToResourceQuery("id:asd id:bbb", false); + OrSelection or = (OrSelection) query.getSelection(); + List selections = or.getSelections(); + assertEquals(1, selections.size()); + assertEquals(IdSelection.class, selections.get(0).getClass()); + IdSelection idSelection = (IdSelection) selections.get(0); + List ids = idSelection.getIds(); + assertEquals(2, ids.size()); + assertEquals("asd", ids.get(0)); + assertEquals("bbb", ids.get(1)); + assertFalse(query.hasNavigation()); + } + + @Test + public void shouldParseName() { + ResourceQuery query = QueryParser.parseToResourceQuery("name:asd", false); + OrSelection or = (OrSelection) query.getSelection(); + List selections = or.getSelections(); + assertEquals(1, selections.size()); + assertEquals(NameSelection.class, selections.get(0).getClass()); + assertEquals("asd", ((NameSelection) selections.get(0)).getName()); + assertFalse(query.hasNavigation()); + } + + @Test + public void shouldParseNames() { + ResourceQuery query = QueryParser.parseToResourceQuery("name:asd name:bbb", false); + OrSelection or = (OrSelection) query.getSelection(); + List selections = or.getSelections(); + assertEquals(2, selections.size()); + assertEquals(NameSelection.class, selections.get(0).getClass()); + assertEquals("asd", ((NameSelection) selections.get(0)).getName()); + + assertEquals(NameSelection.class, selections.get(1).getClass()); + assertEquals("bbb", ((NameSelection) selections.get(1)).getName()); + assertFalse(query.hasNavigation()); + } + + @Test + public void shouldParseType() { + ResourceQuery query = QueryParser.parseToResourceQuery("type:asd", false); + assertFalse(query.hasSelection()); + assertTrue(query.hasNavigation()); + StrolchTypeNavigation navigation = (StrolchTypeNavigation) query.getNavigation(); + assertEquals("asd", navigation.getType()); + } + + @Test + public void shouldParseIdNameType() { + ResourceQuery query = QueryParser.parseToResourceQuery("id:foo name:bar type:asd", false); + OrSelection or = (OrSelection) query.getSelection(); + List selections = or.getSelections(); + assertEquals(2, selections.size()); + assertEquals(IdSelection.class, selections.get(0).getClass()); + assertEquals(NameSelection.class, selections.get(1).getClass()); + assertTrue(query.hasNavigation()); + } + + @Test + public void shouldParseWithWhitespace() { + ResourceQuery query = QueryParser.parseToResourceQuery(" id:foo name:bar type:asd \t ", false); + OrSelection or = (OrSelection) query.getSelection(); + List selections = or.getSelections(); + assertEquals(2, selections.size()); + assertEquals(IdSelection.class, selections.get(0).getClass()); + assertEquals(NameSelection.class, selections.get(1).getClass()); + assertTrue(query.hasNavigation()); + } +} diff --git a/pom.xml b/pom.xml index 9918f6df6..fc20e0b73 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,13 @@ ${maven.build.timestamp} + + + jitpack.io + https://jitpack.io + + + archive.eitchnet.ch