]> git.argeo.org Git - gpl/argeo-suite.git/blob - OdkUtils.java
ff7773818c14d9581150b46abe93cd0f5e69be8f
[gpl/argeo-suite.git] / OdkUtils.java
1 package org.argeo.app.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 import java.nio.charset.StandardCharsets;
9
10 import javax.jcr.ImportUUIDBehavior;
11 import javax.jcr.Node;
12 import javax.jcr.NodeIterator;
13 import javax.jcr.Property;
14 import javax.jcr.RepositoryException;
15 import javax.jcr.Session;
16 import javax.jcr.Value;
17 import javax.jcr.nodetype.NodeType;
18 import javax.jcr.query.Query;
19 import javax.jcr.query.QueryResult;
20 import javax.jcr.query.Row;
21 import javax.jcr.query.RowIterator;
22
23 import org.argeo.api.cms.CmsLog;
24 import org.argeo.app.api.EntityMimeType;
25 import org.argeo.app.api.EntityType;
26 import org.argeo.jcr.Jcr;
27 import org.argeo.jcr.JcrUtils;
28 import org.argeo.jcr.JcrxApi;
29 import org.argeo.util.DigestUtils;
30
31 /** Utilities around ODK. */
32 public class OdkUtils {
33 private final static CmsLog log = CmsLog.getLog(OdkUtils.class);
34
35 public static Node loadOdkForm(Node formBase, String name, InputStream in, InputStream... additionalNodes)
36 throws RepositoryException, IOException {
37 if (!formBase.isNodeType(EntityType.formSet.get()))
38 throw new IllegalArgumentException(
39 "Parent path " + formBase + " must be of type " + EntityType.formSet.get());
40 Node form = JcrUtils.getOrAdd(formBase, name, OrxListName.xform.get(), NodeType.MIX_VERSIONABLE);
41
42 String previousCsum = JcrxApi.getChecksum(form, JcrxApi.MD5);
43 String previousFormId = Jcr.get(form, OrxListName.formID.get());
44 String previousFormVersion = Jcr.get(form, OrxListName.version.get());
45
46 Session s = formBase.getSession();
47 s.importXML(form.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
48
49 for (InputStream additionalIn : additionalNodes) {
50 s.importXML(form.getPath(), additionalIn, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
51 }
52 s.save();
53
54 // manage instances
55 // NodeIterator instances =
56 // form.getNodes("h:html/h:head/xforms:model/xforms:instance");
57 NodeIterator instances = form.getNode("h:html/h:head/xforms:model").getNodes("xforms:instance");
58 Node primaryInstance = null;
59 while (instances.hasNext()) {
60 Node instance = instances.nextNode();
61 if (primaryInstance == null) {
62 primaryInstance = instance;
63 } else {// secondary instances
64 String instanceId = instance.getProperty("id").getString();
65 URI instanceUri = null;
66 if (instance.hasProperty("src"))
67 try {
68 instanceUri = new URI(instance.getProperty("src").getString());
69 } catch (URISyntaxException e) {
70 throw new IllegalArgumentException("Instance " + instanceId + " has a badly formatted URI", e);
71 }
72 if (instanceUri != null) {
73 if ("jr".equals(instanceUri.getScheme())) {
74 String uuid;
75 String mimeType;
76 String encoding = StandardCharsets.UTF_8.name();
77 String type = instanceUri.getHost();
78 String path = instanceUri.getPath();
79 if ("file".equals(type)) {
80 if (!path.endsWith(".xml"))
81 throw new IllegalArgumentException("File uri " + instanceUri + " must end with .xml");
82 // Work around bug in ODK Collect not supporting paths
83 // path = path.substring(0, path.length() - ".xml".length());
84 // Node target = file.getSession().getNode(path);
85 uuid = path.substring(1, path.length() - ".xml".length());
86 mimeType = EntityMimeType.XML.getMimeType();
87 } else if ("file-csv".equals(type)) {
88 if (!path.endsWith(".csv"))
89 throw new IllegalArgumentException("File uri " + instanceUri + " must end with .csv");
90 // Work around bug in ODK Collect not supporting paths
91 // path = path.substring(0, path.length() - ".csv".length());
92 // Node target = file.getSession().getNode(path);
93 uuid = path.substring(1, path.length() - ".csv".length());
94 mimeType = EntityMimeType.CSV.getMimeType();
95 } else {
96 throw new IllegalArgumentException("Unsupported instance type " + type);
97 }
98 Node manifest = JcrUtils.getOrAdd(form, OrxManifestName.manifest.name(),
99 OrxManifestName.manifest.get());
100 Node file = JcrUtils.getOrAdd(manifest, instanceId);
101 file.addMixin(NodeType.MIX_MIMETYPE);
102 file.setProperty(Property.JCR_MIMETYPE, mimeType);
103 file.setProperty(Property.JCR_ENCODING, encoding);
104 Node target = file.getSession().getNodeByIdentifier(uuid);
105
106 if (target.isNodeType(NodeType.NT_QUERY)) {
107 Query query = target.getSession().getWorkspace().getQueryManager().getQuery(target);
108 query.setLimit(10);
109 QueryResult queryResult = query.execute();
110 RowIterator rit = queryResult.getRows();
111 while (rit.hasNext()) {
112 Row row = rit.nextRow();
113 for (Value value : row.getValues()) {
114 System.out.print(value.getString());
115 System.out.print(',');
116 }
117 System.out.print('\n');
118 }
119
120 }
121
122 if (target.isNodeType(NodeType.MIX_REFERENCEABLE)) {
123 file.setProperty(Property.JCR_ID, target);
124 if (file.hasProperty(Property.JCR_PATH))
125 file.getProperty(Property.JCR_PATH).remove();
126 } else {
127 file.setProperty(Property.JCR_PATH, target.getPath());
128 if (file.hasProperty(Property.JCR_ID))
129 file.getProperty(Property.JCR_ID).remove();
130 }
131 }
132 }
133 }
134 }
135
136 if (primaryInstance == null)
137 throw new IllegalArgumentException("No primary instance found in " + form);
138 if (!primaryInstance.hasNodes())
139 throw new IllegalArgumentException("No data found in primary instance of " + form);
140 NodeIterator primaryInstanceChildren = primaryInstance.getNodes();
141 Node data = primaryInstanceChildren.nextNode();
142 if (primaryInstanceChildren.hasNext())
143 throw new IllegalArgumentException("More than one data found in primary instance of " + form);
144 String formId = data.getProperty("id").getString();
145 if (previousFormId != null && !formId.equals(previousFormId))
146 log.warn("Form id of " + form + " changed from " + previousFormId + " to " + formId);
147 form.setProperty(OrxListName.formID.get(), formId);
148 String formVersion = data.getProperty("version").getString();
149
150 if (previousCsum == null)// save before checksuming
151 s.save();
152 String newCsum;
153 try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
154 s.exportDocumentView(form.getPath() + "/" + OdkNames.H_HTML, out, true, false);
155 newCsum = DigestUtils.digest(DigestUtils.MD5, out.toByteArray());
156 }
157 if (previousCsum == null) {
158 JcrxApi.addChecksum(form, newCsum);
159 JcrUtils.updateLastModified(form);
160 form.setProperty(OrxListName.version.get(), formVersion);
161 s.save();
162 s.getWorkspace().getVersionManager().checkpoint(form.getPath());
163 if (log.isDebugEnabled())
164 log.debug("New form " + form);
165 } else {
166 if (newCsum.equals(previousCsum)) {
167 // discard
168 s.refresh(false);
169 if (log.isDebugEnabled())
170 log.debug("Unmodified form " + form);
171 return form;
172 } else {
173 if (formVersion.equals(previousFormVersion)) {
174 s.refresh(false);
175 throw new IllegalArgumentException("Form " + form + " has been changed but version " + formVersion
176 + " has not been changed, discarding changes...");
177 }
178 form.setProperty(OrxListName.version.get(), formVersion);
179 JcrxApi.addChecksum(form, newCsum);
180 JcrUtils.updateLastModified(form);
181 s.save();
182 s.getWorkspace().getVersionManager().checkpoint(form.getPath());
183 if (log.isDebugEnabled()) {
184 log.debug("Updated form " + form);
185 log.debug("Previous csum " + previousCsum);
186 log.debug("New csum " + newCsum);
187 }
188 }
189 }
190 return form;
191 }
192
193 /** Singleton. */
194 private OdkUtils() {
195
196 }
197
198 }