1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.event.comment;
23
24 import static javax.xml.xpath.XPathConstants.STRING;
25 import static org.opencastproject.util.data.functions.Misc.chuck;
26
27 import org.opencastproject.mediapackage.UnsupportedElementException;
28 import org.opencastproject.security.api.User;
29 import org.opencastproject.security.api.UserDirectoryService;
30 import org.opencastproject.util.DateTimeSupport;
31 import org.opencastproject.util.XmlSafeParser;
32
33 import org.apache.commons.io.IOUtils;
34 import org.apache.commons.lang3.BooleanUtils;
35 import org.apache.commons.lang3.StringUtils;
36 import org.w3c.dom.Document;
37 import org.w3c.dom.Element;
38 import org.w3c.dom.Node;
39 import org.w3c.dom.NodeList;
40
41 import java.io.StringWriter;
42 import java.util.Collection;
43 import java.util.Date;
44 import java.util.Optional;
45
46 import javax.xml.parsers.DocumentBuilderFactory;
47 import javax.xml.parsers.ParserConfigurationException;
48 import javax.xml.transform.Transformer;
49 import javax.xml.transform.dom.DOMSource;
50 import javax.xml.transform.stream.StreamResult;
51 import javax.xml.xpath.XPath;
52 import javax.xml.xpath.XPathConstants;
53 import javax.xml.xpath.XPathExpressionException;
54 import javax.xml.xpath.XPathFactory;
55
56
57
58
59 public final class EventCommentParser {
60
61 private static final String NAMESPACE = "http://comment.opencastproject.org";
62
63 private EventCommentParser() {
64 }
65
66
67 private static final XPath xpath = XPathFactory.newInstance().newXPath();
68
69
70
71
72
73
74
75
76
77
78
79
80 public static EventComment getCommentFromXml(String xml, UserDirectoryService userDirectoryService)
81 throws EventCommentException {
82 try {
83 Document doc = XmlSafeParser.newDocumentBuilderFactory().newDocumentBuilder()
84 .parse(IOUtils.toInputStream(xml, "UTF-8"));
85 return commentFromManifest(doc.getDocumentElement(), userDirectoryService);
86 } catch (Exception e) {
87 throw new EventCommentException(e);
88 }
89 }
90
91 private static EventComment commentFromManifest(Node commentNode, UserDirectoryService userDirectoryService)
92 throws UnsupportedElementException {
93 try {
94
95 Long id = null;
96 Double idAsDouble = ((Number) xpath.evaluate("@id", commentNode, XPathConstants.NUMBER)).doubleValue();
97 if (!idAsDouble.isNaN()) {
98 id = idAsDouble.longValue();
99 }
100
101 final String eventId = (String) xpath.evaluate("@eventId", commentNode, STRING);
102 final String organization = (String) xpath.evaluate("@organization", commentNode, STRING);
103
104
105 String text = (String) xpath.evaluate("text/text()", commentNode, XPathConstants.STRING);
106
107
108 Node authorNode = (Node) xpath.evaluate("author", commentNode, XPathConstants.NODE);
109 User author = userFromManifest(authorNode, userDirectoryService);
110
111
112 Boolean resolved = BooleanUtils
113 .toBoolean((Boolean) xpath.evaluate("@resolved", commentNode, XPathConstants.BOOLEAN));
114
115
116 String reason = (String) xpath.evaluate("reason/text()", commentNode, XPathConstants.STRING);
117 if (StringUtils.isNotBlank(reason)) {
118 reason = reason.trim();
119 }
120
121
122 String creationDateString = (String) xpath.evaluate("creationDate/text()", commentNode, XPathConstants.STRING);
123 Date creationDate = new Date(DateTimeSupport.fromUTC(creationDateString));
124
125
126 String modificationDateString = (String) xpath.evaluate("modificationDate/text()", commentNode,
127 XPathConstants.STRING);
128 Date modificationDate = new Date(DateTimeSupport.fromUTC(modificationDateString));
129
130
131 EventComment comment = EventComment.create(Optional.ofNullable(id), eventId, organization,
132 text.trim(), author, reason, resolved, creationDate, modificationDate);
133
134
135 NodeList replyNodes = (NodeList) xpath.evaluate("replies/reply", commentNode, XPathConstants.NODESET);
136 for (int i = 0; i < replyNodes.getLength(); i++) {
137 comment.addReply(replyFromManifest(replyNodes.item(i), userDirectoryService));
138 }
139
140 return comment;
141 } catch (XPathExpressionException e) {
142 throw new UnsupportedElementException("Error while reading comment information from manifest", e);
143 } catch (Exception e) {
144 if (e instanceof UnsupportedElementException) {
145 throw (UnsupportedElementException) e;
146 }
147 throw new UnsupportedElementException(
148 "Error while reading comment creation or modification date information from manifest", e);
149 }
150 }
151
152 private static EventCommentReply replyFromManifest(Node commentReplyNode, UserDirectoryService userDirectoryService)
153 throws UnsupportedElementException {
154 try {
155
156 Long id = null;
157 Double idAsDouble = ((Number) xpath.evaluate("@id", commentReplyNode, XPathConstants.NUMBER)).doubleValue();
158 if (!idAsDouble.isNaN()) {
159 id = idAsDouble.longValue();
160 }
161
162
163 String text = (String) xpath.evaluate("text/text()", commentReplyNode, XPathConstants.STRING);
164
165
166 Node authorNode = (Node) xpath.evaluate("author", commentReplyNode, XPathConstants.NODE);
167 User author = userFromManifest(authorNode, userDirectoryService);
168
169
170 String creationDateString = (String) xpath.evaluate("creationDate/text()", commentReplyNode,
171 XPathConstants.STRING);
172 Date creationDate = new Date(DateTimeSupport.fromUTC(creationDateString));
173
174
175 String modificationDateString = (String) xpath.evaluate("modificationDate/text()", commentReplyNode,
176 XPathConstants.STRING);
177 Date modificationDate = new Date(DateTimeSupport.fromUTC(modificationDateString));
178
179
180 return EventCommentReply.create(Optional.ofNullable(id), text.trim(), author, creationDate, modificationDate);
181 } catch (XPathExpressionException e) {
182 throw new UnsupportedElementException("Error while reading comment reply information from manifest", e);
183 } catch (Exception e) {
184 if (e instanceof UnsupportedElementException) {
185 throw (UnsupportedElementException) e;
186 }
187 throw new UnsupportedElementException(
188 "Error while reading comment reply creation or modification date information from manifest", e);
189 }
190 }
191
192 private static User userFromManifest(Node authorNode, UserDirectoryService userDirectoryService) {
193 try {
194
195 String userName = (String) xpath.evaluate("username/text()", authorNode, XPathConstants.STRING);
196 return userDirectoryService.loadUser(userName);
197 } catch (XPathExpressionException e) {
198 throw new UnsupportedElementException("Error while reading comment author information from manifest", e);
199 }
200 }
201
202
203
204
205
206
207
208
209
210
211 public static String getAsXml(Collection<EventComment> comments) throws EventCommentException {
212 Document doc = newDocument();
213
214 Element commentsXml = doc.createElementNS(NAMESPACE, "comments");
215 commentsXml.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", NAMESPACE);
216 doc.appendChild(commentsXml);
217
218 for (EventComment c : comments) {
219 commentsXml.appendChild(getAsXmlDocument(c, doc));
220 }
221
222 return serializeNode(commentsXml.getOwnerDocument());
223 }
224
225
226
227
228
229
230
231
232
233
234 public static String getAsXml(EventComment comment) throws EventCommentException {
235 Document doc = newDocument();
236
237 Element commentNode = getAsXmlDocument(comment, doc);
238 return serializeNode(doc.appendChild(commentNode).getOwnerDocument());
239 }
240
241
242
243
244
245
246
247
248
249
250 public static String getAsXml(EventCommentReply reply) throws EventCommentException {
251 Document xmlDocument = getAsXmlDocument(reply);
252 return serializeNode(xmlDocument);
253 }
254
255
256
257
258
259
260
261
262 private static Element getAsXmlDocument(EventComment comment, Document doc) {
263
264 Element commentXml = doc.createElementNS(NAMESPACE, "comment");
265 commentXml.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", NAMESPACE);
266
267
268 if (comment.getId().isPresent()) {
269 commentXml.setAttribute("id", comment.getId().get().toString());
270 }
271
272 commentXml.setAttribute("eventId", comment.getEventId());
273 commentXml.setAttribute("organization", comment.getOrganization());
274
275
276 commentXml.setAttribute("resolved", Boolean.toString(comment.isResolvedStatus()));
277
278
279 Element authorNode = getAuthorNode(comment.getAuthor(), doc);
280 commentXml.appendChild(authorNode);
281
282
283 Element creationDate = doc.createElement("creationDate");
284 creationDate.setTextContent(DateTimeSupport.toUTC(comment.getCreationDate().getTime()));
285 commentXml.appendChild(creationDate);
286
287
288 Element modificationDate = doc.createElement("modificationDate");
289 modificationDate.setTextContent(DateTimeSupport.toUTC(comment.getModificationDate().getTime()));
290 commentXml.appendChild(modificationDate);
291
292
293 Element text = doc.createElement("text");
294 if (StringUtils.isNotBlank(comment.getText())) {
295 text.appendChild(doc.createCDATASection(comment.getText()));
296 }
297 commentXml.appendChild(text);
298
299
300 Element reason = doc.createElement("reason");
301 if (StringUtils.isNotBlank(comment.getReason())) {
302 reason.setTextContent(comment.getReason());
303 }
304 commentXml.appendChild(reason);
305
306
307 Element repliesNode = doc.createElement("replies");
308 for (EventCommentReply r : comment.getReplies()) {
309 repliesNode.appendChild(getAsXml(r, doc));
310 }
311 commentXml.appendChild(repliesNode);
312
313 return commentXml;
314 }
315
316
317
318
319
320
321
322
323 private static Document getAsXmlDocument(EventCommentReply reply) {
324 Document doc = newDocument();
325
326 Element replyNode = getAsXml(reply, doc);
327 return doc.appendChild(replyNode).getOwnerDocument();
328 }
329
330 private static Element getAsXml(EventCommentReply reply, Document doc) {
331
332 Element replyXml = doc.createElementNS(NAMESPACE, "reply");
333 replyXml.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns", NAMESPACE);
334
335
336 if (reply.getId().isPresent()) {
337 replyXml.setAttribute("id", reply.getId().get().toString());
338 }
339
340
341 Element authorNode = getAuthorNode(reply.getAuthor(), doc);
342 replyXml.appendChild(authorNode);
343
344
345 Element creationDate = doc.createElement("creationDate");
346 creationDate.setTextContent(DateTimeSupport.toUTC(reply.getCreationDate().getTime()));
347 replyXml.appendChild(creationDate);
348
349
350 Element modificationDate = doc.createElement("modificationDate");
351 modificationDate.setTextContent(DateTimeSupport.toUTC(reply.getModificationDate().getTime()));
352 replyXml.appendChild(modificationDate);
353
354
355 Element text = doc.createElement("text");
356 if (StringUtils.isNotBlank(reply.getText())) {
357 text.appendChild(doc.createCDATASection(reply.getText()));
358 }
359
360 replyXml.appendChild(text);
361
362 return replyXml;
363 }
364
365 private static Element getAuthorNode(User author, Document doc) {
366 Element authorNode = doc.createElement("author");
367 Element username = doc.createElement("username");
368 username.setTextContent(author.getUsername());
369 authorNode.appendChild(username);
370 Element email = doc.createElement("email");
371 email.setTextContent(author.getEmail());
372 authorNode.appendChild(email);
373 Element name = doc.createElement("name");
374 if (StringUtils.isNotBlank(author.getName())) {
375 name.appendChild(doc.createCDATASection(author.getName()));
376 }
377 authorNode.appendChild(name);
378 return authorNode;
379 }
380
381 private static String serializeNode(Document xmlDocument) throws EventCommentException {
382
383 try {
384 Transformer transformer = XmlSafeParser.newTransformerFactory()
385 .newTransformer();
386
387 StringWriter writer = new StringWriter();
388 transformer.transform(new DOMSource(xmlDocument), new StreamResult(writer));
389 return writer.getBuffer().toString().trim();
390 } catch (Exception e) {
391 throw new EventCommentException(e);
392 }
393 }
394
395
396 private static Document newDocument() {
397 final DocumentBuilderFactory docBuilderFactory = XmlSafeParser.newDocumentBuilderFactory();
398 docBuilderFactory.setNamespaceAware(true);
399 try {
400 return docBuilderFactory.newDocumentBuilder().newDocument();
401 } catch (ParserConfigurationException e) {
402 return chuck(e);
403 }
404 }
405
406 }