You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by Kristian Mandrup <kr...@mandrup.dk> on 2005/02/28 17:43:09 UTC

[digester] Patches to xmlrules : NodeCreateRule addon + namespace prefix support

Added PREFIX namespace support for NodeCreateRule: Can now parse a XML Schema correctly ☺
Tested (see Junit TestCase below). Works fine!

+++

public class NodeCreateRule extends Rule {

    private class NodeBuilder
        extends DefaultHandler {


+++
        public void startElement(String namespaceURI, String localName,
                                 String qName, Attributes atts)
            throws SAXException {

            try {
                Node previousTop = top;
                if ((localName == null) || (localName.length() == 0)) { 
                    top = doc.createElement(qName);
                } else {
                    top = doc.createElementNS(namespaceURI, localName);
                }
                String prefix = qName.split(":")[0];
                if (prefix != null) {                   
                    top.setPrefix(prefix);
                }
                for (int i = 0; i < atts.getLength(); i++) {
                    Attr attr = null;
                    String attQname = atts.getQName(i);
                    prefix = null;
                    if (attQname.contains(":"))
                        prefix = attQname.split(":")[0];
                    if ((atts.getLocalName(i) == null) ||
                        (atts.getLocalName(i).length() == 0)) {
                        attr = doc.createAttribute(attQname);
                        attr.setNodeValue(atts.getValue(i));
                        if (prefix != null) {
                            attr.setPrefix(prefix);
                        }
                        ((Element)top).setAttributeNode(attr);
                    } else {
                        attr = doc.createAttributeNS(atts.getURI(i),
                                                     atts.getLocalName(i));
                        attr.setNodeValue(atts.getValue(i));
                        
                        if (prefix != null) {
                            attr.setPrefix(prefix);
                        }
                        ((Element)top).setAttributeNodeNS(attr);
                    }
                }
                previousTop.appendChild(top);
                depth++;
            } catch (DOMException e) {
                throw new SAXException(e.getMessage());
            }

        }

+++

Added support for [NodeCreateRule]

public class DigesterRuleParser extends RuleSetBase {

    public static javax.xml.parsers.DocumentBuilder documentBuilder;
    
    public static javax.xml.parsers.DocumentBuilder getDocumentBuilder() {
        return documentBuilder;
    }
    public static void setDocumentBuilder(
            javax.xml.parsers.DocumentBuilder documentBuilder) {
        DigesterRuleParser.documentBuilder = documentBuilder;
    }

    static {
        try {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        factory.setIgnoringComments(false);  
        factory.setCoalescing(false);
        factory.setIgnoringElementContentWhitespace(false);
        documentBuilder = factory.newDocumentBuilder();
        } catch (ParserConfigurationException e) {
            System.err.println(e);
        }
    }

+++

    public void addRuleInstances(Digester digester) {

        digester.addFactoryCreate("*/node-create-rule", new NodeCreateRuleFactory());
        digester.addRule("*/node-create-rule", new PatternRule("pattern"));
        digester.addSetNext("*/node-create-rule", "add", ruleClassName);


    /**
     * Factory for creating a NodeCreateRule
     */
    protected class NodeCreateRuleFactory extends AbstractObjectCreationFactory {

        private boolean isNotEmpty(String str) {
            return (str != null && str.length() > 0);
        }

        public Object createObject(Attributes attributes) {
            String strDocumentBuilderFactoryClass = attributes
                    .getValue("documentbuilderfactory");
            String strDocumentBuilderClass = attributes
                    .getValue("documentbuilder");
            String strIsNsAware = attributes.getValue("namespaceaware");
            String strIsIgnoreComments = attributes.getValue("ignorecomments");
            String strIsFragment = attributes.getValue("fragment");
            boolean ignoreExceptions = "true".equalsIgnoreCase(attributes
                    .getValue("ignore-exceptions"));
            boolean isNewFactory = false;
//          Use custom DocumentBuilderFactory
            if (isNotEmpty(strDocumentBuilderFactoryClass)) {
                try {
                    DocumentBuilderFactory customDocumentBuilderFactory = (DocumentBuilderFactory) Class
                            .forName(strDocumentBuilderFactoryClass)
                            .newInstance();
                    setDocumentBuilderFactory(customDocumentBuilderFactory);
                    isNewFactory = true;
                } catch (Exception e) {
                    System.err.println(e);
                }

            }
            // Use custom DocumentBuilder
            else if (isNotEmpty(strDocumentBuilderClass)) {
                try {
                    DocumentBuilder customDocumentBuilder = (DocumentBuilder) Class
                            .forName(strDocumentBuilderClass).newInstance();
                    setDocumentBuilder(customDocumentBuilder);
                } catch (Exception e) {
                    System.err.println(e);
                }
            }
            // customize DocumentBuilderFactory
            if (isNotEmpty(strIsNsAware) || isNotEmpty(strIsIgnoreComments)) {
                DocumentBuilderFactory factory = getDocumentBuilderFactory();
                if (isNotEmpty(strIsNsAware))
                    factory.setNamespaceAware(Boolean
                            .parseBoolean(strIsNsAware));
                if (isNotEmpty(strIsIgnoreComments))
                    factory.setNamespaceAware(Boolean
                            .parseBoolean(strIsIgnoreComments));

                setDocumentBuilderFactory(factory);
                isNewFactory = true;
            }
            // create new DocumentBuilder using new factory
            if (isNewFactory) {
                try {
                    setDocumentBuilder(getDocumentBuilderFactory()
                            .newDocumentBuilder());
                } catch (ParserConfigurationException e) {
                    System.err.println(e);
                }
            }
            // Create Rule for DOM Element or DocumentFragment
            if (strIsFragment == null || strIsFragment.length() == 0
                    || !Boolean.parseBoolean(strIsFragment)) {
                return new NodeCreateRule(getDocumentBuilder());
            } else {
                return new NodeCreateRule(Node.DOCUMENT_FRAGMENT_NODE,
                        getDocumentBuilder());
            }
        }
    }
+++
Test class

public class TestNodeCreateRule extends TestCase {
    
    public static String nodeToString(Node node) throws TransformerException {
        Source source = new DOMSource(node);
        StringWriter stringWriter = new StringWriter();
        Result result = new StreamResult(stringWriter);
        TransformerFactory factory = TransformerFactory.newInstance();
        Transformer transformer = factory.newTransformer();
        transformer.transform(source, result);
        return stringWriter.getBuffer().toString();
    }    
    
    public void testNodeCreateRule() throws Exception {
        
            URL rules = ClassLoader.getSystemResource
                ("org/apache/commons/digester/xmlrules/test-node-create-rules-w-rootobj.xml");
            
            String xml = "<?xml version='1.0' ?>"
                         + "<xcd:root xmlns:xcd='http://www.diku.dk'><foo attr='long'><xs:schema targetNamespace='http://openuri.org/easypo' xmlns:xs='http://www.w3.org/2001/XMLSchema' xmlns:po='http://openuri.org/easypo' elementFormDefault='qualified'>"
                         + "<xs:element name='purchase-order' xs:ID='A22'></xs:element></xs:schema></foo></xcd:root>";

            String xml2 = "<?xml version='1.0' ?>"
                + "<xcd:root xmlns:xcd='http://www.diku.dk'><xcd:dtd>this is a dtd</xcd:dtd></xcd:root>";
            
            
            TestObject testObject = new TestObject();
            
            Digester d = DigesterLoader.createDigester(rules);
            d.setNamespaceAware(true);
            d.setRuleNamespaceURI("www.diku.dk");
            testObject = (TestObject) d.parse(new StringReader(xml));                        
            System.out.println("testObject:" + testObject);                                                                
            System.out.println("Node:" + nodeToString(testObject.getNode()));
            System.out.println("BinaryNode:" + nodeToString(testObject.getBinary()));
            System.out.println("Property:" + testObject.getProperty());
        }
}

+++

<!DOCTYPE digester-rules PUBLIC "-//Jakarta Apache //DTD digester-rules XML V1.0//EN" "http://jakarta.apache.org/commons/digester/dtds/digester-rules.dtd">

<digester-rules>
	<pattern value="root/foo">
		<object-create-rule classname="org.apache.commons.digester.xmlrules.TestObject"/>
		<node-create-rule fragment="true"/>
		<set-next-rule methodname="setNode"/>
	</pattern>
	<pattern value="root/dtd">
		<object-create-rule classname="org.apache.commons.digester.xmlrules.TestObject"/>
		<node-create-rule fragment="true"/>
		<set-next-rule methodname="setBinary"/>
	</pattern>
</digester-rules>

+++

public class TestObject {


    private Node binary = null;        

    private Node node = null;



    public Node getNode() {
        return node;
    }
    public void setNode(Node node) {
        this.node = node;
    }
    public Node getBinary() {
        return binary;
    }
    public void setBinary(Node binary) {
        this.binary = binary;
        setProperty(getBinary().getTextContent());
    }
}

---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.779 / Virus Database: 526 - Release Date: 19-10-2004
 



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


Re: [digester] Patches to xmlrules : NodeCreateRule addon + namespace prefix support

Posted by Simon Kitching <sk...@apache.org>.
On Tue, 2005-03-01 at 14:15 +1300, Simon Kitching wrote:
> Hi Kristian,
> 
> On Mon, 2005-02-28 at 17:43 +0100, Kristian Mandrup wrote:
> > Added PREFIX namespace support for NodeCreateRule: Can now parse a XML Schema correctly ☺
> > Tested (see Junit TestCase below). Works fine!
> 
> [snip]
> 
> >                 if ((localName == null) || (localName.length() == 0)) { 
> >                     top = doc.createElement(qName);
> >                 } else {
> >                     top = doc.createElementNS(namespaceURI, localName);
> >                 }
> >                 String prefix = qName.split(":")[0];
> >                 if (prefix != null) {                   
> >                     top.setPrefix(prefix);
> >                 }
> 
> This seems to ensure that if an xml document containing namespaces is parsed with
> a non-namespace-aware xml parser (ie localName is not defined), then a DOM-1 
> (ie non-namespace-aware) element node is created ("createElement"), but then
> the namespace-aware "setPrefix" method is called on it.
> 
> It seems to me that if the createElement (ie non-namespace-aware) method
> is called to create the attribute, then calling setPrefix (ie a
> namespace-aware method) is a bad idea. A prefix is being set, but there
> is no namespace URI associated with the node...

Ok, I've had another look and think I understand at least part of your
patch.



First, you've added support for NodeCreateRule to the xmlrules module.
That's fine - though providing an explicit patch to do *just* this
(including a patch to the dtd and some unit tests) would be preferable
to including this as part of a larger and more complex patch. By "patch"
I mean a file generated by "svn diff ....", which generates a diff file
between your local version (with the changes in it) and the current
version in the apache subversion repository. I would also recommend
leaving out the rather complex code that allows the xmlrules to specify
configuration for a custom DocumentBuilderFactory; I'm not convinced
that this is necessary or useful, and leaving it out of your initial
patch will make it more likely that the initial patch will be merged in
a timely manner.



You're then trying to parse a document containing namespaces, using a
namespace-aware parser. But a pattern can never match an xml element
which has a namespace unless the "setNamespaceURI" method has been
called on the Rule object (either directly, or indirectly via
Rules.setNamespaceURI or Digester.setRuleNamespaceURI).

Note that the setRuleNamespaceURI method used in your testcase has no
effect:
            Digester d = DigesterLoader.createDigester(rules);
            d.setNamespaceAware(true);
            d.setRuleNamespaceURI("www.diku.dk");
That method only affects rules added after the call is made; but by the
time you call it here, all the rules have already been added. In fact,
there is currently no way to use the "setNamespaceURI" functionality
with xmlrules as far as I can see. If you would like to provide a patch
to add that to xmlrules somehow, that would be nice. I can't suggest
how, though. And you'd certainly need to read the RulesBase
implementation of the match(namespace, name) method to understand how
Digester's horribly hacky implementation of namespace support currently
works.


I still don't understand the point of the changes to the NodeCreateRule;
see my comments in the previous email.



Regards,

Simon


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


Re: [digester] Patches to xmlrules : NodeCreateRule addon + namespace prefix support

Posted by Simon Kitching <sk...@apache.org>.
Hi Kristian,

On Mon, 2005-02-28 at 17:43 +0100, Kristian Mandrup wrote:
> Added PREFIX namespace support for NodeCreateRule: Can now parse a XML Schema correctly ☺
> Tested (see Junit TestCase below). Works fine!

[snip]

>                 if ((localName == null) || (localName.length() == 0)) { 
>                     top = doc.createElement(qName);
>                 } else {
>                     top = doc.createElementNS(namespaceURI, localName);
>                 }
>                 String prefix = qName.split(":")[0];
>                 if (prefix != null) {                   
>                     top.setPrefix(prefix);
>                 }

This seems to ensure that if an xml document containing namespaces is parsed with
a non-namespace-aware xml parser (ie localName is not defined), then a DOM-1 
(ie non-namespace-aware) element node is created ("createElement"), but then
the namespace-aware "setPrefix" method is called on it.

It seems to me that if the createElement (ie non-namespace-aware) method
is called to create the attribute, then calling setPrefix (ie a
namespace-aware method) is a bad idea. A prefix is being set, but there
is no namespace URI associated with the node...


>                 for (int i = 0; i < atts.getLength(); i++) {
>                     Attr attr = null;
>                     String attQname = atts.getQName(i);
>                     prefix = null;
>                     if (attQname.contains(":"))
>                         prefix = attQname.split(":")[0];
>                     if ((atts.getLocalName(i) == null) ||
>                         (atts.getLocalName(i).length() == 0)) {
>                         attr = doc.createAttribute(attQname);
>                         attr.setNodeValue(atts.getValue(i));
>                         if (prefix != null) {
>                             attr.setPrefix(prefix);
>                         }

This seems to have the same issue as described above; it seems to me
that if the createAttribute (ie non-namespace-aware) method is called to
create the attribute, then calling setPrefix (ie a namespace-aware
method) is a bad idea. A prefix is being set, but there is no namespace
URI associated with the node...


If I have misunderstood your patch, please let me know.

What is the problem that you are encountering with the current code that
this patch is intended to remedy?

Regards,

Simon


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