You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xalan.apache.org by mi...@apache.org on 2006/01/18 18:05:48 UTC

svn commit: r370188 - /xalan/java/trunk/src/org/apache/xml/serializer/NamespaceMappings.java

Author: minchau
Date: Wed Jan 18 09:05:46 2006
New Revision: 370188

URL: http://svn.apache.org/viewcvs?rev=370188&view=rev
Log:
committing NM2.txt patch in XALANJ-2258
Reviewed by Yash Talwar.

Modified:
    xalan/java/trunk/src/org/apache/xml/serializer/NamespaceMappings.java

Modified: xalan/java/trunk/src/org/apache/xml/serializer/NamespaceMappings.java
URL: http://svn.apache.org/viewcvs/xalan/java/trunk/src/org/apache/xml/serializer/NamespaceMappings.java?rev=370188&r1=370187&r2=370188&view=diff
==============================================================================
--- xalan/java/trunk/src/org/apache/xml/serializer/NamespaceMappings.java (original)
+++ xalan/java/trunk/src/org/apache/xml/serializer/NamespaceMappings.java Wed Jan 18 09:05:46 2006
@@ -20,7 +20,8 @@
 
 import java.util.Enumeration;
 import java.util.Hashtable;
-import java.util.Stack;
+
+import javax.naming.NameParser;
 
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
@@ -55,9 +56,9 @@
  * processed.  At any given moment of processing the currently visible prefixes
  * are on the stack and a prefix can be found given a uri, or a uri can be found
  * given a prefix.
- * 
- * This class is public only because it is used by Xalan. It is not a public API
- * 
+ *
+ * This class is intended for internal use only.  However, it is made public because
+ * other packages require it. 
  * @xsl.usage internal
  */
 public class NamespaceMappings
@@ -70,8 +71,8 @@
 
     /**
      * Each entry (prefix) in this hashtable points to a Stack of URIs
-     * This table maps a prefix (String) to a Stack of prefix mappings.
-     * All mappings in that retrieved stack have the same prefix,
+     * This table maps a prefix (String) to a Stack of NamespaceNodes.
+     * All Namespace nodes in that retrieved stack have the same prefix,
      * though possibly different URI's or depths. Such a stack must have
      * mappings at deeper depths push later on such a stack.  Mappings pushed
      * earlier on the stack will have smaller values for MappingRecord.m_declarationDepth.
@@ -79,16 +80,17 @@
     private Hashtable m_namespaces = new Hashtable();
 
     /** 
-     * The top of this stack contains the MapRecord
-     * of the last declared a namespace.
-     * Used to know how many prefix mappings to pop when leaving
-     * the current element depth.
-     * For every prefix mapping the current element depth is 
-     * pushed on this stack.
-     * That way all prefixes pushed at the current depth can be 
-     * removed at the same time.
-     * Used to ensure prefix/uri map scopes are closed correctly
-     *
+     * This stack is used as a convenience.
+     * It contains the pushed NamespaceNodes (shallowest
+     * to deepest) and is used to delete NamespaceNodes 
+     * when leaving the current element depth 
+     * to returning to the parent. The mappings of the deepest
+     * depth can be popped of the top and the same node
+     * can be removed from the appropriate prefix stack.
+     * 
+     * All prefixes pushed at the current depth can be 
+     * removed at the same time by using this stack to
+     * ensure prefix/uri map scopes are closed correctly.
      */
     private Stack m_nodeStack = new Stack();
 
