use of org.opendatakit.aggregate.exception.ODKIncompleteSubmissionData in project briefcase by opendatakit.
the class BriefcaseFormDefinition method resolveAgainstBriefcaseDefn.
public static final BriefcaseFormDefinition resolveAgainstBriefcaseDefn(File tmpFormFile, boolean copyFile) throws BadFormDefinition {
if (!tmpFormFile.exists()) {
throw new BadFormDefinition("Form directory does not contain form");
}
// parse the temp file into a form definition...
boolean badForm = false;
JavaRosaParserWrapper newDefn;
File briefcaseFormDirectory;
File briefcaseFormFile;
try {
newDefn = new JavaRosaParserWrapper(tmpFormFile, readFile(tmpFormFile));
briefcaseFormDirectory = FileSystemUtils.getFormDirectory(newDefn.getFormName());
briefcaseFormFile = FileSystemUtils.getFormDefinitionFile(briefcaseFormDirectory);
} catch (ODKIncompleteSubmissionData e) {
log.warn("bad form definition", e);
try {
badForm = true;
newDefn = null;
briefcaseFormDirectory = FileSystemUtils.getFormDirectory("_badForm");
briefcaseFormFile = FileSystemUtils.getFormDefinitionFile(briefcaseFormDirectory);
} catch (FileSystemException ex) {
log.error("failed to establish storage location for bad form", e);
throw new BadFormDefinition(ex);
}
} catch (FileSystemException e) {
log.error("failed to establish storage location for form", e);
throw new BadFormDefinition(e);
}
boolean isIdentical = false;
boolean needsMediaUpdate = false;
File revised = new File(briefcaseFormFile.getParentFile(), briefcaseFormFile.getName() + ".revised");
String revisedXml = null;
JavaRosaParserWrapper revisedDefn = null;
// determine the most up-to-date existing definition...
JavaRosaParserWrapper existingDefn;
try {
if (revised.exists()) {
revisedXml = readFile(revised);
revisedDefn = new JavaRosaParserWrapper(revised, revisedXml);
}
if (!briefcaseFormFile.exists()) {
// Rename it to formFile and parse it.
if (copyFile) {
try {
FileUtils.copyFile(tmpFormFile, briefcaseFormFile);
} catch (IOException e) {
String msg = "Unable to copy form definition file into briefcase directory";
log.error(msg, e);
throw new BadFormDefinition(msg);
}
} else {
if (!tmpFormFile.renameTo(briefcaseFormFile)) {
// if cannot rename, try to copy instead (and mark for deletion)
try {
FileUtils.copyFile(tmpFormFile, briefcaseFormFile);
tmpFormFile.deleteOnExit();
} catch (IOException e) {
String msg = "Form directory does not contain form (can neither rename nor copy into briefcase directory)";
log.error(msg);
throw new BadFormDefinition(msg);
}
}
}
// weird if it does...
needsMediaUpdate = !revised.exists();
existingDefn = new JavaRosaParserWrapper(briefcaseFormFile, readFile(briefcaseFormFile));
} else {
// get the current existing definition...
String existingXml = readFile(briefcaseFormFile);
existingDefn = new JavaRosaParserWrapper(briefcaseFormFile, existingXml);
String existingTitle = existingDefn.getFormName();
// compare the two
DifferenceResult result;
if (badForm) {
// newDefn is considered identical to what we have locally...
result = DifferenceResult.XFORMS_IDENTICAL;
} else {
result = JavaRosaParserWrapper.compareXml(newDefn, existingXml, existingTitle, true);
}
if (result == DifferenceResult.XFORMS_DIFFERENT) {
if (revised.exists()) {
result = JavaRosaParserWrapper.compareXml(newDefn, revisedXml, revisedDefn.getFormName(), true);
if (result == DifferenceResult.XFORMS_DIFFERENT) {
throw new BadFormDefinition("Form definitions are incompatible.");
} else if (result != DifferenceResult.XFORMS_EARLIER_VERSION && result != DifferenceResult.XFORMS_MISSING_VERSION && result != DifferenceResult.XFORMS_IDENTICAL) {
if (copyFile) {
try {
FileUtils.copyFile(tmpFormFile, revised);
} catch (IOException e) {
String msg = "Unable to overwrite the '.revised' form definition file in briefcase storage";
log.error(msg, e);
throw new BadFormDefinition(msg);
}
} else {
if (!tmpFormFile.renameTo(revised)) {
// if cannot rename, try to copy instead (and mark for deletion)
try {
FileUtils.copyFile(tmpFormFile, revised);
tmpFormFile.deleteOnExit();
} catch (IOException e) {
String msg = "Form directory does not contain form (can neither rename nor copy into briefcase directory)";
log.error(msg, e);
throw new BadFormDefinition(msg);
}
}
}
needsMediaUpdate = true;
// and re-parse the new revised file (since we just updated it...)
revisedDefn = new JavaRosaParserWrapper(revised, readFile(revised));
} else if (result == DifferenceResult.XFORMS_IDENTICAL) {
// confirm that the media is up-to-date when the forms are
// identical
// allows briefcase to resume a form download when it failed
// during
// the early form-media-fetch phases.
isIdentical = true;
needsMediaUpdate = true;
}
} else {
throw new BadFormDefinition("Form definitions are incompatible.");
}
} else if (result != DifferenceResult.XFORMS_EARLIER_VERSION && result != DifferenceResult.XFORMS_MISSING_VERSION && result != DifferenceResult.XFORMS_IDENTICAL) {
if (!revised.exists()) {
// overwrite everything and re-parse the new file.
if (copyFile) {
try {
FileUtils.copyFile(tmpFormFile, briefcaseFormFile);
} catch (IOException e) {
String msg = "Unable to overwrite form definition file in briefcase storage";
log.error(msg, e);
throw new BadFormDefinition(msg);
}
} else {
if (!tmpFormFile.renameTo(briefcaseFormFile)) {
// if cannot rename, try to copy instead (and mark for deletion)
try {
FileUtils.copyFile(tmpFormFile, briefcaseFormFile);
tmpFormFile.deleteOnExit();
} catch (IOException e) {
String msg = "Form directory does not contain form (can neither rename nor copy into briefcase directory)";
log.error(msg, e);
throw new BadFormDefinition(msg);
}
}
}
needsMediaUpdate = true;
// and re-parse the new form file (since we just updated it...)
existingXml = readFile(briefcaseFormFile);
existingDefn = new JavaRosaParserWrapper(briefcaseFormFile, existingXml);
}
} else if (result == DifferenceResult.XFORMS_IDENTICAL) {
// if a revised form exists, we assume the media is up-to-date in that
// folder. Otherwise, confirm that the media is up-to-date when the
// forms are identical. This allows briefcase to resume a form
// download
// when it failed during the early form-media-fetch phases.
isIdentical = true;
needsMediaUpdate = !revised.exists();
}
}
} catch (ODKIncompleteSubmissionData e) {
throw new BadFormDefinition(e, e.getReason());
}
BriefcaseFormDefinition defn;
if (revised.exists()) {
defn = new BriefcaseFormDefinition(briefcaseFormDirectory, revisedDefn, revised, needsMediaUpdate);
} else {
defn = new BriefcaseFormDefinition(briefcaseFormDirectory, existingDefn, null, needsMediaUpdate);
}
if (!isIdentical && needsMediaUpdate) {
EventBus.publish(new UpdatedBriefcaseFormDefinitionEvent(defn));
}
return defn;
}
use of org.opendatakit.aggregate.exception.ODKIncompleteSubmissionData in project briefcase by opendatakit.
the class BaseFormParserForJavaRosa method parseFormDefinition.
private static final synchronized XFormParserWithBindEnhancements parseFormDefinition(String xml, BaseFormParserForJavaRosa parser) throws ODKIncompleteSubmissionData {
StringReader isr = null;
try {
isr = new StringReader(xml);
Document doc = XFormParser.getXMLDocument(isr);
return new XFormParserWithBindEnhancements(parser, doc);
} catch (Exception e) {
throw new ODKIncompleteSubmissionData(e, Reason.BAD_JR_PARSE);
} finally {
isr.close();
}
}
use of org.opendatakit.aggregate.exception.ODKIncompleteSubmissionData in project briefcase by opendatakit.
the class BaseFormParserForJavaRosa method compareXml.
/**
* Compare two XML files to assess their level of structural difference (if
* any).
*
* @param incomingParser -- parsed version of incoming form
* @param existingXml -- the existing Xml for this form
* @return XFORMS_SHARE_INSTANCE when bodies differ but instances and bindings
* are identical; XFORMS_SHARE_SCHEMA when bodies and/or bindings
* differ, but database structure remains unchanged; XFORMS_DIFFERENT
* when forms are different enough to affect database structure and/or
* encryption.
* @throws ODKIncompleteSubmissionData
*/
public static DifferenceResult compareXml(BaseFormParserForJavaRosa incomingParser, String existingXml, String existingTitle, boolean isWithinUpdateWindow) throws ODKIncompleteSubmissionData {
if (incomingParser == null || existingXml == null) {
throw new ODKIncompleteSubmissionData(Reason.MISSING_XML);
}
// generally only the case within Briefcase
if (incomingParser.xml.equals(existingXml)) {
return DifferenceResult.XFORMS_IDENTICAL;
}
// parse XML
FormDef formDef1;
FormDef formDef2;
BaseFormParserForJavaRosa existingParser = new BaseFormParserForJavaRosa(existingXml, existingTitle, true);
formDef1 = incomingParser.rootJavaRosaFormDef;
formDef2 = existingParser.rootJavaRosaFormDef;
if (formDef1 == null || formDef2 == null) {
throw new ODKIncompleteSubmissionData("Javarosa failed to construct a FormDef. Is this an XForm definition?", Reason.BAD_JR_PARSE);
}
// check that the version is advancing from the earlier
// form upload. The comparison is string-based, not
// numeric-based (OpenRosa compliance). The recommended
// version format is: yyyymmddnn e.g., 2012060100
String ivs = incomingParser.rootElementDefn.versionString;
if (ivs == null) {
// if we are changing the file, the new file must have a version string
return DifferenceResult.XFORMS_MISSING_VERSION;
}
String evs = existingParser.rootElementDefn.versionString;
boolean modelVersionSame = (incomingParser.rootElementDefn.modelVersion == null) ? (existingParser.rootElementDefn.modelVersion == null) : incomingParser.rootElementDefn.modelVersion.equals(existingParser.rootElementDefn.modelVersion);
boolean isEarlierVersion = false;
if (!(evs == null || (modelVersionSame && ivs.length() > evs.length()) || (!modelVersionSame && ivs.compareTo(evs) > 0))) {
// disallow updates if none of the following applies:
// (1) if the existing form does not have a version (the new one does).
// (2) if the existing form and new form have the same model version
// and the new form has more leading zeros.
// (3) if the existing form and new form have different model versions
// and the new version string is lexically greater than the old one.
isEarlierVersion = true;
return DifferenceResult.XFORMS_EARLIER_VERSION;
}
/*
* Changes in encryption (either on or off, or change in key) are a major
* change. We could allow the public key to be revised, but most users won't
* understand that this is possible or know how to do it.
*
* Ignore whether a submission profile is present or absent provided it does
* not affect encryption or change the portion of the form being returned.
*/
SubmissionProfile subProfile1 = formDef1.getSubmissionProfile();
SubmissionProfile subProfile2 = formDef2.getSubmissionProfile();
if (subProfile1 != null && subProfile2 != null) {
// we have two profiles -- check that any encryption key matches...
String publicKey1 = subProfile1.getAttribute(BASE64_RSA_PUBLIC_KEY);
String publicKey2 = subProfile2.getAttribute(BASE64_RSA_PUBLIC_KEY);
if (publicKey1 != null && publicKey2 != null) {
// both have encryption
if (!publicKey1.equals(publicKey2)) {
// keys differ
return (DifferenceResult.XFORMS_DIFFERENT);
}
} else if (publicKey1 != null || publicKey2 != null) {
// one or the other has encryption (and the other doesn't)...
return (DifferenceResult.XFORMS_DIFFERENT);
}
// get the TreeElement (e1, e2) that identifies the portion of the form
// that will be submitted to Aggregate.
IDataReference r;
r = subProfile1.getRef();
AbstractTreeElement<?> e1 = (r != null) ? formDef1.getInstance().resolveReference(r) : null;
r = subProfile2.getRef();
AbstractTreeElement<?> e2 = (r != null) ? formDef2.getInstance().resolveReference(r) : null;
if (e1 != null && e2 != null) {
// Ignore all namespace differences (Aggregate ignores them)...
while (e1 != null && e2 != null) {
if (!e1.getName().equals(e2.getName())) {
return (DifferenceResult.XFORMS_DIFFERENT);
}
e1 = e1.getParent();
e2 = e2.getParent();
}
if (e1 != null || e2 != null) {
// they should both terminate at the same time...
return (DifferenceResult.XFORMS_DIFFERENT);
}
// we may still have differences, but if the overall form
// is identical, we are golden...
} else if (e1 != null || e2 != null) {
// one returns a portion of the form and the other doesn't
return (DifferenceResult.XFORMS_DIFFERENT);
}
} else if (subProfile1 != null) {
if (subProfile1.getAttribute(BASE64_RSA_PUBLIC_KEY) != null) {
// xml1 does encryption, the other doesn't
return (DifferenceResult.XFORMS_DIFFERENT);
}
IDataReference r = subProfile1.getRef();
if (r != null && formDef1.getInstance().resolveReference(r) != null) {
// xml1 returns a portion of the form, the other doesn't
return (DifferenceResult.XFORMS_DIFFERENT);
}
} else if (subProfile2 != null) {
if (subProfile2.getAttribute(BASE64_RSA_PUBLIC_KEY) != null) {
// xml2 does encryption, the other doesn't
return (DifferenceResult.XFORMS_DIFFERENT);
}
IDataReference r = subProfile2.getRef();
if (r != null && formDef2.getInstance().resolveReference(r) != null) {
// xml2 returns a portion of the form, the other doesn't
return (DifferenceResult.XFORMS_DIFFERENT);
}
}
// get data model to compare instances
FormInstance dataModel1 = formDef1.getInstance();
FormInstance dataModel2 = formDef2.getInstance();
if (dataModel1 == null || dataModel2 == null) {
throw new ODKIncompleteSubmissionData("Javarosa failed to construct a FormInstance. Is this an XForm definition?", Reason.BAD_JR_PARSE);
}
// return result of element-by-element instance/binding comparison
DifferenceResult rc = compareTreeElements(dataModel1.getRoot(), incomingParser, dataModel2.getRoot(), existingParser);
if (DifferenceResult.XFORMS_DIFFERENT == rc) {
return rc;
} else if (isEarlierVersion) {
return DifferenceResult.XFORMS_EARLIER_VERSION;
} else {
return rc;
}
}
Aggregations