Hi,

 

First of all, thanks for a great framework/library. Very, very useful. 

 

Last week I tried to replace Saxon-HE 9.2.1.5 in the BetterFORM XForms engine with Saxon 9.3.0.11 with good success. Now, immediately after that, the question came to replace that with the latest 9.4.0.4 version. After making sure everything compiled again, I ran the BetterFORM test and got a lot (25%) failing tests. Luckily, most of the errors were identical.

 

The expanded QName syntax "uri":local is not allowed in this version of XPath/XQuery

 

I found that this message is coming from the ExpressionParser

 

    public void nextToken() throws XPathException {

        try {

            t.next();

            if ((t.currentToken == Token.NAME || t.currentToken == Token.FUNCTION) &&

                    t.currentTokenValue.startsWith("{")) {

                if (allowXPath30Syntax) {

                    t.currentTokenValue = normalizeEQName(t.currentTokenValue);

                } else {

                    throw new XPathException(

                            "The expanded QName syntax \"uri\":local is not allowed in this version of XPath/XQuery");

                }

            }

        } catch (XPathException err) {

            grumble(err.getMessage());

        }

    }

 

An example xpath expression that fails is below where all elements are in the default and EMPTY namespace (xmlns=”” as is common in xforms instances)

 

string(../amount * instance('convTable')/rate[@currency=current()/../currency])

 

When debugging, I found that the “currentTokenValue” of e.g. the amount element is ”{}amount” (without the quotes). When the expression is build, nothing fails but when it is actually evaluated, it fails with the exception above. When I change the xmlns=”” to e.g. xmlns=”bla” I do not get this error anymore and things start working again (to some extend), but that would result in other major changes in all xpath statements).

 

I created a custom build of Saxon-HE with the “if (allowXPath30Syntax)” disabled and then everything starts working again in the default empty namespace. I did not want to set this “allowXPath30Syntax“ to true since it might result in lots of other side effects and/or behavioral issues (not even sure it is allowed in the HE version).

 

One thing I am not sure about if is this only fails if there are functions used that are in the xforms namespace (which is the defaultfunctionnamespace, hence no prefixes). I also noticed is that in the NamePool apidocs a comment was made: “Modified in 9.4 to remove namespace codes.”  So I am not sure if this is a problem in the creating of the name, in the parsing of the expression or that for using 9.4 something has to be changed in the way the api is used.

 

Some more information about the latter:   

-       We do not use the s9api (yet, but I think we cant)

-       We change the default function namespace to the xforms ns

-       We set the ‘backwards compatibility flag’ to true (setting it to false makes no difference)

-       The configuration is ‘basic’ (XPath2) besides custom function libraries being added

-       We use Documents, not Xdm*

 

The two major functions we have that do all this are:

 

    public XPathExpression getXPathExpression(String xpathString, Map prefixMapping, Configuration configuration) throws XPathException {

 

        XPathEvaluator xpe = new XPathEvaluator(configuration);

 

        IndependentContext independentContext = (IndependentContext) xpe.getStaticContext();

        independentContext.setDefaultFunctionNamespace(NamespaceConstants.XFORMS_NS);

        independentContext.setBackwardsCompatibilityMode(true);

 

        for (Iterator it = prefixMapping.entrySet().iterator(); it.hasNext();) {

            Map.Entry entry = (Map.Entry) it.next();

            independentContext.declareNamespace((String) entry.getKey(), (String) entry.getValue());

        }

        independentContext.setFunctionLibrary(fgXFormsFunctionLibrary);

 

 

        XPathExpression exp = xpe.createExpression(xpathString);

        return exp;

    }

 

Which in turn is called from:

 

    public List evaluate(List nodeset, int position, String xpathString, Map prefixMapping, XPathFunctionContext functionContext)

        throws XFormsException {

        if (nodeset != null && nodeset.size() < position) {

            return Collections.EMPTY_LIST;

        }

 

 

        try {

            final XPathExpression exp = getXPathExpression(xpathString, prefixMapping, functionContext != null?functionContext.getXFormsElement().getContainerObject().getConfiguration():XPathCache.kCONFIG);

            final XPathDynamicContext context = exp.createDynamicContext((Item) nodeset.get(position - 1));

            ListSequenceIterator nodesetIt = new ListSequenceIterator(nodeset, position);

            nodesetIt.next();

                    ((XPathContextMajor)context.getXPathContextObject()).setCurrentIterator(nodesetIt);

            context.getXPathContextObject().getController().setUserData(XFormsProcessorImpl.class.toString(), XPathFunctionContext.class.toString(), functionContext);

 

            SequenceIterator it = exp.iterate(context);

 

            int nrOfEntries;

            if ((it.getProperties() & SequenceIterator.LAST_POSITION_FINDER) != 0) {

                nrOfEntries = ((LastPositionFinder) it).getLength();

            } else {

                nrOfEntries = -1;

            }

 

            if (nrOfEntries == 1) {

                return Collections.singletonList(it.next());

            }

 

            final List result = new ArrayList(nrOfEntries > 0 ? nrOfEntries : 20);

            for (Object item = it.next(); item != null; item = it.next()) {

                result.add(item);

            }

 

            return result;

 

        } catch (XPathException e) {

             if (e.getCause() instanceof XFormsException) {

                   throw (XFormsException)e.getCause();

             }

            throw new XFormsException(e.getMessage(), e);

        }

 

    }

 

Thanks in advance,

 

Ronald van Kuijk