package org.argeo.docbook; import static org.argeo.docbook.DbkType.para; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import javax.jcr.ImportUUIDBehavior; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.ValueFormatException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.entity.EntityType; import org.argeo.jcr.Jcr; import org.argeo.jcr.JcrException; import org.argeo.jcr.JcrUtils; import org.argeo.jcr.JcrxApi; /** Utilities around DocBook. */ public class DbkUtils { private final static Log log = LogFactory.getLog(DbkUtils.class); /** Get or add a DocBook element. */ public static Node getOrAddDbk(Node parent, DbkType child) { try { if (!parent.hasNode(child.get())) { return addDbk(parent, child); } else { return parent.getNode(child.get()); } } catch (RepositoryException e) { throw new JcrException("Cannot get or add element " + child.get() + " to " + parent, e); } } /** Add a DocBook element to this node. */ public static Node addDbk(Node parent, DbkType child) { try { Node node = parent.addNode(child.get(), child.get()); return node; } catch (RepositoryException e) { throw new JcrException("Cannot add element " + child.get() + " to " + parent, e); } } /** Whether this DocBook element is of this type. */ public static boolean isDbk(Node node, DbkType type) { return Jcr.getName(node).equals(type.get()); } /** Whether this node is a DocBook type. */ public static boolean isDbk(Node node) { String name = Jcr.getName(node); for (DbkType type : DbkType.values()) { if (name.equals(type.get())) return true; } return false; } public static String getTitle(Node node) { return JcrxApi.getXmlValue(node, DbkType.title.get()); } public static void setTitle(Node node, String txt) { Node titleNode = getOrAddDbk(node, DbkType.title); JcrxApi.setXmlValue(node, titleNode, txt); } public static Node getMetadata(Node infoContainer) { try { if (!infoContainer.hasNode(DbkType.info.get())) return null; Node info = infoContainer.getNode(DbkType.info.get()); if (!info.hasNode(EntityType.local.get())) return null; return info.getNode(EntityType.local.get()); } catch (RepositoryException e) { throw new JcrException("Cannot retrieve metadata from " + infoContainer, e); } } public static Node getChildByRole(Node parent, String role) { try { NodeIterator baseSections = parent.getNodes(); while (baseSections.hasNext()) { Node n = baseSections.nextNode(); String r = Jcr.get(n, DbkAttr.role.name()); if (r != null && r.equals(role)) return n; } return null; } catch (RepositoryException e) { throw new JcrException("Cannot get child from " + parent + " with role " + role, e); } } public static Node addParagraph(Node node, String txt) { Node p = addDbk(node, para); JcrxApi.setXmlValue(node, p, txt); return p; } /** * Removes a paragraph if it empty. The sesison is not saved. * * @return true if the paragraph was empty and it was removed */ public static boolean removeIfEmptyParagraph(Node node) { try { if (isDbk(node, DbkType.para)) { NodeIterator nit = node.getNodes(); if (!nit.hasNext()) { node.remove(); return true; } Node first = nit.nextNode(); if (nit.hasNext()) return false; if (first.getName().equals(Jcr.JCR_XMLTEXT)) { String str = JcrxApi.getXmlValue(first); if (str != null && str.trim().equals("")) { node.remove(); return true; } } else { return false; } } return false; } catch (RepositoryException e) { throw new JcrException("Cannot remove possibly empty paragraph", e); } } public static Node insertImageAfter(Node sibling) { try { Node parent = sibling.getParent(); Node mediaNode = addDbk(parent, DbkType.mediaobject); // TODO optimise? parent.orderBefore(mediaNode.getName() + "[" + mediaNode.getIndex() + "]", sibling.getName() + "[" + sibling.getIndex() + "]"); parent.orderBefore(sibling.getName() + "[" + sibling.getIndex() + "]", mediaNode.getName() + "[" + mediaNode.getIndex() + "]"); Node imageNode = addDbk(mediaNode, DbkType.imageobject); Node imageDataNode = addDbk(imageNode, DbkType.imagedata); // Node infoNode = imageNode.addNode(DocBookTypes.INFO, DocBookTypes.INFO); // Node fileNode = JcrUtils.copyBytesAsFile(mediaFolder, EntityType.box.get(), new byte[0]); // fileNode.addMixin(EntityType.box.get()); // fileNode.setProperty(EntityNames.SVG_WIDTH, 0); // fileNode.setProperty(EntityNames.SVG_LENGTH, 0); // fileNode.addMixin(NodeType.MIX_MIMETYPE); // // // we assume this is a folder next to the main DocBook document // // TODO make it more robust and generic // String fileRef = mediaNode.getName(); // imageDataNode.setProperty(DocBookNames.DBK_FILEREF, fileRef); return mediaNode; } catch (RepositoryException e) { throw new JcrException("Cannot insert empty image after " + sibling, e); } } public static Node insertVideoAfter(Node sibling) { try { Node parent = sibling.getParent(); Node mediaNode = addDbk(parent, DbkType.mediaobject); // TODO optimise? parent.orderBefore(mediaNode.getName() + "[" + mediaNode.getIndex() + "]", sibling.getName() + "[" + sibling.getIndex() + "]"); parent.orderBefore(sibling.getName() + "[" + sibling.getIndex() + "]", mediaNode.getName() + "[" + mediaNode.getIndex() + "]"); Node videoNode = addDbk(mediaNode, DbkType.videoobject); Node videoDataNode = addDbk(videoNode, DbkType.videodata); return mediaNode; } catch (RepositoryException e) { throw new JcrException("Cannot insert empty image after " + sibling, e); } } public static String getMediaFileref(Node node) { try { Node mediadata; if (node.hasNode(DbkType.imageobject.get())) { mediadata = node.getNode(DbkType.imageobject.get()).getNode(DbkType.imagedata.get()); } else if (node.hasNode(DbkType.videoobject.get())) { mediadata = node.getNode(DbkType.videoobject.get()).getNode(DbkType.videodata.get()); } else { throw new IllegalArgumentException("Fileref not found in " + node); } if (mediadata.hasProperty(DbkAttr.fileref.name())) { return mediadata.getProperty(DbkAttr.fileref.name()).getString(); } else { return null; } } catch (RepositoryException e) { throw new JcrException("Cannot retrieve file ref from " + node, e); } } public static void exportXml(Node node, OutputStream out) throws IOException { try { node.getSession().exportDocumentView(node.getPath(), out, false, false); } catch (RepositoryException e) { throw new JcrException("Cannot export " + node + " to XML", e); } } public static void exportToFs(Node baseNode, DbkType type, Path directory) { String fileName = Jcr.getName(baseNode) + ".dbk.xml"; Path filePath = directory.resolve(fileName); Node docBookNode = Jcr.getNode(baseNode, type.get()); if (docBookNode == null) throw new IllegalArgumentException("No " + type.get() + " under " + baseNode); try { Files.createDirectories(directory); try (OutputStream out = Files.newOutputStream(filePath)) { exportXml(docBookNode, out); } JcrUtils.copyFilesToFs(baseNode, directory, true); if (log.isDebugEnabled()) log.debug("DocBook " + baseNode + " exported to " + filePath.toAbsolutePath()); } catch (IOException e) { throw new RuntimeException(e); } } public static void importXml(Node baseNode, InputStream in) throws IOException { try { baseNode.getSession().importXML(baseNode.getPath(), in, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); } catch (RepositoryException e) { throw new JcrException("Cannot import XML to " + baseNode, e); } } /** Singleton. */ private DbkUtils() { } }