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

Remembering previously entered values (auto-fill) #406

Merged
merged 9 commits into from
Mar 12, 2019
22 changes: 22 additions & 0 deletions resources/org/javarosa/xform/parse/last-saved-blank.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<h:html
xmlns:h="http://www.w3.org/1999/xhtml"
xmlns="http://www.w3.org/2002/xforms">

<h:head>
<h:title>Form with last-saved instance (blank)</h:title>
<model>
<instance>
<data>
<item/>
</data>
</instance>
<instance id="last-saved" src="jr://instance/last-saved"/>
<bind nodeset="/data/item" type="string"/>
<setvalue event="xforms-ready" ref="/data/item" value="instance('last-saved')/data/item"/>
</model>
</h:head>

<h:body>
</h:body>

</h:html>
22 changes: 22 additions & 0 deletions resources/org/javarosa/xform/parse/last-saved-filled.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<h:html
xmlns:h="http://www.w3.org/1999/xhtml"
xmlns="http://www.w3.org/2002/xforms">

<h:head>
<h:title>Form with last-saved instance (filled)</h:title>
<model>
<instance>
<data>
<item>Foo</item>
</data>
</instance>
<instance id="last-saved" src="jr://instance/last-saved"/>
<bind nodeset="/data/item" type="string"/>
<setvalue event="xforms-ready" ref="/data/item" value="instance('last-saved')/data/item"/>
</model>
</h:head>

<h:body>
</h:body>

</h:html>
35 changes: 31 additions & 4 deletions src/org/javarosa/xform/parse/XFormParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -349,14 +349,24 @@ public XFormParser(Document form, Document instance) {
}

public FormDef parse() throws IOException {
return parse(null);
}

