You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xalan.apache.org by zo...@apache.org on 2009/11/25 22:55:09 UTC

svn commit: r884287 - in /xalan/java/trunk/src/org/apache/xalan/xsltc: dom/ArrayNodeListIterator.java runtime/BasisLibrary.java runtime/InternalRuntimeError.java

Author: zongaro
Date: Wed Nov 25 21:55:09 2009
New Revision: 884287

URL: http://svn.apache.org/viewvc?rev=884287&view=rev
Log:
Applying patch contributed by Martin von Gagern for XALANJ-2493 and XALANJ-2424.

Quoting from Martin, "The current implementation of nodeList2Iterator is
broken, because it can not deal with attribute nodes. It relies on copyNodes
which in turn tries to add attribute nodes as children of some top level node.
Attributes don't live on the children axis, though, so this is against DOM and
causes a DOM exception in the Xerces DOM implementation and probably most other
implementations. The resulting HIERARCHY_REQUEST_ERR was noted e.g. in
XALANJ-2424.  Furthermore, the implementation is inefficient, because it
manually copies each and every node from the source document to a new DTM to
some new DTM....

I dropped copyNodes in favor of Document.importNode, to avoid code duplication
and rely on supposedly more heavily tested code. I also added another level of
elements, so that there is one such dummy node for every item of the source
list, with always a single child or element. A few assertions help ensure this
single child policy. This is especially important in the new implementation,
because otherwise it would become difficult to get the proxied nodes and the
newly DTMified nodes into correct order.

Added:
    xalan/java/trunk/src/org/apache/xalan/xsltc/dom/ArrayNodeListIterator.java   (with props)
    xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/InternalRuntimeError.java   (with props)
Modified:
    xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java

Added: xalan/java/trunk/src/org/apache/xalan/xsltc/dom/ArrayNodeListIterator.java
URL: http://svn.apache.org/viewvc/xalan/java/trunk/src/org/apache/xalan/xsltc/dom/ArrayNodeListIterator.java?rev=884287&view=auto
==============================================================================
--- xalan/java/trunk/src/org/apache/xalan/xsltc/dom/ArrayNodeListIterator.java (added)
+++ xalan/java/trunk/src/org/apache/xalan/xsltc/dom/ArrayNodeListIterator.java Wed Nov 25 21:55:09 2009
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+/*
+ * $Id$
+ */
+
+package org.apache.xalan.xsltc.dom;
+
+import org.apache.xml.dtm.DTMAxisIterator;
+
+public class ArrayNodeListIterator implements DTMAxisIterator  {
+
+    private int _pos = 0;
+
+    private int _mark = 0;
+
+    private int _nodes[];
+
+    private static final int[] EMPTY = { };
+
+    public ArrayNodeListIterator(int[] nodes) {
+	_nodes = nodes;
+    }
+
+    public int next() {
+	return _pos < _nodes.length ? _nodes[_pos++] : END;
+    }
+
+    public DTMAxisIterator reset() {
+	_pos = 0;
+	return this;
+    }
+
+    public int getLast() {
+	return _nodes.length;
+    }
+
+    public int getPosition() {
+	return _pos;
+    }
+
+    public void setMark() {
+	_mark = _pos;
+    }
+
+    public void gotoMark() {
+	_pos = _mark;
+    }
+
+    public DTMAxisIterator setStartNode(int node) {
+	if (node == END) _nodes = EMPTY;
+	return this;
+    }
+
+    public int getStartNode() {
+	return END;
+    }
+
+    public boolean isReverse() {
+	return false;
+    }
+
+    public DTMAxisIterator cloneIterator() {
+	return new ArrayNodeListIterator(_nodes);
+    }
+
+    public void setRestartable(boolean isRestartable) {
+    }
+
+    public int getNodeByPosition(int position) {
+	return _nodes[position - 1];
+    }
+
+}

Propchange: xalan/java/trunk/src/org/apache/xalan/xsltc/dom/ArrayNodeListIterator.java
------------------------------------------------------------------------------
    svn:executable = *

Modified: xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java
URL: http://svn.apache.org/viewvc/xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java?rev=884287&r1=884286&r2=884287&view=diff
==============================================================================
--- xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java (original)
+++ xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/BasisLibrary.java Wed Nov 25 21:55:09 2009
@@ -38,12 +38,17 @@
 import org.apache.xalan.xsltc.dom.MultiDOM;
 import org.apache.xalan.xsltc.dom.SingletonIterator;
 import org.apache.xalan.xsltc.dom.StepIterator;
+import org.apache.xalan.xsltc.dom.ArrayNodeListIterator;
+import org.apache.xml.dtm.DTM;
 import org.apache.xml.dtm.DTMAxisIterator;
 import org.apache.xml.dtm.DTMManager;
 import org.apache.xml.dtm.ref.DTMDefaultBase;
