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\"")
)
)
),