commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From p...@apache.org
Subject svn commit: r219726 - in /jakarta/commons/proper/jelly/trunk: jelly-tags/xml/ jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/ src/java/org/apache/commons/jelly/ src/java/org/apache/c...
Date Tue, 19 Jul 2005 17:34:02 GMT
Author: polx
Date: Tue Jul 19 10:34:00 2005
New Revision: 219726

URL: http://svn.apache.org/viewcvs?rev=219726&view=rev
Log:
Fixing treatment of namespaced-qualified attributes in XML taglib and
in the xml-output.
This fixes JELLY-213 and JELLY-214.
paul

Added:
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ReplaceNamespaceTag.java
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpace.jelly
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceDefaultNS.jelly
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceDuplicatedNS.jelly
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceWithInnerElements.jelly
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/elementWithNameSpace.jelly
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/elementWithNameSpaceError.jelly
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/namespaceReplace.jelly
    jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/testStaticNamespacedAttributes.jelly
Modified:
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/project.xml
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/AttributeTag.java
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ElementTag.java
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/XMLTagLibrary.java
    jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/TestXMLTags.java
    jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/XMLOutput.java
    jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/StaticTag.java
    jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/StaticTagScript.java
    jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/TagScript.java
    jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/parser/XMLParser.java
    jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/TestCoreTags.java
    jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/core/TestFileTag.java

Modified: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/project.xml
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/project.xml?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/project.xml (original)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/project.xml Tue Jul 19 10:34:00 2005
@@ -20,7 +20,7 @@
   <extend>${basedir}/../tag-project.xml</extend>
   <id>commons-jelly-tags-xml</id>
   <name>commons-jelly-tags-xml</name>
-  <currentVersion>1.1</currentVersion>
+  <currentVersion>1.2-dev</currentVersion>
   <package>org.apache.commons.jelly.tags.xml</package>
   <description>The Jelly XML Tag Library</description>
   <shortDescription>Commons Jelly XML Tag Library</shortDescription>
@@ -37,6 +37,11 @@
     </version>
   </versions>
   <dependencies>
+    <dependency>
+      <id>commons-jelly</id>
+      <version>SNAPSHOT</version>
+    </dependency>
+    
     <!-- run time / in testing-->
 
     <dependency>

