Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose created entities after finalizing form #691

Merged
merged 36 commits into from
Oct 4, 2022
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c15d279
Add dummy entities API for Collect
seadowg Sep 9, 2022
0798659
Spike out exposing created entity once form is finalized
seadowg Sep 9, 2022
19e83e7
Make sure entities are only created when they should be
seadowg Sep 13, 2022
c6e0d57
Support caching form def/instance with entities
seadowg Sep 13, 2022
6b458a7
Move back to using List for saveto refs
seadowg Sep 13, 2022
0b036bd
Remove id attribute from test forms
seadowg Sep 15, 2022
7b31a82
Make sure namespace prefix isn't hardcoded for entities
seadowg Sep 21, 2022
d4f3c49
Add test for selects
seadowg Sep 21, 2022
b547b78
Reimplement entity parsing and processing using plugin style architec…
seadowg Sep 21, 2022
b6ba302
Ignore entities:create if the namespace is incorrect
seadowg Sep 23, 2022
15a0861
Don't create entities if create is not relevant
seadowg Sep 23, 2022
444143d
Only parse dataset out during processing
seadowg Sep 23, 2022
8072261
Don't return all matching children in cases we don't expect more than…
seadowg Sep 23, 2022
a8e5023
Use forEach supported by Android API 21
seadowg Sep 23, 2022
488511c
Optimize imports
seadowg Sep 23, 2022
98aac67
Pull out common Extras
seadowg Sep 30, 2022
4aec176
Rename method
seadowg Sep 30, 2022
4497074
Rename field
seadowg Sep 30, 2022
ee3e78d
Make naming less ambiguous
seadowg Sep 30, 2022
de9ad87
Validate namespace for create action
seadowg Sep 30, 2022
96a057b
Add convenience method for getting namespaced children
seadowg Sep 30, 2022
d8d28c6
Move test
seadowg Sep 30, 2022
43fff8d
Ignore saveto attributes with incorrect namespaces
seadowg Sep 30, 2022
eb6568e
Use simpler parsing code
seadowg Sep 30, 2022
6946ecc
Make it easier to add processor that deals with multiple stages of parse
seadowg Sep 30, 2022
ced91b0
Validate entity version if defined in model
seadowg Sep 30, 2022
71b75af
Keep 'processor' consistent in naming
seadowg Oct 3, 2022
c3f86f2
Make sure forms without entities can be parsed
seadowg Oct 3, 2022
331acd6
Naming improvements
seadowg Oct 3, 2022
e978f64
Remove unsupported forEach
seadowg Oct 3, 2022
d4b9100
Simplify ExternalizableExtras
seadowg Oct 3, 2022
35652f6
Make sure only bind attributes with correct namespace are removed
seadowg Oct 3, 2022
f69c9b2
Add explicit parse exception
seadowg Oct 3, 2022
9b34f4c
Remove IOException from parse() method
seadowg Oct 3, 2022
c20aa0b
Correct version
seadowg Oct 3, 2022
9afa909
Check that parsing works with patch versions
seadowg Oct 4, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 31 additions & 16 deletions src/main/java/org/javarosa/core/model/FormDef.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,6 @@

package org.javarosa.core.model;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.javarosa.core.log.WrappedException;
import org.javarosa.core.model.TriggerableDag.EventNotifierAccessor;
import org.javarosa.core.model.actions.ActionController;
Expand All @@ -55,12 +42,15 @@
import org.javarosa.core.services.locale.Localizer;
import org.javarosa.core.services.storage.IMetaData;
import org.javarosa.core.services.storage.Persistable;
import org.javarosa.core.util.Extras;
import org.javarosa.core.util.externalizable.DeserializationException;
import org.javarosa.core.util.externalizable.ExtUtil;
import org.javarosa.core.util.externalizable.ExtWrapListPoly;
import org.javarosa.core.util.externalizable.ExtWrapMap;
import org.javarosa.core.util.externalizable.ExtWrapNullable;
import org.javarosa.core.util.externalizable.ExtWrapTagged;
import org.javarosa.core.util.externalizable.Externalizable;
import org.javarosa.core.util.externalizable.ExternalizableExtras;
import org.javarosa.core.util.externalizable.PrototypeFactory;
import org.javarosa.debug.EvaluationResult;
import org.javarosa.debug.Event;
Expand All @@ -75,6 +65,22 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;

import static java.util.Collections.emptyList;