+import org.apache.xml.dtm.ref.DTMNodeProxy;
 
 import org.w3c.dom.DOMException;
+import org.w3c.dom.Attr;
 import org.w3c.dom.Document;
+import org.w3c.dom.Element;
 import org.w3c.dom.NodeList;
 import org.xml.sax.SAXException;
 import org.apache.xml.serializer.NamespaceMappings;
@@ -1097,90 +1102,42 @@
     }
     
     /**
-     * Utility function used to copy a node list to be under a parent node.
+     * In a perfect world, this would be the implementation for
+     * nodeList2Iterator. In reality, though, this causes a
+     * ClassCastException in getDTMHandleFromNode because SAXImpl is
+     * not an instance of DOM2DTM. So we use the more lengthy
+     * implementation below until this issue has been addressed.
+     *
+     * @see org.apache.xml.dtm.ref.DTMManagerDefault#getDTMHandleFromNode
      */
-    private static void copyNodes(org.w3c.dom.NodeList nodeList, 
-	org.w3c.dom.Document doc, org.w3c.dom.Node parent)
+    private static DTMAxisIterator nodeList2IteratorUsingHandleFromNode(
+                                        org.w3c.dom.NodeList nodeList,
+                                    	Translet translet, DOM dom)
     {
-        final int size = nodeList.getLength();
-
-          // copy Nodes from NodeList into new w3c DOM
-        for (int i = 0; i < size; i++) 
-        {
-            org.w3c.dom.Node curr = nodeList.item(i);
-            int nodeType = curr.getNodeType();
-            String value = null;
-            try {
-                value = curr.getNodeValue();
-            } catch (DOMException ex) {
-                runTimeError(RUN_TIME_INTERNAL_ERR, ex.getMessage());
-                return;
-            }
-            
-            String nodeName = curr.getNodeName();
-            org.w3c.dom.Node newNode = null; 
-            switch (nodeType){
-                case org.w3c.dom.Node.ATTRIBUTE_NODE:
-                     newNode = doc.createAttributeNS(curr.getNamespaceURI(), 
-			nodeName);
-                     break;
-                case org.w3c.dom.Node.CDATA_SECTION_NODE: 
-                     newNode = doc.createCDATASection(value);
-                     break;
-                case org.w3c.dom.Node.COMMENT_NODE: 
-                     newNode = doc.createComment(value);
-                     break;
-                case org.w3c.dom.Node.DOCUMENT_FRAGMENT_NODE: 
-                     newNode = doc.createDocumentFragment();
-                     break;
-                case org.w3c.dom.Node.DOCUMENT_NODE:
-                     newNode = doc.createElementNS(null, "__document__");
-                     copyNodes(curr.getChildNodes(), doc, newNode);
-                     break;
-                case org.w3c.dom.Node.DOCUMENT_TYPE_NODE:
-                     // nothing?
-                     break;
-                case org.w3c.dom.Node.ELEMENT_NODE: 
-                     // For Element node, also copy the children and the 
-		     // attributes.
-                     org.w3c.dom.Element element = doc.createElementNS(
-			curr.getNamespaceURI(), nodeName);
-                     if (curr.hasAttributes())
-                     {
-                       org.w3c.dom.NamedNodeMap attributes = curr.getAttributes();
-                       for (int k = 0; k < attributes.getLength(); k++) {
-                         org.w3c.dom.Node attr = attributes.item(k);
-                         element.setAttributeNS(attr.getNamespaceURI(), 
-                                 attr.getNodeName(), attr.getNodeValue());
-                       }
-                     }
-                     copyNodes(curr.getChildNodes(), doc, element);
-                     newNode = element;
-                     break;
-                case org.w3c.dom.Node.ENTITY_NODE: 
-                     // nothing ? 
-                     break;
-                case org.w3c.dom.Node.ENTITY_REFERENCE_NODE: 
-                     newNode = doc.createEntityReference(nodeName);
-                     break;
-                case org.w3c.dom.Node.NOTATION_NODE: 
-                     // nothing ? 
-                     break;
-                case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE: 
-                     newNode = doc.createProcessingInstruction(nodeName,
-                        value);
-                     break;
-                case org.w3c.dom.Node.TEXT_NODE: 
-                     newNode = doc.createTextNode(value);
-                     break;
-            }
-            try {
-                parent.appendChild(newNode);
-            } catch (DOMException e) {
-                runTimeError(RUN_TIME_INTERNAL_ERR, e.getMessage());
-                return;
-            }           
-        }
+	final int n = nodeList.getLength();
+	final int[] dtmHandles = new int[n];
+	DTMManager dtmManager = null;
+	if (dom instanceof MultiDOM)
+	    dtmManager = ((MultiDOM) dom).getDTMManager();
+	for (int i = 0; i < n; ++i) {
+	    org.w3c.dom.Node node = nodeList.item(i);
+	    int handle;
+	    if (dtmManager != null) {
+		handle = dtmManager.getDTMHandleFromNode(node);
+	    }
+	    else if (node instanceof DTMNodeProxy
+		     && ((DTMNodeProxy) node).getDTM() == dom) {
+		handle = ((DTMNodeProxy) node).getDTMNodeNumber();
+	    }
+	    else {
+		runTimeError(RUN_TIME_INTERNAL_ERR, "need MultiDOM");
+		return null;
+	    }
+	    dtmHandles[i] = handle;
+	    System.out.println("Node " + i + " has handle 0x" +
+			       Integer.toString(handle, 16));
+	}
+	return new ArrayNodeListIterator(dtmHandles);
     }
 
     /**
@@ -1191,26 +1148,93 @@
                                         org.w3c.dom.NodeList nodeList,
                                     	Translet translet, DOM dom) 
     {
-	// w3c NodeList -> w3c DOM
+	// First pass: build w3c DOM for all nodes not proxied from our DOM.
+	//
+	// Notice: this looses some (esp. parent) context for these nodes,
+	// so some way to wrap the original nodes inside a DTMAxisIterator
+	// might be preferable in the long run.
+	int n = 0; // allow for change in list length, just in case.
 	Document doc = null;
-	try {
-	    doc = ((AbstractTranslet) translet).newDocument("", "__top__");
-	} 
-        catch (javax.xml.parsers.ParserConfigurationException e) {
-	    runTimeError(RUN_TIME_INTERNAL_ERR, e.getMessage());
-            return null;
+	DTMManager dtmManager = null;
+	int[] proxyNodes = new int[nodeList.getLength()];
+	if (dom instanceof MultiDOM)
+	    dtmManager = ((MultiDOM) dom).getDTMManager();
+	for (int i = 0; i < nodeList.getLength(); ++i) {
+	    org.w3c.dom.Node node = nodeList.item(i);
+	    if (node instanceof DTMNodeProxy) {
+		DTMNodeProxy proxy = (DTMNodeProxy)node;
+		DTM nodeDTM = proxy.getDTM();
+		int handle = proxy.getDTMNodeNumber();
+		boolean isOurDOM = (nodeDTM == dom);
+		if (!isOurDOM && dtmManager != null) {
+		    try {
+			isOurDOM = (nodeDTM == dtmManager.getDTM(handle));
+		    }
+		    catch (ArrayIndexOutOfBoundsException e) {
+			// invalid node handle, so definitely not our doc
+		    }
+		}
+		if (isOurDOM) {
+		    proxyNodes[i] = handle;
+		    ++n;
+		    continue;
+		}
+	    }
+	    proxyNodes[i] = DTM.NULL;
+	    int nodeType = node.getNodeType();
+	    if (doc == null) {
+		if (dom instanceof MultiDOM == false) {
+		    runTimeError(RUN_TIME_INTERNAL_ERR, "need MultiDOM");
+		    return null;
+		}
+		try {
+		    AbstractTranslet at = (AbstractTranslet) translet;
+		    doc = at.newDocument("", "__top__");
+		}
+		catch (javax.xml.parsers.ParserConfigurationException e) {
+		    runTimeError(RUN_TIME_INTERNAL_ERR, e.getMessage());
+		    return null;
+		}
+	    }
+	    // Use one dummy element as container for each node of the
+	    // list. That way, it is easier to detect resp. avoid
+	    // funny things which change the number of nodes,
+	    // e.g. auto-concatenation of text nodes.
+	    Element mid;
+	    switch (nodeType) {
+		case org.w3c.dom.Node.ELEMENT_NODE:
+		case org.w3c.dom.Node.TEXT_NODE:
+		case org.w3c.dom.Node.CDATA_SECTION_NODE:
+		case org.w3c.dom.Node.COMMENT_NODE:
+		case org.w3c.dom.Node.ENTITY_REFERENCE_NODE:
+		case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
+		    mid = doc.createElementNS(null, "__dummy__");
+		    mid.appendChild(doc.importNode(node, true));
+		    doc.getDocumentElement().appendChild(mid);
+		    ++n;
+		    break;
+		case org.w3c.dom.Node.ATTRIBUTE_NODE:
+		    // The mid element also serves as a container for
+		    // attributes, avoiding problems with conflicting
+		    // attributes or node order.
+		    mid = doc.createElementNS(null, "__dummy__");
+		    mid.setAttributeNodeNS((Attr)doc.importNode(node, true));
+		    doc.getDocumentElement().appendChild(mid);
+		    ++n;
+		    break;
+		default:
+		    // Better play it safe for all types we aren't sure we know
+		    // how to deal with.
+		    runTimeError(RUN_TIME_INTERNAL_ERR,
+				 "Don't know how to convert node type "
+				 + nodeType);
+	    }
 	}
-        
-        // Copy all the nodes in the nodelist to be under the top element
-        copyNodes(nodeList, doc, doc.getDocumentElement());
 
         // w3cDOM -> DTM -> DOMImpl
-	if (dom instanceof MultiDOM) {
-            final MultiDOM multiDOM = (MultiDOM) dom;
-
-	    DTMDefaultBase dtm = (DTMDefaultBase)((DOMAdapter)multiDOM.getMain()).getDOMImpl();
-	    DTMManager dtmManager = dtm.getManager();
-	    
+	DTMAxisIterator iter = null, childIter = null, attrIter = null;
+	if (doc != null) {
+	    final MultiDOM multiDOM = (MultiDOM) dom;
 	    DOM idom = (DOM)dtmManager.getDTM(new DOMSource(doc), false,
 					      null, true, false);
 	    // Create DOMAdapter and register with MultiDOM
@@ -1223,16 +1247,57 @@
 
 	    DTMAxisIterator iter1 = idom.getAxisIterator(Axis.CHILD);
 	    DTMAxisIterator iter2 = idom.getAxisIterator(Axis.CHILD);
-            DTMAxisIterator iter = new AbsoluteIterator(
+            iter = new AbsoluteIterator(
                 new StepIterator(iter1, iter2));
 
  	    iter.setStartNode(DTMDefaultBase.ROOTNODE);
-	    return iter;
+
+	    childIter = idom.getAxisIterator(Axis.CHILD);
+	    attrIter = idom.getAxisIterator(Axis.ATTRIBUTE);
 	}
-        else {
-	    runTimeError(RUN_TIME_INTERNAL_ERR, "nodeList2Iterator()");
-	    return null;
-        }
+
+	// Second pass: find DTM handles for every node in the list.
+	int[] dtmHandles = new int[n];
+	n = 0;
+	for (int i = 0; i < nodeList.getLength(); ++i) {
+	    if (proxyNodes[i] != DTM.NULL) {
+		dtmHandles[n++] = proxyNodes[i];
+		continue;
+	    }
+	    org.w3c.dom.Node node = nodeList.item(i);
+	    DTMAxisIterator iter3 = null;
+	    int nodeType = node.getNodeType();
+	    switch (nodeType) {
+		case org.w3c.dom.Node.ELEMENT_NODE:
+		case org.w3c.dom.Node.TEXT_NODE:
+		case org.w3c.dom.Node.CDATA_SECTION_NODE:
+		case org.w3c.dom.Node.COMMENT_NODE:
+		case org.w3c.dom.Node.ENTITY_REFERENCE_NODE:
+		case org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE:
+		    iter3 = childIter;
+		    break;
+		case org.w3c.dom.Node.ATTRIBUTE_NODE:
+		    iter3 = attrIter;
+		    break;
+		default:
+		    // Should not happen, as first run should have got all these
+		    throw new InternalRuntimeError("Mismatched cases");
+	    }
+	    if (iter3 != null) {
+		iter3.setStartNode(iter.next());
+		dtmHandles[n] = iter3.next();
+		// For now, play it self and perform extra checks:
+		if (dtmHandles[n] == DTMAxisIterator.END)
+		    throw new InternalRuntimeError("Expected element missing at " + i);
+		if (iter3.next() != DTMAxisIterator.END)
+		    throw new InternalRuntimeError("Too many elements at " + i);
+		++n;
+	    }
+	}
+	if (n != dtmHandles.length)
+	    throw new InternalRuntimeError("Nodes lost in second pass");
+
+	return new ArrayNodeListIterator(dtmHandles);
     }
 
     /**

Added: xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/InternalRuntimeError.java
URL: http://svn.apache.org/viewvc/xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/InternalRuntimeError.java?rev=884287&view=auto
==============================================================================
--- xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/InternalRuntimeError.java (added)
+++ xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/InternalRuntimeError.java Wed Nov 25 21:55:09 2009
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+/*
+ * $Id$
+ */
+
+package org.apache.xalan.xsltc.runtime;
+
+/**
+ * Class to express failed assertions and similar for the xsltc runtime.
+ * As java.lang.AssertionError was introduced in JDK 1.4 we can't use that yet.
+ */
+public class InternalRuntimeError extends Error {
+
+    public InternalRuntimeError(String message) {
+        super(message);
+    }
+
+}

Propchange: xalan/java/trunk/src/org/apache/xalan/xsltc/runtime/InternalRuntimeError.java
------------------------------------------------------------------------------
    svn:executable = *



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