JDK-6305029 : Identity Transform does not demand namespace-prefixes
  • Type: Bug
  • Component: xml
  • Sub-Component: org.xml.sax
  • Affected Version: 5.0,5.0u1,6
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic,windows_xp
  • CPU: generic,x86
  • Submitted: 2005-08-02
  • Updated: 2012-04-25
  • Resolved: 2006-01-31
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other JDK 6
5.0u8Fixed 6 b58Fixed
Related Reports
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.5.0_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_01-b08)
Java HotSpot(TM) Client VM (build 1.5.0_01-b08, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
The identity transformer created through TransformerFactory.newInstance().newTransformer() does not set the namespace-prefixes feature to true on a SAXSource's XMLReader.

In non-prefix mode it is legal for that XMLReader to report "" as the qname of an element in a namespace. The identity transform will not deal with this and report "" as local name to its target.

1.4.2 (TransformerIdentityImpl:404) would explicitly enable this feature:
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", true);


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile and Run the attached program.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
<?xml version="1.0" encoding="UTF-8"?><html:div xmlns:html="http://w3.org/..."/>

ACTUAL -
<?xml version="1.0" encoding="UTF-8"?>< xmlns="http://w3.org/..." xmlns:html="http://w3.org/..."/>


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/*
 * Copyright (c) 2004 CoreMedia AG, Hamburg. All rights reserved.
 */

package test;

import org.xml.sax.*;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Result;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import java.io.ByteArrayInputStream;
import java.io.IOException;

/**
 * Wrap an XMLReader and explicitly report "" as qname if prefixes are off.
 */
public class TestXmlReader implements XMLReader {
  private static final String PREFIXES_FEATURE = "http://xml.org/sax/features/namespace-prefixes";

  XMLReader wrapped;
  boolean prefixes = false;

  public TestXmlReader(XMLReader wrapped) {
    this.wrapped = wrapped;
  }

  public boolean getFeature (String name)
      throws SAXNotRecognizedException, SAXNotSupportedException {
    return wrapped.getFeature(name);
  }

  public void setFeature (String name, boolean value)
      throws SAXNotRecognizedException, SAXNotSupportedException {
    if(name.equals(PREFIXES_FEATURE))
      prefixes = value;

    wrapped.setFeature(name, value);
  }

  public Object getProperty (String name)
      throws SAXNotRecognizedException, SAXNotSupportedException {
    return wrapped.getProperty(name);
  }

  public void setProperty (String name, Object value)
      throws SAXNotRecognizedException, SAXNotSupportedException {
    wrapped.setProperty(name, value);
  }

  public void setEntityResolver (EntityResolver resolver) {
    wrapped.setEntityResolver(resolver);
  }

  public EntityResolver getEntityResolver () {
    return wrapped.getEntityResolver();
  }

  public void setDTDHandler (DTDHandler handler) {
    wrapped.setDTDHandler(handler);
  }

  public DTDHandler getDTDHandler () {
    return wrapped.getDTDHandler();
  }

  public void setContentHandler (final ContentHandler handler) {
    wrapped.setContentHandler(new ContentHandler() {
      ContentHandler delegate = handler;

      public void setDocumentLocator (Locator locator) {
	delegate.setDocumentLocator(locator);
      }

      public void startDocument ()
	  throws SAXException {
	delegate.startDocument();
      }

      public void endDocument()
	  throws SAXException {
	delegate.endDocument();
      }

      public void startPrefixMapping (String prefix, String uri)
	  throws SAXException {
	delegate.startPrefixMapping(prefix, uri);
      }

      public void endPrefixMapping (String prefix)
	  throws SAXException {
	delegate.endPrefixMapping(prefix);
      }

      public void startElement (String uri, String localName,
				String qName, Attributes atts)
	  throws SAXException {
	delegate.startElement(uri, localName, prefixes ? qName : "", atts);
      }

      public void endElement (String uri, String localName,
			      String qName)
	  throws SAXException {
	delegate.endElement(uri, localName, prefixes ? qName : "");
      }

      public void characters (char ch[], int start, int length)
	  throws SAXException {
	delegate.characters(ch, start, length);
      }

      public void ignorableWhitespace (char ch[], int start, int length)
	  throws SAXException {
	delegate.ignorableWhitespace(ch, start, length);
      }

      public void processingInstruction (String target, String data)
	  throws SAXException {
	delegate.processingInstruction(target, data);
      }

      public void skippedEntity (String name)
	  throws SAXException {
	delegate.skippedEntity(name);
      }
    });
  }

  public ContentHandler getContentHandler () {
    return wrapped.getContentHandler();
  }

  public void setErrorHandler (ErrorHandler handler) {
    wrapped.setErrorHandler(handler);
  }

  public ErrorHandler getErrorHandler () {
    return wrapped.getErrorHandler();
  }

  public void parse (InputSource input)
      throws IOException, SAXException {
    System.out.println("prefixes are "+(prefixes ? "on" : "off"));
    wrapped.parse(input);
  }

  public void parse (String systemId)
      throws IOException, SAXException {
    System.out.println("prefixes are "+(prefixes ? "on" : "off"));
    wrapped.parse(systemId);
  }

  public static void main(String[] args) throws TransformerException, SAXException, ParserConfigurationException {
    byte[] xml = "<?xml version=\"1.0\"?><html:div xmlns:html=\"http://w3.org/...\"/>".getBytes();

    SAXSource source = new SAXSource(new InputSource(new ByteArrayInputStream(xml)));
    SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
    saxParserFactory.setNamespaceAware(true);

    SAXParser parser = saxParserFactory.newSAXParser();
    source.setXMLReader(new TestXmlReader(parser.getXMLReader()));

    Result result = new StreamResult(System.out);

    TransformerFactory.newInstance().newTransformer().transform(source, result);
  }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Make the source report qnames even if it's legal to omit them.
Suggested fix and JUnit test case are:

A DESCRIPTION OF THE FIX :
As described in the bug report, the identity transformer relies on qnames which aren't necessarily available in SAX unless you enable the namespace prefixes feature. IdentityTransformer needs to be changed to do so - alternatively it needs to compute the qnames itself.

This diff is against com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl, version 1.6.0-ea-fastdebug-b55.

Cheers
Matthias


@@ -101,7 +100,9 @@
 	"http://xml.org/sax/properties/lexical-handler";
     private static final String NAMESPACE_FEATURE =
 	"http://xml.org/sax/features/namespaces";
-    
+    private static final String NAMESPACE_PREFIXES_FEATURE =
+        "http://xml.org/sax/features/namespace-prefixes";
+
     /**
      * A reference to the translet or null if the identity transform.
      */
@@ -559,6 +560,7 @@
                     // Falls through
                 }
                 reader.setContentHandler(handler);
+                reader.setFeature(NAMESPACE_PREFIXES_FEATURE, true);
 
                 // Create input source from source
                 InputSource input;
@@ -604,6 +606,7 @@
                     // Falls through
                 }
                 reader.setContentHandler(handler);