@@ -110,33 +112,41 @@
      */
     private void initNamespaces()
     {
- 
-
+        // The initial prefix mappings will never be deleted because they are at element depth -1 
+        // (a kludge)
+        
         // Define the default namespace (initially maps to "" uri)
         Stack stack;
-        m_namespaces.put(EMPTYSTRING, stack = new Stack());
-        stack.push(new MappingRecord(EMPTYSTRING,EMPTYSTRING,0));
-
-        m_namespaces.put(XML_PREFIX, stack = new Stack());
-        stack.push(new MappingRecord( XML_PREFIX,
-            "http://www.w3.org/XML/1998/namespace",0));
-
-        m_nodeStack.push(new MappingRecord(null,null,-1));
-
+        MappingRecord nn;
+        nn = new MappingRecord(EMPTYSTRING, EMPTYSTRING, -1);
+        stack = createPrefixStack(EMPTYSTRING);
+        stack.push(nn);
+
+        // define "xml" namespace
+        nn = new MappingRecord(XML_PREFIX, "http://www.w3.org/XML/1998/namespace", -1);
+        stack = createPrefixStack(XML_PREFIX);
+        stack.push(nn);
     }
 
     /**
      * Use a namespace prefix to lookup a namespace URI.
      * 
      * @param prefix String the prefix of the namespace
-     * @return the URI corresponding to the prefix
+     * @return the URI corresponding to the prefix, returns ""
+     * if there is no visible mapping.
      */
     public String lookupNamespace(String prefix)
     {
-        final Stack stack = (Stack) m_namespaces.get(prefix);
-        return stack != null && !stack.isEmpty() ? 
-            ((MappingRecord) stack.peek()).m_uri : null;
+        String uri = null;
+        final Stack stack = getPrefixStack(prefix);
+        if (stack != null && !stack.isEmpty()) {
+            uri = ((MappingRecord) stack.peek()).m_uri;
+        }
+        if (uri == null)
+            uri = EMPTYSTRING;
+        return uri;
     }
+  
     
     MappingRecord getMappingFromPrefix(String prefix) {
         final Stack stack = (Stack) m_namespaces.get(prefix);
@@ -198,7 +208,7 @@
         }
 
         Stack stack;
