Project

Profile

Help

Upgrading from 9.5.x

Added by Divan Mostert over 3 years ago

Hi,

I need to upgrade our Saxon implementation to 9.9 at least, but will take it in small incremental stages from 9.5 onwards.

The first two classes I see issues with are:

net.sf.saxon.tree.iter.EmptyAxisIterator - this have gone completely in 9.6 - what should I use instead? net.sf.saxon.tree.iter.AxisIteratorImpl - we have a few classes extending it, but in 9.6, "position" and "current" class variables are gone.

Any help would be greatly appreciated.

Cheers


Replies (22)

Please register to reply

RE: Upgrading from 9.5.x - Added by Michael Kay over 3 years ago

Yes, you're definitely poking around in internals that are subject to change between releases...

I actually think you may be better off moving to 10.3 in one jump. Otherwise you'll be making changes that have to be changed again later. And in any case, I don't particularly want to have to work out how things worked in 9.6.

Saxon 10 has a static method EmptyIterator.ofNodes() that returns an empty AxisIterator. Not sure when it was introduced. But we did a lot of work over these releases trying to get the iterators to work nicely with Java generics - not always very successfully. See my blog post at

https://dev.saxonica.com/blog/mike/2020/01/java-generics-revisited.html

One change we made to the SequenceIterator interface (again, don't recall exactly when) was that it's no longer required that every SequenceIterator supports position() and current(). Instead, if you're going to need position() and current() (i.e. if you're going to use the iterator to maintain the XPath focus) then you wrap it in a FocusTrackingIterator, which keeps track of position() and current() on your behalf.

RE: Upgrading from 9.5.x - Added by Divan Mostert over 3 years ago

Thanks Mike, I'll try that out.

RE: Upgrading from 9.5.x - Added by Divan Mostert over 3 years ago

Hi Mike,

When you say "wrap it in a FocusTrackingIterator", do you mean extending that instead of AxisIteratorImpl?

Here is a sample of what we currently have for iterating over attributes:

/** * Iterator over all the attributes of this complex element */

private static class AttributeAxisIterator extends AxisIteratorImpl {

    ComplexElementNode parent;
    NodeTest nodeTest;
    int index = 0;
    Iterator iterator;

    /**
     * Construct the iterator
     * @param parent the element whose attributes are required
     * @param nodeTest a test that the selected attributes must satisfy
     */

    public AttributeAxisIterator(ComplexElementNode parent, NodeTest nodeTest) {
        super();
        this.parent = parent;
        this.nodeTest = nodeTest;
        iterator = Utils.attrIterator(parent.value);
    }

    /**
     * Get another iterator over the same sequence of items, positioned at the
     * start of the sequence. It must be possible to call this method at any time, whether
     * none, some, or all of the items in the original iterator have been read. The method
     * is non-destructive: it does not change the state of the original iterator.
     *
     * @return a new iterator over the same sequence
     */

    public AxisIterator getAnother() {
        return new AttributeAxisIterator(parent, nodeTest);
    }

    /**
     * Get the next item in the sequence. <BR>
     *
     * @return the next Item. If there are no more nodes, return null.
     */

    public NodeInfo next() {
        while (true) {
            if (iterator.hasNext()) {
                index++;
                IOContext ioc = (IOContext)iterator.next();
                current = new AttributeNode(ioc.getInstance(), ioc.getComponent(), parent, index);
                if (nodeTest.matches(current)) {
                    position++;
                    return current;
                } else {
                    continue;
                }
            } else {
                current = null;
                position = -1;
                return null;
            }
        }
    }
}

RE: Upgrading from 9.5.x - Added by Michael Kay over 3 years ago

As far as I can see:

  • You can drop the getAnother() method, it isn't needed any more.
  • You don't need to maintain position any more, because you're not using it yourself: Saxon will keep track of position if it needs to, by wrapping your iterator in a FocusTrackingIterator
  • current can become a local variable in the next() method,.
  • There's no AxisIteratorImpl any more because it's not needed; rather than extending AxisIteratorImpl, just implement AxisIterator.

