jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ju...@apache.org
Subject svn commit: r328808 - /incubator/jackrabbit/trunk/contrib/jcr-ext/src/java/org/apache/jackrabbit/xml/DocumentViewExportVisitor.java
Date Thu, 27 Oct 2005 06:42:34 GMT
Author: jukka
Date: Wed Oct 26 23:42:28 2005
New Revision: 328808

URL: http://svn.apache.org/viewcvs?rev=328808&view=rev
Log:
JCR-EXT: Upgrade DocumentViewExportVisitor to JCR 1.0.

Modified:
    incubator/jackrabbit/trunk/contrib/jcr-ext/src/java/org/apache/jackrabbit/xml/DocumentViewExportVisitor.java

Modified: incubator/jackrabbit/trunk/contrib/jcr-ext/src/java/org/apache/jackrabbit/xml/DocumentViewExportVisitor.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/contrib/jcr-ext/src/java/org/apache/jackrabbit/xml/DocumentViewExportVisitor.java?rev=328808&r1=328807&r2=328808&view=diff
==============================================================================
--- incubator/jackrabbit/trunk/contrib/jcr-ext/src/java/org/apache/jackrabbit/xml/DocumentViewExportVisitor.java
(original)
+++ incubator/jackrabbit/trunk/contrib/jcr-ext/src/java/org/apache/jackrabbit/xml/DocumentViewExportVisitor.java
Wed Oct 26 23:42:28 2005
@@ -19,8 +19,9 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
-import javax.jcr.Item;
 import javax.jcr.ItemVisitor;
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
@@ -34,11 +35,7 @@
 import javax.jcr.ValueFormatException;
 
 import org.apache.commons.codec.binary.Base64;
-import org.apache.jackrabbit.name.IllegalNameException;
-import org.apache.jackrabbit.name.NoPrefixDeclaredException;
 import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.name.SessionNamespaceResolver;
-import org.apache.jackrabbit.name.UnknownPrefixException;
 import org.apache.xerces.util.XMLChar;
 import org.xml.sax.Attributes;
 import org.xml.sax.ContentHandler;
@@ -64,7 +61,7 @@
  * contains the titles of the first two levels of the node tree.
  * <pre>
  *     ContentHandler handler = ...;
- *     Node parent = ...;
+ *     final Node parent = ...;
  *     parent.accept(
  *         new DocumentViewExportVisitor(handler, true, false) {
  *
@@ -75,7 +72,7 @@
  *
  *             protected boolean includeNode(Node node)
  *                     throws RepositoryException {
- *                 return (node.getDepth() <= root.getDepth() + 2);
+ *                 return (node.getDepth() <= parent.getDepth() + 2);
  *             }
  *
  *         });
@@ -103,24 +100,24 @@
  * <p>
  * The companion method
  * Session.exportDocumentView(String, OutputStream, boolean, boolean)
- * can be implemented in terms of the above method and the XMLSerializer
- * class from the Xerces library:
+ * can be implemented in terms of the above method:
  * <pre>
- * import org.apache.xml.serialize.XMLSerializer;
- * import org.apache.xml.serialize.OutputFormat;
- *
  *     public void exportDocumentView(
  *             String absPath, OutputStream output,
  *             boolean skipBinary, boolean noRecurse)
  *             throws PathNotFoundException, IOException, RepositoryException {
  *         try {
- *             XMLSerializer serializer =
- *                 new XMLSerializer(output, new OutputFormat());
- *             exportDocView(
- *                     absPath, serializer.asContentHandler(),
- *                     binaryAsLink, noRecurse);
+ *             SAXTransformerFactory factory = (SAXTransformerFactory)
+ *                 SAXTransformerFactory.newInstance();
+ *             TransformerHandler handler = factory.newTransformerHandler();
+ *             handler.setResult(new StreamResult(out));
+ *             exportDocumentView(absPath, handler, skipBinary, noRecurse);
+ *         } catch (TransformerConfigurationException e) {
+ *             throw new IOException(
+ *                     "Unable to configure a SAX transformer: " + e.getMessage());
  *         } catch (SAXException e) {
- *             throw new IOException(e.getMessage());
+ *             throw new IOException(
+ *                     "Unable to serialize a SAX stream: " + e.getMessage());
  *         }
  *     }
  * </pre>
