From 0556f8bf4048556a52ca84e64d98eb7dfbd4ef7d Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Mon, 4 Dec 2023 11:37:20 -0800 Subject: [PATCH 01/14] feat: parses group_key from --- .../java/org/commcare/suite/model/QueryPrompt.java | 13 ++++++++++++- .../java/org/commcare/xml/QueryPromptParser.java | 4 +++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/commcare/suite/model/QueryPrompt.java b/src/main/java/org/commcare/suite/model/QueryPrompt.java index 5653f2559f..98678922fc 100644 --- a/src/main/java/org/commcare/suite/model/QueryPrompt.java +++ b/src/main/java/org/commcare/suite/model/QueryPrompt.java @@ -74,6 +74,9 @@ public class QueryPrompt implements Externalizable { @Nullable private QueryPromptCondition validation; + @Nullable + private String groupKey; + @SuppressWarnings("unused") public QueryPrompt() { } @@ -81,7 +84,7 @@ public QueryPrompt() { public QueryPrompt(String key, String appearance, String input, String receive, String hidden, DisplayUnit display, ItemsetBinding itemsetBinding, XPathExpression defaultValueExpr, boolean allowBlankValue, XPathExpression exclude, - QueryPromptCondition required, QueryPromptCondition validation) { + QueryPromptCondition required, QueryPromptCondition validation, String groupKey) { this.key = key; this.appearance = appearance; this.input = input; @@ -94,6 +97,7 @@ public QueryPrompt(String key, String appearance, String input, String receive, this.exclude = exclude; this.required = required; this.validation = validation; + this.groupKey = groupKey; } @Override @@ -111,6 +115,7 @@ public void readExternal(DataInputStream in, PrototypeFactory pf) exclude = (XPathExpression)ExtUtil.read(in, new ExtWrapNullable(new ExtWrapTagged()), pf); validation = (QueryPromptCondition)ExtUtil.read(in, new ExtWrapNullable(QueryPromptCondition.class), pf); required = (QueryPromptCondition)ExtUtil.read(in, new ExtWrapNullable(QueryPromptCondition.class), pf); + groupKey = (String)ExtUtil.read(in, new ExtWrapNullable(String.class), pf); } @Override @@ -128,6 +133,7 @@ public void writeExternal(DataOutputStream out) throws IOException { ExtUtil.write(out, new ExtWrapNullable(exclude == null ? null : new ExtWrapTagged(exclude))); ExtUtil.write(out, new ExtWrapNullable(validation)); ExtUtil.write(out, new ExtWrapNullable(required)); + ExtUtil.write(out, new ExtWrapNullable(groupKey)); } public String getKey() { @@ -186,6 +192,11 @@ public QueryPromptCondition getValidation() { return validation; } + @Nullable + public String getGroupKey() { + return groupKey; + } + /** * @return whether the prompt has associated choices to select from */ diff --git a/src/main/java/org/commcare/xml/QueryPromptParser.java b/src/main/java/org/commcare/xml/QueryPromptParser.java index fdae744720..cbf639b87a 100644 --- a/src/main/java/org/commcare/xml/QueryPromptParser.java +++ b/src/main/java/org/commcare/xml/QueryPromptParser.java @@ -40,6 +40,7 @@ public class QueryPromptParser extends CommCareElementParser { private static final String ATTR_REQUIRED = "required"; private static final String ATTR_VALIDATION_TEST = "test"; private static final String NAME_TEXT = "text"; + private static final String ATTR_GROUP_KEY = "group_key"; public QueryPromptParser(KXmlParser parser) { super(parser); @@ -60,6 +61,7 @@ public QueryPrompt parse() throws InvalidStructureException, IOException, XmlPul XPathExpression defaultValue = xpathPropertyValue(defaultValueString); String excludeValueString = parser.getAttributeValue(null, ATTR_EXCLUDE); XPathExpression exclude = xpathPropertyValue(excludeValueString); + String groupKey = parser.getAttributeValue(null, ATTR_GROUP_KEY); XPathExpression oldRequired = xpathPropertyValue(parser.getAttributeValue(null, ATTR_REQUIRED)); QueryPromptCondition validation = null; @@ -86,7 +88,7 @@ public QueryPrompt parse() throws InvalidStructureException, IOException, XmlPul return new QueryPrompt(key, appearance, input, receive, hidden, display, itemsetBinding, defaultValue, allowBlankValue, exclude, - required, validation); + required, validation, groupKey); } private QueryPromptCondition parseRequiredBlock(String key) From 3935a612cdacd638f1221052be7ea95de56d88ae Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Mon, 4 Dec 2023 15:07:22 -0800 Subject: [PATCH 02/14] feat: parses group element from suite --- .../org/commcare/suite/model/QueryGroup.java | 43 +++++++++++++++++++ .../org/commcare/xml/QueryGroupParser.java | 38 ++++++++++++++++ .../org/commcare/xml/SessionDatumParser.java | 7 ++- 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/commcare/suite/model/QueryGroup.java create mode 100644 src/main/java/org/commcare/xml/QueryGroupParser.java diff --git a/src/main/java/org/commcare/suite/model/QueryGroup.java b/src/main/java/org/commcare/suite/model/QueryGroup.java new file mode 100644 index 0000000000..4d9813b494 --- /dev/null +++ b/src/main/java/org/commcare/suite/model/QueryGroup.java @@ -0,0 +1,43 @@ +package org.commcare.suite.model; + +import org.javarosa.core.util.externalizable.DeserializationException; +import org.javarosa.core.util.externalizable.ExtUtil; +import org.javarosa.core.util.externalizable.Externalizable; +import org.javarosa.core.util.externalizable.PrototypeFactory; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +// Model for node +public class QueryGroup implements Externalizable { + + private String key; + private DisplayUnit display; + + public QueryGroup(String key, DisplayUnit display) { + this.key = key; + this.display = display; + } + + @Override + public void readExternal(DataInputStream in, PrototypeFactory pf) + throws IOException, DeserializationException { + key = (String)ExtUtil.read(in, String.class, pf); + display = (DisplayUnit)ExtUtil.read(in, DisplayUnit.class, pf); + } + + @Override + public void writeExternal(DataOutputStream out) throws IOException { + ExtUtil.write(out, key); + ExtUtil.write(out, display); + } + + public String getKey() { + return key; + } + + public DisplayUnit getDisplay() { + return display; + } +} diff --git a/src/main/java/org/commcare/xml/QueryGroupParser.java b/src/main/java/org/commcare/xml/QueryGroupParser.java new file mode 100644 index 0000000000..5e97982dce --- /dev/null +++ b/src/main/java/org/commcare/xml/QueryGroupParser.java @@ -0,0 +1,38 @@ +package org.commcare.xml; + +import org.commcare.suite.model.DisplayUnit; +import org.commcare.suite.model.QueryGroup; +import org.javarosa.xml.util.InvalidStructureException; +import org.javarosa.xml.util.UnfullfilledRequirementsException; +import org.kxml2.io.KXmlParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +public class QueryGroupParser extends CommCareElementParser { + + private static final String NAME_PROMPT = "group"; + private static final String ATTR_KEY = "key"; + private static final String NAME_DISPLAY = "display"; + + public QueryGroupParser(KXmlParser parser) { + super(parser); + } + + @Override + public QueryGroup parse() throws InvalidStructureException, IOException, XmlPullParserException, + UnfullfilledRequirementsException { + checkNode("group"); + + String key = parser.getAttributeValue(null, ATTR_KEY); + DisplayUnit display = null; + + while (nextTagInBlock(NAME_PROMPT)) { + if (NAME_DISPLAY.equalsIgnoreCase(parser.getName())) { + display = parseDisplayBlock(); + } + } + + return new QueryGroup(key, display); + } +} diff --git a/src/main/java/org/commcare/xml/SessionDatumParser.java b/src/main/java/org/commcare/xml/SessionDatumParser.java index a841188065..9f044b1c0d 100644 --- a/src/main/java/org/commcare/xml/SessionDatumParser.java +++ b/src/main/java/org/commcare/xml/SessionDatumParser.java @@ -6,6 +6,7 @@ import org.commcare.suite.model.FormIdDatum; import org.commcare.suite.model.MultiSelectEntityDatum; import org.commcare.suite.model.QueryData; +import org.commcare.suite.model.QueryGroup; import org.commcare.suite.model.QueryPrompt; import org.commcare.suite.model.RemoteQueryDatum; import org.commcare.suite.model.SessionDatum; @@ -129,6 +130,7 @@ private RemoteQueryDatum parseRemoteQueryDatum() boolean dynamicSearch = "true".equals(parser.getAttributeValue(null, "dynamic_search")); Text title = null; Text description = null; + OrderedHashtable groupPrompts = new OrderedHashtable<>(); ArrayList hiddenQueryValues = new ArrayList(); while (nextTagInBlock("query")) { String tagName = parser.getName(); @@ -143,9 +145,12 @@ private RemoteQueryDatum parseRemoteQueryDatum() } else if ("description".equals(tagName)) { nextTagInBlock("description"); description = new TextParser(parser).parse(); + } else if ("group".equals(tagName)){ + String key = parser.getAttributeValue(null, "key"); + groupPrompts.put(key, new QueryGroupParser(parser).parse()); } } return new RemoteQueryDatum(queryUrl, queryResultStorageInstance, hiddenQueryValues, - userQueryPrompts, useCaseTemplate, defaultSearch, dynamicSearch, title, description); + userQueryPrompts, useCaseTemplate, defaultSearch, dynamicSearch, title, description, groupPrompts); } } From 7af8ca5f12a3bac7d0d652aba23c42de35786842 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Mon, 4 Dec 2023 17:02:38 -0800 Subject: [PATCH 03/14] feat: serialization and deserialization support for the userQueryGroupHeaders field. --- .../java/org/commcare/suite/model/QueryGroup.java | 4 ++++ .../org/commcare/suite/model/RemoteQueryDatum.java | 13 ++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/commcare/suite/model/QueryGroup.java b/src/main/java/org/commcare/suite/model/QueryGroup.java index 4d9813b494..c2f8b62f9b 100644 --- a/src/main/java/org/commcare/suite/model/QueryGroup.java +++ b/src/main/java/org/commcare/suite/model/QueryGroup.java @@ -15,6 +15,10 @@ public class QueryGroup implements Externalizable { private String key; private DisplayUnit display; + @SuppressWarnings("unused") + public QueryGroup() { + } + public QueryGroup(String key, DisplayUnit display) { this.key = key; this.display = display; diff --git a/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java b/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java index d25dc1cf87..e64f97404e 100644 --- a/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java +++ b/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java @@ -28,6 +28,7 @@ public class RemoteQueryDatum extends SessionDatum { private List hiddenQueryValues; private OrderedHashtable userQueryPrompts; + private OrderedHashtable userQueryGroupHeaders; private boolean useCaseTemplate; private boolean defaultSearch; private boolean dynamicSearch; @@ -46,10 +47,12 @@ public RemoteQueryDatum() { public RemoteQueryDatum(URL url, String storageInstance, List hiddenQueryValues, OrderedHashtable userQueryPrompts, - boolean useCaseTemplate, boolean defaultSearch, boolean dynamicSearch, Text title, Text description) { + boolean useCaseTemplate, boolean defaultSearch, boolean dynamicSearch, Text title, Text description, + OrderedHashtable userQueryGroupHeaders) { super(storageInstance, url.toString()); this.hiddenQueryValues = hiddenQueryValues; this.userQueryPrompts = userQueryPrompts; + this.userQueryGroupHeaders = userQueryGroupHeaders; this.useCaseTemplate = useCaseTemplate; this.defaultSearch = defaultSearch; this.dynamicSearch = dynamicSearch; @@ -61,6 +64,10 @@ public OrderedHashtable getUserQueryPrompts() { return userQueryPrompts; } + public OrderedHashtable getUserQueryGroupHeaders() { + return userQueryGroupHeaders; + } + public List getHiddenQueryValues() { return hiddenQueryValues; } @@ -104,6 +111,9 @@ public void readExternal(DataInputStream in, PrototypeFactory pf) userQueryPrompts = (OrderedHashtable)ExtUtil.read(in, new ExtWrapMap(String.class, QueryPrompt.class, ExtWrapMap.TYPE_ORDERED), pf); + userQueryGroupHeaders = + (OrderedHashtable)ExtUtil.read(in, + new ExtWrapMap(String.class, QueryGroup.class, ExtWrapMap.TYPE_ORDERED), pf); title = (Text) ExtUtil.read(in, new ExtWrapNullable(Text.class), pf); description = (Text) ExtUtil.read(in, new ExtWrapNullable(Text.class), pf); useCaseTemplate = ExtUtil.readBool(in); @@ -116,6 +126,7 @@ public void writeExternal(DataOutputStream out) throws IOException { super.writeExternal(out); ExtUtil.write(out, new ExtWrapList(hiddenQueryValues, new ExtWrapTagged())); ExtUtil.write(out, new ExtWrapMap(userQueryPrompts)); + ExtUtil.write(out, new ExtWrapMap(userQueryGroupHeaders)); ExtUtil.write(out, new ExtWrapNullable(title)); ExtUtil.write(out, new ExtWrapNullable(description)); ExtUtil.writeBool(out, useCaseTemplate); From 96ffdfa9a934e4244a474589079864c113d68ea1 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Mon, 4 Dec 2023 19:00:50 -0800 Subject: [PATCH 04/14] feat: adds a groupHeaders param to query definition --- src/cli/java/org/commcare/util/screen/QueryScreen.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cli/java/org/commcare/util/screen/QueryScreen.java b/src/cli/java/org/commcare/util/screen/QueryScreen.java index 8f6a352759..a2d2360e71 100644 --- a/src/cli/java/org/commcare/util/screen/QueryScreen.java +++ b/src/cli/java/org/commcare/util/screen/QueryScreen.java @@ -18,6 +18,7 @@ import org.commcare.session.RemoteQuerySessionManager; import org.commcare.suite.model.RemoteQueryDatum; import org.commcare.suite.model.QueryPrompt; +import org.commcare.suite.model.QueryGroup; import org.javarosa.core.model.instance.ExternalDataInstance; import org.javarosa.core.model.instance.ExternalDataInstanceSource; import org.javarosa.core.services.locale.Localization; @@ -44,6 +45,7 @@ public class QueryScreen extends Screen { private RemoteQuerySessionManager remoteQuerySessionManager; protected OrderedHashtable userInputDisplays; + protected OrderedHashtable groupHeaders; private SessionWrapper sessionWrapper; private String[] fields; private String mTitle; @@ -94,6 +96,7 @@ public void init(SessionWrapper sessionWrapper) throws CommCareSessionException mTitle = getTitleLocaleString(); description = getDescriptionLocaleString(); dynamicSearch = getQueryDatum().getDynamicSearch(); + groupHeaders = getQueryDatum().getUserQueryGroupHeaders(); } private String getTitleLocaleString() { @@ -262,6 +265,10 @@ public OrderedHashtable getUserInputDisplays() { return userInputDisplays; } + public OrderedHashtable getGroupHeaders() { + return groupHeaders; + } + public String getCurrentMessage() { return currentMessage; } From e51a1493b85696020fbdc94fabe7c036819624a9 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Thu, 7 Dec 2023 15:56:56 -0800 Subject: [PATCH 05/14] test: group_key attribute of prompt element is parsed correctly --- .../java/org/commcare/xml/QueryDataParserTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/test/java/org/commcare/xml/QueryDataParserTest.java b/src/test/java/org/commcare/xml/QueryDataParserTest.java index d5ea08833b..e4b3a7f91a 100644 --- a/src/test/java/org/commcare/xml/QueryDataParserTest.java +++ b/src/test/java/org/commcare/xml/QueryDataParserTest.java @@ -143,4 +143,14 @@ public void testParseQueryData_badNesting() throws XmlPullParserException, IOExc } catch (InvalidStructureException ignored) { } } + + @Test + public void testParseValueData_withGroupKeyAttribute() + throws InvalidStructureException, XmlPullParserException, + IOException, UnfullfilledRequirementsException { + String query = ""; + QueryPromptParser parser = ParserTestUtils.buildParser(query, QueryPromptParser.class); + QueryPrompt queryData = parser.parse(); + assertEquals("group_header_1", queryData.getGroupKey()); + } } From e151a12c5dc18b0e46f30fb698f7f735700bdc85 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Thu, 7 Dec 2023 16:56:56 -0800 Subject: [PATCH 06/14] test: group element is parsed and contains key --- .../java/org/commcare/xml/QueryDataParserTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/java/org/commcare/xml/QueryDataParserTest.java b/src/test/java/org/commcare/xml/QueryDataParserTest.java index e4b3a7f91a..4a4e1fb811 100644 --- a/src/test/java/org/commcare/xml/QueryDataParserTest.java +++ b/src/test/java/org/commcare/xml/QueryDataParserTest.java @@ -7,6 +7,7 @@ import org.commcare.suite.model.QueryData; import org.commcare.suite.model.QueryPrompt; +import org.commcare.suite.model.QueryGroup; import org.javarosa.core.model.condition.EvaluationContext; import org.javarosa.core.model.instance.DataInstance; import org.javarosa.xml.util.InvalidStructureException; @@ -153,4 +154,14 @@ public void testParseValueData_withGroupKeyAttribute() QueryPrompt queryData = parser.parse(); assertEquals("group_header_1", queryData.getGroupKey()); } + + @Test + public void testParseValueData_withGroup() + throws InvalidStructureException, XmlPullParserException, + IOException, UnfullfilledRequirementsException { + String query = ""; + QueryGroupParser parser = ParserTestUtils.buildParser(query, QueryGroupParser.class); + QueryGroup queryData = parser.parse(); + assertEquals("group_header_0", queryData.getKey()); + } } From 83ac7aa82be627f35d606b555456bbf8162eb236 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Tue, 12 Dec 2023 13:43:33 -0800 Subject: [PATCH 07/14] style: correct variable name --- src/main/java/org/commcare/xml/QueryGroupParser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/commcare/xml/QueryGroupParser.java b/src/main/java/org/commcare/xml/QueryGroupParser.java index 5e97982dce..49e8266d1a 100644 --- a/src/main/java/org/commcare/xml/QueryGroupParser.java +++ b/src/main/java/org/commcare/xml/QueryGroupParser.java @@ -11,7 +11,7 @@ public class QueryGroupParser extends CommCareElementParser { - private static final String NAME_PROMPT = "group"; + private static final String NAME_GROUP = "group"; private static final String ATTR_KEY = "key"; private static final String NAME_DISPLAY = "display"; @@ -27,7 +27,7 @@ public QueryGroup parse() throws InvalidStructureException, IOException, XmlPull String key = parser.getAttributeValue(null, ATTR_KEY); DisplayUnit display = null; - while (nextTagInBlock(NAME_PROMPT)) { + while (nextTagInBlock(NAME_GROUP)) { if (NAME_DISPLAY.equalsIgnoreCase(parser.getName())) { display = parseDisplayBlock(); } From 7d0abe88cb2e60cb84e568c7048715dc8d107d19 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Tue, 12 Dec 2023 13:53:18 -0800 Subject: [PATCH 08/14] refactor: groupHeaders do not need to be ordered. It will be used by queryPrompts so utillizes order of queryPrompts --- src/cli/java/org/commcare/util/screen/QueryScreen.java | 4 ++-- .../java/org/commcare/suite/model/RemoteQueryDatum.java | 9 +++++---- src/main/java/org/commcare/xml/SessionDatumParser.java | 3 ++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/cli/java/org/commcare/util/screen/QueryScreen.java b/src/cli/java/org/commcare/util/screen/QueryScreen.java index a2d2360e71..89658d6c7f 100644 --- a/src/cli/java/org/commcare/util/screen/QueryScreen.java +++ b/src/cli/java/org/commcare/util/screen/QueryScreen.java @@ -45,7 +45,7 @@ public class QueryScreen extends Screen { private RemoteQuerySessionManager remoteQuerySessionManager; protected OrderedHashtable userInputDisplays; - protected OrderedHashtable groupHeaders; + protected Hashtable groupHeaders; private SessionWrapper sessionWrapper; private String[] fields; private String mTitle; @@ -265,7 +265,7 @@ public OrderedHashtable getUserInputDisplays() { return userInputDisplays; } - public OrderedHashtable getGroupHeaders() { + public Hashtable getGroupHeaders() { return groupHeaders; } diff --git a/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java b/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java index e64f97404e..5dd73d3888 100644 --- a/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java +++ b/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java @@ -1,6 +1,7 @@ package org.commcare.suite.model; import org.javarosa.core.util.OrderedHashtable; +import java.util.Hashtable; import org.javarosa.core.util.externalizable.DeserializationException; import org.javarosa.core.util.externalizable.ExtUtil; import org.javarosa.core.util.externalizable.ExtWrapBase; @@ -28,7 +29,7 @@ public class RemoteQueryDatum extends SessionDatum { private List hiddenQueryValues; private OrderedHashtable userQueryPrompts; - private OrderedHashtable userQueryGroupHeaders; + private Hashtable userQueryGroupHeaders; private boolean useCaseTemplate; private boolean defaultSearch; private boolean dynamicSearch; @@ -48,7 +49,7 @@ public RemoteQueryDatum(URL url, String storageInstance, List hiddenQueryValues, OrderedHashtable userQueryPrompts, boolean useCaseTemplate, boolean defaultSearch, boolean dynamicSearch, Text title, Text description, - OrderedHashtable userQueryGroupHeaders) { + Hashtable userQueryGroupHeaders) { super(storageInstance, url.toString()); this.hiddenQueryValues = hiddenQueryValues; this.userQueryPrompts = userQueryPrompts; @@ -64,7 +65,7 @@ public OrderedHashtable getUserQueryPrompts() { return userQueryPrompts; } - public OrderedHashtable getUserQueryGroupHeaders() { + public Hashtable getUserQueryGroupHeaders() { return userQueryGroupHeaders; } @@ -112,7 +113,7 @@ public void readExternal(DataInputStream in, PrototypeFactory pf) (OrderedHashtable)ExtUtil.read(in, new ExtWrapMap(String.class, QueryPrompt.class, ExtWrapMap.TYPE_ORDERED), pf); userQueryGroupHeaders = - (OrderedHashtable)ExtUtil.read(in, + (Hashtable)ExtUtil.read(in, new ExtWrapMap(String.class, QueryGroup.class, ExtWrapMap.TYPE_ORDERED), pf); title = (Text) ExtUtil.read(in, new ExtWrapNullable(Text.class), pf); description = (Text) ExtUtil.read(in, new ExtWrapNullable(Text.class), pf); diff --git a/src/main/java/org/commcare/xml/SessionDatumParser.java b/src/main/java/org/commcare/xml/SessionDatumParser.java index 9f044b1c0d..79b837e007 100644 --- a/src/main/java/org/commcare/xml/SessionDatumParser.java +++ b/src/main/java/org/commcare/xml/SessionDatumParser.java @@ -21,6 +21,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Hashtable; /** * @author ctsims @@ -130,7 +131,7 @@ private RemoteQueryDatum parseRemoteQueryDatum() boolean dynamicSearch = "true".equals(parser.getAttributeValue(null, "dynamic_search")); Text title = null; Text description = null; - OrderedHashtable groupPrompts = new OrderedHashtable<>(); + Hashtable groupPrompts = new Hashtable<>(); ArrayList hiddenQueryValues = new ArrayList(); while (nextTagInBlock("query")) { String tagName = parser.getName(); From 31321d9274565a9379ee0d541e6cb8505c8ed5c6 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Tue, 12 Dec 2023 14:22:12 -0800 Subject: [PATCH 09/14] style: use constant variable --- src/main/java/org/commcare/xml/QueryGroupParser.java | 2 +- src/main/java/org/commcare/xml/SessionDatumParser.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/commcare/xml/QueryGroupParser.java b/src/main/java/org/commcare/xml/QueryGroupParser.java index 49e8266d1a..20049a950d 100644 --- a/src/main/java/org/commcare/xml/QueryGroupParser.java +++ b/src/main/java/org/commcare/xml/QueryGroupParser.java @@ -11,7 +11,7 @@ public class QueryGroupParser extends CommCareElementParser { - private static final String NAME_GROUP = "group"; + public static final String NAME_GROUP = "group"; private static final String ATTR_KEY = "key"; private static final String NAME_DISPLAY = "display"; diff --git a/src/main/java/org/commcare/xml/SessionDatumParser.java b/src/main/java/org/commcare/xml/SessionDatumParser.java index 79b837e007..4a85c1700f 100644 --- a/src/main/java/org/commcare/xml/SessionDatumParser.java +++ b/src/main/java/org/commcare/xml/SessionDatumParser.java @@ -146,7 +146,7 @@ private RemoteQueryDatum parseRemoteQueryDatum() } else if ("description".equals(tagName)) { nextTagInBlock("description"); description = new TextParser(parser).parse(); - } else if ("group".equals(tagName)){ + } else if (QueryGroupParser.NAME_GROUP.equals(tagName)){ String key = parser.getAttributeValue(null, "key"); groupPrompts.put(key, new QueryGroupParser(parser).parse()); } From abc40067ff154abfd2d125110f23b31969ba4b81 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Tue, 12 Dec 2023 14:24:27 -0800 Subject: [PATCH 10/14] refactor: use key that QueryGroupParser already parsed --- src/main/java/org/commcare/xml/SessionDatumParser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/commcare/xml/SessionDatumParser.java b/src/main/java/org/commcare/xml/SessionDatumParser.java index 4a85c1700f..78339104a7 100644 --- a/src/main/java/org/commcare/xml/SessionDatumParser.java +++ b/src/main/java/org/commcare/xml/SessionDatumParser.java @@ -147,8 +147,8 @@ private RemoteQueryDatum parseRemoteQueryDatum() nextTagInBlock("description"); description = new TextParser(parser).parse(); } else if (QueryGroupParser.NAME_GROUP.equals(tagName)){ - String key = parser.getAttributeValue(null, "key"); - groupPrompts.put(key, new QueryGroupParser(parser).parse()); + QueryGroup queryGroup = new QueryGroupParser(parser).parse(); + groupPrompts.put(queryGroup.getKey(), queryGroup); } } return new RemoteQueryDatum(queryUrl, queryResultStorageInstance, hiddenQueryValues, From 3fec35003f2f4270ac2c84e8b968d064bf3db843 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Tue, 12 Dec 2023 15:05:14 -0800 Subject: [PATCH 11/14] feat: validate group block contains expected elements/attributes --- src/main/java/org/commcare/xml/QueryGroupParser.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/java/org/commcare/xml/QueryGroupParser.java b/src/main/java/org/commcare/xml/QueryGroupParser.java index 20049a950d..fd442e5a68 100644 --- a/src/main/java/org/commcare/xml/QueryGroupParser.java +++ b/src/main/java/org/commcare/xml/QueryGroupParser.java @@ -30,9 +30,19 @@ public QueryGroup parse() throws InvalidStructureException, IOException, XmlPull while (nextTagInBlock(NAME_GROUP)) { if (NAME_DISPLAY.equalsIgnoreCase(parser.getName())) { display = parseDisplayBlock(); + } else { + throw new InvalidStructureException( + "Unrecognised node " + parser.getName() + "in validation for group " + key); } } + if (key == null) { + throw new InvalidStructureException(" block must define a 'key' attribute", parser); + } + if (display == null) { + throw new InvalidStructureException(" block must define a element", parser); + } + return new QueryGroup(key, display); } } From 2b4ba65b1ffdeaea1429db098d7b44f3d66391b2 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Tue, 12 Dec 2023 15:19:59 -0800 Subject: [PATCH 12/14] minor style + refactor --- src/main/java/org/commcare/suite/model/RemoteQueryDatum.java | 4 ++-- src/main/java/org/commcare/xml/QueryGroupParser.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java b/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java index 5dd73d3888..7ad5dc5589 100644 --- a/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java +++ b/src/main/java/org/commcare/suite/model/RemoteQueryDatum.java @@ -47,8 +47,8 @@ public RemoteQueryDatum() { */ public RemoteQueryDatum(URL url, String storageInstance, List hiddenQueryValues, - OrderedHashtable userQueryPrompts, - boolean useCaseTemplate, boolean defaultSearch, boolean dynamicSearch, Text title, Text description, + OrderedHashtable userQueryPrompts, boolean useCaseTemplate, + boolean defaultSearch, boolean dynamicSearch, Text title, Text description, Hashtable userQueryGroupHeaders) { super(storageInstance, url.toString()); this.hiddenQueryValues = hiddenQueryValues; diff --git a/src/main/java/org/commcare/xml/QueryGroupParser.java b/src/main/java/org/commcare/xml/QueryGroupParser.java index fd442e5a68..fe6850f41d 100644 --- a/src/main/java/org/commcare/xml/QueryGroupParser.java +++ b/src/main/java/org/commcare/xml/QueryGroupParser.java @@ -22,7 +22,7 @@ public QueryGroupParser(KXmlParser parser) { @Override public QueryGroup parse() throws InvalidStructureException, IOException, XmlPullParserException, UnfullfilledRequirementsException { - checkNode("group"); + checkNode(NAME_GROUP); String key = parser.getAttributeValue(null, ATTR_KEY); DisplayUnit display = null; From 643a7a2cb16a045afabd130766b54204e37382bf Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Tue, 12 Dec 2023 15:37:20 -0800 Subject: [PATCH 13/14] test: adds display block to group element to pass validation --- src/test/java/org/commcare/xml/QueryDataParserTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/commcare/xml/QueryDataParserTest.java b/src/test/java/org/commcare/xml/QueryDataParserTest.java index 4a4e1fb811..9f9f922ad0 100644 --- a/src/test/java/org/commcare/xml/QueryDataParserTest.java +++ b/src/test/java/org/commcare/xml/QueryDataParserTest.java @@ -159,7 +159,9 @@ public void testParseValueData_withGroupKeyAttribute() public void testParseValueData_withGroup() throws InvalidStructureException, XmlPullParserException, IOException, UnfullfilledRequirementsException { - String query = ""; + String query = "" + + "" + + ""; QueryGroupParser parser = ParserTestUtils.buildParser(query, QueryGroupParser.class); QueryGroup queryData = parser.parse(); assertEquals("group_header_0", queryData.getKey()); From 15a0d9ecbc5783cc28d53531e81312208866f4f9 Mon Sep 17 00:00:00 2001 From: Jonathan Tang Date: Tue, 12 Dec 2023 17:29:25 -0800 Subject: [PATCH 14/14] refactor: moves evaluation of groupHeader text to QueryScreen --- .../java/org/commcare/util/screen/QueryScreen.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/cli/java/org/commcare/util/screen/QueryScreen.java b/src/cli/java/org/commcare/util/screen/QueryScreen.java index 89658d6c7f..210c0ced58 100644 --- a/src/cli/java/org/commcare/util/screen/QueryScreen.java +++ b/src/cli/java/org/commcare/util/screen/QueryScreen.java @@ -269,6 +269,17 @@ public Hashtable getGroupHeaders() { return groupHeaders; } + public Hashtable evalGroupHeaders() { + Hashtable queryGroupMap = new Hashtable<>(); + for (Map.Entry entry : groupHeaders.entrySet()) { + String key = entry.getKey(); + QueryGroup queryGroupItem = entry.getValue(); + String text = queryGroupItem.getDisplay().getText().evaluate(sessionWrapper.getEvaluationContext()); + queryGroupMap.put(key, text); + } + return queryGroupMap; + } + public String getCurrentMessage() { return currentMessage; }