]> git.argeo.org Git - gpl/argeo-suite.git/blob - org.argeo.app.ui/src/org/argeo/app/ui/docbook/DbkTextInterpreter.java
Default edition layer supports SWT UI provider
[gpl/argeo-suite.git] / org.argeo.app.ui / src / org / argeo / app / ui / docbook / DbkTextInterpreter.java
1 package org.argeo.app.ui.docbook;
2
3 import static org.argeo.app.docbook.DbkType.para;
4 import static org.argeo.app.docbook.DbkType.title;
5 import static org.argeo.app.docbook.DbkUtils.isDbk;
6
7 import java.io.ByteArrayInputStream;
8 import java.io.IOException;
9 import java.io.InputStream;
10 import java.io.StringReader;
11 import java.nio.charset.StandardCharsets;
12 import java.util.List;
13
14 import javax.jcr.ImportUUIDBehavior;
15 import javax.jcr.Item;
16 import javax.jcr.Node;
17 import javax.jcr.NodeIterator;
18 import javax.jcr.Property;
19 import javax.jcr.PropertyIterator;
20 import javax.jcr.RepositoryException;
21 import javax.xml.parsers.DocumentBuilderFactory;
22
23 import org.apache.commons.io.IOUtils;
24 import org.argeo.app.docbook.DbkAttr;
25 import org.argeo.app.docbook.DbkType;
26 import org.argeo.jcr.Jcr;
27 import org.argeo.jcr.JcrException;
28
29 /** Based on HTML with a few Wiki-like shortcuts. */
30 public class DbkTextInterpreter implements TextInterpreter {
31 private DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
32
33 private String linkCssClass = DbkType.link.name();
34
35 @Override
36 public void write(Item item, String content) {
37 try {
38 if (item instanceof Node) {
39 Node node = (Node) item;
40 if (isDbk(node, para) || isDbk(node, title)) {
41 String raw = convertToStorage(node, content);
42 validateBeforeStoring(raw);
43
44 String jcrUuid = node.getIdentifier();
45 // if (node.hasProperty(Property.JCR_UUID))
46 // jcrUuid = node.getProperty(Property.JCR_UUID).getString();
47 // else {
48 // // TODO use time based
49 // jcrUuid = UUID.randomUUID().toString();
50 // node.setProperty(Property.JCR_UUID, jcrUuid);
51 // node.getSession().save();
52 // }
53
54 StringBuilder namespaces = new StringBuilder();
55 namespaces.append(" xmlns:dbk=\"http://docbook.org/ns/docbook\"");
56 namespaces.append(" xmlns:jcr=\"http://www.jcp.org/jcr/1.0\"");
57 namespaces.append(" xmlns:xlink=\"http://www.w3.org/1999/xlink\"");
58 raw = "<" + node.getName() + " jcr:uuid=\"" + jcrUuid + "\"" + namespaces + ">" + raw + "</"
59 + node.getName() + ">";
60 // System.out.println(raw);
61 try (InputStream in = new ByteArrayInputStream(raw.getBytes(StandardCharsets.UTF_8))) {
62 node.getSession().importXML(node.getParent().getPath(), in,
63 ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
64 // node.getSession().save();
65 } catch (IOException e) {
66 throw new IllegalArgumentException("Cannot parse raw content of " + node, e);
67 }
68
69 // try {
70 // DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
71 // Document document;
72 // try (Reader in = new StringReader(raw)) {
73 // document = documentBuilder.parse(new InputSource(in));
74 // }
75 // NodeList nl = document.getChildNodes();
76 // for (int i = 0; i < nl.getLength(); i++) {
77 // org.w3c.dom.Node n = nl.item(i);
78 // if (node instanceof Text) {
79 //
80 // }
81 // }
82 // } catch (ParserConfigurationException | SAXException | IOException e) {
83 // throw new IllegalArgumentException("Cannot parse raw content of " + node, e);
84 // }
85
86 // Node jcrText;
87 // if (!node.hasNode(Jcr.JCR_XMLTEXT))
88 // jcrText = node.addNode(Jcr.JCR_XMLTEXT, JcrxType.JCRX_XMLTEXT);
89 // else
90 // jcrText = node.getNode(Jcr.JCR_XMLTEXT);
91 // jcrText.setProperty(Jcr.JCR_XMLCHARACTERS, raw);
92 } else {
93 throw new IllegalArgumentException("Don't know how to interpret " + node);
94 }
95 } else {// property
96 Property property = (Property) item;
97 property.setValue(content);
98 }
99 // item.getSession().save();
100 } catch (RepositoryException e) {
101 throw new JcrException("Cannot set content on " + item, e);
102 }
103 }
104
105 @Override
106 public String read(Item item) {
107 try {
108 String raw = raw(item);
109 return convertFromStorage(item, raw);
110 } catch (RepositoryException e) {
111 throw new JcrException("Cannot get " + item + " for edit", e);
112 }
113 }
114
115 @Override
116 public String raw(Item item) {
117 try {
118 item.getSession().refresh(true);
119 if (item instanceof Node) {
120 Node node = (Node) item;
121 if (isDbk(node, para) || isDbk(node, title)) {
122 StringBuilder sb = new StringBuilder();
123 readXml(node, sb);
124 // NodeIterator nit = node.getNodes();
125 // while (nit.hasNext()) {
126 // Node child = nit.nextNode();
127 // if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
128 // Node jcrText = node.getNode(Jcr.JCR_XMLTEXT);
129 // String txt = jcrText.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
130 // // TODO make it more robust
131 // // txt = txt.replace("\n", "").replace("\t", "");
132 // txt = txt.replace("\t", " ");
133 // sb.append(txt);
134 // } else {
135 // try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
136 // child.getSession().exportDocumentView(child.getPath(), out, true, false);
137 // sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8));
138 // } catch (IOException e) {
139 // throw new IllegalStateException("Cannot export " + child, e);
140 // }
141 // }
142 // }
143 return sb.toString();
144 } else {
145 throw new IllegalArgumentException("Don't know how to interpret " + node);
146 }
147 } else {// property
148 Property property = (Property) item;
149 return property.getString();
150 }
151 } catch (RepositoryException e) {
152 throw new JcrException("Cannot get " + item + " content", e);
153 }
154 }
155
156 private void readXml(Node node, StringBuilder sb) throws RepositoryException {
157 NodeIterator nit = node.getNodes();
158 while (nit.hasNext()) {
159 Node child = nit.nextNode();
160 if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
161 String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
162 // TODO make it more robust
163 // txt = txt.replace("\n", "").replace("\t", "");
164 txt = txt.replace("\t", " ");
165 sb.append(txt);
166 } else {
167 sb.append('<').append(child.getName());
168 PropertyIterator pit = child.getProperties();
169 properties: while (pit.hasNext()) {
170 Property p = pit.nextProperty();
171 if (p.getName().startsWith("jcr:"))
172 continue properties;
173 sb.append(' ').append(p.getName()).append("=\"").append(p.getString()).append('\"');
174 }
175 sb.append('>');
176 readXml(child, sb);
177 // try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
178 // child.getSession().exportDocumentView(child.getPath(), out, true, false);
179 // sb.append(new String(out.toByteArray(), StandardCharsets.UTF_8));
180 // } catch (IOException e) {
181 // throw new IllegalStateException("Cannot export " + child, e);
182 // }
183 sb.append("</").append(child.getName()).append('>');
184 }
185 }
186 }
187
188 private void readAsSimpleHtml(Node node, StringBuilder sb) throws RepositoryException {
189 NodeIterator nit = node.getNodes();
190 while (nit.hasNext()) {
191 Node child = nit.nextNode();
192 if (child.getName().equals(Jcr.JCR_XMLTEXT)) {
193 String txt = child.getProperty(Jcr.JCR_XMLCHARACTERS).getString();
194 // TODO make it more robust
195 // txt = txt.replace("\n", "").replace("\t", "");
196 txt = txt.replace("\t", " ");
197 String html = textToSimpleHtml(txt);
198 sb.append(html);
199 } else if (child.getName().equals(DbkType.link.get())) {
200 if (child.hasProperty(DbkAttr.XLINK_HREF)) {
201 String href = child.getProperty(DbkAttr.XLINK_HREF).getString();
202 // TODO deal with other forbidden XML characters?
203 href = href.replace("&", "&amp;");
204 sb.append("<a class='" + linkCssClass + "' href='").append(href).append("'>");
205 readAsSimpleHtml(child, sb);
206 sb.append("</a>");
207 }
208 } else {
209 // ignore
210 }
211 }
212 }
213
214 private String textToSimpleHtml(String raw) {
215 // FIXME the saved data should be corrected instead.
216 if (raw.indexOf('&') >= 0) {
217 raw = raw.replace("&", "&amp;");
218 }
219 if (raw.indexOf('<') >= 0) {
220 raw = raw.replace("<", "&lt;");
221 }
222 if (raw.indexOf('>') >= 0) {
223 raw = raw.replace(">", "&gt;");
224 }
225 if (raw.indexOf('\"') >= 0) {
226 raw = raw.replace("\"", "&quot;");
227 }
228 if (raw.indexOf('\'') >= 0) {
229 raw = raw.replace("\'", "&apos;");
230 }
231 // raw = "<span style='text-align:justify'>" + raw + "</span>";
232 if (raw.length() == 0)
233 return raw;
234 try (StringReader reader = new StringReader(raw)) {
235 List<String> lines = IOUtils.readLines(reader);
236 if (lines.size() == 1)
237 return lines.get(0);
238 StringBuilder sb = new StringBuilder(raw.length() + lines.size() * BR_LENGTH);
239 for (int i = 0; i < lines.size(); i++) {
240 if (i != 0)
241 sb.append("<br/>");
242 sb.append(lines.get(i));
243 }
244 return sb.toString();
245 } catch (IOException e) {
246 throw new RuntimeException(e);
247 }
248 }
249
250 final static int BR_LENGTH = "<br/>".length();
251
252 public String readSimpleHtml(Item item) {
253 try {
254 StringBuilder sb = new StringBuilder();
255 // sb.append("<div style='text-align: justify;'>");
256 readAsSimpleHtml((Node) item, sb);
257 // sb.append("</div>");
258 // System.out.println(sb);
259 return sb.toString();
260 } catch (RepositoryException e) {
261 throw new JcrException("Cannot convert " + item + " to simple HTML", e);
262 }
263 }
264
265 // EXTENSIBILITY
266 /**
267 * To be overridden, in order to make sure that only valid strings are being
268 * stored.
269 */
270 protected void validateBeforeStoring(String raw) {
271 }
272
273 /** To be overridden, in order to support additional formatting. */
274 protected String convertToStorage(Item item, String content) throws RepositoryException {
275 return content;
276
277 }
278
279 /** To be overridden, in order to support additional formatting. */
280 protected String convertFromStorage(Item item, String content) throws RepositoryException {
281 return content;
282 }
283 }