X-Git-Url: http://git.argeo.org/?a=blobdiff_plain;f=org.argeo.app.jcr%2Fsrc%2Forg%2Fargeo%2Fapp%2Fjcr%2Fodk%2FOdkJcrUtils.java;fp=org.argeo.app.jcr%2Fsrc%2Forg%2Fargeo%2Fapp%2Fjcr%2Fodk%2FOdkJcrUtils.java;h=881523f42f927293f1c5a9b36fb492af4669dd12;hb=d6c9d33b61e475914d3f8d7534374ed30eca8150;hp=0000000000000000000000000000000000000000;hpb=618968cf9d259ccded45a9455a26c516dbfe828f;p=gpl%2Fargeo-suite.git diff --git a/org.argeo.app.jcr/src/org/argeo/app/jcr/odk/OdkJcrUtils.java b/org.argeo.app.jcr/src/org/argeo/app/jcr/odk/OdkJcrUtils.java new file mode 100644 index 0000000..881523f --- /dev/null +++ b/org.argeo.app.jcr/src/org/argeo/app/jcr/odk/OdkJcrUtils.java @@ -0,0 +1,196 @@ +package org.argeo.app.jcr.odk; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.Node; +import javax.jcr.NodeIterator; +import javax.jcr.Property; +import javax.jcr.RepositoryException; +import javax.jcr.Session; +import javax.jcr.nodetype.NodeType; + +import org.argeo.api.cms.CmsLog; +import org.argeo.app.api.EntityMimeType; +import org.argeo.app.api.EntityType; +import org.argeo.app.odk.OdkNames; +import org.argeo.app.odk.OrxListName; +import org.argeo.app.odk.OrxManifestName; +import org.argeo.cms.util.DigestUtils; +import org.argeo.jcr.Jcr; +import org.argeo.jcr.JcrUtils; +import org.argeo.jcr.JcrxApi; + +/** Utilities around ODK. */ +public class OdkJcrUtils { + private final static CmsLog log = CmsLog.getLog(OdkJcrUtils.class); + + public static Node loadOdkForm(Node formBase, String name, InputStream in, InputStream... additionalNodes) + throws RepositoryException, IOException { + if (!formBase.isNodeType(EntityType.formSet.get())) + throw new IllegalArgumentException( + "Parent path " + formBase + " must be of type " + EntityType.formSet.get()); + Node form = JcrUtils.getOrAdd(formBase, name, OrxListName.xform.get(), NodeType.MIX_VERSIONABLE); + + String previousCsum = JcrxApi.getChecksum(form, JcrxApi.MD5); + String previousFormId = Jcr.get(form, OrxListName.formID.get()); + String previousFormVersion = Jcr.get(form, OrxListName.version.get()); + + Session s = formBase.getSession(); + s.importXML(form.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + + for (InputStream additionalIn : additionalNodes) { + s.importXML(form.getPath(), additionalIn, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + } + s.save(); + + // manage instances + // NodeIterator instances = + // form.getNodes("h:html/h:head/xforms:model/xforms:instance"); + NodeIterator instances = form.getNode("h:html/h:head/xforms:model").getNodes("xforms:instance"); + Node primaryInstance = null; + while (instances.hasNext()) { + Node instance = instances.nextNode(); + if (primaryInstance == null) { + primaryInstance = instance; + } else {// secondary instances + String instanceId = instance.getProperty("id").getString(); + URI instanceUri = null; + if (instance.hasProperty("src")) + try { + instanceUri = new URI(instance.getProperty("src").getString()); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Instance " + instanceId + " has a badly formatted URI", e); + } + if (instanceUri != null) { + if ("jr".equals(instanceUri.getScheme())) { + String uuid; + String mimeType; + String encoding = StandardCharsets.UTF_8.name(); + String type = instanceUri.getHost(); + String path = instanceUri.getPath(); + if ("file".equals(type)) { + if (!path.endsWith(".xml")) + throw new IllegalArgumentException("File uri " + instanceUri + " must end with .xml"); + // Work around bug in ODK Collect not supporting paths + // path = path.substring(0, path.length() - ".xml".length()); + // Node target = file.getSession().getNode(path); + uuid = path.substring(1, path.length() - ".xml".length()); + mimeType = EntityMimeType.XML.getMimeType(); + } else if ("file-csv".equals(type)) { + if (!path.endsWith(".csv")) + throw new IllegalArgumentException("File uri " + instanceUri + " must end with .csv"); + // Work around bug in ODK Collect not supporting paths + // path = path.substring(0, path.length() - ".csv".length()); + // Node target = file.getSession().getNode(path); + uuid = path.substring(1, path.length() - ".csv".length()); + mimeType = EntityMimeType.CSV.getMimeType(); + } else { + throw new IllegalArgumentException("Unsupported instance type " + type); + } + Node manifest = JcrUtils.getOrAdd(form, OrxManifestName.manifest.name(), + OrxManifestName.manifest.get()); + Node file = JcrUtils.getOrAdd(manifest, instanceId); + file.addMixin(NodeType.MIX_MIMETYPE); + file.setProperty(Property.JCR_MIMETYPE, mimeType); + file.setProperty(Property.JCR_ENCODING, encoding); + Node target = file.getSession().getNodeByIdentifier(uuid); + +// if (target.isNodeType(NodeType.NT_QUERY)) { +// Query query = target.getSession().getWorkspace().getQueryManager().getQuery(target); +// query.setLimit(10); +// QueryResult queryResult = query.execute(); +// RowIterator rit = queryResult.getRows(); +// while (rit.hasNext()) { +// Row row = rit.nextRow(); +// for (Value value : row.getValues()) { +// System.out.print(value.getString()); +// System.out.print(','); +// } +// System.out.print('\n'); +// } +// +// } + + if (target.isNodeType(NodeType.MIX_REFERENCEABLE)) { + file.setProperty(Property.JCR_ID, target); + if (file.hasProperty(Property.JCR_PATH)) + file.getProperty(Property.JCR_PATH).remove(); + } else { + file.setProperty(Property.JCR_PATH, target.getPath()); + if (file.hasProperty(Property.JCR_ID)) + file.getProperty(Property.JCR_ID).remove(); + } + } + } + } + } + + if (primaryInstance == null) + throw new IllegalArgumentException("No primary instance found in " + form); + if (!primaryInstance.hasNodes()) + throw new IllegalArgumentException("No data found in primary instance of " + form); + NodeIterator primaryInstanceChildren = primaryInstance.getNodes(); + Node data = primaryInstanceChildren.nextNode(); + if (primaryInstanceChildren.hasNext()) + throw new IllegalArgumentException("More than one data found in primary instance of " + form); + String formId = data.getProperty("id").getString(); + if (previousFormId != null && !formId.equals(previousFormId)) + log.warn("Form id of " + form + " changed from " + previousFormId + " to " + formId); + form.setProperty(OrxListName.formID.get(), formId); + String formVersion = data.getProperty("version").getString(); + + if (previousCsum == null)// save before checksuming + s.save(); + String newCsum; + try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { + s.exportDocumentView(form.getPath() + "/" + OdkNames.H_HTML, out, true, false); + newCsum = DigestUtils.digest(DigestUtils.MD5, out.toByteArray()); + } + if (previousCsum == null) { + JcrxApi.addChecksum(form, newCsum); + JcrUtils.updateLastModified(form); + form.setProperty(OrxListName.version.get(), formVersion); + s.save(); + s.getWorkspace().getVersionManager().checkpoint(form.getPath()); + if (log.isDebugEnabled()) + log.debug("New form " + form); + } else { + if (newCsum.equals(previousCsum)) { + // discard + s.refresh(false); + if (log.isDebugEnabled()) + log.debug("Unmodified form " + form); + return form; + } else { + if (formVersion.equals(previousFormVersion)) { + s.refresh(false); + throw new IllegalArgumentException("Form " + form + " has been changed but version " + formVersion + + " has not been changed, discarding changes..."); + } + form.setProperty(OrxListName.version.get(), formVersion); + JcrxApi.addChecksum(form, newCsum); + JcrUtils.updateLastModified(form); + s.save(); + s.getWorkspace().getVersionManager().checkpoint(form.getPath()); + if (log.isDebugEnabled()) { + log.debug("Updated form " + form); + log.debug("Previous csum " + previousCsum); + log.debug("New csum " + newCsum); + } + } + } + return form; + } + + /** Singleton. */ + private OdkJcrUtils() { + + } + +}