+                reader.setFeature(NAMESPACE_PREFIXES_FEATURE, true);
 
                 // Start pushing SAX events
                 reader.parse(input);


JUnit TESTCASE :
package test.xml;

import junit.framework.TestCase;
import org.xml.sax.*;
import org.xml.sax.helpers.AttributesImpl;

import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.sax.SAXSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.StringWriter;

public class PrefixesTest extends TestCase {
  public void testNamespacePrefixesFeature() throws TransformerException {
    SAXSource saxSource = new SAXSource(new MyXMLReader(), new InputSource());

    StringWriter builder = new StringWriter();
    TransformerFactory.newInstance().newTransformer().transform(saxSource, new StreamResult(builder));

    assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?><prefix:localName xmlns:prefix=\"namespaceUri\"/>", builder.toString());
  }

  private static class MyXMLReader implements XMLReader {
    private static final String NAMESPACES = "http://xml.org/sax/features/namespaces";
    private static final String NAMESPACE_PREFIXES = "http://xml.org/sax/features/namespace-prefixes";

    boolean namespaces = true;
    boolean namespacePrefixes = false;

    private EntityResolver resolver;
    private DTDHandler dtdHandler;
    private ContentHandler contentHandler;
    private ErrorHandler errorHandler;

    public boolean getFeature(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
      if(name.equals(NAMESPACES)) return namespaces;
      else if(name.equals(NAMESPACE_PREFIXES)) return namespacePrefixes;
      else throw new SAXNotRecognizedException();
    }

    public void setFeature(String name, boolean value) throws SAXNotRecognizedException, SAXNotSupportedException {
      if(name.equals(NAMESPACES)) namespaces = value;
      else if(name.equals(NAMESPACE_PREFIXES)) namespacePrefixes = value;
      else throw new SAXNotRecognizedException();
    }

    public Object getProperty(String name) throws SAXNotRecognizedException, SAXNotSupportedException {
      return null;
    }

    public void setProperty(String name, Object value) throws SAXNotRecognizedException, SAXNotSupportedException {
    }

    public void setEntityResolver(EntityResolver resolver) {
      this.resolver = resolver;
    }

    public EntityResolver getEntityResolver() {
      return resolver;
    }

    public void setDTDHandler(DTDHandler handler) {
      dtdHandler = handler;
    }

    public DTDHandler getDTDHandler() {
      return dtdHandler;
    }

    public void setContentHandler(ContentHandler handler) {
      contentHandler = handler;
    }

    public ContentHandler getContentHandler() {
      return contentHandler;
    }

    public void setErrorHandler(ErrorHandler handler) {
      errorHandler = handler;
    }

    public ErrorHandler getErrorHandler() {
      return errorHandler;
    }

    public void parse(InputSource input) throws IOException, SAXException {
      parse();
    }

    public void parse(String systemId) throws IOException, SAXException {
      parse();
    }

    private void parse() throws SAXException {
      contentHandler.startDocument();
      contentHandler.startPrefixMapping("prefix", "namespaceUri");

      AttributesImpl atts = new AttributesImpl();
      if(namespacePrefixes)
        atts.addAttribute("", "xmlns:prefix", "xmlns:prefix", "CDATA", "namespaceUri");

      contentHandler.startElement("namespaceUri", "localName", namespacePrefixes ? "prefix:localName" : "", atts);
      contentHandler.endElement("namespaceUri", "localName", namespacePrefixes ? "prefix:localName" : "");
      contentHandler.endPrefixMapping("prefix");
      contentHandler.endDocument();
    }
  }
}


FIX FOR BUG NUMBER:
6305029

Comments
EVALUATION Contribution-Forum:https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?messageID=12041&forumID=1463
16-03-2006

EVALUATION although bug is only against SAXSource, fix and unit test are also against StreamSource as StreamSource uses an XMLReader under the covers. private static final String NAMESPACE_PREFIXES_FEATURE = "http://xml.org/sax/features/namespace-prefixes"; ... reader.setFeature(NAMESPACE_PREFIXES_FEATURE, true); note that fix is slightly different than that supplied by user. feature is set in try block in case of Exception.
17-10-2005