15d52f3a9a42340f164976c74d0bb06f2f9635dd
[gpl/argeo-suite.git] / publishing / org.argeo.publishing.ui / src / org / argeo / docbook / DbkUtils.java
1 package org.argeo.docbook;
2
3 import static org.argeo.docbook.DbkType.para;
4
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.OutputStream;
8 import java.nio.file.Files;
9 import java.nio.file.Path;
10
11 import javax.jcr.ImportUUIDBehavior;
12 import javax.jcr.Node;
13 import javax.jcr.NodeIterator;
14 import javax.jcr.PathNotFoundException;
15 import javax.jcr.RepositoryException;
16 import javax.jcr.ValueFormatException;
17
18 import org.apache.commons.logging.Log;
19 import org.apache.commons.logging.LogFactory;
20 import org.argeo.entity.EntityType;
21 import org.argeo.jcr.Jcr;
22 import org.argeo.jcr.JcrException;
23 import org.argeo.jcr.JcrUtils;
24 import org.argeo.jcr.JcrxApi;
25
26 /** Utilities around DocBook. */
27 public class DbkUtils {
28         private final static Log log = LogFactory.getLog(DbkUtils.class);
29
30         /** Get or add a DocBook element. */
31         public static Node getOrAddDbk(Node parent, DbkType child) {
32                 try {
33                         if (!parent.hasNode(child.get())) {
34                                 return addDbk(parent, child);
35                         } else {
36                                 return parent.getNode(child.get());
37                         }
38                 } catch (RepositoryException e) {
39                         throw new JcrException("Cannot get or add element " + child.get() + " to " + parent, e);
40                 }
41         }
42
43         /** Add a DocBook element to this node. */
44         public static Node addDbk(Node parent, DbkType child) {
45                 try {
46                         Node node = parent.addNode(child.get(), child.get());
47                         return node;
48                 } catch (RepositoryException e) {
49                         throw new JcrException("Cannot add element " + child.get() + " to " + parent, e);
50                 }
51         }
52
53         /** Whether this DocBook element is of this type. */
54         public static boolean isDbk(Node node, DbkType type) {
55                 return Jcr.getName(node).equals(type.get());
56         }
57
58         /** Whether this node is a DocBook type. */
59         public static boolean isDbk(Node node) {
60                 String name = Jcr.getName(node);
61                 for (DbkType type : DbkType.values()) {
62                         if (name.equals(type.get()))
63                                 return true;
64                 }
65                 return false;
66         }
67
68         public static String getTitle(Node node) {
69                 return JcrxApi.getXmlValue(node, DbkType.title.get());
70         }
71
72         public static void setTitle(Node node, String txt) {
73                 Node titleNode = getOrAddDbk(node, DbkType.title);
74                 JcrxApi.setXmlValue(node, titleNode, txt);
75         }
76
77         public static Node getMetadata(Node infoContainer) {
78                 try {
79                         if (!infoContainer.hasNode(DbkType.info.get()))
80                                 return null;
81                         Node info = infoContainer.getNode(DbkType.info.get());
82                         if (!info.hasNode(EntityType.local.get()))
83                                 return null;
84                         return info.getNode(EntityType.local.get());
85                 } catch (RepositoryException e) {
86                         throw new JcrException("Cannot retrieve metadata from " + infoContainer, e);
87                 }
88         }
89
90         public static Node getChildByRole(Node parent, String role) {
91                 try {
92                         NodeIterator baseSections = parent.getNodes();
93                         while (baseSections.hasNext()) {
94                                 Node n = baseSections.nextNode();
95                                 String r = Jcr.get(n, DbkAttr.role.name());
96                                 if (r != null && r.equals(role))
97                                         return n;
98                         }
99                         return null;
100                 } catch (RepositoryException e) {
101                         throw new JcrException("Cannot get child from " + parent + " with role " + role, e);
102                 }
103         }
104
105         public static Node addParagraph(Node node, String txt) {
106                 Node p = addDbk(node, para);
107                 JcrxApi.setXmlValue(node, p, txt);
108                 return p;
109         }
110
111         /**
112          * Removes a paragraph if it empty. The sesison is not saved.
113          * 
114          * @return true if the paragraph was empty and it was removed
115          */
116         public static boolean removeIfEmptyParagraph(Node node) {
117                 try {
118                         if (isDbk(node, DbkType.para)) {
119                                 NodeIterator nit = node.getNodes();
120                                 if (!nit.hasNext()) {
121                                         node.remove();
122                                         return true;
123                                 }
124                                 Node first = nit.nextNode();
125                                 if (nit.hasNext())
126                                         return false;
127                                 if (first.getName().equals(Jcr.JCR_XMLTEXT)) {
128                                         String str = JcrxApi.getXmlValue(first);
129                                         if (str != null && str.trim().equals("")) {
130                                                 node.remove();
131                                                 return true;
132                                         }
133                                 } else {
134                                         return false;
135                                 }
136                         }
137                         return false;
138                 } catch (RepositoryException e) {
139                         throw new JcrException("Cannot remove possibly empty paragraph", e);
140                 }
141         }
142
143         public static Node insertImageAfter(Node sibling) {
144                 try {
145
146                         Node parent = sibling.getParent();
147                         Node mediaNode = addDbk(parent, DbkType.mediaobject);
148                         // TODO optimise?
149                         parent.orderBefore(mediaNode.getName() + "[" + mediaNode.getIndex() + "]",
150                                         sibling.getName() + "[" + sibling.getIndex() + "]");
151                         parent.orderBefore(sibling.getName() + "[" + sibling.getIndex() + "]",
152                                         mediaNode.getName() + "[" + mediaNode.getIndex() + "]");
153
154                         Node imageNode = addDbk(mediaNode, DbkType.imageobject);
155                         Node imageDataNode = addDbk(imageNode, DbkType.imagedata);
156 //                      Node infoNode = imageNode.addNode(DocBookTypes.INFO, DocBookTypes.INFO);
157 //                      Node fileNode = JcrUtils.copyBytesAsFile(mediaFolder, EntityType.box.get(), new byte[0]);
158 //                      fileNode.addMixin(EntityType.box.get());
159 //                      fileNode.setProperty(EntityNames.SVG_WIDTH, 0);
160 //                      fileNode.setProperty(EntityNames.SVG_LENGTH, 0);
161 //                      fileNode.addMixin(NodeType.MIX_MIMETYPE);
162 //
163 //                      // we assume this is a folder next to the main DocBook document
164 //                      // TODO make it more robust and generic
165 //                      String fileRef = mediaNode.getName();
166 //                      imageDataNode.setProperty(DocBookNames.DBK_FILEREF, fileRef);
167                         return mediaNode;
168                 } catch (RepositoryException e) {
169                         throw new JcrException("Cannot insert empty image after " + sibling, e);
170                 }
171         }
172
173         public static Node insertVideoAfter(Node sibling) {
174                 try {
175
176                         Node parent = sibling.getParent();
177                         Node mediaNode = addDbk(parent, DbkType.mediaobject);
178                         // TODO optimise?
179                         parent.orderBefore(mediaNode.getName() + "[" + mediaNode.getIndex() + "]",
180                                         sibling.getName() + "[" + sibling.getIndex() + "]");
181                         parent.orderBefore(sibling.getName() + "[" + sibling.getIndex() + "]",
182                                         mediaNode.getName() + "[" + mediaNode.getIndex() + "]");
183
184                         Node videoNode = addDbk(mediaNode, DbkType.videoobject);
185                         Node videoDataNode = addDbk(videoNode, DbkType.videodata);
186                         return mediaNode;
187                 } catch (RepositoryException e) {
188                         throw new JcrException("Cannot insert empty image after " + sibling, e);
189                 }
190         }
191
192         public static String getMediaFileref(Node node) {
193                 try {
194                         Node mediadata;
195                         if (node.hasNode(DbkType.imageobject.get())) {
196                                 mediadata = node.getNode(DbkType.imageobject.get()).getNode(DbkType.imagedata.get());
197                         } else if (node.hasNode(DbkType.videoobject.get())) {
198                                 mediadata = node.getNode(DbkType.videoobject.get()).getNode(DbkType.videodata.get());
199                         } else {
200                                 throw new IllegalArgumentException("Fileref not found in " + node);
201                         }
202
203                         if (mediadata.hasProperty(DbkAttr.fileref.name())) {
204                                 return mediadata.getProperty(DbkAttr.fileref.name()).getString();
205                         } else {
206                                 return null;
207                         }
208                 } catch (RepositoryException e) {
209                         throw new JcrException("Cannot retrieve file ref from " + node, e);
210                 }
211         }
212
213         public static void exportXml(Node node, OutputStream out) throws IOException {
214                 try {
215                         node.getSession().exportDocumentView(node.getPath(), out, false, false);
216                 } catch (RepositoryException e) {
217                         throw new JcrException("Cannot export " + node + " to XML", e);
218                 }
219         }
220
221         public static void exportToFs(Node baseNode, DbkType type, Path directory) {
222                 String fileName = Jcr.getName(baseNode) + ".dbk.xml";
223                 Path filePath = directory.resolve(fileName);
224                 Node docBookNode = Jcr.getNode(baseNode, type.get());
225                 if (docBookNode == null)
226                         throw new IllegalArgumentException("No " + type.get() + " under " + baseNode);
227                 try {
228                         Files.createDirectories(directory);
229                         try (OutputStream out = Files.newOutputStream(filePath)) {
230                                 exportXml(docBookNode, out);
231                         }
232                         JcrUtils.copyFilesToFs(baseNode, directory, true);
233                         if (log.isDebugEnabled())
234                                 log.debug("DocBook " + baseNode + " exported to " + filePath.toAbsolutePath());
235                 } catch (IOException e) {
236                         throw new RuntimeException(e);
237                 }
238         }
239
240         public static void importXml(Node baseNode, InputStream in) throws IOException {
241                 try {
242                         baseNode.getSession().importXML(baseNode.getPath(), in,
243                                         ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING);
244                 } catch (RepositoryException e) {
245                         throw new JcrException("Cannot import XML to " + baseNode, e);
246                 }
247
248         }
249
250         /** Singleton. */
251         private DbkUtils() {
252         }
253
254 }