You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by ju...@apache.org on 2006/03/22 23:38:27 UTC

svn commit: r387959 - in /jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml: DocViewImportHandler.java ImportHandler.java NamespaceContext.java SysViewImportHandler.java TargetImportHandler.java

Author: jukka
Date: Wed Mar 22 14:38:25 2006
New Revision: 387959

URL: http://svn.apache.org/viewcvs?rev=387959&view=rev
Log:
JCR-325: Applied the namespace-context.patch that makes the namespace context immutable and thus safe to use even after the processing of the current XML element.

Added:
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/NamespaceContext.java   (with props)
Modified:
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/ImportHandler.java
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java
    jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/TargetImportHandler.java

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java?rev=387959&r1=387958&r2=387959&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/DocViewImportHandler.java Wed Mar 22 14:38:25 2006
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.core.xml;
 
 import org.apache.jackrabbit.name.NameException;
-import org.apache.jackrabbit.name.NamespaceResolver;
 import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.util.ISO9075;
 import org.apache.jackrabbit.core.NodeId;
@@ -56,8 +55,8 @@
      * @param importer
      * @param nsContext
      */
-    DocViewImportHandler(Importer importer, NamespaceResolver nsContext) {
-        super(importer, nsContext);
+    DocViewImportHandler(Importer importer) {
+        super(importer);
     }
 
     /**

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/ImportHandler.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/ImportHandler.java?rev=387959&r1=387958&r2=387959&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/ImportHandler.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/ImportHandler.java Wed Mar 22 14:38:25 2006
@@ -16,10 +16,13 @@
  */
 package org.apache.jackrabbit.core.xml;
 
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
 import org.apache.jackrabbit.core.NamespaceRegistryImpl;
 import org.apache.jackrabbit.name.NamespaceResolver;
 import org.apache.jackrabbit.name.QName;
-import org.apache.jackrabbit.name.AbstractNamespaceResolver;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.xml.sax.Attributes;
@@ -28,7 +31,6 @@
 import org.xml.sax.SAXException;
 import org.xml.sax.SAXParseException;
 import org.xml.sax.helpers.DefaultHandler;
-import org.xml.sax.helpers.NamespaceSupport;
 
 import javax.jcr.NamespaceException;
 import javax.jcr.RepositoryException;
@@ -58,28 +60,22 @@
     protected final NamespaceResolver nsResolver;
 
     protected Locator locator;
-    protected ContentHandler targetHandler;
-    protected boolean systemViewXML;
-    protected boolean initialized;
 
-    protected final NamespaceContext nsContext;
+    private TargetImportHandler targetHandler;
 
     /**
-     * this flag is used to determine whether a namespace context needs to be
-     * started in the startElement event or if the namespace context has already
-     * been started in a preceeding startPrefixMapping event;
-     * the flag is set per element in the first startPrefixMapping event and is
-     * cleared again in the following startElement event;
+     * The local namespace mappings reported by
+     * {@link #startPrefixMapping(String, String)}. These mappings are used
+     * to instantiate the local namespace context in
+     * {@link #startElement(String, String, String, Attributes)}.
      */
-    protected boolean nsContextStarted;
+    private Map localNamespaceMappings;
 
     public ImportHandler(Importer importer, NamespaceResolver nsResolver,
                          NamespaceRegistryImpl nsReg) {
         this.importer = importer;
         this.nsResolver = nsResolver;
         this.nsReg = nsReg;
-
-        nsContext = new NamespaceContext();
     }
 
     //---------------------------------------------------------< ErrorHandler >
@@ -119,27 +115,18 @@
      * {@inheritDoc}
      */
     public void startDocument() throws SAXException {
-        systemViewXML = false;
-        initialized = false;
         targetHandler = null;
 
-        /**
-         * start initial context containing existing mappings reflected
-         * by nsResolver
-         */
-        nsContext.reset();
-        nsContext.pushContext();
         try {
+            localNamespaceMappings = new HashMap();
             String[] uris = nsReg.getURIs();
             for (int i = 0; i < uris.length; i++) {
-                nsContext.declarePrefix(nsResolver.getPrefix(uris[i]), uris[i]);
+                localNamespaceMappings.put(
+                        nsResolver.getPrefix(uris[i]), uris[i]);
             }
         } catch (RepositoryException re) {
             throw new SAXException(re);
         }
-
-        // initialize flag
-        nsContextStarted = false;
     }
 
     /**
@@ -148,48 +135,45 @@
     public void endDocument() throws SAXException {
         // delegate to target handler
         targetHandler.endDocument();
-        // cleanup
-        nsContext.reset();
     }
 
     /**
-     * {@inheritDoc}
+     * Records the given namespace mapping to be included in the local
+     * namespace context. The local namespace context is instantiated
+     * in {@link #startElement(String, String, String, Attributes)} using
+     * all the the namespace mappings recorded for the current XML element.
+     * <p>
+     * The namespace is also recorded in the persistent namespace registry
+     * unless it is already known.
+     *
+     * @param prefix namespace prefix
+     * @param uri namespace URI
      */
     public void startPrefixMapping(String prefix, String uri)
             throws SAXException {
-        // check if new context needs to be started
-        if (!nsContextStarted) {
-            // entering new namespace context
-            nsContext.pushContext();
-            nsContextStarted = true;
-        }
+        localNamespaceMappings.put(prefix, uri);
 
         try {
             // this will trigger NamespaceException if namespace is unknown
-            nsContext.getPrefix(uri);
+            nsReg.getPrefix(uri);
         } catch (NamespaceException nse) {
-            // namespace is not yet registered ...
-            String newPrefix;
-            if ("".equals(prefix)) {
-                /**
-                 * the xml document specifies a default namespace
-                 * (i.e. an empty prefix); we need to create a random
-                 * prefix as the empty prefix is reserved according
-                 * to the JCR spec.
-                 */
-                newPrefix = nsReg.getUniquePrefix(uri);
-            } else {
-                newPrefix = prefix;
-            }
-            // register new namespace
             try {
-                nsReg.registerNamespace(newPrefix, uri);
+                // namespace is not yet registered ...
+                if (prefix.length() == 0) {
+                    /**
+                     * the xml document specifies a default namespace
+                     * (i.e. an empty prefix); we need to create a random
+                     * prefix as the empty prefix is reserved according
+                     * to the JCR spec.
+                     */
+                    prefix = nsReg.getUniquePrefix(uri);
+                }
+                // register new namespace
+                nsReg.registerNamespace(prefix, uri);
             } catch (RepositoryException re) {
                 throw new SAXException(re);
             }
         }
-        // map namespace in this context to given prefix
-        nsContext.declarePrefix(prefix, uri);
     }
 
     /**
@@ -207,30 +191,22 @@
      */
     public void startElement(String namespaceURI, String localName, String qName,
                              Attributes atts) throws SAXException {
-        // check if new context needs to be started
-        if (!nsContextStarted) {
-            // there hasn't been a preceeding startPrefixMapping event
-            // so enter new namespace context
-            nsContext.pushContext();
-        } else {
-            // reset flag
-            nsContextStarted = false;
-        }
-
-        if (!initialized) {
+        if (targetHandler == null) {
             // the namespace of the first element determines the type of XML
             // (system view/document view)
-            systemViewXML = QName.NS_SV_URI.equals(namespaceURI);
-
-            if (systemViewXML) {
-                targetHandler = new SysViewImportHandler(importer, nsContext);
+            if (QName.NS_SV_URI.equals(namespaceURI)) {
+                targetHandler = new SysViewImportHandler(importer);
             } else {
-                targetHandler = new DocViewImportHandler(importer, nsContext);
+                targetHandler = new DocViewImportHandler(importer);
             }
+
             targetHandler.startDocument();
-            initialized = true;
         }
 
+        // Start a namespace context for this element
+        targetHandler.startNamespaceContext(localNamespaceMappings);
+        localNamespaceMappings.clear();
+
         // delegate to target handler
         targetHandler.startElement(namespaceURI, localName, qName, atts);
     }
@@ -244,15 +220,14 @@
     }
 
     /**
+     * Delegates the call to the underlying target handler and asks the
+     * handler to end the current namespace context.
      * {@inheritDoc}
      */
     public void endElement(String namespaceURI, String localName, String qName)
             throws SAXException {
-        // leaving element, pop namespace context
-        nsContext.popContext();
-
-        // delegate to target handler
         targetHandler.endElement(namespaceURI, localName, qName);
+        targetHandler.endNamespaceContext();
     }
 
     /**
@@ -260,92 +235,5 @@
      */
     public void setDocumentLocator(Locator locator) {
         this.locator = locator;
-    }
-
-    //--------------------------------------------------------< inner classes >
-    /**
-     * <code>NamespaceContext</code> supports scoped namespace declarations.
-     */
-    class NamespaceContext extends AbstractNamespaceResolver {
-
-        private final NamespaceSupport nsContext;
-
-        /**
-         * NamespaceSupport doesn't accept "" as default uri;
-         * internally we're using " " instead
-         */
-        private static final String DUMMY_DEFAULT_URI = " ";
-
-        NamespaceContext() {
-            nsContext = new NamespaceSupport();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        void popContext() {
-            nsContext.popContext();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        void pushContext() {
-            nsContext.pushContext();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        void reset() {
-            nsContext.reset();
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        boolean declarePrefix(String prefix, String uri) {
-            if (QName.NS_DEFAULT_URI.equals(uri)) {
-                uri = DUMMY_DEFAULT_URI;
-            }
-            return nsContext.declarePrefix(prefix, uri);
-        }
-
-        //------------------------------------------------< NamespaceResolver >
-        /**
-         * {@inheritDoc}
-         */
-        public String getURI(String prefix) throws NamespaceException {
-            String uri = nsContext.getURI(prefix);
-            if (uri == null) {
-                throw new NamespaceException("unknown prefix");
-            } else if (DUMMY_DEFAULT_URI.equals(uri)) {
-                return QName.NS_DEFAULT_URI;
-            } else {
-                return uri;
-            }
-        }
-
-        /**
-         * {@inheritDoc}
-         */
-        public String getPrefix(String uri) throws NamespaceException {
-            if (QName.NS_DEFAULT_URI.equals(uri)) {
-                uri = DUMMY_DEFAULT_URI;
-            }
-            String prefix = nsContext.getPrefix(uri);
-            if (prefix == null) {
-                /**
-                 * NamespaceSupport#getPrefix will never return the empty
-                 * (default) prefix; we have to do a reverse-lookup to check
-                 * whether it's the current default namespace
-                 */
-                if (uri.equals(nsContext.getURI(QName.NS_EMPTY_PREFIX))) {
-                    return QName.NS_EMPTY_PREFIX;
-                }
-                throw new NamespaceException("unknown uri");
-            }
-            return prefix;
-        }
     }
 }

Added: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/NamespaceContext.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/NamespaceContext.java?rev=387959&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/NamespaceContext.java (added)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/NamespaceContext.java Wed Mar 22 14:38:25 2006
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation or its licensors,
+ *                     as applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.jackrabbit.core.xml;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.jcr.NamespaceException;
+
+import org.apache.jackrabbit.name.IllegalNameException;
+import org.apache.jackrabbit.name.NamespaceResolver;
+import org.apache.jackrabbit.name.NoPrefixDeclaredException;
+import org.apache.jackrabbit.name.QName;
+import org.apache.jackrabbit.name.UnknownPrefixException;
+
+/**
+ * Hierarchically scoped namespace resolver. Each NamespaceContext instance
+ * contains an immutable set of namespace mappings and a reference (possibly
+ * <code>null</code>) to a parent NamespaceContext. Namespace resolution is
+ * performed by first looking at the local namespace mappings and then using
+ * the parent resolver if no local match is found.
+ * <p>
+ * The local namespace mappings are stored internally as two hash maps, one
+ * that maps the namespace prefixes to namespace URIs and another that contains
+ * the reverse mapping.
+ */
+class NamespaceContext implements NamespaceResolver {
+
+    /**
+     * The parent namespace context.
+     */
+    private final NamespaceContext parent;
+
+    /**
+     * The namespace prefix to namespace URI mapping.
+     */
+    private final Map prefixToURI;
+
+    /**
+     * The namespace URI to namespace prefix mapping.
+     */
+    private final Map uriToPrefix;
+
+    /**
+     * Creates a NamespaceContext instance with the given parent context
+     * and local namespace mappings.
+     *
+     * @param parent parent context
+     * @param mappings local namespace mappings (prefix -> URI)
+     */
+    public NamespaceContext(NamespaceContext parent, Map mappings) {
+        this.parent = parent;
+        this.prefixToURI = new HashMap();
+        this.uriToPrefix = new HashMap();
+
+        Iterator iterator = mappings.entrySet().iterator();
+        while (iterator.hasNext()) {
+            Map.Entry mapping = (Map.Entry) iterator.next();
+            prefixToURI.put(mapping.getKey(), mapping.getValue());
+            uriToPrefix.put(mapping.getValue(), mapping.getKey());
+        }
+    }
+
+    /**
+     * Returns the parent namespace context.
+     *
+     * @return parent namespace context
+     */
+    public NamespaceContext getParent() {
+        return parent;
+
+    }
+    //------------------------------------------------< NamespaceResolver >
+
+    /**
+     * Returns the namespace URI mapped to the given prefix.
+     *
+     * @param prefix namespace prefix
+     * @return namespace URI
+     * @throws NamespaceException if the prefix is not mapped
+     */
+    public String getURI(String prefix) throws NamespaceException {
+        String uri = (String) prefixToURI.get(prefix);
+        if (uri != null) {
+            return uri;
+        } else if (parent != null) {
+            return parent.getURI(prefix);
+        } else {
+            throw new NamespaceException("Unknown prefix: " + prefix);
+        }
+    }
+
+    /**
+     * Returns the namespace prefix mapped to the given URI.
+     *
+     * @param uri namespace URI
+     * @return namespace prefix
+     * @throws NamespaceException if the URI is not mapped
+     */
+    public String getPrefix(String uri) throws NamespaceException {
+        String prefix = (String) uriToPrefix.get(uri);
+        if (prefix != null) {
+            return prefix;
+        } else if (parent != null) {
+            return parent.getPrefix(uri);
+        } else {
+            throw new NamespaceException("Unknown URI: " + uri);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public String getJCRName(QName name) throws NoPrefixDeclaredException {
+        return name.toJCRName(this);
+    }
+
+    /** {@inheritDoc} */
+    public QName getQName(String name)
+            throws IllegalNameException, UnknownPrefixException {
+        return QName.fromJCRName(name, this);
+    }
+
+}

Propchange: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/NamespaceContext.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java?rev=387959&r1=387958&r2=387959&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/SysViewImportHandler.java Wed Mar 22 14:38:25 2006
@@ -17,7 +17,6 @@
 package org.apache.jackrabbit.core.xml;
 
 import org.apache.jackrabbit.name.IllegalNameException;
-import org.apache.jackrabbit.name.NamespaceResolver;
 import org.apache.jackrabbit.name.QName;
 import org.apache.jackrabbit.name.UnknownPrefixException;
 import org.apache.jackrabbit.core.NodeId;
@@ -60,8 +59,8 @@
      * @param importer
      * @param nsContext
      */
-    SysViewImportHandler(Importer importer, NamespaceResolver nsContext) {
-        super(importer, nsContext);
+    SysViewImportHandler(Importer importer) {
+        super(importer);
     }
 
     private void processNode(ImportState state, boolean start, boolean end)

Modified: jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/TargetImportHandler.java
URL: http://svn.apache.org/viewcvs/jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/TargetImportHandler.java?rev=387959&r1=387958&r2=387959&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/TargetImportHandler.java (original)
+++ jackrabbit/trunk/jackrabbit/src/main/java/org/apache/jackrabbit/core/xml/TargetImportHandler.java Wed Mar 22 14:38:25 2006
@@ -16,7 +16,6 @@
  */
 package org.apache.jackrabbit.core.xml;
 
-import org.apache.jackrabbit.name.NamespaceResolver;
 import org.apache.jackrabbit.util.TransientFileFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -31,6 +30,7 @@
 import java.io.Writer;
 import java.io.FileOutputStream;
 import java.io.OutputStreamWriter;
+import java.util.Map;
 
 import javax.jcr.RepositoryException;
 
@@ -44,12 +44,18 @@
     private static Logger log = LoggerFactory.getLogger(TargetImportHandler.class);
 
     protected final Importer importer;
-    protected final NamespaceResolver nsContext;
 
-    protected TargetImportHandler(Importer importer,
-                                  NamespaceResolver nsContext) {
+    /**
+     * The current namespace context. A new namespace context is created
+     * for each XML element and the parent reference is used to link the
+     * namespace contexts together in a tree hierarchy. This variable contains
+     * a reference to the namespace context associated with the XML element
+     * that is currently being processed.
+     */
+    protected NamespaceContext nsContext;
+
+    protected TargetImportHandler(Importer importer) {
         this.importer = importer;
-        this.nsContext = nsContext;
     }
 
     /**
@@ -84,6 +90,7 @@
     public void startDocument() throws SAXException {
         try {
             importer.start();
+            nsContext = null;
         } catch (RepositoryException re) {
             throw new SAXException(re);
         }
@@ -102,6 +109,27 @@
         } catch (RepositoryException re) {
             throw new SAXException(re);
         }
+    }
+
+    /**
+     * Starts a local namespace context for the current XML element.
+     * This method is called by {@link ImportHandler} when the processing of
+     * an XML element starts. The given local namespace mappings have been
+     * recorded by {@link ImportHandler#startPrefixMapping(String, String)}
+     * for the current XML element.
+     *
+     * @param mappings local namespace mappings
+     */
+    public final void startNamespaceContext(Map mappings) {
+        nsContext = new NamespaceContext(nsContext, mappings);
+    }
+
+    /**
+     * Restores the parent namespace context. This method is called by
+     * {@link ImportHandler} when the processing of an XML element ends.
+     */
+    public final void endNamespaceContext() {
+        nsContext = nsContext.getParent();
     }
 
     //--------------------------------------------------------< inner classes >