RE: Upgrading from 9.5.x - Added by Divan Mostert over 3 years ago

Ok, great, that is somewhat what I already started doing.

Three further questions:

  1. Inside the next() method implementation I see code like the following(can also be seen in previous code snippet)
if (nodeTest.matches(current))

How do I deal with this, since the matches() method's signature has changed?

  1. AxisIteratorOverSequence is gone too. What should I use as replacement?

  2. The method getNodeKindMask() no longer exist, so how should I check the following:

if ((nodeTest.getNodeKindMask() & (1<<Type.PROCESSING_INSTRUCTION)) != 0)

RE: Upgrading from 9.5.x - Added by Michael Kay over 3 years ago

  1. The Javadoc for matches(item, typeHierarchy) says the second argument is used only when matching function items, so you can probably get away with supplying null. But it's probably safer, given that current is a NodeInfo, to supply current.getConfiguration().getTypeHierarchy().

  2. It may be simplest just to reimplement it, it shouldn't be much more than

public class AxisIteratorOverSequence<T extends NodeInfo> implements AxisIterator<T> {

    private SequenceIterator<T> base;

    public AxisIteratorOverSequence(SequenceIterator<T> base) {
        this.base = base;
    }

    public T next() {
        try {
            return base.next();
        } catch (XPathException err) {
            throw new AssertionError(err);
        }
    }

    public void close() {
        return base.close();
    }
}

But there may be better approaches depending on why exactly you're using it; if Saxon no longer needed it, it's quite possible that you don't need it either.

  1. nodeTest.getUType().overlaps(UType.PI)

RE: Upgrading from 9.5.x - Added by Divan Mostert over 3 years ago

Ok, next hurdle :-)

We have a class implementing the DocumentInfo interface, which has now been made into an abstract class:

So I have changed :

public class DocumentNode implements DocumentInfo, Node, DataNode {
..
}

to

public class DocumentNode extends DocumentInfo implements Node, DataNode  {
...
}

... but when I execute my tests the XQuery tests fails with:

"DocumentInfo must only be used for document nodes at net.sf.saxon.om.DocumentInfo.(DocumentInfo.java:30) at biz.c24.io.api.data.saxon.DocumentNode.(DocumentNode.java:71) at biz.c24.io.api.data.XQueryTest.executeXQuery(XQueryTest.java:84) at biz.c24.io.api.data.XQueryTest.testXQuery(XQueryTest.java:44) .... "

RE: Upgrading from 9.5.x - Added by Michael Kay over 3 years ago

I hadn't actually realised this was C24 - now I understand a bit better what you're doing!

Most of the functionality that was originally in DocumentInfo moved into the new TreeInfo object; the TreeInfo contains information about a node tree whether or not it is rooted at a document node. (I think the trigger for this was implementing XSLT 3.0 accumulators). DocumentInfo was retained up to 9.9 with the comment

/**
 * The class DocumentInfo is retained in Saxon 9.7 to preserve a level of backwards compatibility
 * for applications that use the method {@link net.sf.saxon.Configuration#buildDocument(Source)}
 * method to construct a tree. In earlier releases it was an interface implemented by all document
 * nodes; from 9.7 it is a wrapper object around the NodeInfo object that represents the actual
 * document node
 */

but in 10.0 it has disappeared entirely.

For many tree models, it makes sense to have a Document object that implements NodeInfo, has nodeKind()==Type.DOCUMENT, and also implements TreeInfo. Alternatively, use two separate objects for the document node and the TreeInfo, in which case you can use a GenericTreeInfo as your TreeInfo object. For an example of the first approach, see net.sf.saxon.tree.linked.DocumentImpl; for the second, see net.sf.saxon.option.axiom.AxiomDocument.

RE: Upgrading from 9.5.x - Added by Divan Mostert over 3 years ago

Ah, sorry, I should have mentioned that earlier. :-)

One reason for the upgrade is one of our clients use Camel 3.5.0, which supports Saxon 9.9.

With this in mind, I decided to not go all the way to Saxon 10 as it may not work with Camel 3.5.0.