Modified: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/AttributeTag.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/AttributeTag.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/AttributeTag.java (original)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/AttributeTag.java Tue Jul 19 10:34:00 2005
@@ -26,6 +26,8 @@
   * @version $Revision$
   */
 public class AttributeTag extends TagSupport {
+     /** The namespace URI. */
+    private String namespace;
 
     /** the name of the attribute. */
     private String name;
@@ -38,10 +40,11 @@
     //-------------------------------------------------------------------------
     public void doTag(XMLOutput output) throws JellyTagException {
         ElementTag tag = (ElementTag) findAncestorWithClass( ElementTag.class );
-        if ( tag == null ) {
-            throw new JellyTagException( "<attribute> tag must be enclosed inside an <element> tag" );
+        if (tag == null) {
+            throw new JellyTagException(
+                    "<attribute> tag must be enclosed inside an <element> tag" );
         }
-        tag.setAttributeValue( getName(), getBodyText( false ) );
+        tag.setAttributeValue(getName(), getBodyText(false), getURI());
     }
 
     // Properties
@@ -53,10 +56,25 @@
     public String getName() {
         return name;
     }
+
     /**
-     * Sets the name of the attribute
+     * Sets the name of the attribute.
      */
     public void setName(String name) {
         this.name = name;
+    }
+
+    /**
+     * @return the namespace URI of the element
+     */
+    public String getURI() {
+        return namespace;
+    }
+
+    /**
+     * Sets the namespace URI of the element
+     */
+    public void setURI(String namespace) {
+        this.namespace = namespace;
     }
 }

Modified: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ElementTag.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ElementTag.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ElementTag.java (original)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ElementTag.java Tue Jul 19 10:34:00 2005
@@ -15,6 +15,7 @@
  */
 package org.apache.commons.jelly.tags.xml;
 
+import org.apache.commons.jelly.JellyException;
 import org.apache.commons.jelly.JellyTagException;
 import org.apache.commons.jelly.TagSupport;
 import org.apache.commons.jelly.XMLOutput;
@@ -22,8 +23,6 @@
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.AttributesImpl;
 
-import java.io.IOException;
-
 /** A tag to produce an XML element which can contain other attributes
   * or elements like the <code>&lt;xsl:element&gt;</code> tag.
   *
@@ -32,16 +31,16 @@
   */
 public class ElementTag extends TagSupport {
 
-    /** The namespace URI */
+    /** The namespace URI. */
     private String namespace;
 
-    /** The qualified name */
+    /** The qualified name. */
     private String name;
 
-    /** The XML Attributes */
+    /** The XML Attributes. */
     private AttributesImpl attributes = new AttributesImpl();
 
-    /** flag set if attributes are output */
+    /** flag set if attributes are output. */
     private boolean outputAttributes;
 
     public ElementTag() {
@@ -52,11 +51,12 @@
      *
      * @param name of the attribute
      * @param value of the attribute
-     * @throws JellyException if the start element has already been output.
+     * @param uri namespace of the attribute
+     * @throws JellyTagException if the start element has already been output.
      *   Attributes must be set on the outer element before any content
      *   (child elements or text) is output
      */
-    public void setAttributeValue( String name, String value ) throws JellyTagException {
+    public void setAttributeValue(String name, String value, String uri) throws JellyTagException {
         if (outputAttributes) {
             throw new JellyTagException(
                 "Cannot set the value of attribute: "
@@ -67,13 +67,22 @@
         // ### we'll assume that all attributes are in no namespace!
         // ### this is severely limiting!
         // ### we should be namespace aware
-        int index = attributes.getIndex("", name);
+        // NAMESPACE FIXED:
+        int idx = name.indexOf(':');
+        final String localName = (idx >= 0)
+            ? name.substring(idx + 1)
+            : name;
+        final String nsUri = (uri != null)
+            ? uri
+            : "";
+
+        int index = attributes.getIndex(nsUri, localName);
         if (index >= 0) {
             attributes.removeAttribute(index);
         }
         // treat null values as no attribute
         if (value != null) {
-            attributes.addAttribute("", name, name, "CDATA", value);
+            attributes.addAttribute(nsUri, localName, name, "CDATA", value);
         }
     }
 
@@ -107,22 +116,21 @@
                 super.endElement(uri, localName, qName);
             }
 
-            public void characters(char ch[], int start, int length) throws SAXException {
+            public void characters(char[] ch, int start, int length) throws SAXException {
                 initialize();
                 super.characters(ch, start, length);
             }
 
-            public void ignorableWhitespace(char ch[], int start, int length)
+            public void ignorableWhitespace(char[] ch, int start, int length)
                 throws SAXException {
                 initialize();
                 super.ignorableWhitespace(ch, start, length);
             }
 
             public void objectData(Object object)
-                throws SAXException
-            {
+                throws SAXException {
                 initialize();
-                super.objectData( object );
+                super.objectData(object);
             }
 
             public void processingInstruction(String target, String data)
@@ -133,7 +141,7 @@
 
             /**
              * Ensure that the outer start element is generated
-             * before any content is output
+             * before any content is output.
              */
             protected void initialize() throws SAXException {
                 if (!outputAttributes) {

Added: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ReplaceNamespaceTag.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ReplaceNamespaceTag.java?rev=219726&view=auto
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ReplaceNamespaceTag.java (added)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/ReplaceNamespaceTag.java Tue Jul 19 10:34:00 2005
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2002,2004 The Apache Software Foundation.
+ *
+ * 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.commons.jelly.tags.xml;
+
+import org.apache.commons.jelly.JellyTagException;
+import org.apache.commons.jelly.MissingAttributeException;
+import org.apache.commons.jelly.TagSupport;
+import org.apache.commons.jelly.XMLOutput;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Replace namespace is a filter to change the namespace of any
+ * elemement attribute passing through it.
+ * 
+ * @author Diogo Quintela <dquintela@gmail.com>
+ */
+public class ReplaceNamespaceTag extends TagSupport {
+    private String fromNamespace;
+    private String toNamespace;
+
+    public ReplaceNamespaceTag() {
+    }
+
+    //  Tag interface
+    //-------------------------------------------------------------------------
+    public void doTag(XMLOutput output) throws MissingAttributeException, JellyTagException {
+        final String fromURI = (fromNamespace != null) ? fromNamespace : "";
+        final String toURI = (toNamespace != null) ? toNamespace : "";
+        XMLOutput newOutput = output;
+
+        if (!toURI.equals(fromURI)) {
+            newOutput = new XMLOutput(output) {
+                public void startElement(String uri, String localName, String qName, Attributes atts)
+                    throws SAXException {
+                    super.startElement(replaceURI(uri), localName, qName, replaceURI(atts));
+                }
+
+                public void endElement(String uri, String localName, String qName)
+                    throws SAXException {
+                    super.endElement(replaceURI(uri), localName, qName);
+                }
+
+                public void startPrefixMapping(String prefix, String uri)
+                    throws SAXException {
+                    super.startPrefixMapping(prefix, replaceURI(uri));
+                }
+
+                private String replaceURI(String uri) {
+                    String newUri = uri;
+
+                    if (fromURI.equals((uri != null) ? uri : "")) {
+                        newUri = toURI;
+                    }
+
+                    return newUri;
+                }
+
+                private Attributes replaceURI(Attributes atts) {
+                    AttributesImpl newAttsImpl = new AttributesImpl();
+
+                    for (int i = 0; i < atts.getLength(); i++) {
+                        // Normally attributes don't have namespaces
+                        // But may have (only if on form prefix:attr) ?
+                        // So, we'll only replace if needed
+                        String QName = atts.getQName(i);
+                        String newUri = atts.getURI(i);
+                        int idx = QName.indexOf(':');
+
+                        if (idx >= 0) {
+                            newUri = replaceURI(newUri);
+                        }
+
+                        newAttsImpl.addAttribute(newUri, atts.getLocalName(i), atts.getQName(i),
+                            atts.getType(i), atts.getValue(i));
+                    }
+
+                    return newAttsImpl;
+                }
+            };
+        }
+
+        invokeBody(newOutput);
+    }
+
+    /**
+     * @return the source namespace URI to replace
+     */
+    public String getFromURI() {
+        return fromNamespace;
+    }
+
+    /**
+     * Sets the source namespace URI to replace.
+     */
+    public void setFromURI(String namespace) {
+        this.fromNamespace = namespace;
+    }
+
+    /**
+     * @return the destination namespace URI to replace
+     */
+    public String getToURI() {
+        return toNamespace;
+    }
+
+    /**
+     * Sets the destination namespace URI to replace.
+     */
+    public void setToURI(String namespace) {
+        this.toNamespace = namespace;
+    }
+}
\ No newline at end of file

Modified: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/XMLTagLibrary.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/XMLTagLibrary.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/XMLTagLibrary.java (original)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/java/org/apache/commons/jelly/tags/xml/XMLTagLibrary.java Tue Jul 19 10:34:00 2005
@@ -53,6 +53,7 @@
         registerTag("expr", ExprTag.class);
         registerTag("element", ElementTag.class);
         registerTag("attribute", AttributeTag.class);
+        registerTag("replaceNamespace", ReplaceNamespaceTag.class);
         registerTag("copy", CopyTag.class);
         registerTag("copyOf", CopyOfTag.class);
         registerTag("comment", CommentTag.class);

Modified: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/TestXMLTags.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/TestXMLTags.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/TestXMLTags.java (original)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/TestXMLTags.java Tue Jul 19 10:34:00 2005
@@ -19,15 +19,20 @@
 import java.io.FileInputStream;
 import java.io.InputStream;
 import java.io.StringWriter;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
+import junit.framework.Assert;
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
 import junit.textui.TestRunner;
 
 import org.apache.commons.jelly.JellyContext;
+import org.apache.commons.jelly.JellyException;
 import org.apache.commons.jelly.Script;
 import org.apache.commons.jelly.XMLOutput;
 import org.apache.commons.jelly.parser.XMLParser;
@@ -36,6 +41,8 @@
 import org.dom4j.Document;
 import org.dom4j.DocumentHelper;
 import org.dom4j.Node;
+import org.dom4j.io.SAXContentHandler;
+import org.dom4j.io.XMLWriter;
 
 /** Tests the parser, the engine and the XML tags
   *
@@ -84,57 +91,158 @@
             log.debug("Evaluated script as...");
             log.debug(text);
         }
-        assertEquals("Produces the correct output", "It works!", text);
+        assertEquals("Should produce the correct output", "It works!", text);
+    }
+
+    public void testElementWithNameSpace() throws Exception {
+        String text = evaluteScriptAsText(testBaseDir + "/elementWithNameSpace.jelly");
+        assertEquals("Should produce the correct output",
+                "<env:Envelope "+
+                "xmlns:env=\"http://schemas.xmlsoap.org/soap/envelope/\" "+
+                "env:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"+
+                "</env:Envelope>", text);
+    }
+
+    public void testElementWithNameSpaceError() throws Exception {
+        try {
+            evaluteScriptAsText(testBaseDir + "/elementWithNameSpaceError.jelly");
+            Assert.fail("We should have bailed out with an JellyException");
+        } catch (JellyException jex) {
+            assertTrue(jex.getReason().startsWith("Cannot set same prefix to diferent URI in same node"));
+        }
+    }
+
+    public void testNamespaceReplace() throws Exception {
+        // For this test when we not set "ns" var with expected namespace, it
+        // is expected to repeat the same two times
+        String text = evaluteScriptAsText(testBaseDir + "/namespaceReplace.jelly");
+        String repeatingText = "<test-subnode attr=\"test\"><test-anotherSubNode></test-anotherSubNode><test-anotherSubNodeAgain xmlns:other=\"\" other:abc=\"testValue\"></test-anotherSubNodeAgain></test-subnode>";
+        assertEquals("Should produce the correct output",
+                "<test-node xmlns:test=\"http://apache/testNS\" test:abc=\"testValue\">"+
+                repeatingText + repeatingText +
+                "</test-node>", text);
+
+        Map ctxVars = new HashMap();
+        ctxVars.put("ns", "http://java/ns");
+
+        text = evaluteScriptAsText(testBaseDir + "/namespaceReplace.jelly", ctxVars);
+
+        String firstTrunk =
+        "<test-subnode xmlns=\"\" attr=\"test\">" +
+        "<test-anotherSubNode>" +
+        "</test-anotherSubNode>" +
+        "<test-anotherSubNodeAgain xmlns:other=\"http://java/ns\" xmlns=\"http://java/ns\" other:abc=\"testValue\">" +
+        "</test-anotherSubNodeAgain>" +
+        "</test-subnode>";
+
+        String secondTrunk =
+            "<test-subnode attr=\"test\">" +
+            "<test-anotherSubNode>" +
+            "</test-anotherSubNode>" +
+            "<test-anotherSubNodeAgain xmlns:other=\"http://java/ns\" other:abc=\"testValue\">" +
+            "</test-anotherSubNodeAgain>" +
+            "</test-subnode>";
+
+        System.out.println("TestXMLTags.testNamespaceReplace() text="+text);
+        assertEquals("Should produce the correct output",
+                "<test-node xmlns:test=\"http://apache/testNS\" xmlns=\"http://java/ns\" test:abc=\"testValue\">"+
+                firstTrunk + secondTrunk +
+                "</test-node>", text);
+    }
+
+    public void testAttributeNameSpaceDuplicatedNS() throws Exception {
+        try {
+            evaluteScriptAsText(testBaseDir + "/attributeNameSpaceDuplicatedNS.jelly");
+            Assert.fail("We should have bailed out with an JellyException");
+        } catch (JellyException jex) {
+            assertTrue(jex.getReason().startsWith("Cannot set same prefix to diferent URI in same node"));
+        }
+    }
+
+    public void testAttributeNameSpace() throws Exception {
+        String text = evaluteScriptAsText(testBaseDir + "/attributeNameSpace.jelly");
+        System.out.println(text);
+        assertEquals("Should produce the correct output",
+                "<top-node xmlns=\"abc\">"+
+                "<test-node xmlns:test=\"http://apache/testNS\" xmlns=\"http://apache/trueNS\" test:abc=\"testValue\" abc2=\"testValue\" abc3=\"testValue\">"+
+                "<test:test-subnode><node-at-same-ns-as-top xmlns=\"abc\">"+
+                "</node-at-same-ns-as-top>"+
+                "</test:test-subnode>"+
+                "</test-node>"+
+                "</top-node>", text);
+    }
+
+    public void testAttributeNameSpaceDefaultNS() throws Exception {
+        String text = evaluteScriptAsText(testBaseDir + "/attributeNameSpaceDefaultNS.jelly");
+        System.out.println(text);
+        assertEquals("Should produce the correct output",
+                "<top-node>"+
+                "<test-node xmlns:test=\"http://apache/testNS\" xmlns=\"http://apache/trueNS\" test:abc=\"testValue\" abc2=\"testValue\" abc3=\"testValue\">"+
+                "<test:test-subnode><node-at-same-ns-as-top xmlns=\"\">"+
+                "</node-at-same-ns-as-top>"+
+                "</test:test-subnode>"+
+                "</test-node>"+
+                "</top-node>", text);
+    }
+
+    public void testAttributeNameSpaceWithInnerElements() throws Exception {
+        String text = evaluteScriptAsText(testBaseDir + "/attributeNameSpaceWithInnerElements.jelly");
+        assertEquals("Should produce the correct output",
+                "<test-node xmlns:test=\"http://apache/testNS\" test:abc=\"testValue\" abc2=\"testValue\" abc3=\"testValue\">"+
+                "<test-sub-node xmlns:test2=\"http://apache/testNS\" xmlns:test3=\"http://apache/anotherNS\" test:abc=\"testValue\" test2:abc2=\"testValue\" test3:abc3=\"testValue\">"+
+                "</test-sub-node>"+
+                "</test-node>"
+                , text);
     }
 
     public void testTransform() throws Exception {
         String text = evaluteScriptAsText(testBaseDir + "/transformExample.jelly");
-        assertEquals("Produces the correct output", "It works!", text);
+        assertEquals("Should produce the correct output", "It works!", text);
     }
 
     public void testTransformAllInLine() throws Exception {
         String text = evaluteScriptAsText(testBaseDir + "/transformExampleAllInLine.jelly");
-        assertEquals("Produces the correct output", "It works!", text);
+        assertEquals("Should produce the correct output", "It works!", text);
     }
 
     public void testTransformParams() throws Exception {
         String text = evaluteScriptAsText(testBaseDir + "/transformParamExample.jelly");
-        assertEquals("Produces the correct output", "It works!", text);
+        assertEquals("Should produce the correct output", "It works!", text);
     }
 
     public void testTransformParamsInLine() throws Exception {
 
         String text = evaluteScriptAsText(testBaseDir + "/transformParamExample2.jelly");
-        assertEquals("Produces the correct output", "It works!", text);
+        assertEquals("Should produce the correct output", "It works!", text);
     }
 
     public void testTransformSAXOutput() throws Exception {
         String text = evaluteScriptAsText(testBaseDir + "/transformExampleSAXOutput.jelly");
-        assertEquals("Produces the correct output", "It works!", text);
+        assertEquals("Should produce the correct output", "It works!", text);
     }
 
     public void testTransformSAXOutputNestedTransforms() throws Exception {
         String text = evaluteScriptAsText(testBaseDir +
             "/transformExampleSAXOutputNestedTransforms.jelly");
-        assertEquals("Produces the correct output", "It works!", text);
+        assertEquals("Should produce the correct output", "It works!", text);
     }
 
     public void testTransformSchematron() throws Exception {
         String text = evaluteScriptAsText(testBaseDir +
             "/schematron/transformSchematronExample.jelly");
-        assertEquals("Produces the correct output", "Report count=1:assert count=2", text);
+        assertEquals("Should produce the correct output", "Report count=1:assert count=2", text);
     }
 
     public void testTransformXmlVar() throws Exception {
         String text = evaluteScriptAsText(testBaseDir +
             "/transformExampleXmlVar.jelly");
-        assertEquals("Produces the correct output", "It works!", text);
+        assertEquals("Should produce the correct output", "It works!", text);
     }
 
     public void testDoctype() throws Exception {
         String text = evaluteScriptAsText(testBaseDir +
             "/testDoctype.jelly");
-        assertEquals("Produces the correct output", "<!DOCTYPE foo PUBLIC \"publicID\" \"foo.dtd\">\n<foo></foo>", text);
+        assertEquals("Should produce the correct output", "<!DOCTYPE foo PUBLIC \"publicID\" \"foo.dtd\">\n<foo></foo>", text);
     }
 
     public void runUnitTest(String name) throws Exception {
@@ -172,7 +280,23 @@
      * returns the whitespace trimmed output as text
      */
     protected String evaluteScriptAsText(String fileName) throws Exception {
+        return evaluteScriptAsText(fileName, null);
+    }
+
+    /**
+     * Evaluates the script by the given file name and
+     * returns the whitespace trimmed output as text
+     */
+    protected String evaluteScriptAsText(String fileName, Map ctxVars) throws Exception {
         JellyContext context = new JellyContext();
+        if (ctxVars != null) {
+            Set keys = ctxVars.keySet();
+            for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
+                String key = (String) iterator.next();
+                Object value = ctxVars.get(key);
+                context.setVariable(key, value);
+            }
+        }
 
         // allow scripts to refer to any resource inside this project
         // using an absolute URI like /src/test/org/apache/foo.xml
@@ -190,4 +314,49 @@
         }
         return text;
     }
+
+    protected String evaluteScriptAsTextUsingSaxContentHandler(String fileName, Map ctxVars) throws Exception {
+        org.dom4j.io.OutputFormat outputFormat = new org.dom4j.io.OutputFormat();
+        outputFormat.setSuppressDeclaration(true);
+        outputFormat.setNewlines(false);
+        outputFormat.setIndent(false);
+        outputFormat.setExpandEmptyElements(true);
+        //outputFormat.setIndentSize(4);
+
+        StringWriter buffer = new StringWriter();
+        XMLWriter xmlWriter = new XMLWriter(buffer, outputFormat);
+        // xmlWriter.setEscapeText(false);
+
+        SAXContentHandler saxHandler = new SAXContentHandler();
+        XMLOutput output = new XMLOutput(saxHandler);
+
+        // now run a script using a URL
+        JellyContext context = new JellyContext();
+        if (ctxVars != null) {
+            Set keys = ctxVars.keySet();
+            for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
+                String key = (String) iterator.next();
+                Object value = ctxVars.get(key);
+                context.setVariable(key, value);
+            }
+        }
+
+        // allow scripts to refer to any resource inside this project
+        // using an absolute URI like /src/test/org/apache/foo.xml
+        context.setRootURL(new File(".").toURL());
+
+        output.startDocument();
+        context.runScript(new File(fileName), output);
+        output.endDocument();
+        xmlWriter.write(saxHandler.getDocument());
+        xmlWriter.flush();
+
+        String text = buffer.toString().trim();
+        if (log.isDebugEnabled()) {
+            log.debug("Evaluated script as...");
+            log.debug(text);
+        }
+        return text;
+    }
+
 }

Added: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpace.jelly
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpace.jelly?rev=219726&view=auto
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpace.jelly (added)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpace.jelly Tue Jul 19 10:34:00 2005
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<j:jelly xmlns:j="jelly:core" xmlns:x="jelly:xml" xmlns="abc">
+    <top-node>
+        <x:element URI="http://apache/trueNS" name="test-node">
+            <x:attribute URI="http://apache/testNS" name="test:abc" trim="true">testValue</x:attribute>
+            <!-- attributes without ':' should not have namespace -->
+            <x:attribute URI="http://apache/testNS" name="abc2" trim="true">testValue</x:attribute>
+            <x:attribute name="abc3" trim="true">testValue</x:attribute>
+            <x:element URI="http://apache/testNS" name="test:test-subnode">
+                <node-at-same-ns-as-top/>
+            </x:element>
+        </x:element>
+    </top-node>
+</j:jelly>
+

Added: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceDefaultNS.jelly
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceDefaultNS.jelly?rev=219726&view=auto
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceDefaultNS.jelly (added)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceDefaultNS.jelly Tue Jul 19 10:34:00 2005
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<j:jelly xmlns:j="jelly:core" xmlns:x="jelly:xml">
+    <top-node>
+        <x:element URI="http://apache/trueNS" name="test-node">
+            <x:attribute URI="http://apache/testNS" name="test:abc" trim="true">testValue</x:attribute>
+            <!-- attributes without ':' should not have namespace -->
+            <x:attribute URI="http://apache/testNS" name="abc2" trim="true">testValue</x:attribute>
+            <x:attribute name="abc3" trim="true">testValue</x:attribute>
+            <x:element URI="http://apache/testNS" name="test:test-subnode">
+                <node-at-same-ns-as-top/>
+            </x:element>
+        </x:element>
+    </top-node>
+</j:jelly>
+

Added: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceDuplicatedNS.jelly
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceDuplicatedNS.jelly?rev=219726&view=auto
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceDuplicatedNS.jelly (added)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceDuplicatedNS.jelly Tue Jul 19 10:34:00 2005
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<j:jelly xmlns:j="jelly:core" xmlns:x="jelly:xml">
+    <x:element name="test-node">
+        <x:attribute URI="http://apache/testNS" name="test:abc" trim="true">testValue</x:attribute>
+        <x:attribute URI="http://apache/anotherNS" name="test:abc2" trim="true">testValue</x:attribute>
+    </x:element>
+</j:jelly>
+

Added: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceWithInnerElements.jelly
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceWithInnerElements.jelly?rev=219726&view=auto
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceWithInnerElements.jelly (added)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/attributeNameSpaceWithInnerElements.jelly Tue Jul 19 10:34:00 2005
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<j:jelly xmlns:j="jelly:core" xmlns:x="jelly:xml">
+    <x:element name="test-node">
+        <x:attribute URI="http://apache/testNS" name="test:abc" trim="true">testValue</x:attribute>
+        <!-- attributes without ':' should not have namespace -->
+        <x:attribute URI="http://apache/testNS" name="abc2" trim="true">testValue</x:attribute>
+        <x:attribute name="abc3" trim="true">testValue</x:attribute>
+
+        <x:element name="test-sub-node">
+            <x:attribute URI="http://apache/testNS" name="test:abc" trim="true">testValue</x:attribute>
+            <x:attribute URI="http://apache/testNS" name="test2:abc2" trim="true">testValue</x:attribute>
+            <x:attribute URI="http://apache/anotherNS" name="test3:abc3" trim="true">testValue</x:attribute>
+        </x:element>
+    </x:element>
+</j:jelly>
+

Added: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/elementWithNameSpace.jelly
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/elementWithNameSpace.jelly?rev=219726&view=auto
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/elementWithNameSpace.jelly (added)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/elementWithNameSpace.jelly Tue Jul 19 10:34:00 2005
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<j:jelly xmlns:j="jelly:core" xmlns:x="jelly:xml">
+    <x:element URI="http://schemas.xmlsoap.org/soap/envelope/" name="env:Envelope">
+        <x:attribute URI="http://schemas.xmlsoap.org/soap/envelope/" 
+                     name="env:encodingStyle" trim="true">
+            http://schemas.xmlsoap.org/soap/encoding/
+        </x:attribute>
+    </x:element>
+</j:jelly>
+

Added: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/elementWithNameSpaceError.jelly
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/elementWithNameSpaceError.jelly?rev=219726&view=auto
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/elementWithNameSpaceError.jelly (added)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/elementWithNameSpaceError.jelly Tue Jul 19 10:34:00 2005
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<j:jelly xmlns:j="jelly:core" xmlns:x="jelly:xml">
+    <x:element URI="http://schemas.xmlsoap.org/soap/envelope/" name="env:Envelope">
+        <x:attribute name="env:encodingStyle" trim="true">
+            http://schemas.xmlsoap.org/soap/encoding/
+        </x:attribute>
+    </x:element>
+</j:jelly>
+

Added: jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/namespaceReplace.jelly
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/namespaceReplace.jelly?rev=219726&view=auto
==============================================================================
--- jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/namespaceReplace.jelly (added)
+++ jakarta/commons/proper/jelly/trunk/jelly-tags/xml/src/test/org/apache/commons/jelly/tags/xml/namespaceReplace.jelly Tue Jul 19 10:34:00 2005
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<j:jelly xmlns:j="jelly:core" xmlns:x="jelly:xml">
+    <x:element URI="${ns}" name="test-node">
+        <x:attribute URI="http://apache/testNS" name="test:abc" trim="true">testValue</x:attribute>
+
+        <test-subnode attr="test">
+            <test-anotherSubNode />
+            <x:element URI="${ns}" name="test-anotherSubNodeAgain">
+                <x:attribute URI="${ns}" name="other:abc" trim="true">testValue</x:attribute>
+            </x:element>
+        </test-subnode>
+
+        <x:replaceNamespace toURI="${ns}">
+            <test-subnode attr="test">
+                <test-anotherSubNode />
+                <x:element URI="${ns}" name="test-anotherSubNodeAgain">
+                    <x:attribute URI="${ns}" name="other:abc" trim="true">testValue</x:attribute>
+                </x:element>
+            </test-subnode>
+        </x:replaceNamespace>
+    </x:element>
+</j:jelly>
+

Modified: jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/XMLOutput.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/XMLOutput.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/XMLOutput.java (original)
+++ jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/XMLOutput.java Tue Jul 19 10:34:00 2005
@@ -20,6 +20,11 @@
 import java.io.OutputStream;
 import java.io.UnsupportedEncodingException;
 import java.io.Writer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -44,29 +49,35 @@
 public class XMLOutput implements ContentHandler, LexicalHandler {
 
     protected static final String[] LEXICAL_HANDLER_NAMES =
-        {
-            "http://xml.org/sax/properties/lexical-handler",
-            "http://xml.org/sax/handlers/LexicalHandler" };
+     {
+        "http://xml.org/sax/properties/lexical-handler",
+        "http://xml.org/sax/handlers/LexicalHandler" };
 
-    /** empty attributes */
+    /** Empty attributes. */
     private static final Attributes EMPTY_ATTRIBUTES = new AttributesImpl();
 
     /** The Log to which logging calls will be made. */
     private static final Log log = LogFactory.getLog(XMLOutput.class);
 
-    /** the default for escaping of text */
+    /** the default for escaping of text. */
     private static final boolean DEFAULT_ESCAPE_TEXT = false;
 
-    /** The SAX ContentHandler that output goes to */
+    /** The SAX ContentHandler that output goes to. */
     private ContentHandler contentHandler;
 
-    /** The SAX LexicalHandler that output goes to */
+    /** The SAX LexicalHandler that output goes to. */
     private LexicalHandler lexicalHandler;
 
+    /** Stack of kown namespaces. */
+    private NamespaceStack namespaceStack = new NamespaceStack();
 
     public XMLOutput() {
     }
 
+    /** The XML-output will relay the SAX events to the indicated
+     * contentHandler.
+     * @param contentHandler
+     */
     public XMLOutput(ContentHandler contentHandler) {
         this.contentHandler = contentHandler;
         // often classes will implement LexicalHandler as well
@@ -75,6 +86,11 @@
         }
     }
 
+    /** The XML-output will relay the SAX events to the indicated
+     * content-handler lexical-handler.
+     * @param contentHandler
+     * @param lexicalHandler
+     */
     public XMLOutput(
         ContentHandler contentHandler,
         LexicalHandler lexicalHandler) {
@@ -92,16 +108,25 @@
     }
 
     /**
-     * Provides a useful hook that implementations can use to close the
-     * underlying OutputStream or Writer
+     * Provides a useful hook that implementations
+     * can use to close the
+     * underlying OutputStream or Writer.
+     *
+     * @throws IOException
      */
     public void close() throws IOException {
     }
 
+    /** Flushes the underlying stream if {@link XMLWriter}
+     * or {@link XMLOutput}.
+     *
+     * @throws IOException
+     */
     public void flush() throws IOException {
-        if( contentHandler instanceof XMLWriter )
-        {
+        if (contentHandler instanceof XMLWriter) {
             ((XMLWriter)contentHandler).flush();
+        } else if (contentHandler instanceof XMLOutput) {
+            ((XMLOutput)contentHandler).flush();
         }
     }
 
@@ -109,7 +134,7 @@
     //-------------------------------------------------------------------------
 
     /**
-     * Creates an XMLOutput from an existing SAX XMLReader
+     * Creates an XMLOutput from an existing SAX XMLReader.
      */
     public static XMLOutput createXMLOutput(XMLReader xmlReader) {
         XMLOutput output = new XMLOutput(xmlReader.getContentHandler());
@@ -122,10 +147,11 @@
                     output.setLexicalHandler((LexicalHandler) value);
                     break;
                 }
-            }
-            catch (Exception e) {
+            } catch (Exception e) {
                 // ignore any unsupported-operation exceptions
-                if (log.isDebugEnabled()) log.debug("error setting lexical handler properties", e);
+                if (log.isDebugEnabled()) {
+                    log.debug("error setting lexical handler properties", e);
+                }
             }
         }
         return output;
@@ -145,10 +171,9 @@
      *
      * @param writer is the writer to output to
      * @param escapeText is whether or not text output will be escaped. This must be true
-     * if the underlying output is XML or could be false if the underlying output is textual.
+     *   if the underlying output is XML or could be false if the underlying output is textual.
      */
-    public static XMLOutput createXMLOutput(Writer writer, boolean escapeText)
-    {
+    public static XMLOutput createXMLOutput(Writer writer, boolean escapeText) {
         XMLWriter xmlWriter = new XMLWriter(writer);
         xmlWriter.setEscapeText(escapeText);
         return createXMLOutput(xmlWriter);
@@ -169,8 +194,11 @@
      * @param out is the output stream to write
      * @param escapeText is whether or not text output will be escaped. This must be true
      * if the underlying output is XML or could be false if the underlying output is textual.
+     * @throws UnsupportedEncodingException if the underlying write could not
+     *   be created.
      */
-    public static XMLOutput createXMLOutput(OutputStream out, boolean escapeText) throws UnsupportedEncodingException {
+    public static XMLOutput createXMLOutput(OutputStream out, boolean escapeText)
+            throws UnsupportedEncodingException {
         XMLWriter xmlWriter = new XMLWriter(out);
         xmlWriter.setEscapeText(escapeText);
         return createXMLOutput(xmlWriter);
@@ -193,7 +221,7 @@
     /**
      * Outputs the given String as a piece of valid text in the
      * XML event stream.
-     * Any special XML characters should be properly escaped.
+     * Any special XML characters should come out properly escaped.
      */
     public void write(String text) throws SAXException {
         char[] ch = text.toCharArray();
@@ -212,7 +240,7 @@
     }
 
     /**
-     * Outputs a comment to the XML stream
+     * Outputs a comment to the XML stream.
      */
     public void writeComment(String text) throws SAXException {
         char[] ch = text.toCharArray();
@@ -220,21 +248,24 @@
     }
 
     /**
-     * Helper method for outputting a start element event for an element in no namespace
+     * Helper method for outputting a start element event
+     * for an element in no namespace.
      */
     public void startElement(String localName) throws SAXException {
         startElement("", localName, localName, EMPTY_ATTRIBUTES);
     }
 
     /**
-     * Helper method for outputting a start element event for an element in no namespace
+     * Helper method for outputting a start element event
+     * for an element in no namespace.
      */
     public void startElement(String localName, Attributes attributes) throws SAXException {
         startElement("", localName, localName, attributes);
     }
 
     /**
-     * Helper method for outputting an end element event for an element in no namespace
+     * Helper method for outputting an end element event
+     * for an element in no namespace.
      */
     public void endElement(String localName) throws SAXException {
         endElement("", localName, localName);
@@ -344,7 +375,9 @@
      * @see #startElement
      */
     public void startPrefixMapping(String prefix, String uri) throws SAXException {
-        contentHandler.startPrefixMapping(prefix, uri);
+        namespaceStack.pushNamespace(prefix, uri);
+        // contentHandler.startPrefixMapping(prefix, uri) will be called if needed
+        // in pushNamespace
     }
 
     /**
@@ -364,7 +397,9 @@
      * @see #endElement
      */
     public void endPrefixMapping(String prefix) throws SAXException {
-        contentHandler.endPrefixMapping(prefix);
+        namespaceStack.popNamespace(prefix);
+        // End prefix mapping was already called after endElement
+        // contentHandler.endPrefixMapping(prefix);
     }
 
     /**
@@ -435,7 +470,28 @@
         String qName,
         Attributes atts)
         throws SAXException {
+
+        int idx = qName.indexOf(':');
+        String attNsPrefix = "";
+        if (idx >= 0) {
+            attNsPrefix = qName.substring(0, idx);
+        }
+        namespaceStack.pushNamespace(attNsPrefix, uri);
+        for (int i = 0; i < atts.getLength(); i++) {
+            String attQName = atts.getQName(i);
+            // An attribute only has an namespace if has a prefix
+            // If not, stays in namespace of containing node
+            idx = attQName.indexOf(':');
+            if (idx >= 0) {
+                attNsPrefix = attQName.substring(0, idx);
+                String attUri = atts.getURI(i);
+                namespaceStack.pushNamespace(attNsPrefix, attUri);
+            }
+        }
+
         contentHandler.startElement(uri, localName, qName, atts);
+        // Inform namespaceStack of a new depth
+        namespaceStack.increaseLevel();
     }
 
     /**
@@ -462,6 +518,9 @@
     public void endElement(String uri, String localName, String qName)
         throws SAXException {
         contentHandler.endElement(uri, localName, qName);
+        // Inform namespaceStack to return to previous depth
+        namespaceStack.decreaseLevel();
+        namespaceStack.popNamespaces();
     }
 
     /**
@@ -507,7 +566,7 @@
      * @see #ignorableWhitespace
      * @see org.xml.sax.Locator
      */
-    public void characters(char ch[], int start, int length) throws SAXException {
+    public void characters(char[] ch, int start, int length) throws SAXException {
         contentHandler.characters(ch, start, length);
     }
 
@@ -535,7 +594,7 @@
      *            wrapping another exception.
      * @see #characters
      */
-    public void ignorableWhitespace(char ch[], int start, int length)
+    public void ignorableWhitespace(char[] ch, int start, int length)
         throws SAXException {
         contentHandler.ignorableWhitespace(ch, start, length);
     }
@@ -855,4 +914,127 @@
         return answer;
     }
 
+    private final class NamespaceStack {
+        /** A list of maps: Each map contains prefix->uri mapping */
+        private List nsStack;
+
+        private NamespaceStack() {
+            this.nsStack = new ArrayList();
+            this.nsStack.add(new HashMap());
+        }
+
+        private boolean isRootNodeDefaultNs(String prefix, String uri) {
+            return ("".equals(prefix) && "".equals(uri) && nsStack.size() == 1);
+        }
+
+        public void pushNamespace(String prefix, String uri) throws SAXException {
+            Map prefixUriMap;
+
+            if (prefix == null) {
+                prefix = "";
+            }
+            if (uri == null) {
+                uri = "";
+            }
+
+            if ("xml".equals(prefix)) {
+                // We should ignore setting 'xml' prefix
+                // As declared in java of ContentHandler#startPrefixMapping
+                return;
+            }
+
+
+            // Lets find out if we already declared this same prefix,
+            // if not declare in current depth map (the first of list)
+            // and call contentHandler.startPrefixMapping(prefix, uri);
+            boolean isNew = true;
+            for (Iterator iter = nsStack.iterator(); iter.hasNext();) {
+                prefixUriMap = (Map) iter.next();
+                if (prefixUriMap.containsKey(prefix)) {
+                    if (uri.equals(prefixUriMap.get(prefix))) {
+                        // Its an active namespace already
+                        // System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.pushNamespace() IS NOT NEW prefix="+prefix+",uri="+uri);
+                        isNew = false;
+                    }
+                    // We found it in stack
+                    // If it was exacly the same, we won't bother
+                    break;
+                }
+            }
+
+            if (isNew) {
+                // not declared sometime before
+                prefixUriMap = (Map) nsStack.get(0); // Current depth map
+                // Sanity check: Don't let two prefixes for diferent uris in
+                // same depth
+                if (prefixUriMap.containsKey(prefix)) {
+                    if (!uri.equals(prefixUriMap.get(prefix))) {
+                        throw new SAXException("Cannot set same prefix to diferent URI in same node: trying to add prefix \""
+                                + prefix + "\" for uri \""+uri+"\" whereas the declared ones are " + prefixUriMap);
+                    }
+                } else {
+                    prefixUriMap.put(prefix, uri);
+
+                    // To avoid setting xmlns="" for top node (not very nice :D)
+                    // We need to especificaly check this condition
+                    if (!isRootNodeDefaultNs(prefix, uri)) {
+//                        System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.pushNamespace() prefix="+prefix+",uri="+uri);
+                        contentHandler.startPrefixMapping(prefix, uri);
+                    }
+                }
+            }
+        }
+
+        public void popNamespaces() throws SAXException {
+            Map prefixUriMap = (Map)nsStack.get(0);
+            for (Iterator iter = prefixUriMap.keySet().iterator();iter.hasNext();) {
+                String prefix = (String)iter.next();
+                String uri = (String) prefixUriMap.get(prefix);
+                iter.remove();
+
+                // If we havent called startPrefixMapping for root node if we wanted to avoid xmlns=""
+                // We aren't going to call endPrefixMapping neither
+                if (!isRootNodeDefaultNs(prefix, uri)) {
+//                    System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.popNamespaces() prefix="+prefix);
+                    contentHandler.endPrefixMapping(prefix);
+                }
+            }
+        }
+
+        public void popNamespace(String prefix) throws SAXException {
+            Map prefixUriMap = (Map)nsStack.get(0);
+
+            if (prefix == null) {
+                prefix = "";
+            }
+
+            if ("xml".equals(prefix)) {
+                // We should ignore setting 'xml' prefix
+                // As declared in java of ContentHandler#startPrefixMapping
+                return;
+            }
+
+            if (prefixUriMap.containsKey(prefix)) {
+                String uri = (String) prefixUriMap.get(prefix);
+                prefixUriMap.remove(prefix);
+                // If we havent called startPrefixMapping for root node if we wanted to avoid xmlns=""
+                // We aren't going to call endPrefixMapping neither
+                if (!isRootNodeDefaultNs(prefix, uri)) {
+//                    System.out.println(">>>"+XMLOutput.this.hashCode()+">NamespaceStack.popNamespace() prefix="+prefix);
+                    contentHandler.endPrefixMapping(prefix);
+                }
+            }/* else {
+                improper nesting ? or already removed in popNamespaces
+            }
+            */
+        }
+
+        public void decreaseLevel() {
+            nsStack.remove(0);
+        }
+
+        public void increaseLevel() {
+            nsStack.add(0, new HashMap());
+        }
+    }
 }

Modified: jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/StaticTag.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/StaticTag.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/StaticTag.java (original)
+++ jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/StaticTag.java Tue Jul 19 10:34:00 2005
@@ -69,6 +69,15 @@
         }
     }
 
+    public void setAttribute(String name, String prefix, String nsURI, Object value) {
+        if(value==null)
+            return;
+        if(prefix!=null && prefix.length()>0)
+            attributes.addAttribute(nsURI,name,prefix+":"+name,"CDATA",value.toString());
+        else
+            attributes.addAttribute("",name,name,"CDATA",value.toString());
+    }
+
     // DynaTag interface
     //-------------------------------------------------------------------------
     public void setAttribute(String name, Object value) throws JellyTagException {

Modified: jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/StaticTagScript.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/StaticTagScript.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/StaticTagScript.java (original)
+++ jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/StaticTagScript.java Tue Jul 19 10:34:00 2005
@@ -86,7 +86,10 @@
             for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
                 Map.Entry entry = (Map.Entry) iter.next();
                 String name = (String) entry.getKey();
-                Expression expression = (Expression) entry.getValue();
+                if(name.indexOf(':')!=-1)
+                    name = name.substring(name.indexOf(':')+1);
+                ExpressionAttribute expat = (ExpressionAttribute) entry.getValue();
+                Expression expression = expat.exp;
 
                 Object value = null;
 
@@ -96,7 +99,10 @@
                     value = expression.evaluate(context);
                 }
 
-                dynaTag.setAttribute(name, value);
+                if(expat.prefix!=null || expat.prefix.length()>0 && tag instanceof StaticTag)
+                    ((StaticTag) dynaTag).setAttribute(name,expat.prefix, expat.nsURI,value);
+                else
+                    dynaTag.setAttribute(name, value);
             }
 
             tag.doTag(output);

Modified: jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/TagScript.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/TagScript.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/TagScript.java (original)
+++ jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/impl/TagScript.java Tue Jul 19 10:34:00 2005
@@ -170,7 +170,19 @@
         if (log.isDebugEnabled()) {
             log.debug("adding attribute name: " + name + " expression: " + expression);
         }
-        attributes.put(name, expression);
+        attributes.put(name, new ExpressionAttribute(name,expression));
+    }
+
+    /** Add an initialization attribute for the tag.
+     * This method must be called after the setTag() method
+     */
+    public void addAttribute(String name, String prefix, String nsURI, Expression expression) {
+        if (log.isDebugEnabled()) {
+            log.debug("adding attribute name: " + name + " expression: " + expression);
+        }
+        if(name.indexOf(':')==-1)
+            name = prefix + ':' + name;
+        attributes.put(name, new ExpressionAttribute(name,prefix,nsURI,expression));
     }
 
     /**
@@ -206,7 +218,7 @@
                 for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
                     Map.Entry entry = (Map.Entry) iter.next();
                     String name = (String) entry.getKey();
-                    Expression expression = (Expression) entry.getValue();
+                    Expression expression = ((ExpressionAttribute) entry.getValue()).exp;
 
                     Class type = dynaTag.getAttributeType(name);
                     Object value = null;
@@ -225,7 +237,7 @@
                 for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
                     Map.Entry entry = (Map.Entry) iter.next();
                     String name = (String) entry.getKey();
-                    Expression expression = (Expression) entry.getValue();
+                    Expression expression = ((ExpressionAttribute) entry.getValue()).exp;
 
                     DynaProperty property = dynaBean.getDynaClass().getDynaProperty(name);
                     if (property == null) {
@@ -690,3 +702,21 @@
         throw new JellyTagException(e, fileName, elementName, columnNumber, lineNumber);
     }
 }
+
+
+class ExpressionAttribute {
+    public ExpressionAttribute(String name, Expression exp) {
+        this(name,"","",exp);
+    }
+    public ExpressionAttribute(String name, String prefix, String nsURI, Expression exp) {
+        this.name = name;
+        this.prefix = prefix;
+        this.nsURI = nsURI;
+        this.exp = exp;
+    }
+
+    String name;
+    String prefix;
+    String nsURI;
+    Expression exp;
+}
\ No newline at end of file

Modified: jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/parser/XMLParser.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/parser/XMLParser.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/parser/XMLParser.java (original)
+++ jakarta/commons/proper/jelly/trunk/src/java/org/apache/commons/jelly/parser/XMLParser.java Tue Jul 19 10:34:00 2005
@@ -1066,7 +1066,12 @@
                         attributeValue, getExpressionFactory()
                     );
                 String attrQName = list.getQName(i);
-                script.addAttribute(attrQName, expression);
+                int p = attrQName.indexOf(':');
+                String prefix = p>=0 ?
+                        attrQName.substring(0,p):
+                        "";
+                script.addAttribute(list.getLocalName(i),
+                        prefix, list.getURI(i), expression);
             }
             return script;
         }

Modified: jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/TestCoreTags.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/TestCoreTags.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/TestCoreTags.java (original)
+++ jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/TestCoreTags.java Tue Jul 19 10:34:00 2005
@@ -105,4 +105,24 @@
         textScript.trimStartWhitespace();
         assertEquals("foo", textScript.getText());
     }
+
+
+    public void testStaticNamespacedAttributes() throws Exception {
+        InputStream in = new FileInputStream("src/test/org/apache/commons/jelly/testStaticNamespacedAttributes.jelly");
+        XMLParser parser = new XMLParser();
+        Script script = parser.parse(in);
+        script = script.compile();
+        log.debug("Found: " + script);
+        JellyContext context = new JellyContext();
+        StringWriter buffer = new StringWriter();
+        script.run(context, XMLOutput.createXMLOutput(buffer));
+        String text = buffer.toString().trim();
+        if (log.isDebugEnabled()) {
+            log.debug("Evaluated script as...");
+            log.debug(text);
+        }
+        assertEquals("Should produces the correct output",
+                "<blip xmlns:blop=\"blop\" blop:x=\"blip\"></blip>",
+                text);
+    }
 }

Modified: jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/core/TestFileTag.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/core/TestFileTag.java?rev=219726&r1=219725&r2=219726&view=diff
==============================================================================
--- jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/core/TestFileTag.java (original)
+++ jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/core/TestFileTag.java Tue Jul 19 10:34:00 2005
@@ -55,7 +55,7 @@
 
         //FIXME This doesn't take into account attribute ordering
         assertEquals("fully qualified attributes not passed",
-                "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\"></html>",
+                "<html xmlns=\"http://www.w3.org/1999/xhtml\" lang=\"en\" xml:lang=\"en\"></html>",
                 data);
     }
 

Added: jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/testStaticNamespacedAttributes.jelly
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/testStaticNamespacedAttributes.jelly?rev=219726&view=auto
==============================================================================
--- jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/testStaticNamespacedAttributes.jelly (added)
+++ jakarta/commons/proper/jelly/trunk/src/test/org/apache/commons/jelly/testStaticNamespacedAttributes.jelly Tue Jul 19 10:34:00 2005
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<j:jelly xmlns:j="jelly:core">
+	<blip xmlns:blop="blop" blop:x="blip"/>
+</j:jelly>



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org


Mime
View raw message