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

Commit

Permalink
Resolve all jr:// URIs to media directory
Browse files Browse the repository at this point in the history
Needed to build a FormDef for files with external secondary instances.
  • Loading branch information
lognaturel committed Oct 12, 2020
1 parent b55a171 commit 52c586d
Show file tree
Hide file tree
Showing 11 changed files with 217 additions and 23 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,7 @@ protected BaseFormParserForJavaRosa(String existingXml, String existingTitle, bo
xml = existingXml;

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

XFormParserWithBindEnhancements xfp = parseFormDefinition(xml, this);
try {
Expand Down Expand Up @@ -568,8 +571,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 +587,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
Original file line number Diff line number Diff line change
Expand Up @@ -372,9 +372,15 @@ void unsetTestingConnection() {
private Font $$$getFont$$$(String fontName, int style, int size, Font currentFont) {
if (currentFont == null) return null;
String resultName;
if (fontName == null) {resultName = currentFont.getName();} else {
if (fontName == null) {
resultName = currentFont.getName();
} else {
Font testFont = new Font(fontName, Font.PLAIN, 10);
if (testFont.canDisplay('a') && testFont.canDisplay('1')) {resultName = fontName;} else {resultName = currentFont.getName();}
if (testFont.canDisplay('a') && testFont.canDisplay('1')) {
resultName = fontName;
} else {
resultName = currentFont.getName();
}
}
return new Font(resultName, style >= 0 ? style : currentFont.getStyle(), size >= 0 ? size : currentFont.getSize());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,16 +392,24 @@ void unsetTestingConnection() {
private Font $$$getFont$$$(String fontName, int style, int size, Font currentFont) {
if (currentFont == null) return null;
String resultName;
if (fontName == null) {resultName = currentFont.getName();} else {
if (fontName == null) {
resultName = currentFont.getName();
} else {
Font testFont = new Font(fontName, Font.PLAIN, 10);
if (testFont.canDisplay('a') && testFont.canDisplay('1')) {resultName = fontName;} else {resultName = currentFont.getName();}
if (testFont.canDisplay('a') && testFont.canDisplay('1')) {
resultName = fontName;
} else {
resultName = currentFont.getName();
}
}
return new Font(resultName, style >= 0 ? style : currentFont.getStyle(), size >= 0 ? size : currentFont.getSize());
}

/**
* @noinspection ALL
*/
public JComponent $$$getRootComponent$$$() { return dialog; }
public JComponent $$$getRootComponent$$$() {
return dialog;
}

}
17 changes: 13 additions & 4 deletions src/org/opendatakit/briefcase/ui/settings/SettingsPanelForm.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
import org.apache.http.HttpHost;
import org.opendatakit.briefcase.model.BriefcasePreferences;
import org.opendatakit.briefcase.reused.OptionalProduct;
Expand Down Expand Up @@ -348,7 +349,7 @@ private void createUIComponents() {
gbc.gridwidth = 5;
gbc.fill = GridBagConstraints.BOTH;
container.add(panel1, gbc);
panel1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Troubleshooting"));
panel1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Troubleshooting", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, null, null));
reloadCacheButton = new JButton();
reloadCacheButton.setEnabled(false);
reloadCacheButton.setText("Reload forms from storage location");
Expand Down Expand Up @@ -607,16 +608,24 @@ private void createUIComponents() {
private Font $$$getFont$$$(String fontName, int style, int size, Font currentFont) {
if (currentFont == null) return null;
String resultName;
if (fontName == null) {resultName = currentFont.getName();} else {
if (fontName == null) {
resultName = currentFont.getName();
} else {
Font testFont = new Font(fontName, Font.PLAIN, 10);
if (testFont.canDisplay('a') && testFont.canDisplay('1')) {resultName = fontName;} else {resultName = currentFont.getName();}
if (testFont.canDisplay('a') && testFont.canDisplay('1')) {
resultName = fontName;
} else {
resultName = currentFont.getName();
}
}
return new Font(resultName, style >= 0 ? style : currentFont.getStyle(), size >= 0 ? size : currentFont.getSize());
}

/**
* @noinspection ALL
*/
public JComponent $$$getRootComponent$$$() { return container; }
public JComponent $$$getRootComponent$$$() {
return container;
}

}
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 = formDir.resolve(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 52c586d

Please sign in to comment.