Hi all,

I'm trying to learn how to use JXPath with DOM in order to speed up some code that uses a lot of xpath. I've seen blog posts suggesting it's about twice as fast as JAXP's XPath processor...

The problem I'm running into is when I construct a JXPathContext around a node down in the DOM tree, then try to select a node elsewhere in the tree using the ancestor:: axis. I'm attaching a sample XML file and unit test that shows what I'm trying to do.

I've run this through a debugger, and it appears that the DOMNodePointer.getImmediateParent() doesn't even try to look at the Node.getParentNode()...if it doesn't have a pre-existing parent (from its ctor) then it just dumbly returns the null parent.

I haven't done enough research yet to know how to get DOMNodePointer to populate its parent (using the public API, not the nuts-and-bolts impl details), but in the attached example you can see I try two approaches:

1. the naive approach, which is also the last one in the code. IMO, this one should work!

2. a brute-force alternative, where JXPathContext instances for each intermediate node are created to inherit in the right order, all the way back to the document itself. From my partial reading of the code, this should work even if the naive approach doesn't.

Neither of these works, though. Can someone shed some light on it, or let me know if I've found a bug (seems like a common use case)...

Thanks,

-john

--
John Casey
GitHub - http://github.com/jdcasey
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"; xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd";>
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.test</groupId>
  <artifactId>test-project</artifactId>
  <version>1.0</version>

  <dependencies>
    <dependency>
      <groupId>org.group</groupId>
      <artifactId>artifact-id</artifactId>
      <version>2.6</version>
    </dependency>
  </dependencies>
  
</project>
package org.commonjava.maven.galley.maven.model.view;

import java.io.InputStream;
import java.io.StringWriter;
import java.util.List;
import java.util.Stack;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.jxpath.JXPathContext;
import org.junit.Before;
import org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

public class JXPathContextAncestryTest
{
    @Test
    public void basicJXPathTest()
        throws Exception
    {
        final InputStream is = Thread.currentThread()
                                     .getContextClassLoader()
                                     .getResourceAsStream( 
"jxpath/simple.pom.xml" );

        final Document document = DocumentBuilderFactory.newInstance()
                                                        .newDocumentBuilder()
                                                        .parse( is );

        final JXPathContext ctx = JXPathContext.newContext( document );

        document.getDocumentElement()
                .removeAttribute( "xmlns" );

        final String projectGroupIdPath = "ancestor::project/groupId";

        // NOT what's failing...just populating the node set to traverse in 
order to feed the ancestor:: axis test.
        final List<?> nodes = ctx.selectNodes( 
"/project/dependencies/dependency" );
        for ( final Object object : nodes )
        {
            final Node node = (Node) object;
            dump( node );

            final Stack<Node> revPath = new Stack<Node>();

            Node parent = node;
            while ( parent != null )
            {
                revPath.push( parent );
                parent = parent.getParentNode();
            }

            JXPathContext nodeCtx = null;
            while ( !revPath.isEmpty() )
            {
                final Node part = revPath.pop();
                if ( nodeCtx == null )
                {
                    nodeCtx = JXPathContext.newContext( part );
                }
                else
                {
                    nodeCtx = JXPathContext.newContext( nodeCtx, part );
                }
            }

            System.out.println( "Path derived from context: '" + 
nodeCtx.getNamespaceContextPointer()
                                                                        
.asPath() + "'" );

            // brute-force approach...try to force population of the parent 
pointers by painstakingly constructing contexts for all intermediate nodes.
            System.out.println( "Selecting groupId for declaring project using 
path-derived context..." );
            System.out.println( nodeCtx.getValue( projectGroupIdPath ) );

            // Naive approach...this has all the context info it needs to get 
parent contexts up to and including the document!
            System.out.println( "Selecting groupId for declaring project using 
non-derived context..." );
            System.out.println( JXPathContext.newContext( node )
                                             .getValue( projectGroupIdPath ) );
        }
    }

    private DocumentBuilder docBuilder;

    private Transformer transformer;

    @Before
    public void setup()
        throws Exception
    {
        docBuilder = DocumentBuilderFactory.newInstance()
                                           .newDocumentBuilder();

        transformer = TransformerFactory.newInstance()
                                        .newTransformer();

        transformer.setOutputProperty( OutputKeys.OMIT_XML_DECLARATION, "yes" );
        transformer.setOutputProperty( OutputKeys.METHOD, "xml" );
        transformer.setOutputProperty( OutputKeys.INDENT, "yes" );
        transformer.setOutputProperty( OutputKeys.ENCODING, "UTF-8" );
        transformer.setOutputProperty( 
"{http://xml.apache.org/xslt}indent-amount";, "2" );
    }

    protected void dump( final Node node )
        throws Exception
    {
        if ( node == null )
        {
            System.out.println( "Cannot dump null node." );
            return;
        }

        final StringWriter sw = new StringWriter();

        transformer.transform( new DOMSource( docBuilder.newDocument()
                                                        .importNode( node, true 
) ), new StreamResult( sw ) );

        System.out.println( sw );
    }
}

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to