Work on ODK manifest support.
[gpl/argeo-suite.git] / knowledge / org.argeo.support.odk / src / org / argeo / support / odk / OdkUtils.java
1 package org.argeo.support.odk;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.IOException;
5 import java.io.InputStream;
6 import java.net.URI;
7 import java.net.URISyntaxException;
8
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;
16
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;
24
25 /** Utilities around ODK. */
26 public class OdkUtils {
27         private final static Log log = LogFactory.getLog(OdkUtils.class);
28
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);
34
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());
38
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);
43 //              }
44
45                 // manage instances
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"))
58                                         try {
59                                                 instanceUri = new URI(instance.getProperty("src").getString());
60                                         } catch (URISyntaxException e) {
61                                                 throw new IllegalArgumentException("Instance " + instanceId + " has a badly formatted URI", e);
62                                         }
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();
79                                                         } else {
80                                                                 file.setProperty(Property.JCR_PATH, target.getPath());
81                                                                 if (file.hasProperty(Property.JCR_ID))
82                                                                         file.getProperty(Property.JCR_ID).remove();
83                                                         }
84                                                 }
85                                         }
86                                 }
87                         }
88                 }
89
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();
103
104                 if (previousCsum == null)// save before checksuming
105                         s.save();
106                 String newCsum;
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());
110                 }
111                 if (previousCsum == null) {
112                         JcrxApi.addChecksum(form, newCsum);
113                         JcrUtils.updateLastModified(form);
114                         form.setProperty(OrxListName.version.get(), formVersion);
115                         s.save();
116                         s.getWorkspace().getVersionManager().checkpoint(form.getPath());
117                         if (log.isDebugEnabled())
118                                 log.debug("New form " + form);
119                 } else {
120                         if (newCsum.equals(previousCsum)) {
121                                 // discard
122                                 s.refresh(false);
123                                 if (log.isDebugEnabled())
124                                         log.debug("Unmodified form " + form);
125                                 return;
126                         } else {
127                                 if (formVersion.equals(previousFormVersion)) {
128                                         s.refresh(false);
129                                         throw new IllegalArgumentException("Form " + form + " has been changed but version " + formVersion
130                                                         + " has not been changed, discarding changes...");
131                                 }
132                                 form.setProperty(OrxListName.version.get(), formVersion);
133                                 JcrxApi.addChecksum(form, newCsum);
134                                 JcrUtils.updateLastModified(form);
135                                 s.save();
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);
141                                 }
142                         }
143                 }
144         }
145
146         /** Singleton. */
147         private OdkUtils() {
148
149         }
150
151 }