@@ -150,7 +147,18 @@
      * The root node of the serialization tree. This is the node that
      * is mapped to the root element of the serialized XML stream.
      */
-    protected Node root;
+    private Node root;
+
+    /**
+     * The current session.
+     */
+    private Session session;
+
+    /**
+     * The prefix mapped to the <code>http://www.jcp.org/jcr/1.0</code>
+     * namespace in the current session.
+     */
+    private String jcr;
 
     /**
      * Creates an visitor for exporting content using the document view
@@ -199,19 +207,32 @@
             // start document
             if (root == null) {
                 root = node;
+                session = node.getSession();
+                jcr = session.getNamespacePrefix(QName.NS_JCR_URI); 
                 handler.startDocument();
 
-                Session session = root.getSession();
                 String[] prefixes = session.getNamespacePrefixes();
                 for (int i = 0; i < prefixes.length; i++) {
-                    handler.startPrefixMapping(prefixes[i],
-                            session.getNamespaceURI(prefixes[i]));
+                    String uri = session.getNamespaceURI(prefixes[i]);
+                    if (!uri.equals(QName.NS_XML_URI)) {
+                        handler.startPrefixMapping(prefixes[i], uri);
+                    }
                 }
             }
 
             // export current node
-            if (!node.getName().equals(QName.JCR_XMLTEXT)) {
-                exportNode(node);
+            String name = node.getName();
+            if (!name.equals(jcr + ":xmltext")) {
+                int colon = name.indexOf(':');
+                if (colon != -1) {
+                    String prefix = name.substring(0, colon);
+                    name = name.substring(colon + 1);
+                    exportNode(node, prefix, escapeName(name));
+                } else if (name.length() > 0) {
+                    exportNode(node, "", escapeName(name));
+                } else {
+                    exportNode(node, jcr, "root");
+                }
             } else if (node != root) {
                 exportText(node);
             } else {
@@ -220,8 +241,17 @@
 
             // end document
             if (root == node) {
+                String[] prefixes = session.getNamespacePrefixes();
+                for (int i = 0; i < prefixes.length; i++) {
+                    String uri = session.getNamespaceURI(prefixes[i]);
+                    if (!uri.equals(QName.NS_XML_URI)) {
+                        handler.endPrefixMapping(prefixes[i]);
+                    }
+                }
                 handler.endDocument();
             }
+        } catch (IOException e) {
+            throw new RepositoryException(e);
         } catch (SAXException e) {
             throw new RepositoryException(e);
         }
@@ -274,14 +304,9 @@
     private void exportText(Node node)
             throws SAXException, RepositoryException {
         try {
-            Property property = node.getProperty(
-                    QName.JCR_XMLCHARACTERS.toJCRName(
-                            new SessionNamespaceResolver(node.getSession())));
-            char[] characters = property.getString().toCharArray();
+            Property property = node.getProperty(jcr + ":xmlcharacters");
+            char[] characters = filterXML(property.getString());
             handler.characters(characters, 0, characters.length);
-        } catch (NoPrefixDeclaredException ex) {
-            throw new RepositoryException(
-                    "The JCR namespace prefix is not available", ex);
         } catch (PathNotFoundException ex) {
             // ignore empty jcr:xmltext nodes
         } catch (ValueFormatException ex) {
@@ -291,26 +316,33 @@
 
     /**
      * Serializes the given node to the XML stream. Generates an element
-     * with the same name as this node, and maps node properties to
-     * attributes of the generated element. If the noRecurse flag is false,
-     * then child nodes are serialized as sub-elements.
+     * with the given name, and maps node properties to attributes of the
+     * generated element. If the noRecurse flag is false, then child nodes
+     * are serialized as sub-elements.
      *
      * @param node the given node
+     * @param prefix namespace prefix
+     * @param name escaped local name
+     * @throws IOException if a problem with binary values occurred
      * @throws SAXException on SAX errors
      * @throws RepositoryException on repository errors
      */
