diff --git a/src/main/java/org/javarosa/core/model/instance/TreeElement.java b/src/main/java/org/javarosa/core/model/instance/TreeElement.java index 48bdad219..f73226662 100644 --- a/src/main/java/org/javarosa/core/model/instance/TreeElement.java +++ b/src/main/java/org/javarosa/core/model/instance/TreeElement.java @@ -15,13 +15,6 @@ */ package org.javarosa.core.model.instance; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - import org.javarosa.core.model.Constants; import org.javarosa.core.model.FormDef; import org.javarosa.core.model.FormElementStateListener; @@ -52,6 +45,13 @@ import org.javarosa.xpath.expr.XPathStringLiteral; import org.jetbrains.annotations.Nullable; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + /** *

An element of a FormInstance.

* @@ -580,11 +580,18 @@ private String getAttributeValue(TreeElement attribute) { } } + @Nullable public String getAttributeValue() { if ( !isAttribute() ) { throw new IllegalStateException("this is not an attribute"); } - return getValue().uncast().getString(); + + IAnswerData value = getValue(); + if (value != null) { + return value.uncast().getString(); + } else { + return null; + } } @Override @@ -593,6 +600,7 @@ public TreeElement getAttribute(String namespace, String name) { } @Override + @Nullable public String getAttributeValue(String namespace, String name) { TreeElement element = getAttribute(namespace,name); return element == null ? null: getAttributeValue(element); diff --git a/src/main/java/org/javarosa/entities/Entity.java b/src/main/java/org/javarosa/entities/Entity.java index 9e67067e1..38dcea514 100644 --- a/src/main/java/org/javarosa/entities/Entity.java +++ b/src/main/java/org/javarosa/entities/Entity.java @@ -9,14 +9,17 @@ public class Entity { public final String dataset; public final List> properties; + + @Nullable public final String id; @Nullable public final String label; + public final Integer version; public final EntityAction action; - public Entity(EntityAction action, String dataset, String id, @Nullable String label, Integer version, List> properties) { + public Entity(EntityAction action, String dataset, @Nullable String id, @Nullable String label, Integer version, List> properties) { this.dataset = dataset; this.id = id; this.label = label; diff --git a/src/main/java/org/javarosa/entities/internal/EntityFormParser.java b/src/main/java/org/javarosa/entities/internal/EntityFormParser.java index ed6395249..add81232e 100644 --- a/src/main/java/org/javarosa/entities/internal/EntityFormParser.java +++ b/src/main/java/org/javarosa/entities/internal/EntityFormParser.java @@ -1,5 +1,6 @@ package org.javarosa.entities.internal; +import org.javarosa.core.model.data.IAnswerData; import org.javarosa.core.model.instance.FormInstance; import org.javarosa.core.model.instance.TreeElement; import org.javarosa.entities.EntityAction; @@ -22,12 +23,19 @@ public static String parseLabel(TreeElement entity) { TreeElement labelElement = entity.getFirstChild("label"); if (labelElement != null) { - return (String) labelElement.getValue().getValue(); + IAnswerData labelValue = labelElement.getValue(); + + if (labelValue != null) { + return (String) labelValue.getValue(); + } else { + return null; + } } else { return null; } } + @Nullable public static String parseId(TreeElement entity) { return entity.getAttributeValue("", "id"); } diff --git a/src/test/java/org/javarosa/entities/EntitiesTest.java b/src/test/java/org/javarosa/entities/EntitiesTest.java index 193a4cddc..7675b0844 100644 --- a/src/test/java/org/javarosa/entities/EntitiesTest.java +++ b/src/test/java/org/javarosa/entities/EntitiesTest.java @@ -127,6 +127,47 @@ public void fillingFormWithCreate_makesEntityAvailable() throws IOException, XFo assertThat(entities.get(0).action, equalTo(EntityAction.CREATE)); } + @Test + public void fillingFormWithCreate_withoutAnId_makesEntityAvailable() throws IOException, XFormParser.ParseException { + Scenario scenario = Scenario.init("Create entity form", XFormsElement.html( + asList( + new Pair<>("entities", "http://www.opendatakit.org/xforms/entities") + ), + head( + title("Create entity form"), + model(asList(new Pair<>("entities:entities-version", "2022.1.1")), + mainInstance( + t("data id=\"create-entity-form\"", + t("id"), + t("name"), + t("meta", + t("entity dataset=\"people\" create=\"1\" id=\"\"", + t("label") + ) + ) + ) + ), + bind("/data/id").type("string"), + bind("/data/meta/entity/@id").type("string").calculate("/data/id"), + bind("/data/meta/entity/label").type("string").calculate("/data/name") + ) + ), + body( + input("/data/id"), + input("/data/name") + ) + )); + + scenario.getFormEntryController().addPostProcessor(new EntityFormFinalizationProcessor()); + scenario.finalizeInstance(); + + List entities = scenario.getFormEntryController().getModel().getExtras().get(Entities.class).getEntities(); + assertThat(entities.size(), equalTo(1)); + assertThat(entities.get(0).dataset, equalTo("people")); + assertThat(entities.get(0).id, equalTo(null)); + assertThat(entities.get(0).action, equalTo(EntityAction.CREATE)); + } + @Test public void fillingFormWithUpdate_makesEntityAvailable() throws IOException, XFormParser.ParseException { Scenario scenario = Scenario.init("Create entity form", XFormsElement.html( @@ -174,7 +215,7 @@ public void fillingFormWithUpdate_makesEntityAvailable() throws IOException, XFo @Test public void fillingFormWithUpdate_andNoLabel_makesEntityAvailableWithNullLabel() throws IOException, XFormParser.ParseException { - Scenario scenario = Scenario.init("Create entity form", XFormsElement.html( + Scenario scenario = Scenario.init("Update entity form", XFormsElement.html( asList( new Pair<>("entities", "http://www.opendatakit.org/xforms/entities") ), @@ -213,6 +254,42 @@ public void fillingFormWithUpdate_andNoLabel_makesEntityAvailableWithNullLabel() assertThat(entities.get(0).properties, equalTo(asList(new Pair<>("name", "Tom Wambsgans")))); } + @Test + public void fillingFormWithUpdate_withNullId_makesEntityAvailable() throws IOException, XFormParser.ParseException { + Scenario scenario = Scenario.init("Create entity form", XFormsElement.html( + asList( + new Pair<>("entities", "http://www.opendatakit.org/xforms/entities") + ), + head( + title("Update entity form"), + model(asList(new Pair<>("entities:entities-version", "2023.1.0")), + mainInstance( + t("data id=\"update-entity-form\"", + t("id"), + t("meta", + t("entity dataset=\"people\" update=\"1\" id=\"\" baseVersion=\"\"") + ) + ) + ), + bind("/data/id").type("string"), + bind("/data/meta/entity/@id").type("string").calculate("/data/id").readonly() + ) + ), + body( + input("/data/id") + ) + )); + + scenario.getFormEntryController().addPostProcessor(new EntityFormFinalizationProcessor()); + scenario.finalizeInstance(); + + List entities = scenario.getFormEntryController().getModel().getExtras().get(Entities.class).getEntities(); + assertThat(entities.size(), equalTo(1)); + assertThat(entities.get(0).dataset, equalTo("people")); + assertThat(entities.get(0).id, equalTo(null)); + assertThat(entities.get(0).action, equalTo(EntityAction.UPDATE)); + } + @Test public void fillingFormWithCreateAndUpdate_makesEntityAvailableAsSecondVersion() throws IOException, XFormParser.ParseException { Scenario scenario = Scenario.init("Create entity form", XFormsElement.html( @@ -315,7 +392,7 @@ public void fillingFormWithDynamicCreateExpression_conditionallyCreatesEntities( t("name"), t("join"), t("meta", - t("entity dataset=\"members\" create=\"\"") + t("entity dataset=\"members\" create=\"\" id=\"1\"") ) ) ), @@ -365,7 +442,7 @@ public void entityFormCanBeSerialized() throws IOException, DeserializationExcep t("data id=\"create-entity-form\"", t("name"), t("meta", - t("entities:entity dataset=\"people\" create=\"1\"") + t("entities:entity dataset=\"people\" create=\"1\" id=\"1\"") ) ) ), @@ -405,7 +482,7 @@ public void entitiesNamespaceWorksRegardlessOfName() throws IOException, Deseria t("data id=\"create-entity-form\"", t("name"), t("meta", - t("entity dataset=\"people\" create=\"1\"") + t("entity dataset=\"people\" create=\"1\" id=\"1\"") ) ) ), @@ -441,7 +518,7 @@ public void fillingFormWithSelectSaveTo_andWithCreate_savesValuesCorrectlyToEnti t("data id=\"create-entity-form\"", t("team"), t("meta", - t("entity dataset=\"people\" create=\"1\"") + t("entity dataset=\"people\" create=\"1\" id=\"1\"") ) ) ), @@ -477,7 +554,7 @@ public void whenSaveToQuestionIsNotAnswered_entityPropertyIsEmptyString() throws t("data id=\"create-entity-form\"", t("name"), t("meta", - t("entity dataset=\"people\" create=\"1\"") + t("entity dataset=\"people\" create=\"1\" id=\"1\"") ) ) ),