I'd rather upgrade from 9.9 later next year when we move to a new minor release again.

Does this sound like a reasonable approach to you and would your above comment still hold true for 9.9?

RE: Upgrading from 9.5.x - Added by Michael Kay over 3 years ago

Yes, that sounds perfectly reasonable. The move from 9.9 to 10 should be much simpler especially if you've taken things like the DocumentInfo to TreeInfo issue into account already. We did make some changes from 9.9 to 10 to simplify the use of Java generics. The other significant changes from 9.9 to 10.0 are in the handling of namespaces, both in the NodeInfo interface and on the Receiver pipeline. You might like to peek ahead to 10.0 for a preview of how that will affect you.

RE: Upgrading from 9.5.x - Added by Divan Mostert over 3 years ago

Ok, I've now migrated the classes over as I think it should be, but some of my tests are falling over - most seems to be a stackoverflow, which suggest it's not iterating through the tree as it should.

If I email(or attach here) you the classes(it's only one package, with your name all over it :-) ), would you be able to eye it over just to see if I made any obvious mistakes?

RE: Upgrading from 9.5.x - Added by Michael Kay over 3 years ago

OK, I'll take a look. You can use direct email, or mark the attachments as private.

RE: Upgrading from 9.5.x - Added by Divan Mostert over 3 years ago

Ok, I'll email them.

RE: Upgrading from 9.5.x - Added by Divan Mostert over 3 years ago

Ok, I managed to find the error in my ways and it seems the unit tests all passed, so just need to run some further integration tests to make sure all is well.

RE: Upgrading from 9.5.x - Added by Divan Mostert over 3 years ago

Hi Mike,

Just to let you know that all the integration tests also passed.

Thanks a lot for your assistance.

Regards

Divan

RE: Upgrading from 9.5.x - Added by Michael Kay over 3 years ago

Great, thanks for letting me know, and I'm sorry that I left you to do all the work in the end!

RE: Upgrading from 9.5.x - Added by Divan Mostert over 3 years ago

No worries, I may bug you again when we move to v10 :-)

RE: Upgrading from 9.5.x - Added by Divan Mostert almost 3 years ago

Hi Mike,

The XPath expression now returns a subclass of net.sf.saxon.value.AtomicValue, whereas previously it returned primitive types.

Do you have some utility class I can use to convert it or do I have to write something myself?

Regards

Divan

RE: Upgrading from 9.5.x - Added by Michael Kay almost 3 years ago

When you say "The XPath expression now returns..." what actual Java method are you using to evaluate the expression?

It's always possible to extract primitive values from instances of net.sf.saxon.value.AtomicValue, though the details depend on what class of value you've got. All atomic values support getStringValue(), all numeric values support getDoubleValue(), etc.

RE: Upgrading from 9.5.x - Added by Divan Mostert almost 3 years ago

This is the line executing the statement:

List tempList = xpe.evaluate(xpe.createDynamicContext(new DocumentNode(config, (cdo == o)? o : cdo, ignoreDocumentNode, namespaceAware)));

Where "xpe" is a net.sf.saxon.sxpath.XPathExpression.

I saw the "getStringValue()", but wasn't sure if I should be using those.

RE: Upgrading from 9.5.x - Added by Michael Kay almost 3 years ago

Yes, in 9.5 that call invoked an overload of evaluate() that converted items to the "best fit" native Java type using

List<Object> result = (List)PJConverter.ToCollection.INSTANCE.convert(
                extent, List.class, dynamicContext.getXPathContextObject());

You can still use the PJConverter logic to do the conversion if you want, but the results can be a little unpredictable (what exactly do you want a DateTimeValue to be converted into?) and I would suggest doing the conversion yourself so you have precise control and better type safety.

RE: Upgrading from 9.5.x - Added by Divan Mostert almost 3 years ago

Yes, for DateTimeValue, it could be java.util.Date or java.time.ZonedDateTime.

Ok, I will just handle the conversion myself.

Thanks a lot

    (1-22/22)

    Please register to reply