-    private void exportNode(Node node)
-            throws SAXException, RepositoryException {
-        QName name = getName(node);
-        String localName = escapeName(name.getLocalName());
-        String prefixedName =
-            node.getSession().getNamespacePrefix(name.getNamespaceURI())
-            + ":" + localName;
+    private void exportNode(Node node, String prefix, String name)
+            throws IOException, SAXException, RepositoryException {
+        // Set up element name components
+        String prefixedName = name;
+        if (prefix.length() > 0) {
+            prefixedName = prefix + ":" + name;
+        } else {
+            prefixedName = name;
+        }
+        String uri = session.getNamespaceURI(prefix);
+        if (uri.length() == 0) {
+            uri = null;
+        }
 
         // Start element
-        handler.startElement(
-                name.getNamespaceURI(), localName, prefixedName,
-                getAttributes(node));
+        handler.startElement(uri, name, prefixedName, getAttributes(node));
 
         // Visit child nodes (unless denied by the noRecurse flag)
         if (!noRecurse) {
@@ -324,8 +356,7 @@
         }
 
         // End element
-        handler.endElement(
-                name.getNamespaceURI(), name.getLocalName(), node.getName());
+        handler.endElement(uri, name, prefixedName);
     }
 
     /**
@@ -335,111 +366,83 @@
      *
      * @param node the given node
      * @return document view attributes of the node
+     * @throws IOException if a problem with binary values occurred
      * @throws RepositoryException on repository errors
      */
-    private Attributes getAttributes(Node node) throws RepositoryException {
-        try {
-            AttributesImpl attributes = new AttributesImpl();
-            
-            PropertyIterator properties = node.getProperties();
-            while (properties.hasNext()) {
-                Property property = properties.nextProperty();
-                /*
-                 return !property.getName().equals(XMLCHARACTERS)
-                 && (!skipBinary || property.getType() != PropertyType.BINARY);
-                 */
-                if (includeProperty(property)) {
-                    QName name = getName(property);
-                    attributes.addAttribute(
-                            name.getNamespaceURI(),
-                            escapeName(name.getLocalName()),
-                            escapeName(name.toJCRName(
-                                    new SessionNamespaceResolver(property.getSession()))),
-                                    "CDATA", escapeValue(property));
+    private Attributes getAttributes(Node node)
+            throws IOException, RepositoryException {
+        AttributesImpl attributes = new AttributesImpl();
+        
+        PropertyIterator properties = node.getProperties();
+        while (properties.hasNext()) {
+            Property property = properties.nextProperty();
+            if (!(skipBinary && property.getType() == PropertyType.BINARY)
+                    && includeProperty(property)) {
+                String name = property.getName();
+                String value = escapeValue(property);
+
+                String prefixedName;
+                String uri;
+                int colon = name.indexOf(':');
+                if (colon != -1) {
+                    String prefix = name.substring(0, colon);
+                    uri = session.getNamespaceURI(prefix);
+                    name = escapeName(name.substring(colon + 1));
+                    prefixedName = prefix + ":" + name;
+                } else {
+                    uri = session.getNamespaceURI("");
+                    name = escapeName(name);
+                    prefixedName = name;
                 }
+                attributes.addAttribute(uri, name, prefixedName, "CDATA", value);
             }
-            
-            return attributes;
-        } catch (NoPrefixDeclaredException e) {
-            throw new RepositoryException(e);
         }
+        
+        return attributes;
     }
 
-    /**
-     * Returns the qualified XML name of the given item. If the item name
-     * is prefixed, then the respective namespace URI is looked up from the
-     * session and returned as a part of the qualified name. If the item
-     * name is not prefixed, then the returned qualified name will use the
-     * null namespace and the default prefix. The local part of the qualified
-     * name will be escaped using ISO 9075 rules. If the given item is the
-     * root node, then the special name "jcr:root" is returned
-     * <p>
-     * See section 6.4.2 of the JCR specification for more details about
-     * name handling in the XML document view serialization.
-     *
-     * @param item the given item
-     * @return qualified XML name of the item
-     * @throws RepositoryException on repository errors
-     * @see Name
-     */
-    private QName getName(Item item) throws RepositoryException {
-        try {
-            String name = item.getName();
-            if (name.length() > 0) {
-                return QName.fromJCRName(name, new SessionNamespaceResolver(item.getSession()));
-            } else {
-                return QName.JCR_ROOT;
+    private static char[] filterXML(String value) {
+        char[] characters = value.toCharArray();
+        for (int i = 0; i < characters.length; i++) {
+            if (XMLChar.isInvalid(characters[i])) {
+                characters[i] = ' '; // TODO: What's the correct escape?
             }
-        } catch (IllegalNameException e) {
-            throw new RepositoryException(e);
-        } catch (UnknownPrefixException e) {
-            throw new RepositoryException(e);
         }
+        return characters;
     }
 
     /**
-     * Escapes the given character into a hex escape sequence
-     * <code>_xXXXX_</code> where <code>XXXX</code> is the (uppercase)
-     * hexadecimal representation of the given character.
-     *
-     * @param ch character to be escaped
-     * @return escape string
-     */
-    private static String escapeChar(char ch) {
-        String hex = "000" + Integer.toHexString((int) ch).toUpperCase();
-        return "_x" + hex.substring(hex.length() - 4) + "_";
-    }
-
-    /**
-     * Escapes the given name or prefix according to the rules of section
-     * 6.4.3 of the JSR 170 specification (version 0.16.2).
+     * Escapes the given JCR name according to the rules of section
+     * 6.4.3 of the JSR 170 specification.
      *
-     * @param name original name or prefix
-     * @return escaped name or prefix
+     * @param name JCR name
+     * @return escaped name
      */
     private static String escapeName(String name) {
-        if (name.length() == 0) {
-            return name;
-        }
-
         StringBuffer buffer = new StringBuffer();
 
-        // First character
-        if (!XMLChar.isNameStart(name.charAt(0))
-            || name.startsWith("_x")
-            || (name.length() >= 3
-                && "xml".equalsIgnoreCase(name.substring(0, 3)))) {
-            buffer.append(escapeChar(name.charAt(0)));
-        } else {
-            buffer.append(name.charAt(0));
-        }
-
-        // Rest of the characters
-        for (int i = 1; i < name.length(); i++) {
-            if (!XMLChar.isName(name.charAt(i)) || name.startsWith("_x", i)) {
-                buffer.append(escapeChar(name.charAt(i)));
+        int colon = name.indexOf(':');
+        if (colon != -1) {
+            buffer.append(name.substring(0, colon + 1));
+            name = name.substring(colon + 1);
+        }
+
+        Pattern pattern = Pattern.compile("_([0-9a-fA-F]{4}_)");
+        Matcher matcher = pattern.matcher(name);
+        char[] characters = filterXML(matcher.replaceAll("_x005f_$1"));
+
+        for (int i = 0; i < characters.length; i++) {
+            char ch = characters[i];
+            if ((i == 0) ? XMLChar.isNCNameStart(ch) : XMLChar.isNCName(ch)) {
+                String hex = Integer.toHexString((int) ch);
+                buffer.append("_x");
+                for (int j = 4; j > hex.length(); j--) {
+                    buffer.append('0');
+                }
+                buffer.append(hex);
+                buffer.append('_');
             } else {
-                buffer.append(name.charAt(i));
+                buffer.append(ch);
             }
         }
 
@@ -447,26 +450,52 @@
     }
 
     /**
-     * Escapes the given value according to the rules of section 6.4.4 of
-     * the JSR 170 specification (version 0.16.2).
+     * Returns the string representation of the given value. Binary values
+     * are encoded in Base64, while other values are just converted to their
+     * string format.
      *
      * @param value original value
+     * @param escape whether to apply value escapes
      * @return escaped value
+     * @throws IOException if a problem with binary values occurred
+     * @throws RepositoryException on repository errors
      */
-    private static String escapeValue(String value) {
-        StringBuffer buffer = new StringBuffer();
-
-        for (int i = 1; i < value.length(); i++) {
-            if (value.charAt(i) == ' ') {
-                buffer.append("_x0020_");
-            } else if (value.startsWith("_x", i)) {
-                buffer.append("_x005F_");
-            } else {
-                buffer.append(value.charAt(i));
+    private static String escapeValue(Value value, boolean escape)
+            throws IOException, RepositoryException {
+        if (value.getType() == PropertyType.BINARY) {
+            ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+            InputStream input = value.getStream();
+            try {
+                byte[] bytes = new byte[4096];
+                for (int n = input.read(bytes); n != -1; n = input.read(bytes)) {
+                    buffer.write(bytes, 0, n);
+                }
+            } finally {
+                input.close();
+            }
+            return new String(Base64.encodeBase64(buffer.toByteArray()), "ASCII");
+        } else if (escape) {
+            StringBuffer buffer = new StringBuffer();
+            Pattern pattern = Pattern.compile("_([0-9a-fA-F]{4}_)");
+            Matcher matcher = pattern.matcher(value.getString());
+            char[] characters = filterXML(matcher.replaceAll("_x005f_$1"));
+            for (int i = 0; i < characters.length; i++) {
+                if (characters[i] == ' ') {
+                    buffer.append("_x0020_");
+                } else if (characters[i] == '\t') {
+                    buffer.append("_x0009_");
+                } else if (characters[i] == '\r') {
+                    buffer.append("_x000D_");
+                } else if (characters[i] == '\n') {
+                    buffer.append("_x000A_");
+                } else {
+                    buffer.append(characters[i]);
+                }
             }
+            return buffer.toString();
+        } else {
+            return new String(filterXML(value.getString()));
         }
-
-        return buffer.toString();
     }
 
     /**
@@ -478,52 +507,24 @@
      *
      * @param property the given property
      * @return document view representation of the property value
+     * @throws IOException if a problem with binary values occurred
      * @throws RepositoryException on repository errors
      */
     private static String escapeValue(Property property)
-            throws RepositoryException {
-        try {
-            if (property.getDefinition().isMultiple()) {
-                StringBuffer buffer = new StringBuffer();
-
-                Value[] values = property.getValues();
-                for (int i = 0; i < values.length; i++) {
-                    if (i > 0) {
-                        buffer.append(' ');
-                    }
-                    if (values[i].getType() == PropertyType.BINARY) {
-                        buffer.append(encodeValue(values[i].getStream()));
-                    } else {
-                        buffer.append(escapeValue(values[i].getString()));
-                    }
+            throws IOException, RepositoryException {
+        if (property.getDefinition().isMultiple()) {
+            StringBuffer buffer = new StringBuffer();
+            Value[] values = property.getValues();
+            for (int i = 0; i < values.length; i++) {
+                if (i > 0) {
+                    buffer.append(' ');
                 }
-
-                return buffer.toString();
-            } else if (property.getType() == PropertyType.BINARY) {
-                return encodeValue(property.getStream());
-            } else {
-                return property.getString();
+                buffer.append(escapeValue(values[i], true));
             }
-        } catch (IOException ex) {
-            throw new RepositoryException(ex);
-        }
-    }
-
-    /**
-     * Encodes the given binary stream using Base64 encoding.
-     *
-     * @param input original binary value
-     * @return Base64-encoded value
-     * @throws IOException on IO errors
-     */
-    private static String encodeValue(InputStream input) throws IOException {
-        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
-        byte[] bytes = new byte[4096];
-        for (int n = input.read(bytes); n != -1; n = input.read(bytes)) {
-            buffer.write(bytes, 0, n);
+            return buffer.toString();
+        } else {
+            return escapeValue(property.getValue(), false);
         }
-        return
-            new String(Base64.encodeBase64(buffer.toByteArray()), "US-ASCII");
     }
 
 }



Mime
View raw message