/**
* @see #parse()
*
* @param lastSavedSrc The src of the last-saved instance of this form (for auto-filling). If null,
* no data will be loaded and the instance will be blank.
*/
public FormDef parse(String lastSavedSrc) throws IOException {
cooperka marked this conversation as resolved.
Show resolved Hide resolved
if (_f == null) {
logger.info("Parsing form...");

if (_xmldoc == null) {
_xmldoc = getXMLDocument(_reader, stringCache);
}

parseDoc(buildNamespacesMap(_xmldoc.getRootElement()));
parseDoc(buildNamespacesMap(_xmldoc.getRootElement()), lastSavedSrc);

//load in a custom xml instance, if applicable
if (_instReader != null) {
Expand Down Expand Up @@ -439,7 +449,7 @@ public static Document getXMLDocument(Reader reader, CacheTable<String> stringCa
return doc;
}

private void parseDoc(Map<String, String> namespacePrefixesByUri) {
private void parseDoc(Map<String, String> namespacePrefixesByUri, String lastSavedSrc) {
cooperka marked this conversation as resolved.
Show resolved Hide resolved
final StopWatch codeTimer = StopWatch.start();
_f = new FormDef();

Expand All @@ -459,9 +469,9 @@ private void parseDoc(Map<String, String> namespacePrefixesByUri) {
for (int instanceIndex = 1; instanceIndex < instanceNodes.size(); instanceIndex++) {
final Element instance = instanceNodes.get(instanceIndex);
final String instanceId = instanceNodeIdStrs.get(instanceIndex);
final String instanceSrc = instance.getAttributeValue(null, "src");
final String instanceSrc = parseInstanceSrc(instance, lastSavedSrc);

if (instanceSrc != null && instanceSrc.toLowerCase().startsWith("jr://file")) { // file or file-csv
if (instanceSrc != null) {
final ExternalDataInstance externalDataInstance;
try {
externalDataInstance = ExternalDataInstance.build(instanceSrc, instanceId);
Expand Down Expand Up @@ -513,6 +523,23 @@ This will lead to inconsistency between prefixes used in the form definition (bi
logger.info(codeTimer.logLine("Creating FormDef from parsed XML"));
}

private String parseInstanceSrc(Element instance, String lastSavedSrc) {
String rawSrc = instance.getAttributeValue(null, "src");
String rawSrcLower = rawSrc == null ? null : rawSrc.toLowerCase();

if (rawSrc == null) {
// It's internal, so src is null.
return null;
} else if (rawSrcLower.startsWith("jr://file/") || rawSrcLower.startsWith("jr://file-csv/")) {
return rawSrc;
} else if (rawSrcLower.equals("jr://instance/last-saved")) {
return lastSavedSrc;
} else {
logger.warn("Invalid instance `src`: " + rawSrc);
return null;
}
}

private final Set<String> validElementNames = unmodifiableSet(new HashSet<>(asList(
"html",
"head",
Expand Down
12 changes: 11 additions & 1 deletion src/org/javarosa/xform/util/XFormUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ public static FormDef getFormRaw(InputStreamReader isr) throws XFormParseExcepti
* @throws XFormParseException if the form can’t be parsed
*/
public static FormDef getFormFromInputStream(InputStream is) throws XFormParseException {
return getFormFromInputStream(is, null);
}

/**
* @see #getFormFromInputStream(InputStream)
*
* @param lastSavedSrc The src of the last-saved instance of this form (for auto-filling). If null,
* no data will be loaded and the instance will be blank.
*/
public static FormDef getFormFromInputStream(InputStream is, String lastSavedSrc) throws XFormParseException {
InputStreamReader isr = null;
try {
try {
Expand All @@ -84,7 +94,7 @@ public static FormDef getFormFromInputStream(InputStream is) throws XFormParseEx
}

XFormParser xFormParser = _factory.getXFormParser(isr);
return xFormParser.parse();
return xFormParser.parse(lastSavedSrc);
} catch(IOException e) {
throw new XFormParseException("IO Exception during parse! " + e.getMessage());
} finally {
Expand Down
4 changes: 4 additions & 0 deletions test/org/javarosa/xform/parse/FormParserHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@ private FormParserHelper() {
public static FormDef parse(Path formName) throws IOException {
return XFormUtils.getFormFromInputStream(new FileInputStream(formName.toString()));
}

public static FormDef parse(Path formName, String lastSavedSrc) throws IOException {
return XFormUtils.getFormFromInputStream(new FileInputStream(formName.toString()), lastSavedSrc);
}
}
29 changes: 29 additions & 0 deletions test/org/javarosa/xform/parse/XFormParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.core.model.QuestionDef;
import org.javarosa.core.reference.ReferenceManager;
import org.javarosa.core.reference.ReferenceManagerTestUtils;
import org.javarosa.core.services.PrototypeManager;
import org.javarosa.core.services.transport.payload.ByteArrayPayload;
import org.javarosa.core.util.JavaRosaCoreModule;
Expand Down Expand Up @@ -126,6 +127,34 @@ public void parsesPreloadForm() throws IOException {
assertEquals("internal select 10", formDef.getTitle());
}

@Test public void parsesLastSavedInstanceWithNullSrc() throws IOException {
Path formName = r("last-saved-blank.xml");
FormDef formDef = parse(formName, null);
assertEquals("Form with last-saved instance (blank)", formDef.getTitle());

DataInstance lastSaved = formDef.getNonMainInstance("last-saved");
AbstractTreeElement root = lastSaved.getRoot();
assertEquals(0, root.getNumChildren());
}

@Test public void parsesLastSavedInstanceWithFilledForm() throws IOException {
Path formName = r("last-saved-blank.xml");
Path lastSavedSubmissionDirectory = r("last-saved-filled.xml").toAbsolutePath().getParent();
ReferenceManagerTestUtils.setUpSimpleReferenceManager("file", lastSavedSubmissionDirectory);
FormDef formDef = parse(formName, "jr://file/last-saved-filled.xml");
assertEquals("Form with last-saved instance (blank)", formDef.getTitle());

DataInstance lastSaved = formDef.getNonMainInstance("last-saved");
AbstractTreeElement root = lastSaved.getRoot();
AbstractTreeElement item = root
.getChild("head", 0)
.getChild("model", 0)
.getChild("instance", 0)
.getChild("data", 0)
.getChild("item", 0);
assertEquals("Foo", item.getValue().getDisplayText());
}

@Test public void parsesExternalSecondaryInstanceForm() throws IOException, XPathSyntaxException {
Path formName = EXTERNAL_SECONDARY_INSTANCE_XML;
mapFileToResourcePath(formName);
Expand Down