1 package org
.argeo
.support
.odk
;
3 import java
.io
.ByteArrayOutputStream
;
4 import java
.io
.IOException
;
5 import java
.io
.InputStream
;
7 import java
.net
.URISyntaxException
;
9 import javax
.jcr
.ImportUUIDBehavior
;
10 import javax
.jcr
.Node
;
11 import javax
.jcr
.NodeIterator
;
12 import javax
.jcr
.Property
;
13 import javax
.jcr
.RepositoryException
;
14 import javax
.jcr
.Session
;
15 import javax
.jcr
.nodetype
.NodeType
;
17 import org
.apache
.commons
.logging
.Log
;
18 import org
.apache
.commons
.logging
.LogFactory
;
19 import org
.argeo
.entity
.EntityType
;
20 import org
.argeo
.jcr
.Jcr
;
21 import org
.argeo
.jcr
.JcrUtils
;
22 import org
.argeo
.jcr
.JcrxApi
;
23 import org
.argeo
.util
.DigestUtils
;
25 /** Utilities around ODK. */
26 public class OdkUtils
{
27 private final static Log log
= LogFactory
.getLog(OdkUtils
.class);
29 public static void loadOdkForm(Node formBase
, String name
, InputStream in
) throws RepositoryException
, IOException
{
30 if (!formBase
.isNodeType(EntityType
.formSet
.get()))
31 throw new IllegalArgumentException(
32 "Parent path " + formBase
+ " must be of type " + EntityType
.formSet
.get());
33 Node form
= JcrUtils
.getOrAdd(formBase
, name
, OrxListName
.xform
.get(), NodeType
.MIX_VERSIONABLE
);
35 String previousCsum
= JcrxApi
.getChecksum(form
, JcrxApi
.MD5
);
36 String previousFormId
= Jcr
.get(form
, OrxListName
.formID
.get());
37 String previousFormVersion
= Jcr
.get(form
, OrxListName
.version
.get());
39 Session s
= formBase
.getSession();
40 // String res = "/odk/apafSession.odk.xml";
41 // try (InputStream in = getClass().getClassLoader().getResourceAsStream(res)) {
42 s
.importXML(form
.getPath(), in
, ImportUUIDBehavior
.IMPORT_UUID_COLLISION_REPLACE_EXISTING
);
46 // NodeIterator instances =
47 // form.getNodes("h:html/h:head/xforms:model/xforms:instance");
48 NodeIterator instances
= form
.getNode("h:html/h:head/xforms:model").getNodes("xforms:instance");
49 Node primaryInstance
= null;
50 while (instances
.hasNext()) {
51 Node instance
= instances
.nextNode();
52 if (primaryInstance
== null) {
53 primaryInstance
= instance
;
54 } else {// secondary instances
55 String instanceId
= instance
.getProperty("id").getString();
56 URI instanceUri
= null;
57 if (instance
.hasProperty("src"))
59 instanceUri
= new URI(instance
.getProperty("src").getString());
60 } catch (URISyntaxException e
) {
61 throw new IllegalArgumentException("Instance " + instanceId
+ " has a badly formatted URI", e
);
63 if (instanceUri
!= null) {
64 if ("jr".equals(instanceUri
.getScheme())) {
65 String type
= instanceUri
.getHost();
66 if ("file".equals(type
)) {
67 Node manifest
= JcrUtils
.getOrAdd(form
, OrxManifestName
.manifest
.name(),
68 OrxManifestName
.manifest
.get());
69 Node file
= JcrUtils
.getOrAdd(manifest
, instanceId
);
70 String path
= instanceUri
.getPath();
71 if (!path
.endsWith(".xml"))
72 throw new IllegalArgumentException("File uri " + instanceUri
+ " must end with .xml");
73 path
= path
.substring(0, path
.length() - ".xml".length());
74 Node target
= file
.getSession().getNode(path
);
75 if (target
.isNodeType(NodeType
.MIX_REFERENCEABLE
)) {
76 file
.setProperty(Property
.JCR_ID
, target
);
77 if (file
.hasProperty(Property
.JCR_PATH
))
78 file
.getProperty(Property
.JCR_PATH
).remove();
80 file
.setProperty(Property
.JCR_PATH
, target
.getPath());
81 if (file
.hasProperty(Property
.JCR_ID
))
82 file
.getProperty(Property
.JCR_ID
).remove();
90 if (primaryInstance
== null)
91 throw new IllegalArgumentException("No primary instance found in " + form
);
92 if (!primaryInstance
.hasNodes())
93 throw new IllegalArgumentException("No data found in primary instance of " + form
);
94 NodeIterator primaryInstanceChildren
= primaryInstance
.getNodes();
95 Node data
= primaryInstanceChildren
.nextNode();
96 if (primaryInstanceChildren
.hasNext())
97 throw new IllegalArgumentException("More than one data found in primary instance of " + form
);
98 String formId
= data
.getProperty("id").getString();
99 if (previousFormId
!= null && !formId
.equals(previousFormId
))
100 log
.warn("Form id of " + form
+ " changed from " + previousFormId
+ " to " + formId
);
101 form
.setProperty(OrxListName
.formID
.get(), formId
);
102 String formVersion
= data
.getProperty("version").getString();
104 if (previousCsum
== null)// save before checksuming
107 try (ByteArrayOutputStream out
= new ByteArrayOutputStream()) {
108 s
.exportDocumentView(form
.getPath() + "/" + OdkNames
.H_HTML
, out
, true, false);
109 newCsum
= DigestUtils
.digest(DigestUtils
.MD5
, out
.toByteArray());
111 if (previousCsum
== null) {
112 JcrxApi
.addChecksum(form
, newCsum
);
113 JcrUtils
.updateLastModified(form
);
114 form
.setProperty(OrxListName
.version
.get(), formVersion
);
116 s
.getWorkspace().getVersionManager().checkpoint(form
.getPath());
117 if (log
.isDebugEnabled())
118 log
.debug("New form " + form
);
120 if (newCsum
.equals(previousCsum
)) {
123 if (log
.isDebugEnabled())
124 log
.debug("Unmodified form " + form
);
127 if (formVersion
.equals(previousFormVersion
)) {
129 throw new IllegalArgumentException("Form " + form
+ " has been changed but version " + formVersion
130 + " has not been changed, discarding changes...");
132 form
.setProperty(OrxListName
.version
.get(), formVersion
);
133 JcrxApi
.addChecksum(form
, newCsum
);
134 JcrUtils
.updateLastModified(form
);
136 s
.getWorkspace().getVersionManager().checkpoint(form
.getPath());
137 if (log
.isDebugEnabled()) {
138 log
.debug("Updated form " + form
);
139 log
.debug("Previous csum " + previousCsum
);
140 log
.debug("New csum " + newCsum
);