Skip to content
This repository has been archived by the owner on Apr 16, 2022. It is now read-only.

Commit

Permalink
Merge pull request #879 from lognaturel/external-instances
Browse files Browse the repository at this point in the history
  • Loading branch information
lognaturel authored Oct 27, 2020
2 parents 59d0578 + 2845985 commit 98fb98e
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

package org.opendatakit.aggregate.parser;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.StringReader;
Expand All @@ -32,6 +33,7 @@
import org.javarosa.core.model.instance.FormInstance;
import org.javarosa.core.model.instance.TreeElement;
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.core.reference.ReferenceManager;
import org.javarosa.core.services.PrototypeManager;
import org.javarosa.model.xform.XFormsModule;
import org.javarosa.xform.parse.XFormParser;
Expand Down Expand Up @@ -342,7 +344,7 @@ private String extractBase64FieldEncryptionKey(TreeElement submissionElement) {
*
* @throws ODKIncompleteSubmissionData
*/
protected BaseFormParserForJavaRosa(String existingXml, String existingTitle, boolean allowLegacy)
protected BaseFormParserForJavaRosa(String existingXml, String existingTitle, File mediaDirectory, boolean allowLegacy)
throws ODKIncompleteSubmissionData {
if (existingXml == null) {
throw new ODKIncompleteSubmissionData(Reason.MISSING_XML);
Expand All @@ -351,6 +353,8 @@ protected BaseFormParserForJavaRosa(String existingXml, String existingTitle, bo
xml = existingXml;

initializeJavaRosa();
ReferenceManager.instance().reset();
ReferenceManager.instance().addReferenceFactory(new MediaFileReferenceFactory(mediaDirectory));

XFormParserWithBindEnhancements xfp = parseFormDefinition(xml, this);
try {
Expand Down Expand Up @@ -568,8 +572,8 @@ private List<Element> getBindingsForTreeElement(TreeElement treeElement) {
* encryption.
* @throws ODKIncompleteSubmissionData
*/
public static DifferenceResult compareXml(BaseFormParserForJavaRosa incomingParser,
String existingXml, String existingTitle, boolean isWithinUpdateWindow)
public static DifferenceResult compareXml(BaseFormParserForJavaRosa incomingParser, String existingXml,
String existingTitle, File mediaDirectory, boolean isWithinUpdateWindow)
throws ODKIncompleteSubmissionData {
if (incomingParser == null || existingXml == null) {
throw new ODKIncompleteSubmissionData(Reason.MISSING_XML);
Expand All @@ -584,7 +588,7 @@ public static DifferenceResult compareXml(BaseFormParserForJavaRosa incomingPars
FormDef formDef1;
FormDef formDef2;
BaseFormParserForJavaRosa existingParser = new BaseFormParserForJavaRosa(existingXml,
existingTitle, true);
existingTitle, mediaDirectory, true);
formDef1 = incomingParser.rootJavaRosaFormDef;
formDef2 = existingParser.rootJavaRosaFormDef;
if (formDef1 == null || formDef2 == null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.opendatakit.aggregate.parser;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.javarosa.core.reference.InvalidReferenceException;
import org.javarosa.core.reference.Reference;
import org.javarosa.core.reference.ReferenceFactory;

/**
* ReferenceFactory implementation that resolves any jr:// URI to the specified media folder. Most methods are
* unimplemented and throw UnsupportedOperationException if used.
*/
@SuppressWarnings("checkstyle:ParameterName")
public class MediaFileReferenceFactory implements ReferenceFactory {
private final File mediaDirectory;

public MediaFileReferenceFactory(File mediaDirectory) {
this.mediaDirectory = mediaDirectory;
}

@Override
public boolean derives(String URI) {
return true;
}

@Override
public Reference derive(String URI) throws InvalidReferenceException {
return new Reference() {
@Override
public String getLocalURI() {
return mediaDirectory.getAbsolutePath() + URI.substring(URI.lastIndexOf('/'));
}

@Override
public boolean doesBinaryExist() throws IOException {
throw new UnsupportedOperationException();
}

@Override
public InputStream getStream() throws IOException {
throw new UnsupportedOperationException();
}

@Override
public String getURI() {
throw new UnsupportedOperationException();
}

@Override
public boolean isReadOnly() {
return false;
}

@Override
public OutputStream getOutputStream() throws IOException {
throw new UnsupportedOperationException();
}

@Override
public void remove() throws IOException {
throw new UnsupportedOperationException();
}

@Override
public Reference[] probeAlternativeReferences() {
throw new UnsupportedOperationException();
}
};
}

@Override
public Reference derive(String URI, String context) throws InvalidReferenceException {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.opendatakit.briefcase.model;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.opendatakit.briefcase.util.FileSystemUtils.getMediaDirectory;

import java.io.BufferedReader;
import java.io.File;
Expand Down Expand Up @@ -160,7 +161,8 @@ public static BriefcaseFormDefinition resolveAgainstBriefcaseDefn(File tmpFormFi
// newDefn is considered identical to what we have locally...
result = DifferenceResult.XFORMS_IDENTICAL;
} else {
result = JavaRosaParserWrapper.compareXml(newDefn, existingXml, existingTitle, true);
result = JavaRosaParserWrapper.compareXml(newDefn, existingXml, existingTitle,
getMediaDirectory(briefcaseFormDirectory), true);
}

if (result == DifferenceResult.XFORMS_DIFFERENT) {
Expand All @@ -169,6 +171,7 @@ public static BriefcaseFormDefinition resolveAgainstBriefcaseDefn(File tmpFormFi
newDefn,
revisedXml,
Objects.requireNonNull(revisedDefn).getFormName(),
getMediaDirectory(briefcaseFormDirectory),
true
);
if (result == DifferenceResult.XFORMS_DIFFERENT) {
Expand Down
11 changes: 3 additions & 8 deletions src/org/opendatakit/briefcase/util/FileSystemUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -164,14 +164,9 @@ public static File getFormDefinitionFile(File formDirectory) throws FileSystemEx
return new File(formDirectory, formDirectory.getName() + ".xml");
}

static File getMediaDirectory(File formDirectory)
throws FileSystemException {
File mediaDir = new File(formDirectory, formDirectory.getName() + "-media");
if (!mediaDir.exists() && !mediaDir.mkdirs()) {
throw new FileSystemException("unable to create directory: " + mediaDir.getAbsolutePath());
}

return mediaDir;
// May or may not actually exist on disk depending on whether the form definition has attached media.
public static File getMediaDirectory(File formDirectory) throws FileSystemException {
return new File(formDirectory, formDirectory.getName() + "-media");
}

static File getFormInstancesDirectory(File formDirectory) throws FileSystemException {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.opendatakit.briefcase.util;

import static org.opendatakit.briefcase.util.FileSystemUtils.getMediaDirectory;

import java.io.File;
import org.javarosa.core.model.instance.TreeElement;
import org.opendatakit.aggregate.exception.ODKIncompleteSubmissionData;
Expand All @@ -10,7 +12,7 @@ public class JavaRosaParserWrapper extends BaseFormParserForJavaRosa {
private final File formDefinitionFile;

public JavaRosaParserWrapper(File formDefinitionFile, String inputXml) throws ODKIncompleteSubmissionData {
super(inputXml, null, true);
super(inputXml, null, getMediaDirectory(formDefinitionFile.getParentFile()), true);
this.formDefinitionFile = formDefinitionFile;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package org.opendatakit.briefcase.model;

import static java.nio.file.Files.delete;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.opendatakit.briefcase.util.FileSystemUtils.getMediaDirectory;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.opendatakit.briefcase.util.BadFormDefinition;

public class BriefcaseFormDefinitionWithExternalDataTest {
private Path formDir;
private Path formFile;
private Path mediaDir;
private Path mediaFile;

@Before
public void setUp() {
try {
formDir = Files.createTempDirectory("briefcase_test");
formFile = formDir.resolve("form.xml");
Path sourceFile = Paths.get(BriefcaseFormDefinitionWithExternalDataTest.class.getResource("form-with-external-secondary-instance.xml").toURI());
Files.copy(sourceFile, formFile);

mediaDir = getMediaDirectory(formDir.toFile()).toPath();
Files.createDirectories(mediaDir);
mediaFile = mediaDir.resolve("external-xml.xml");
Path sourceMedia = Paths.get(BriefcaseFormDefinitionWithExternalDataTest.class.getResource("external-xml.xml").toURI());
Files.copy(sourceMedia, mediaFile);
} catch (Throwable t) {
throw new RuntimeException(t);
}
}

@Test
public void buildsFormDef_whenDefinitionReferencesExternalSecondaryInstance() throws BadFormDefinition {
BriefcaseFormDefinition briefcaseFormDefinition = new BriefcaseFormDefinition(formDir.toFile(), formFile.toFile());

assertThat(briefcaseFormDefinition.getFormName(), is("Form with external secondary instance"));
}

@After
public void tearDown() throws IOException {
delete(formFile);
delete(mediaFile);
delete(mediaDir);
delete(formDir);
}
}
14 changes: 14 additions & 0 deletions test/resources/org/opendatakit/briefcase/model/external-xml.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<root>
<item>
<label>A</label>
<name>a</name>
</item>
<item>
<label>B</label>
<name>b</name>
</item>
<item>
<label>C</label>
<name>c</name>
</item>
</root>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<h:html xmlns="http://www.w3.org/2002/xforms"
xmlns:h="http://www.w3.org/1999/xhtml">

<h:head>
<h:title>Form with external secondary instance</h:title>
<model>
<instance>
<data id="external-instance">
<first_item_label/>
</data>
</instance>

<instance id="external-xml" src="jr://file/external-xml.xml"/>

<bind nodeset="/data/first_item_label" calculate="instance('external-xml')/item[name = 'a']/label"/>
</model>
</h:head>

<h:body>
</h:body>

</h:html>

0 comments on commit 98fb98e

Please sign in to comment.