use of org.javarosa.core.model.instance.TreeElement in project briefcase by opendatakit.
the class BaseFormParserForJavaRosa method getBindingsForTreeElement.
/**
* Get all recorded bindings for a given TreeElement
*
* @param treeElement
* @return
*/
private List<Element> getBindingsForTreeElement(TreeElement treeElement) {
List<Element> l = new ArrayList<>();
String nodeset = "/" + getTreeElementPath(treeElement);
for (int i = 0; i < this.bindElements.size(); i++) {
Element element = this.bindElements.get(i);
if (element.getAttributeValue("", NODESET_ATTR).equalsIgnoreCase(nodeset)) {
l.add(element);
}
}
return (l);
}
use of org.javarosa.core.model.instance.TreeElement in project briefcase by opendatakit.
the class BaseFormParserForJavaRosa method extractBase64FieldEncryptionKey.
/**
* Field-level encryption support. Forms with field-level encryption must have
* a meta block with a BASE64_ENCRYPTED_FIELD_KEY entry.
*
* @return base64EncryptedFieldRsaPublicKey string if field encryption is
* present.
*/
private String extractBase64FieldEncryptionKey(TreeElement submissionElement) {
TreeElement meta = findDepthFirst(submissionElement, "meta");
if (meta != null) {
List<TreeElement> l;
// Save the base64 RSA-Encrypted symmetric encryption key
// we are using for field encryption.
// Do not encrypt the form if we can't save this encrypted key...
l = meta.getChildrenWithName(BASE64_ENCRYPTED_FIELD_KEY);
if (l.size() == 1) {
TreeElement ek = l.get(0);
String base64EncryptedFieldRsaPublicKey = getBindAttribute(ek, BASE64_RSA_PUBLIC_KEY);
if (base64EncryptedFieldRsaPublicKey != null && base64EncryptedFieldRsaPublicKey.trim().length() == 0) {
base64EncryptedFieldRsaPublicKey = null;
}
return base64EncryptedFieldRsaPublicKey;
}
}
return null;
}
use of org.javarosa.core.model.instance.TreeElement in project briefcase by opendatakit.
the class BaseFormParserForJavaRosa method compareTreeElements.
/**
* Compare two parsed TreeElements to assess their level of structural
* difference (if any).
*
* @param treeElement1
* @param treeElement2
* @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.
*/
public static DifferenceResult compareTreeElements(TreeElement treeElement1, BaseFormParserForJavaRosa parser1, TreeElement treeElement2, BaseFormParserForJavaRosa parser2) {
boolean smalldiff = false;
boolean bigdiff = false;
// compare names
if (!treeElement1.getName().equals(treeElement2.getName())) {
bigdiff = true;
}
// treeElement1)
for (int i = 0; i < treeElement1.getAttributeCount(); i++) {
String attributeNamespace = treeElement1.getAttributeNamespace(i);
if (attributeNamespace != null && attributeNamespace.length() == 0) {
attributeNamespace = null;
}
String attributeName = treeElement1.getAttributeName(i);
String fullAttributeName = (attributeNamespace == null ? attributeName : attributeNamespace + ":" + attributeName);
// see if there's a difference in this attribute
if (!treeElement1.getAttributeValue(i).equals(treeElement2.getAttributeValue(attributeNamespace, attributeName))) {
// here, changes are ALLOWED by default, unless to a listed attribute
if (!NonchangeableInstanceAttributes.contains(fullAttributeName.toLowerCase())) {
smalldiff = true;
} else {
bigdiff = true;
}
}
}
// check core instance attributes only in treeElement2
for (int i = 0; i < treeElement2.getAttributeCount(); i++) {
String attributeNamespace = treeElement2.getAttributeNamespace(i);
if (attributeNamespace != null && attributeNamespace.length() == 0) {
attributeNamespace = null;
}
String attributeName = treeElement2.getAttributeName(i);
String fullAttributeName = (attributeNamespace == null ? attributeName : attributeNamespace + ":" + attributeName);
// see if this is an attribute only in treeElement2
if (treeElement1.getAttributeValue(attributeNamespace, attributeName) == null) {
// here, changes are ALLOWED by default, unless to a listed attribute
if (!NonchangeableInstanceAttributes.contains(fullAttributeName.toLowerCase())) {
smalldiff = true;
} else {
bigdiff = true;
}
}
}
// note attributes don't actually include bindings; thus, check raw bindings
// also one-by-one (starting with bindings1)
List<Element> bindings1 = parser1.getBindingsForTreeElement(treeElement1);
List<Element> bindings2 = parser2.getBindingsForTreeElement(treeElement2);
for (int i = 0; i < bindings1.size(); i++) {
Element binding = bindings1.get(i);
for (int j = 0; j < binding.getAttributeCount(); j++) {
String attributeNamespace = binding.getAttributeNamespace(j);
if (attributeNamespace != null && attributeNamespace.length() == 0) {
attributeNamespace = null;
}
String attributeName = binding.getAttributeName(j);
String fullAttributeName = (attributeNamespace == null ? attributeName : attributeNamespace + ":" + attributeName);
if (!fullAttributeName.equalsIgnoreCase(NODESET_ATTR)) {
// see if there's a difference in this attribute
String value1 = binding.getAttributeValue(j);
String value2 = getBindingAttributeValue(bindings2, attributeNamespace, attributeName);
if (!value1.equals(value2)) {
if (fullAttributeName.toLowerCase().equals(TYPE_ATTR) && value1 != null && value2 != null && ((value1.toLowerCase().equals("string") && value2.toLowerCase().equals("select1")) || (value1.toLowerCase().equals("select1") && value2.toLowerCase().equals("string")))) {
// handle changes between string and select1 data types as special
// (allowable) case
smalldiff = true;
} else {
// attribute
if (ChangeableBindAttributes.contains(fullAttributeName.toLowerCase())) {
smalldiff = true;
} else {
bigdiff = true;
}
}
}
}
}
}
// check binding attributes only in bindings2
for (int i = 0; i < bindings2.size(); i++) {
Element binding = bindings2.get(i);
for (int j = 0; j < binding.getAttributeCount(); j++) {
String attributeNamespace = binding.getAttributeNamespace(j);
if (attributeNamespace != null && attributeNamespace.length() == 0) {
attributeNamespace = null;
}
String attributeName = binding.getAttributeName(j);
String fullAttributeName = (attributeNamespace == null ? attributeName : attributeNamespace + ":" + attributeName);
if (!fullAttributeName.equalsIgnoreCase(NODESET_ATTR)) {
// see if this is an attribute only in bindings2
if (getBindingAttributeValue(bindings1, attributeNamespace, attributeName) == null) {
// attribute
if (ChangeableBindAttributes.contains(fullAttributeName.toLowerCase())) {
smalldiff = true;
} else {
bigdiff = true;
}
}
}
}
}
// Issue 786 -- we need to handle repeat groups.
// If we have a repeat without a jr:template="" attribute, then
// that element appears as a index [0] repeat within the form
// definition and as an INDEX_TEMPLATE element (it is copied).
// Otherwise, if you have specified the jr:template attribute,
// it appears only as an INDEX_TEMPLATE element.
@SuppressWarnings("unused") int template1DropCount = 0;
// get non-template entries for treeElement1
List<TreeElement> element1ExcludingRepeatIndex0Children = new ArrayList<>();
for (int i = 0; i < treeElement1.getNumChildren(); i++) {
TreeElement child = treeElement1.getChildAt(i);
if (child.isRepeatable()) {
if (child.getMult() != TreeReference.INDEX_TEMPLATE) {
template1DropCount++;
log.debug("element1:dropping " + child.getName());
continue;
}
log.debug("element1:retaining " + child.getName());
}
element1ExcludingRepeatIndex0Children.add(child);
}
@SuppressWarnings("unused") int template2DropCount = 0;
// get non-template entries for treeElement2
Map<String, TreeElement> element2ExcludingRepeatIndex0Children = new HashMap<>();
for (int i = 0; i < treeElement2.getNumChildren(); i++) {
TreeElement child = treeElement2.getChildAt(i);
if (child.isRepeatable()) {
if (child.getMult() != TreeReference.INDEX_TEMPLATE) {
template2DropCount++;
log.debug("element2:dropping " + child.getName());
continue;
}
log.debug("element2:retaining " + child.getName());
}
if (element2ExcludingRepeatIndex0Children.get(child.getName()) != null) {
// consider children not uniquely named as big differences
bigdiff = true;
}
element2ExcludingRepeatIndex0Children.put(child.getName(), child);
}
// compare children
if (element1ExcludingRepeatIndex0Children.size() != element2ExcludingRepeatIndex0Children.size()) {
// consider differences in basic structure (e.g., number and grouping of
// fields) as big
bigdiff = true;
} else {
for (int i = 0; i < element1ExcludingRepeatIndex0Children.size(); i++) {
TreeElement childElement1 = element1ExcludingRepeatIndex0Children.get(i);
TreeElement childElement2 = element2ExcludingRepeatIndex0Children.get(childElement1.getName());
if (childElement2 != null) {
// recursively compare children...
switch(compareTreeElements(childElement1, parser1, childElement2, parser2)) {
case XFORMS_SHARE_SCHEMA:
smalldiff = true;
break;
case XFORMS_DIFFERENT:
bigdiff = true;
break;
default:
// no update for the other cases (IDENTICAL, EARLIER, MISSING, SHARE_INSTANCE)
break;
}
} else {
// consider children not found as big differences
bigdiff = true;
}
}
}
// return appropriate value
if (bigdiff) {
return (DifferenceResult.XFORMS_DIFFERENT);
} else if (smalldiff) {
return (DifferenceResult.XFORMS_SHARE_SCHEMA);
} else {
return (DifferenceResult.XFORMS_SHARE_INSTANCE);
}
}
use of org.javarosa.core.model.instance.TreeElement in project briefcase by opendatakit.
the class ExportToCsv method emitCsvHeaders.
private boolean emitCsvHeaders(OutputStreamWriter osw, TreeElement primarySet, TreeElement treeElement, boolean first) throws IOException {
// OK -- group with at least one element -- assume no value...
// TreeElement list has the begin and end tags for the nested groups.
// Swallow the end tag by looking to see if the prior and current
// field names are the same.
TreeElement prior = null;
for (int i = 0; i < treeElement.getNumChildren(); ++i) {
TreeElement current = (TreeElement) treeElement.getChildAt(i);
if ((prior != null) && (prior.getName().equals(current.getName()))) {
// it is the end-group tag... seems to happen with two adjacent repeat
// groups
log.info("repeating tag at " + i + " skipping " + current.getName());
prior = current;
} else {
switch(current.getDataType()) {
case org.javarosa.core.model.Constants.DATATYPE_TEXT:
/**
* Text question
* type.
*/
case org.javarosa.core.model.Constants.DATATYPE_INTEGER:
/**
* Numeric
* question type. These are numbers without decimal points
*/
case org.javarosa.core.model.Constants.DATATYPE_DECIMAL:
/**
* Decimal
* question type. These are numbers with decimals
*/
case org.javarosa.core.model.Constants.DATATYPE_DATE:
/**
* Date question
* type. This has only date component without time.
*/
case org.javarosa.core.model.Constants.DATATYPE_TIME:
/**
* Time question
* type. This has only time element without date
*/
case org.javarosa.core.model.Constants.DATATYPE_DATE_TIME:
/**
* Date and
* Time question type. This has both the date and time components
*/
case org.javarosa.core.model.Constants.DATATYPE_CHOICE:
/**
* This is a
* question with alist of options where not more than one option can
* be selected at a time.
*/
case org.javarosa.core.model.Constants.DATATYPE_CHOICE_LIST:
/**
* This is a
* question with alist of options where more than one option can be
* selected at a time.
*/
case org.javarosa.core.model.Constants.DATATYPE_BOOLEAN:
/**
* Question with
* true and false answers.
*/
case org.javarosa.core.model.Constants.DATATYPE_BARCODE:
/**
* Question with
* barcode string answer.
*/
case org.javarosa.core.model.Constants.DATATYPE_BINARY:
/**
* Question with
* external binary answer.
*/
default:
case org.javarosa.core.model.Constants.DATATYPE_UNSUPPORTED:
emitString(osw, first, getFullName(current, primarySet));
first = false;
break;
case org.javarosa.core.model.Constants.DATATYPE_GEOPOINT:
/**
* Question with location answer.
*/
emitString(osw, first, getFullName(current, primarySet) + "-Latitude");
emitString(osw, false, getFullName(current, primarySet) + "-Longitude");
emitString(osw, false, getFullName(current, primarySet) + "-Altitude");
emitString(osw, false, getFullName(current, primarySet) + "-Accuracy");
first = false;
break;
case org.javarosa.core.model.Constants.DATATYPE_NULL:
/*
* for nodes that
* have no data,
* or data type
* otherwise
* unknown
*/
if (current.isRepeatable()) {
// repeatable group...
emitString(osw, first, "SET-OF-" + getFullName(current, primarySet));
first = false;
processRepeatingGroupDefinition(current, primarySet, true);
} else if (current.getNumChildren() == 0 && current != briefcaseLfd.getSubmissionElement()) {
// assume fields that don't have children are string fields.
emitString(osw, first, getFullName(current, primarySet));
first = false;
} else {
/* one or more children -- this is a non-repeating group */
first = emitCsvHeaders(osw, primarySet, current, first);
}
break;
}
prior = current;
}
}
return first;
}
use of org.javarosa.core.model.instance.TreeElement in project briefcase by opendatakit.
the class ExportToCsv method emitSubmissionCsv.
private boolean emitSubmissionCsv(OutputStreamWriter osw, EncryptionInformation ei, Element submissionElement, TreeElement primarySet, TreeElement treeElement, boolean first, String uniquePath, File instanceDir) throws IOException {
// OK -- group with at least one element -- assume no value...
// TreeElement list has the begin and end tags for the nested groups.
// Swallow the end tag by looking to see if the prior and current
// field names are the same.
TreeElement prior = null;
for (int i = 0; i < treeElement.getNumChildren(); ++i) {
TreeElement current = (TreeElement) treeElement.getChildAt(i);
log.debug(" element name: " + current.getName());
if ((prior != null) && (prior.getName().equals(current.getName()))) {
// it is the end-group tag... seems to happen with two adjacent repeat
// groups
log.info("repeating tag at " + i + " skipping " + current.getName());
prior = current;
} else {
Element ec = findElement(submissionElement, current.getName());
switch(current.getDataType()) {
case org.javarosa.core.model.Constants.DATATYPE_TEXT:
/**
* Text question
* type.
*/
case org.javarosa.core.model.Constants.DATATYPE_INTEGER:
/**
* Numeric
* question type. These are numbers without decimal points
*/
case org.javarosa.core.model.Constants.DATATYPE_DECIMAL:
/**
* Decimal
* question type. These are numbers with decimals
*/
case org.javarosa.core.model.Constants.DATATYPE_CHOICE:
/**
* This is a
* question with alist of options where not more than one option can
* be selected at a time.
*/
case org.javarosa.core.model.Constants.DATATYPE_CHOICE_LIST:
/**
* This is a
* question with alist of options where more than one option can be
* selected at a time.
*/
case org.javarosa.core.model.Constants.DATATYPE_BOOLEAN:
/**
* Question with
* true and false answers.
*/
case org.javarosa.core.model.Constants.DATATYPE_BARCODE:
/**
* Question with
* barcode string answer.
*/
default:
case org.javarosa.core.model.Constants.DATATYPE_UNSUPPORTED:
if (ec == null) {
emitString(osw, first, null);
} else {
emitString(osw, first, getSubmissionValue(ei, current, ec));
}
first = false;
break;
case org.javarosa.core.model.Constants.DATATYPE_DATE:
/**
* Date question type. This has only date component without time.
*/
if (ec == null) {
emitString(osw, first, null);
} else {
String value = getSubmissionValue(ei, current, ec);
if (value == null || value.length() == 0) {
emitString(osw, first, null);
} else {
Date date = WebUtils.parseDate(value);
DateFormat formatter = DateFormat.getDateInstance();
emitString(osw, first, formatter.format(date));
}
}
first = false;
break;
case org.javarosa.core.model.Constants.DATATYPE_TIME:
/**
* Time question type. This has only time element without date
*/
if (ec == null) {
emitString(osw, first, null);
} else {
String value = getSubmissionValue(ei, current, ec);
if (value == null || value.length() == 0) {
emitString(osw, first, null);
} else {
Date date = WebUtils.parseDate(value);
DateFormat formatter = DateFormat.getTimeInstance();
emitString(osw, first, formatter.format(date));
}
}
first = false;
break;
case org.javarosa.core.model.Constants.DATATYPE_DATE_TIME:
/**
* Date and Time question type. This has both the date and time
* components
*/
if (ec == null) {
emitString(osw, first, null);
} else {
String value = getSubmissionValue(ei, current, ec);
if (value == null || value.length() == 0) {
emitString(osw, first, null);
} else {
Date date = WebUtils.parseDate(value);
DateFormat formatter = DateFormat.getDateTimeInstance();
emitString(osw, first, formatter.format(date));
}
}
first = false;
break;
case org.javarosa.core.model.Constants.DATATYPE_GEOPOINT:
/**
* Question with location answer.
*/
String compositeValue = (ec == null) ? null : getSubmissionValue(ei, current, ec);
compositeValue = (compositeValue == null) ? null : compositeValue.trim();
// emit separate lat, long, alt, acc columns...
if (compositeValue == null || compositeValue.length() == 0) {
for (int count = 0; count < 4; ++count) {
emitString(osw, first, null);
first = false;
}
} else {
String[] values = compositeValue.split(" ");
for (String value : values) {
emitString(osw, first, value);
first = false;
}
for (int count = values.length; count < 4; ++count) {
emitString(osw, first, null);
first = false;
}
}
break;
case org.javarosa.core.model.Constants.DATATYPE_BINARY:
/**
* Question with external binary answer.
*/
String binaryFilename = getSubmissionValue(ei, current, ec);
if (binaryFilename == null || binaryFilename.length() == 0) {
emitString(osw, first, null);
first = false;
} else {
if (exportMedia) {
if (!outputMediaDir.exists()) {
if (!outputMediaDir.mkdir()) {
EventBus.publish(new ExportProgressEvent("Unable to create destination media directory", briefcaseLfd));
return false;
}
}
int dotIndex = binaryFilename.lastIndexOf(".");
String namePart = (dotIndex == -1) ? binaryFilename : binaryFilename.substring(0, dotIndex);
String extPart = (dotIndex == -1) ? "" : binaryFilename.substring(dotIndex);
File binaryFile = new File(instanceDir, binaryFilename);
String destBinaryFilename = binaryFilename;
int version = 1;
File destFile = new File(outputMediaDir, destBinaryFilename);
boolean exists = false;
String binaryFileHash = null;
String destFileHash = null;
if (destFile.exists() && binaryFile.exists()) {
binaryFileHash = FileSystemUtils.getMd5Hash(binaryFile);
while (destFile.exists()) {
if (fileHashMap.containsKey(destFile.getName())) {
destFileHash = fileHashMap.get(destFile.getName());
} else {
destFileHash = FileSystemUtils.getMd5Hash(destFile);
if (destFileHash != null) {
fileHashMap.put(destFile.getName(), destFileHash);
}
}
if (binaryFileHash != null && destFileHash != null && destFileHash.equals(binaryFileHash)) {
exists = true;
break;
}
destBinaryFilename = namePart + "-" + (++version) + extPart;
destFile = new File(outputMediaDir, destBinaryFilename);
}
}
if (binaryFile.exists() && exists == false) {
FileUtils.copyFile(binaryFile, destFile);
}
emitString(osw, first, MEDIA_DIR + File.separator + destFile.getName());
} else {
emitString(osw, first, binaryFilename);
}
first = false;
}
break;
case org.javarosa.core.model.Constants.DATATYPE_NULL:
/*
* for nodes that
* have no data,
* or data type
* otherwise
* unknown
*/
if (current.isRepeatable()) {
if (prior == null || !current.getName().equals(prior.getName())) {
// repeatable group...
if (ec == null) {
emitString(osw, first, null);
first = false;
} else {
String uniqueGroupPath = uniquePath + "/" + getFullName(current, primarySet);
emitString(osw, first, uniqueGroupPath);
first = false;
// first time processing this repeat group (ignore templates)
List<Element> ecl = findElementList(submissionElement, current.getName());
emitRepeatingGroupCsv(ei, ecl, current, uniquePath, uniqueGroupPath, instanceDir);
}
}
} else if (current.getNumChildren() == 0 && current != briefcaseLfd.getSubmissionElement()) {
// assume fields that don't have children are string fields.
if (ec == null) {
emitString(osw, first, null);
first = false;
} else {
emitString(osw, first, getSubmissionValue(ei, current, ec));
first = false;
}
} else {
/* one or more children -- this is a non-repeating group */
first = emitSubmissionCsv(osw, ei, ec, primarySet, current, first, uniquePath, instanceDir);
}
break;
}
prior = current;
}
}
return first;
}
Aggregations