/**
* Definition of a form. This has some meta data about the form definition and a
* collection of groups together with question branching or skipping rules.
Expand All @@ -89,6 +95,7 @@ public class FormDef implements IFormElement, Localizable, Persistable, IMetaDat
public static final int TEMPLATING_RECURSION_LIMIT = 10;

private static EventNotifier defaultEventNotifier = new EventNotifierSilent();
private ExternalizableExtras extras = new ExternalizableExtras();

/**
* Takes a (possibly relative) reference, and makes it absolute based on its parent.
Expand Down Expand Up @@ -1002,9 +1009,9 @@ public void preloadInstance(TreeElement node) {
// }
}

public boolean postProcessInstance() {
public void postProcessInstance() {
actionController.triggerActionsFromEvent(Actions.EVENT_XFORMS_REVALIDATE, elementsWithActionTriggeredByToplevelEvent, this);
return postProcessInstance(mainInstance.getRoot());
postProcessInstance(mainInstance.getRoot());
}

/**
Expand Down Expand Up @@ -1112,6 +1119,8 @@ public void readExternal(DataInputStream dis, PrototypeFactory pf) throws IOExce

List<TreeReference> treeReferencesWithActions = (List<TreeReference>) ExtUtil.read(dis, new ExtWrapListPoly(), pf);
elementsWithActionTriggeredByToplevelEvent = getElementsFromReferences(treeReferencesWithActions);

extras = (ExternalizableExtras) ExtUtil.read(dis, ExternalizableExtras.class);
}

/**
Expand Down Expand Up @@ -1215,6 +1224,8 @@ public void writeExternal(DataOutputStream dos) throws IOException {
ExtUtil.write(dos, new ExtWrapNullable(actionController));
ExtUtil.write(dos, new ExtWrapListPoly(new ArrayList<>(actions)));
ExtUtil.write(dos, new ExtWrapListPoly(getReferencesFromElements(elementsWithActionTriggeredByToplevelEvent)));

ExtUtil.write(dos, extras);
}

/**
Expand Down Expand Up @@ -1600,7 +1611,7 @@ public String getAdditionalAttribute(String namespace, String name) {
@Override
public List<TreeElement> getAdditionalAttributes() {
// Not supported.
return Collections.emptyList();
return emptyList();
}

public <X extends XFormExtension> X getExtension(Class<X> extension) {
Expand Down Expand Up @@ -1690,4 +1701,8 @@ private String getFormXmlPath() {
public HashMap<String, DataInstance> getFormInstances() {
return formInstances;
}

public Extras<Externalizable> getExtras() {
return extras;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.javarosa.core.model.data.IAnswerData;
import org.javarosa.core.model.instance.utils.ITreeVisitor;
import org.javarosa.xpath.expr.XPathExpression;
import org.jetbrains.annotations.Nullable;

public interface AbstractTreeElement<T extends AbstractTreeElement> {

Expand All @@ -15,6 +16,13 @@ public interface AbstractTreeElement<T extends AbstractTreeElement> {

public abstract String getInstanceName();

@Nullable
public T getFirstChild(String name);

@Nullable
public T getFirstChild(String namespace, String name);

@Nullable
public abstract T getChild(String name, int multiplicity);

/**
Expand Down
14 changes: 6 additions & 8 deletions src/main/java/org/javarosa/core/model/instance/FormInstance.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,6 @@

package org.javarosa.core.model.instance;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;

import org.javarosa.core.model.FormDef;
import org.javarosa.core.model.data.IAnswerData;
import org.javarosa.core.model.instance.utils.ITreeVisitor;
Expand All @@ -34,6 +28,12 @@
import org.javarosa.core.util.externalizable.ExtWrapNullable;
import org.javarosa.core.util.externalizable.PrototypeFactory;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;


/**
* This class represents the xform model instance
Expand Down Expand Up @@ -312,10 +312,8 @@ public void readExternal(DataInputStream in, PrototypeFactory pf) throws IOExcep
super.readExternal(in, pf);
schema = (String) ExtUtil.read(in, new ExtWrapNullable(String.class), pf);
dateSaved = (Date) ExtUtil.read(in, new ExtWrapNullable(Date.class), pf);

namespaces = (HashMap<String,Object>)ExtUtil.read(in, new ExtWrapMap(String.class, String.class));
setRoot((TreeElement) ExtUtil.read(in, TreeElement.class, pf));

}

public void writeExternal(DataOutputStream out) throws IOException {
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/org/javarosa/core/model/instance/TreeElement.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.javarosa.xpath.expr.XPathExpression;
import org.javarosa.xpath.expr.XPathPathExpr;
import org.javarosa.xpath.expr.XPathStringLiteral;
import org.jetbrains.annotations.Nullable;

/**
* <p>An element of a FormInstance.</p>
Expand Down Expand Up @@ -221,6 +222,25 @@ public void setValue(IAnswerData value) {
}

@Override
@Nullable
public TreeElement getFirstChild(String name) {
return getChild(name, 0);
}

@Nullable
@Override
public TreeElement getFirstChild(String namespace, String name) {
TreeElement firstChild = getFirstChild(name);

if (firstChild == null || firstChild.getNamespace().equals(namespace)) {
return firstChild;
} else {
return null;
}
}

@Override
@Nullable
public TreeElement getChild(String name, int multiplicity) {
return children.get(name, multiplicity);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.javarosa.core.model.instance.TreeElement;
import org.javarosa.core.model.instance.TreeReference;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Iterator;
Expand Down Expand Up @@ -72,6 +73,7 @@ public List<TreeElement> get(String name) {
}

/** Gets the child with the specified name and multiplicity */
@Nullable
public TreeElement get(String name, int multiplicity) {
TreeElementChildrenList.ElementAndLoc el = getChildAndLoc(name, multiplicity);
if (el == null) {
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/org/javarosa/core/util/Extras.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.javarosa.core.util;

import java.util.HashMap;

/**
* Store for objects that allows them to be retrieved by type without any casting.
*
* @param <T> allows a super type to be enforced for all the stored objects
*/
public class Extras<T> {

protected final HashMap<String, T> map = new HashMap<>();

public void put(T extra) {
map.put(extra.getClass().getName(), extra);
}

public <U extends T> U get(Class<U> key) {
return (U) map.get(key.getName());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.javarosa.core.util.externalizable;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class ExtWrapExternalizable extends ExternalizableWrapper {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the relationship between this and ExtWrapBase? Seems they're nearly identical? Should this maybe extend ExtWrapBase or is it possible/reasonable to use ExtWrapBase directly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I hadn't seen ExtWrapBase - will have a look at it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExtWrapBased doesn't "externalize" the object's type meaning that it must be known at compile time. This doesn't work when externalizing a value that is being referenced by a super type like with Extras.


public ExtWrapExternalizable() {
}

public ExtWrapExternalizable(Externalizable externalizable) {
this.val = externalizable;
}

@Override
public ExternalizableWrapper clone(Object val) {
return null;
}

@Override
public void metaReadExternal(DataInputStream in, PrototypeFactory pf) throws IOException, DeserializationException {

}

@Override
public void metaWriteExternal(DataOutputStream out) throws IOException {

}

@Override
public void readExternal(DataInputStream in, PrototypeFactory pf) throws IOException, DeserializationException {
try {
String className = ExtUtil.readString(in);
Class<?> clazz = Class.forName(className);

this.val = ExtUtil.read(in, clazz);
} catch (ClassNotFoundException e) {
throw new DeserializationException("Couldn't find class from serialize class name!");
}
}

@Override
public void writeExternal(DataOutputStream out) throws IOException {
ExtUtil.write(out, val.getClass().getName());
ExtUtil.write(out, val);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.javarosa.core.util.externalizable;

import org.javarosa.core.util.Extras;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;

public class ExternalizableExtras extends Extras<Externalizable> implements Externalizable {

@Override
public void readExternal(DataInputStream in, PrototypeFactory pf) throws IOException, DeserializationException {
HashMap<String, Externalizable> extras = (HashMap<String, Externalizable>) ExtUtil.read(in, new ExtWrapMap(String.class, new ExtWrapExternalizable()), pf);
extras.forEach((s, externalizable) -> put(externalizable));
}

@Override
public void writeExternal(DataOutputStream out) throws IOException {
HashMap<Object, Object> wrappedParseAttachments = new HashMap<>();
map.forEach((key, value) -> {
wrappedParseAttachments.put(key, new ExtWrapExternalizable(value));
});

ExtUtil.write(out, new ExtWrapMap(wrappedParseAttachments));
}
}
16 changes: 16 additions & 0 deletions src/main/java/org/javarosa/entities/Entity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.javarosa.entities;

import kotlin.Pair;

import java.util.List;

public class Entity {

public final String dataset;
public final List<Pair<String, String>> properties;

public Entity(String dataset, List<Pair<String, String>> properties) {
this.dataset = dataset;
this.properties = properties;
}
}
43 changes: 43 additions & 0 deletions src/main/java/org/javarosa/entities/EntityFinalizer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.javarosa.entities;

import kotlin.Pair;
import org.javarosa.core.model.FormDef;
import org.javarosa.core.model.IDataReference;
import org.javarosa.core.model.instance.FormInstance;
import org.javarosa.entities.internal.Entities;
import org.javarosa.entities.internal.EntityDatasetParser;
import org.javarosa.entities.internal.EntityFormExtra;
import org.javarosa.form.api.FormEntryModel;
import org.javarosa.form.api.FormEntryFinalizer;
import org.javarosa.model.xform.XPathReference;

import java.util.List;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;

public class EntityFinalizer implements FormEntryFinalizer {

@Override
public void processForm(FormEntryModel formEntryModel) {
FormDef formDef = formEntryModel.getForm();
FormInstance mainInstance = formDef.getMainInstance();

EntityFormExtra entityFormExtra = formDef.getExtras().get(EntityFormExtra.class);
List<Pair<XPathReference, String>> saveTos = entityFormExtra.getSaveTos();

String dataset = EntityDatasetParser.parseFirstDatasetToCreate(mainInstance);
if (dataset != null) {
List<Pair<String, String>> fields = saveTos.stream().map(saveTo -> {
IDataReference reference = saveTo.getFirst();
String answer = mainInstance.resolveReference(reference).getValue().getDisplayText();
return new Pair<>(saveTo.getSecond(), answer);
}).collect(Collectors.toList());

formEntryModel.getExtras().put(new Entities(asList(new Entity(dataset, fields))));
} else {
formEntryModel.getExtras().put(new Entities(emptyList()));
}
}
}
Loading