-        if ((stack = (Stack) m_namespaces.get(prefix)) != null)
+        if ((stack = getPrefixStack(prefix)) != null)
         {
             stack.pop();
             return true;
@@ -212,29 +222,94 @@
      * @param uri a String with the uri to which the prefix is to map
      * @param elemDepth the depth of current declaration
      */
-    boolean pushNamespace(String prefix, String uri, int elemDepth)
+    public boolean pushNamespace(String prefix, String uri, int elemDepth)
     {
+        boolean pushed;
         // Prefixes "xml" and "xmlns" cannot be redefined
-        if (prefix.startsWith(XML_PREFIX))
+        if (!prefix.startsWith(XML_PREFIX))
         {
-            return false;
-        }
 
-        Stack stack;
-        // Get the stack that contains URIs for the specified prefix
-        if ((stack = (Stack) m_namespaces.get(prefix)) == null)
-        {
-            m_namespaces.put(prefix, stack = new Stack());
-        }
+            Stack stack;
+            // Get the stack that contains URIs for the specified prefix
+            if ((stack = getPrefixStack(prefix)) == null)
+                stack = createPrefixStack(prefix);
+
+            switch (stack.top)
+            {
+                case -1 :
+                    pushed = true;  // stack is empty, so push the new one on the stack
+                    break;
+                case 0 :
+                    {
+                        // only one thing on the stack, if the new one is the
+                        // same prefix/uri mapping as the old one don't push, 
+                        // but if the uri's differ push it on the stack.
+                        MappingRecord pm = (MappingRecord) stack.peek();
+                        if (uri.equals(pm.m_uri))
+                            pushed = false;
+                        else
+                            pushed = true;
+                    }
+                    break;
+                default : // 2 or more things on the stack
+                    {
+                        MappingRecord pm = (MappingRecord) stack.peek();
+                        if (null == pm.m_uri
+                            && !uri.equals(EMPTYSTRING)
+                            && pm.m_declarationDepth == elemDepth)
+                        {
+                            // The top of the stack masks shallower mappings and
+                            // the new mapping is at the same depth as the masking
+                            // and the masked mapping is the same as the new one
+                            
+                            MappingRecord pm2 =
+                                (MappingRecord) stack.peek(stack.top - 1);
+                            if (uri.equals(pm2.m_uri))
+                            {
+                                // The masked mapping is the same as the new one
+                                // so don't push this mapping, but delete the masking
+                                // one by popping it off the top
+                                // This is an optimization to re-use the ancestors mapping.
+                                pushed = false;
+                                stack.pop();
+                            }
+                            else
+                                pushed = true;
+                            // the ancestors mapping differs, so push it
+                        }
+                        else
+                        {
+                            // The mapping at the top of the stack is not a masking
+                            // or is shallower than the new one or the new mapping is
+                            // itself not a masking
+                            if (uri.equals(pm.m_uri))
+                            {
+                                pushed = false;
+                                // old and new URI's are the same, don't push
+                                // this is an optimization to re-use the ancestors mapping
+                            }
+                            else
+                                pushed = true;
+                                // old and new URI's differ, so push                      
+                        }
+                    }
+                break;
+            }
+            if (pushed)
+            {
+
+                MappingRecord namespaceNode =
+                    new MappingRecord(prefix, uri, elemDepth);
+                stack.push(namespaceNode);
+                //        m_nodeStack.push(new Integer(elemDepth));
+                m_nodeStack.push(namespaceNode);
+            }
 
-        if (!stack.empty() && uri.equals(((MappingRecord)stack.peek()).m_uri))
-        {
-            return false;
         }
-        MappingRecord map = new MappingRecord(prefix,uri,elemDepth);
-        stack.push(map);
-        m_nodeStack.push(map);
-        return true;
+        else
+            pushed = false;
+
+        return pushed;
     }
 
     /**
@@ -251,29 +326,49 @@
         {
             if (m_nodeStack.isEmpty())
                 return;
-            MappingRecord map = (MappingRecord)(m_nodeStack.peek());
+            MappingRecord map = (MappingRecord) (m_nodeStack.peek());
             int depth = map.m_declarationDepth;
-            if (depth < elemDepth)
-                return;
+            if (elemDepth < 1 || map.m_declarationDepth < elemDepth)
+                break;
             /* the depth of the declared mapping is elemDepth or deeper
              * so get rid of it
              */
 
-            map = (MappingRecord) m_nodeStack.pop();
-            final String prefix = map.m_prefix; 
-            popNamespace(prefix);
-            if (saxHandler != null)
+            MappingRecord nm1 = (MappingRecord) m_nodeStack.pop();
+            // pop the node from the stack
+            String prefix = map.m_prefix;
+
+            Stack prefixStack = getPrefixStack(prefix);
+            MappingRecord nm2 = (MappingRecord) prefixStack.peek();
+            if (nm1 == nm2)
             {
-                try
+                // It would be nice to always pop() but we
+                // need to check that the prefix stack still has
+                // the node we want to get rid of. This is because
+                // the optimization of essentially this situation:
+                // <a xmlns:x="abc"><b xmlns:x="" xmlns:x="abc" /></a>
+                // will remove both mappings in <b> because the
+                // new mapping is the same as the masked one and we get
+                // <a xmlns:x="abc"><b/></a>
+                // So we are only removing xmlns:x="" or
+                // xmlns:x="abc" from the depth of element <b>
+                // when going back to <a> if in fact they have
+                // not been optimized away.
+                // 
+                prefixStack.pop();
+                if (saxHandler != null)
                 {
-                    saxHandler.endPrefixMapping(prefix);
-                }
-                catch (SAXException e)
-                {
-                    // not much we can do if they aren't willing to listen
+                    try
+                    {
+                        saxHandler.endPrefixMapping(prefix);
+                    }
+                    catch (SAXException e)
+                    {
+                        // not much we can do if they aren't willing to listen
+                    }
                 }
             }
-               
+
         }
     }
 
