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