package org.argeo.jcr.xml; import java.io.IOException; import java.io.Writer; import java.util.Map; import java.util.TreeMap; import javax.jcr.Node; import javax.jcr.NodeIterator; import javax.jcr.Property; import javax.jcr.PropertyIterator; import javax.jcr.RepositoryException; import javax.jcr.Value; import javax.jcr.nodetype.NodeType; import org.argeo.jcr.Jcr; /** Utilities around JCR and XML. */ public class JcrXmlUtils { /** * Convenience method calling {@link #toXmlElements(Writer, Node, boolean)} with * false. */ public static void toXmlElements(Writer writer, Node node) throws RepositoryException, IOException { toXmlElements(writer, node, null, false, false, false); } /** * Write JCR properties as XML elements in a tree structure whose elements are * named by node primary type. * * @param writer the writer to use * @param node the subtree * @param depth maximal depth, or if null the whole * subtree. It must be positive, with depth 0 * describing just the node without its children. * @param withMetadata whether to write the primary type and mixins as * elements * @param withPrefix whether to keep the namespace prefixes * @param propertiesAsElements whether single properties should be written as * elements rather than attributes. If * false, multiple properties will be * skipped. */ public static void toXmlElements(Writer writer, Node node, Integer depth, boolean withMetadata, boolean withPrefix, boolean propertiesAsElements) throws RepositoryException, IOException { if (depth != null && depth < 0) throw new IllegalArgumentException("Depth " + depth + " is negative."); if (node.getName().equals(Jcr.JCR_XMLTEXT)) { writer.write(node.getProperty(Jcr.JCR_XMLCHARACTERS).getString()); return; } if (!propertiesAsElements) { Map attrs = new TreeMap<>(); PropertyIterator pit = node.getProperties(); properties: while (pit.hasNext()) { Property p = pit.nextProperty(); if (!p.isMultiple()) { String pName = p.getName(); if (!withMetadata && (pName.equals(Jcr.JCR_PRIMARY_TYPE) || pName.equals(Jcr.JCR_UUID) || pName.equals(Jcr.JCR_CREATED) || pName.equals(Jcr.JCR_CREATED_BY) || pName.equals(Jcr.JCR_LAST_MODIFIED) || pName.equals(Jcr.JCR_LAST_MODIFIED_BY))) continue properties; attrs.put(withPrefix(p.getName(), withPrefix), p.getString()); } } if (withMetadata && node.hasProperty(Property.JCR_UUID)) attrs.put("id", "urn:uuid:" + node.getProperty(Property.JCR_UUID).getString()); attrs.put(withPrefix ? Jcr.JCR_NAME : "name", node.getName()); writeStart(writer, withPrefix(node.getPrimaryNodeType().getName(), withPrefix), attrs, node.hasNodes()); } else { if (withMetadata && node.hasProperty(Property.JCR_UUID)) { writeStart(writer, withPrefix(node.getPrimaryNodeType().getName(), withPrefix), "id", "urn:uuid:" + node.getProperty(Property.JCR_UUID).getString()); } else { writeStart(writer, withPrefix(node.getPrimaryNodeType().getName(), withPrefix)); } // name writeStart(writer, withPrefix ? Jcr.JCR_NAME : "name"); writer.append(node.getName()); writeEnd(writer, withPrefix ? Jcr.JCR_NAME : "name"); } // mixins if (withMetadata) { for (NodeType mixin : node.getMixinNodeTypes()) { writeStart(writer, withPrefix ? Jcr.JCR_MIXIN_TYPES : "mixinTypes"); writer.append(mixin.getName()); writeEnd(writer, withPrefix ? Jcr.JCR_MIXIN_TYPES : "mixinTypes"); } } // properties as elements if (propertiesAsElements) { PropertyIterator pit = node.getProperties(); properties: while (pit.hasNext()) { Property p = pit.nextProperty(); if (p.isMultiple()) { for (Value value : p.getValues()) { writeStart(writer, withPrefix(p.getName(), withPrefix)); writer.write(value.getString()); writeEnd(writer, withPrefix(p.getName(), withPrefix)); } } else { Value value = p.getValue(); String pName = p.getName(); if (!withMetadata && (pName.equals(Jcr.JCR_PRIMARY_TYPE) || pName.equals(Jcr.JCR_UUID) || pName.equals(Jcr.JCR_CREATED) || pName.equals(Jcr.JCR_CREATED_BY) || pName.equals(Jcr.JCR_LAST_MODIFIED) || pName.equals(Jcr.JCR_LAST_MODIFIED_BY))) continue properties; writeStart(writer, withPrefix(p.getName(), withPrefix)); writer.write(value.getString()); writeEnd(writer, withPrefix(p.getName(), withPrefix)); } } } // children if (node.hasNodes()) { if (depth == null || depth > 0) { NodeIterator nit = node.getNodes(); while (nit.hasNext()) { toXmlElements(writer, nit.nextNode(), depth == null ? null : depth - 1, withMetadata, withPrefix, propertiesAsElements); } } writeEnd(writer, withPrefix(node.getPrimaryNodeType().getName(), withPrefix)); } } private static String withPrefix(String str, boolean withPrefix) { if (withPrefix) return str; int index = str.indexOf(':'); if (index < 0) return str; return str.substring(index + 1); } private static void writeStart(Writer writer, String tagName) throws IOException { writer.append('<'); writer.append(tagName); writer.append('>'); } private static void writeStart(Writer writer, String tagName, String attr, String value) throws IOException { writer.append('<'); writer.append(tagName); writer.append(' '); writer.append(attr); writer.append("=\""); writer.append(value); writer.append("\">"); } private static void writeStart(Writer writer, String tagName, Map attrs, boolean hasChildren) throws IOException { writer.append('<'); writer.append(tagName); for (String attr : attrs.keySet()) { writer.append(' '); writer.append(attr); writer.append("=\""); writer.append(attrs.get(attr)); writer.append('\"'); } if (hasChildren) writer.append('>'); else writer.append("/>"); } private static void writeEnd(Writer writer, String tagName) throws IOException { writer.append("'); } /** Singleton. */ private JcrXmlUtils() { } }