@@ -293,7 +388,8 @@
      */
     public Object clone() throws CloneNotSupportedException {
         NamespaceMappings clone = new NamespaceMappings();
-        clone.m_nodeStack = (Stack) m_nodeStack.clone();
+        clone.m_nodeStack = (NamespaceMappings.Stack) m_nodeStack.clone();        
+        clone.count = this.count;
         clone.m_namespaces = (Hashtable) m_namespaces.clone();
         
         clone.count = count;
@@ -306,20 +402,131 @@
         this.count = 0;
         this.m_namespaces.clear();
         this.m_nodeStack.clear();        
+        
         initNamespaces();
     }
     
+    /**
+     * Just a little class that ties the 3 fields together
+     * into one object, and this simplifies the pushing
+     * and popping of namespaces to one push or one pop on
+     * one stack rather than on 3 separate stacks.
+     */
     class MappingRecord {
         final String m_prefix;  // the prefix
-        final String m_uri;     // the uri
+        final String m_uri;     // the uri, possibly "" but never null
         // the depth of the element where declartion was made
         final int m_declarationDepth;
         MappingRecord(String prefix, String uri, int depth) {
             m_prefix = prefix;
-            m_uri = uri;
+            m_uri = (uri==null)? EMPTYSTRING : uri;
             m_declarationDepth = depth;
-            
         }
+    }    
+    
+    /**
+     * Rather than using java.util.Stack, this private class
+     * provides a minimal subset of methods and is faster
+     * because it is not thread-safe.
+     */
+    private class Stack {
+        private int top = -1;
+        private int max = 20;
+        Object[] m_stack = new Object[max];
+        
+        public Object clone() throws CloneNotSupportedException {
+            NamespaceMappings.Stack clone = new NamespaceMappings.Stack();  
+            clone.max = this.max;
+            clone.top = this.top;
+            clone.m_stack = new Object[clone.max];
+            for (int i=0; i <= top; i++) {
+            	// We are just copying references to immutable MappingRecord objects here
+            	// so it is OK if the clone has references to these.
+            	clone.m_stack[i] = this.m_stack[i];
+            }
+            return clone;            
+        }
+        
+        public Stack()
+        {
+        }
+        
+        public Object push(Object o) {
+            top++;
+            if (max <= top) {
+                int newMax = 2*max + 1;
+                Object[] newArray = new Object[newMax];
+                System.arraycopy(m_stack,0, newArray, 0, max);
+                max = newMax;
+                m_stack = newArray;
+            }
+            m_stack[top] = o;
+            return o;
+        }
+        
+        public Object pop() {
+            Object o;
+            if (0 <= top) {
+                o = m_stack[top];
+                // m_stack[top] = null;  do we really care?
+                top--;
+            }
+            else
+                o = null;
+            return o;
+        }
+        
+        public Object peek() {
+            Object o;
+            if (0 <= top) {
+                o = m_stack[top];
+            }
+            else
+                o = null;
+            return o;
+        }
+        
+        public Object peek(int idx) {
+            return m_stack[idx];
+        }
+        
+        public boolean isEmpty() {
+            return (top < 0);
+        }
+        public boolean empty() {
+            return (top < 0);
+        }
+        
+        public void clear() {
+            for (int i=0; i<= top; i++)
+                m_stack[i] = null;
+            top = -1;
+        }  
+        
+        public Object getElement(int index) {
+            return m_stack[index];      
+        }
+    }
+    /**
+     * A more type-safe way to get a stack of prefix mappings
+     * from the Hashtable m_namespaces
+     * (this is the only method that does the type cast).
+     */
+
+    private Stack getPrefixStack(String prefix) {
+        Stack fs = (Stack) m_namespaces.get(prefix);
+        return fs;
+    }
+    
+    /**
+     * A more type-safe way of saving stacks under the
+     * m_namespaces Hashtable.
+     */
+    private Stack createPrefixStack(String prefix)
+    {
+        Stack fs = new Stack();
+        m_namespaces.put(prefix, fs);
+        return fs;
     }
 
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: xalan-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: xalan-cvs-help@xml.apache.org