All Downloads are FREE. Search and download functionalities are using the official Maven repository.

lux.compiler.SaxonTranslator Maven / Gradle / Ivy

There is a newer version: 1.1.0
Show newest version
package lux.compiler;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import lux.exception.LuxException;
import lux.xml.QName;
import lux.xml.ValueType;
import lux.xpath.AbstractExpression;
import lux.xpath.BinaryOperation;
import lux.xpath.BinaryOperation.Operator;
import lux.xpath.Dot;
import lux.xpath.FunCall;
import lux.xpath.LiteralExpression;
import lux.xpath.Namespace;
import lux.xpath.PathExpression;
import lux.xpath.PathStep;
import lux.xpath.Predicate;
import lux.xpath.Root;
import lux.xpath.Sequence;
import lux.xpath.Subsequence;
import lux.xpath.UnaryMinus;
import lux.xquery.AttributeConstructor;
import lux.xquery.CommentConstructor;
import lux.xquery.ComputedElementConstructor;
import lux.xquery.Conditional;
import lux.xquery.DocumentConstructor;
import lux.xquery.ElementConstructor;
import lux.xquery.FLWOR;
import lux.xquery.FLWORClause;
import lux.xquery.ForClause;
import lux.xquery.FunctionDefinition;
import lux.xquery.InstanceOf;
import lux.xquery.LetClause;
import lux.xquery.ModuleImport;
import lux.xquery.OrderByClause;
import lux.xquery.ProcessingInstructionConstructor;
import lux.xquery.Satisfies;
import lux.xquery.Satisfies.Quantifier;
import lux.xquery.SortKey;
import lux.xquery.TextConstructor;
import lux.xquery.Variable;
import lux.xquery.VariableDefinition;
import lux.xquery.WhereClause;
import lux.xquery.XQuery;
import net.sf.saxon.Configuration;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.flwor.Clause;
import net.sf.saxon.expr.flwor.FLWORExpression;
import net.sf.saxon.expr.flwor.LocalVariableBinding;
import net.sf.saxon.expr.instruct.Block;
import net.sf.saxon.expr.instruct.Choose;
import net.sf.saxon.expr.instruct.Comment;
import net.sf.saxon.expr.instruct.ComputedAttribute;
import net.sf.saxon.expr.instruct.ComputedElement;
import net.sf.saxon.expr.instruct.CopyOf;
import net.sf.saxon.expr.instruct.DocumentInstr;
import net.sf.saxon.expr.instruct.FixedAttribute;
import net.sf.saxon.expr.instruct.FixedElement;
import net.sf.saxon.expr.instruct.GlobalVariable;
import net.sf.saxon.expr.instruct.ProcessingInstruction;
import net.sf.saxon.expr.instruct.UserFunctionParameter;
import net.sf.saxon.expr.instruct.ValueOf;
import net.sf.saxon.expr.parser.Token;
import net.sf.saxon.expr.sort.DocumentSorter;
import net.sf.saxon.expr.sort.SortKeyDefinition;
import net.sf.saxon.functions.StandardFunction;
import net.sf.saxon.functions.StandardFunction.Entry;
import net.sf.saxon.lib.FeatureKeys;
import net.sf.saxon.lib.NamespaceConstant;
import net.sf.saxon.om.*;
import net.sf.saxon.pattern.CombinedNodeTest;
import net.sf.saxon.pattern.DocumentNodeTest;
import net.sf.saxon.pattern.LocalNameTest;
import net.sf.saxon.pattern.NameTest;
import net.sf.saxon.pattern.NamespaceTest;
import net.sf.saxon.pattern.NodeTest;
import net.sf.saxon.query.QueryModule;
import net.sf.saxon.query.XQueryExpression;
import net.sf.saxon.query.XQueryFunction;
import net.sf.saxon.s9api.XQueryExecutable;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.AtomicType;
import net.sf.saxon.type.BuiltInAtomicType;
import net.sf.saxon.type.ErrorType;
import net.sf.saxon.type.ItemType;
import net.sf.saxon.type.Type;
import net.sf.saxon.value.AtomicValue;
import net.sf.saxon.value.BigIntegerValue;
import net.sf.saxon.value.CalendarValue;
import net.sf.saxon.value.Cardinality;
import net.sf.saxon.value.DurationValue;
import net.sf.saxon.value.QNameValue;
import net.sf.saxon.value.SequenceType;

import net.sf.saxon.z.IntSet;
import net.sf.saxon.z.IntUniversalSet;
import org.apache.commons.lang.StringUtils;

/*
TODO: AdjacentTextNodeMerger, EmptyTextNodeRemover, DocumentSorter, CastToList, CastableToList, SingletonAtomizer,
 */

/**
 * Translates Saxon XPath 2.0 Expressions into Lux AbstractExpressions.
 */
public class SaxonTranslator {
    
    public static final String CODEPOINT_COLLATION = "http://www.w3.org/2005/xpath-functions/collation/codepoint";

    private Configuration config;
    private HashMap namespaceDeclarations;
    private static final HashMap dispatcher = new HashMap();
    static {
        for (ExprClass eclass : ExprClass.values()) {
            dispatcher.put (eclass.toString(), eclass);            
        } 
    }
    private QueryModule queryModule;
    
    public SaxonTranslator (Configuration config) {
        this.config = config;
        namespaceDeclarations = new HashMap();
    }
    
    /** Converts from a Saxon to a lux xquery representation.
     * @param xquery a Saxon representation of an XQuery module
     * @return a lux representation of an equivalent XQuery module
     */

    public XQuery queryFor(XQueryExecutable xquery) {
        XQueryExpression saxonQuery = xquery.getUnderlyingCompiledQuery();
        queryModule = saxonQuery.getStaticContext();
        Iterator moduleIter = queryModule.getExecutable().getQueryLibraryModules();
        ArrayList importedModules = new ArrayList();
        while (moduleIter.hasNext()) {
            QueryModule importedModule = (QueryModule) moduleIter.next();
            String moduleNamespace = importedModule.getModuleNamespace();
            String prefix = getPrefixForNamespace(moduleNamespace);
            if (!StringUtils.isBlank(prefix)) {
                // if prefix is blank, that indicates the imported module was not used
                importedModules.add(new ModuleImport(prefix, moduleNamespace, importedModule.getSystemId()));
            }
        }
        //StructuredQName[] extVars = saxonQuery.getExternalVariableNames();
        // Namespace declarations are accumulated while walking the expression trees:
        initializeNamespaces(saxonQuery);
        FunctionDefinition[] functionDefinitions = getFunctionDefinitions();
        AbstractExpression body = exprFor (saxonQuery.getExpression());
        String defaultCollation = queryModule.getDefaultCollationName();
        if (defaultCollation.equals (CODEPOINT_COLLATION)) {
            defaultCollation = null;
        }
        VariableDefinition[] variableDefinitions = getVariableDefinitions(queryModule);
        Boolean isPreserveNamespaces = (Boolean) config.getConfigurationProperty(FeatureKeys.XQUERY_PRESERVE_NAMESPACES);
        Boolean isInheritNamespaces = (Boolean) config.getConfigurationProperty(FeatureKeys.XQUERY_INHERIT_NAMESPACES);
        if (queryModule.isPreserveNamespaces() == isPreserveNamespaces &&
                queryModule.isInheritNamespaces() == isInheritNamespaces) {
            // both settings are the same as the current config, so no need to insert an explicit declaration in the query
            isPreserveNamespaces = isInheritNamespaces = null;
        } else {
            isPreserveNamespaces = queryModule.isPreserveNamespaces();
            isInheritNamespaces = queryModule.isInheritNamespaces();
        }
        return new XQuery(
                queryModule.getDefaultElementNamespace(),
                queryModule.getDefaultFunctionNamespace(),
                defaultCollation,
                importedModules.toArray(new ModuleImport[importedModules.size()]),
                getNamespaceDeclarations(), 
                variableDefinitions, 
                functionDefinitions, 
                body,
                queryModule.getBaseURI(),
                isPreserveNamespaces,
                isInheritNamespaces,
                queryModule.isEmptyLeast());
    }

    private void initializeNamespaces(XQueryExpression saxonQuery) {
        namespaceDeclarations.clear();
        NamespaceResolver ns = saxonQuery.getStaticContext().getNamespaceResolver();
        Iterator prefixes = ns.iteratePrefixes();
        while (prefixes.hasNext()) {
            String prefix = prefixes.next();
            String nsURI = ns.getURIForPrefix(prefix, false);
            if (!(NamespaceConstant.isReservedInQuery(nsURI) ||
                    (prefix.equals("saxon") && nsURI.equals(NamespaceConstant.SAXON)) ||
                    (prefix.equals("local") && nsURI.equals(NamespaceConstant.LOCAL)) ||
                    (prefix.equals("err") && nsURI.equals(NamespaceConstant.ERR)) ||
                    (prefix.equals("lux") && nsURI.equals(FunCall.LUX_NAMESPACE))))
            {
                namespaceDeclarations.put(prefix, nsURI);
            }
        }
    }

    private Namespace[] getNamespaceDeclarations() {
        // String defElementNS = queryModule.getDefaultElementNamespace();
        // String defFunctionNS = queryModule.getDefaultFunctionNamespace();
        // We'd like to get our hands on queryModule.explicitPrologNamespaces :(
        // Without it we have to resort to some scanning ugliness in search of 
        // all namespaces.
        Namespace[] decls = new Namespace[namespaceDeclarations.size()];
        int i = 0;
        for (Map.Entry entry : namespaceDeclarations.entrySet()) {
            decls[i++] = new Namespace (entry.getKey(), entry.getValue());
        }
        return decls;
    }
    
    private VariableDefinition[] getVariableDefinitions(QueryModule module) {
        ArrayList defs = new ArrayList();
        Iterator decls = module.getModuleVariables();
        while (decls.hasNext()) {
            GlobalVariable decl = decls.next();
            GlobalVariable global = decl.getUltimateOriginalVariable();
            String typeDesc = getTypeDescription (decl.getRequiredType());
            Variable var = global != null ? (Variable) exprFor(global) : new Variable(qnameFor(decl.getVariableQName()));
            int order = decl.getLineNumber() * 1000000 + decl.getColumnNumber();
            VariableDefinition def = new VariableDefinition(var, exprFor(decl.getSelectExpression()), typeDesc, order);
            defs.add(def);
        }
        VariableDefinition [] orderedDefs = defs.toArray(new VariableDefinition[0]);
        Arrays.sort(orderedDefs);
        return orderedDefs;
    }
    
    private void addNamespaceDeclaration (QName qname) {
        String prefix = qname.getPrefix();
        String namespaceURI = qname.getNamespaceURI();
        if (! namespaceDeclarations.containsKey(prefix)) {
            // We only want the outermost binding of each prefix; these
            // are used to generate the namespace declarations in the query preamble
            namespaceDeclarations.put(prefix, namespaceURI);
        }
    }

    private FunctionDefinition[] getFunctionDefinitions() {
        ArrayList functionDefinitions = new ArrayList();
        Iterator functions = queryModule.getLocalFunctionLibrary().getFunctionDefinitions();
        while (functions.hasNext()) {
            XQueryFunction function = functions.next();
            UserFunctionParameter[] params = function.getParameterDefinitions();
            Variable[] args = new Variable[params.length];
            for (int i = 0; i < params.length; i++) {
                QName argname = qnameFor (params[i].getVariableQName());
                addNamespaceDeclaration(argname);
                args[i] = new Variable (argname, getTypeDescription(params[i].getRequiredType()));
            }
            QName fname = qnameFor(function.getFunctionName());
            addNamespaceDeclaration(fname);
            SequenceType resultType = function.getResultType();
            ItemType returnType = resultType.getPrimaryType();
            QName returnTypeName = null;
            if (returnType instanceof NameTest) {
            	returnTypeName = qnameForNameCode (((NameTest) returnType).getFingerprint());
            }
            FunctionDefinition fdef = new FunctionDefinition(fname, 
                    valueTypeForItemType(resultType.getPrimaryType()), 
                    cardinalityOf(resultType), returnTypeName,  
                    args, exprFor (function.getBody()));  
            functionDefinitions.add (fdef);
        }
        return functionDefinitions.toArray(new FunctionDefinition[0]);
    }
    
    private String getTypeDescription (SequenceType sequenceType) {
     // typeDesc = decl.getRequiredType().getPrimaryType().toString();
        if (sequenceType == null) {
            return null;
        }
        String typeDesc;
        ItemType itemType = sequenceType.getPrimaryType();
        if (itemType.isAtomicType()) {
            typeDesc = sequenceType.toString();
        } else {
            // Note: we drop specializations of the element() (and document()) types since
        	// compile-time errors will already have been reported, and run-time errors are handled
        	// using the "treat as" operator.
            typeDesc = itemType.getPrimitiveItemType().toString();
            int cardinality = sequenceType.getCardinality();
            if (cardinality == StaticProperty.ALLOWS_ONE_OR_MORE) {
                typeDesc = typeDesc + '+';
            } else if (cardinality == StaticProperty.ALLOWS_ZERO_OR_MORE) {
                typeDesc = typeDesc + '*';
            } else if (cardinality == StaticProperty.ALLOWS_ZERO_OR_ONE) {
                typeDesc = typeDesc + '?';
            }
        }
        return typeDesc;
    }
    
    public AbstractExpression exprFor (AtomicSequenceConverter atomizer) {
        return exprFor (atomizer.getBaseExpression());
    }

    /* return a QName suitable for use as a constructor of the given type */
    private QName qnameFor (AtomicType type) {
        if (type.isBuiltInType()) {
            return qnameFor (((BuiltInAtomicType)type).getQualifiedName());
        }
        return qnameForNameCode(type.getNameCode());
    }

    public AbstractExpression exprFor (Atomizer atomizer) {
        Expression base = atomizer.getBaseExpression();
        return exprFor (base);
        //return new FunCall (FunCall.FN_DATA, ValueType.ATOMIC, exprFor (base));
    }
    
    public AbstractExpression exprFor (AxisExpression expr) {
        PathStep.Axis axis;
        switch (expr.getAxis()) {
        case AxisInfo.ANCESTOR: axis = PathStep.Axis.Ancestor; break;
        case AxisInfo.PARENT: axis = PathStep.Axis.Parent; break;
        case AxisInfo.DESCENDANT: axis = PathStep.Axis.Descendant; break;
        case AxisInfo.PRECEDING: axis = PathStep.Axis.Preceding; break;
        case AxisInfo.FOLLOWING: axis = PathStep.Axis.Following; break;
        case AxisInfo.SELF: axis = PathStep.Axis.Self; break;
        case AxisInfo.PRECEDING_SIBLING: axis = PathStep.Axis.PrecedingSibling; break;
        case AxisInfo.FOLLOWING_SIBLING: axis = PathStep.Axis.FollowingSibling; break;
        case AxisInfo.ANCESTOR_OR_SELF: axis = PathStep.Axis.AncestorSelf; break;
        case AxisInfo.DESCENDANT_OR_SELF: axis = PathStep.Axis.DescendantSelf; break;
        case AxisInfo.ATTRIBUTE: axis = PathStep.Axis.Attribute; break;
        case AxisInfo.CHILD: axis = PathStep.Axis.Child; break;
        default: throw new IllegalArgumentException("Unsupported axis in expression: " + expr.toString());
        }
        AbstractExpression ae = exprFor (axis, expr.getNodeTest());
        /*
        if (!Axis.isForwards[expr.getAxis()]) {
            ae = new FunCall (new QName("reverse"), ae);
        }
        */
        return ae;
    }
    
    private BinaryOperation exprFor (PathStep.Axis axis, CombinedNodeTest nodeTest) {
        NodeTest[] tests = nodeTest.getComponentNodeTests();
        BinaryOperation.Operator op = operatorFor(nodeTest.getOperator());
        return new BinaryOperation (exprFor(axis, tests[0]), op, exprFor(axis, tests[1]));
    }
    
    private lux.xpath.NodeTest nodeTestFor (DocumentNodeTest nodeTest) {
        NodeTest elementTest = nodeTest.getElementTest();
        int nameCode = elementTest.getFingerprint();
        return new lux.xpath.NodeTest (ValueType.DOCUMENT, qnameForNameCode(nameCode));
    }

    private QName qnameForNameCode(int nameCode) {
        StructuredQName sqname = config.getNamePool().getStructuredQName(nameCode);
        String prefix = sqname.getPrefix();
        String namespace = sqname.getURI();
        if (StringUtils.isBlank(prefix) && StringUtils.isNotBlank(namespace)) {
            prefix = getPrefixForNamespace(namespace);
        }
        return new QName (namespace, sqname.getLocalPart(), prefix);
    }
    
    public AbstractExpression exprFor (PathStep.Axis axis, NodeTest nodeTest) {
        if (nodeTest != null && nodeTest instanceof CombinedNodeTest) {
            return exprFor (axis, (CombinedNodeTest) nodeTest);
        }        
        return new PathStep (axis, nodeTestFor (nodeTest));
    }

    private lux.xpath.NodeTest nodeTestFor (NodeTest nodeTest) {
        if (nodeTest == null) {
            return new lux.xpath.NodeTest(ValueType.NODE);
        }
        if (nodeTest instanceof DocumentNodeTest) {
            return nodeTestFor ((DocumentNodeTest) nodeTest);
        }
        int nameCode = nodeTest.getFingerprint();
        ValueType nodeType = valueTypeForItemType(nodeTest);
        if (nameCode >= 0) { // matches a single node name 
            return new lux.xpath.NodeTest (nodeType, qnameForNameCode(nameCode));
        } else { // matches multiple node names
            if (nodeTest instanceof LocalNameTest) {
                return new lux.xpath.NodeTest (nodeType, new QName(null, ((LocalNameTest)nodeTest).getLocalName(), "*"));
            }
            if (nodeTest instanceof NamespaceTest) {
                NamespaceTest namespaceTest = (NamespaceTest) nodeTest;
                String namespace = namespaceTest.getNamespaceURI();
                String prefix = getPrefixForNamespace (namespace);
                QName qname = new QName(namespace, "*", prefix);
                addNamespaceDeclaration(qname);
                return new lux.xpath.NodeTest (nodeType, qname);
            }
            IntSet nameCodes = nodeTest.getRequiredNodeNames();
            if (nameCodes == IntUniversalSet.getInstance()) {
                return new lux.xpath.NodeTest (nodeType);
            } 
            throw new IllegalArgumentException("Unsupported node test: " + nodeTest.toString());
        }
    }

    private String getPrefixForNamespace(String namespace) {
        String prefix = config.getNamePool().suggestPrefixForURI(namespace);
        if (prefix == null && queryModule != null) {
            // queryModule can be null when optimizing a query fragment
            NamespaceResolver resolver = queryModule.getNamespaceResolver();
            Iterator prefixes = resolver.iteratePrefixes();
            while (prefixes.hasNext()) {
                String p = prefixes.next();
                if (namespace.equals(resolver.getURIForPrefix(p, true))) {
                    prefix = p;
                    break;
                }
            }
        }
        if (prefix != null) {
            namespaceDeclarations.put(prefix, namespace);
        } else {
            for (Map.Entry ns : namespaceDeclarations.entrySet()) {
                if (ns.getValue().equals(namespace))
                    return ns.getKey();
            }
            // this will happen if queryModule is null; it's actually ok in the only known case, which 
            // is when we translate fragments while checking for document ordering (in which case the 
            // exactitude of the QName translation is irrelevant).
            prefix = "";
        }
        return prefix;
    }

    // covers ArithmeticExpression, BooleanExpression, GeneralComparison, ValueComparison,
    // IdentityComparison, RangeExpression
    public AbstractExpression exprFor (BinaryExpression expr) {
        Expression [] operands = expr.getOperands();
        BinaryOperation.Operator op = operatorFor(expr.getOperator());
        if (operands[0] instanceof AtomicSequenceConverter || operands[1] instanceof AtomicSequenceConverter) {
            // Saxon optimizes some general sequences into atomic sequences in a way that 
            // we can't represent in XQuery directly.  So we use a General Comparison in that case
            // which we *think* will mimic Saxon's behavior
            op = generalizeOperator (op);
        }
        return new BinaryOperation (exprFor(operands[0]), op, exprFor(operands[1]));
    }
    
    private static BinaryOperation.Operator generalizeOperator (BinaryOperation.Operator op) {
        switch (op) {
        case AEQ: return BinaryOperation.Operator.EQUALS;
        case ANE : return BinaryOperation.Operator.NE;
        case ALT : return BinaryOperation.Operator.LT;
        case ALE : return BinaryOperation.Operator.LE;
        case AGT : return BinaryOperation.Operator.GT;
        case AGE : return BinaryOperation.Operator.GE;
        default: return op;
        }        
    }

    private static BinaryOperation.Operator operatorFor(int op) {
        switch (op) {
        case Token.AND: return BinaryOperation.Operator.AND;
        case Token.OR: return BinaryOperation.Operator.OR;
        case Token.INTERSECT: return BinaryOperation.Operator.INTERSECT;
        case Token.EXCEPT: return BinaryOperation.Operator.EXCEPT;
        case Token.UNION: return BinaryOperation.Operator.UNION;
        case Token.PLUS: return BinaryOperation.Operator.ADD;
        case Token.MINUS: return BinaryOperation.Operator.SUB;
        case Token.MULT: return BinaryOperation.Operator.MUL;
        case Token.DIV: return BinaryOperation.Operator.DIV;
        case Token.IDIV: return BinaryOperation.Operator.IDIV;
        case Token.MOD: return BinaryOperation.Operator.MOD;
        case Token.EQUALS: return BinaryOperation.Operator.EQUALS;
        case Token.NE: return BinaryOperation.Operator.NE;
        case Token.LT: return BinaryOperation.Operator.LT;
        case Token.GT: return BinaryOperation.Operator.GT;
        case Token.LE: return BinaryOperation.Operator.LE;
        case Token.GE: return BinaryOperation.Operator.GE;
        case Token.FEQ: return BinaryOperation.Operator.AEQ;
        case Token.FNE: return BinaryOperation.Operator.ANE;
        case Token.FLT: return BinaryOperation.Operator.ALT;
        case Token.FLE: return BinaryOperation.Operator.ALE;
        case Token.FGT: return BinaryOperation.Operator.AGT;
        case Token.FGE: return BinaryOperation.Operator.AGE;
        case Token.IS: return BinaryOperation.Operator.IS;
        case Token.PRECEDES: return BinaryOperation.Operator.BEFORE;
        case Token.FOLLOWS: return BinaryOperation.Operator.AFTER;
        case Token.TO: return BinaryOperation.Operator.TO;
        case Token.NEGATE: return BinaryOperation.Operator.SUB;
        default: throw new IllegalArgumentException("Unsupported operator: " + op);
        }
    }

    public AbstractExpression exprFor (Block expr) {
        return exprFor (expr.getChildren());
    }

    public AbstractExpression exprFor (CastExpression expr) {
        Expression base = expr.getBaseExpression();
        AtomicType type = expr.getTargetType();
        return castExprFor(exprFor(base), type);
    }
    
    public AbstractExpression exprFor (CastableExpression expr) {
        Expression base = expr.getBaseExpression();
        AtomicType type = expr.getTargetType();
        return new lux.xquery.CastableExpression(exprFor(base), type.toString());
    }
    
    private AbstractExpression castExprFor (AbstractExpression ae, AtomicType type) {
        if (type.isAbstract()) {
            return ae;
        }
        return new FunCall (qnameFor(type), valueTypeForItemType(type), ae);        
    }
    
    public AbstractExpression exprFor (ItemChecker checker) {
        Expression base = checker.getBaseExpression();
        AbstractExpression baseExpr = exprFor(base);
        return baseExpr;
    }

    private Sequence exprFor (Expression[] exprs) {
        AbstractExpression[] aex = new AbstractExpression [exprs.length];
        for (int i = 0 ; i < exprs.length; i++) {
            aex[i] = exprFor (exprs[i]);
        }
        return new Sequence (aex);
    }
        
    public AbstractExpression exprFor (Choose choose) {
        // convert a list of condition/action pairs (a la XSLT) to a chain
        // of if-then-else conditions a-la XQuery
        Expression[] conds = choose.getConditions();
        Expression[] actions = choose.getActions();
        int l = conds.length;
        if (actions.length != conds.length) {
            throw new LuxException ("Choose must have the same number of actions as conditions");            
        }
        if (l < 2) {
            return new Conditional (exprFor (conds[0]), exprFor(actions[0]), LiteralExpression.EMPTY);
        }
        l -= 2;
        AbstractExpression tail = new Conditional(exprFor (conds[l]), exprFor (actions[l]), exprFor(actions[l+1]));
        while (l-- > 0) {
            tail = new Conditional (exprFor (conds[l]), exprFor(actions[l]), tail);
        }
        return tail;
    }

    public AbstractExpression exprFor (Comment expr) {
        return new CommentConstructor(exprFor(expr.getContentExpression()));
    }
    
    public AbstractExpression exprFor (ComputedAttribute expr) {
        return new AttributeConstructor(exprFor(expr.getNameExpression()), 
                exprFor(expr.getContentExpression()));
    }
    
    public AbstractExpression exprFor (ComputedElement expr) {
        return new ComputedElementConstructor(exprFor (expr.getNameExpression()), 
                exprFor (expr.getContentExpression()));
    }

    public AbstractExpression exprFor (ContextItemExpression expr) {
        return new Dot();
    }

    private AbstractExpression exprFor(CopyOf expr) {
        return exprFor (expr.getSelectExpression());
    }
    
    public AbstractExpression exprFor (DocumentInstr documentInstr) {
        return new DocumentConstructor (exprFor (documentInstr.getContentExpression()));
    }
    
    public AbstractExpression exprFor (FilterExpression expr) {
        AbstractExpression filtered = exprFor (expr.getControllingExpression());
        AbstractExpression filter = exprFor (expr.getFilter());
        return new Predicate(filtered, filter);
    }

    public AbstractExpression exprFor (FirstItemExpression expr) {
        return new Subsequence (exprFor (expr.getBaseExpression()), LiteralExpression.ONE, LiteralExpression.ONE);
    }

    public AbstractExpression exprFor (FixedAttribute attribute) {
        NodeName name = attribute.getAttributeName();
        QName qname = qnameFor (name.getStructuredQName());
        AttributeConstructor att = new AttributeConstructor(
                new LiteralExpression(qname.toString(), ValueType.STRING), 
                exprFor (attribute.getContentExpression()));
        return att;
    }
    
    public AbstractExpression exprFor (FixedElement element) {
        NodeName name = element.getElementName();
        QName qname = qnameFor (name.getStructuredQName());
        AbstractExpression content = exprFor (element.getContentExpression());
        Namespace [] namespaces = namespacesFor (element.getActiveNamespaces());
        return new ElementConstructor(qname, namespaces, content);
    }

    public AbstractExpression exprFor (ForExpression forExpr) {
        StructuredQName var = forExpr.getVariableQName();
        Expression seq = forExpr.getSequence();
        Expression returns = forExpr.getAction();
        PositionVariable posvar = forExpr.getPositionVariable();
        Variable pos = null;
        if (posvar != null) {
            pos = new Variable(qnameFor(posvar.getVariableQName()));
        }
        return new FLWOR(exprFor(returns), new ForClause (new Variable(qnameFor(var)), pos, exprFor(seq)));
    }

    public AbstractExpression exprFor (FunctionCall funcall) {
        if (funcall.getFunctionName().equals(itemAtQName)) {
            return new Subsequence(exprFor (funcall.getArguments()[0]), exprFor(funcall.getArguments()[1]), LiteralExpression.ONE);
        }
        else if (functionEqualsBuiltin(funcall, "reverse")) {
            // Saxon wraps a call to reverse() around reverse axis expressions; its axis expression
            // always returns items in axis (reverse) order, unlike an xpath axis expression, whose results
            // are returned in different order depending on the context
            Expression arg = funcall.getArguments()[0];
            if ((arg.getSpecialProperties() & StaticProperty.REVERSE_DOCUMENT_ORDER) != 0 ||
                    (! Cardinality.allowsMany(arg.getCardinality()))) {
                // if (arg instanceof AxisExpression && (!Axis.isForwards[((AxisExpression) arg).getAxis()])) {
                // wrap in a sequence so as to preserve document order using an expression that will serialize
                // to an appropriate xpath syntax 
                return new Sequence (exprFor (arg));
            }        
        }
        else if (functionEqualsBuiltin(funcall, "subsequence")) {
            if (funcall.getNumberOfArguments() == 2) {
            	// This is actually dead code, since Saxon always creates a TailExpression, but we have no guarantee,
            	// so we keep this just in case that should change
                return new Subsequence (exprFor(funcall.getArguments()[0]), exprFor(funcall.getArguments()[1]));
            } else {
                if (funcall.getNumberOfArguments() != 3) {
                    throw new LuxException ("call to subsequence has " + funcall.getNumberOfArguments() + " arguments?");                    
                }
                return new Subsequence (exprFor(funcall.getArguments()[0]), exprFor(funcall.getArguments()[1]), exprFor(funcall.getArguments()[2]));
            }
        }
        /*
        if (functionEqualsBuiltin(funcall, "string-join")) {
            // undo this optimization of Saxon's
            if (funcall.getArguments()[0] instanceof AtomicSequenceConverter) {
                return exprFor (funcall.getArguments()[0]);
            }
        }
        */
        Expression[] args = funcall.getArguments();
        AbstractExpression[] aargs = new AbstractExpression[args.length];
        for (int i = 0; i < args.length; i++) {
            aargs[i] = exprFor (args[i]);
        }
        Entry entry = StandardFunction.getFunction(funcall.getFunctionName().getDisplayName(), aargs.length);
        ValueType returnType = entry != null ? valueTypeForItemType (entry.itemType) : ValueType.VALUE;
        QName fnQName = qnameFor (funcall.getFunctionName());
        if (functionEqualsBuiltin(funcall, "root") || fnQName.equals(FunCall.LUX_SEARCH)) 
        {
            // root() may return an element when executed in the context of a fragment
            // However for the purposes of our optimizer, we want to know if it is returning
            // documents.  We only optimize absolute expressions, and this inference is correct in those cases.
            returnType = ValueType.DOCUMENT;
        }
        return new FunCall (qnameFor (funcall.getFunctionName()), returnType, aargs);
    }
    
    private boolean functionEqualsBuiltin (FunctionCall funcall, String builtinFunction) {
        return funcall.getFunctionName().getDisplayName().equals (builtinFunction);
    }
    
    public AbstractExpression exprFor (GlobalVariable var) {
        // TODO: encode the declared type
        return new Variable (qnameFor (var.getVariableQName()));
    }
    
    public AbstractExpression exprFor (IntegerRangeTest rangeTest) {
        return new BinaryOperation
            (new BinaryOperation
             (exprFor (rangeTest.getValueExpression()), Operator.ALE, 
              exprFor (rangeTest.getMaxValueExpression())),
             Operator.AND,
             new BinaryOperation
             (exprFor (rangeTest.getValueExpression()), Operator.AGE, 
              exprFor (rangeTest.getMinValueExpression())));
    }
    
    private AbstractExpression exprFor (InstanceOfExpression expr) {
        ItemType type = expr.getRequiredItemType();
        String typeExpr;
        if (type.isPlainType()) {
            typeExpr = type.toString();
        } else if (type instanceof NodeTest) {
            lux.xpath.NodeTest nodeTest = nodeTestFor((NodeTest)type);
            typeExpr = nodeTest.toString();
        } else {
            throw new LuxException ("Unsupported node test in instance-of expression: " + expr.toString());
        }
        return new InstanceOf(typeExpr, exprFor (expr.getBaseExpression()));
    }

    private ValueType valueTypeForItemType(ItemType itemType) {
        if (itemType.isAtomicType()) {
            switch (itemType.getPrimitiveType()) {
            case StandardNames.XS_STRING:
            case StandardNames.XS_ANY_URI:
                return ValueType.STRING;
            case StandardNames.XS_DATE:
                return ValueType.DATE;
            case StandardNames.XS_DATE_TIME:
                return ValueType.DATE_TIME;
            case StandardNames.XS_G_YEAR:
                return ValueType.YEAR;
            case StandardNames.XS_G_YEAR_MONTH:
                return ValueType.YEAR_MONTH;
            case StandardNames.XS_G_MONTH:
                return ValueType.MONTH;
            case StandardNames.XS_G_DAY:
                return ValueType.DAY;
            case StandardNames.XS_G_MONTH_DAY:
                return ValueType.MONTH_DAY;
            case StandardNames.XS_DAY_TIME_DURATION:
                return ValueType.DAY_TIME_DURATION;
            case StandardNames.XS_YEAR_MONTH_DURATION:
                return ValueType.YEAR_MONTH_DURATION;
            case StandardNames.XS_INT:
                return ValueType.INT;
            case StandardNames.XS_INTEGER:
                if (itemType.equals(BuiltInAtomicType.INT)) {
                    return ValueType.INT;
                }
                return ValueType.INTEGER;
            case StandardNames.XS_DOUBLE:
                return ValueType.DOUBLE;
            case StandardNames.XS_FLOAT:
                return ValueType.FLOAT;
            case StandardNames.XS_DECIMAL:
                return ValueType.DECIMAL;
            case StandardNames.XS_BOOLEAN:
                return ValueType.BOOLEAN;
            case StandardNames.XS_TIME:
                return ValueType.TIME;
            case StandardNames.XS_HEX_BINARY:
                return ValueType.HEX_BINARY;
            case StandardNames.XS_BASE64_BINARY:
                return ValueType.BASE64_BINARY;
            case StandardNames.XS_UNTYPED_ATOMIC:
                return ValueType.UNTYPED_ATOMIC;
            case StandardNames.XS_QNAME:
                return ValueType.QNAME;
            default:
                return ValueType.ATOMIC;
            }
        }
        if (itemType instanceof NodeTest) {
            NodeTest nodeTest = (NodeTest) itemType;
            switch (nodeTest.getPrimitiveType()) {
            case Type.NODE: return ValueType.NODE;
            case Type.ELEMENT: return ValueType.ELEMENT;
            case Type.TEXT: return ValueType.TEXT;
            case Type.ATTRIBUTE: return ValueType.ATTRIBUTE;
            case Type.DOCUMENT: return ValueType.DOCUMENT;
            case Type.PROCESSING_INSTRUCTION: return ValueType.PROCESSING_INSTRUCTION;
            case Type.COMMENT: return ValueType.COMMENT;
            case Type.EMPTY: return ValueType.EMPTY;
            }
        }
        if (itemType == ErrorType.getInstance()) {
            return ValueType.EMPTY;
        }
        // could be a function type? or namespace()?
        return ValueType.VALUE;
    }

    public AbstractExpression exprFor (LastItemExpression expr) {
        return new Subsequence (exprFor (expr.getBaseExpression()), FunCall.LastExpression, LiteralExpression.ONE);
    }

    public AbstractExpression exprFor (LetExpression let) {
        StructuredQName var = let.getVariableQName();
        Expression seq = let.getSequence();
        Expression returns = let.getAction();
        return new FLWOR(exprFor(returns), new LetClause (new Variable(qnameFor(var), getTypeDescription(let.getRequiredType())), exprFor(seq)));
    }
    
    public AbstractExpression exprFor (Literal literal) {
        // This could be a sequence!!
        GroundedValue value = literal.getValue();
        try {
            int len = value.getLength();
            if (len == 0) {
                return LiteralExpression.EMPTY;
            }
            SequenceIterator iter = value.iterate();
            if (len > 1) {
                ArrayList items = new ArrayList();
                Item member;
                while ((member = iter.next()) != null) {
                    if (member instanceof AtomicValue) {
                        items.add (exprFor ((AtomicValue) member));
                    } else {
                        throw new LuxException ("unsupported node in a literal sequence: " + literal.toString());
                    }                    
                }
                return new Sequence (items.toArray(new LiteralExpression[0]));
            }
            return exprFor ((AtomicValue) iter.next());
        } catch (XPathException e) {
            throw new LuxException (e);
        }
    }

    public LiteralExpression exprFor (AtomicValue value) {
        ValueType type = valueTypeForItemType(value.getItemType());
        if (value instanceof CalendarValue || value instanceof DurationValue || 
            value instanceof BigIntegerValue) {
            //return new LiteralExpression(((CalendarValue)value).getCalendar(), type);
            // , value.getPrimitiveType().getQualifiedName().toString()
            return new LiteralExpression(value.getStringValue(), type);
        }
        if (value instanceof QNameValue) {
            return new LiteralExpression (qnameFor (((QNameValue) value).getStructuredQName()), type);
        }
        try {
            Object oval = SequenceTool.convertToJava(value);
            return new LiteralExpression(oval, type);
        } catch (XPathException e) {
            throw new LuxException (e);
        }
    }
    public AbstractExpression exprFor (NegateExpression expr) {
        return new UnaryMinus(exprFor (expr.getBaseExpression()));
    }

    public AbstractExpression exprFor (ParentNodeExpression expr) {
        return new PathStep(PathStep.Axis.Parent, new lux.xpath.NodeTest(ValueType.NODE));
    }

    public AbstractExpression exprFor (ProcessingInstruction pi) {
        return new ProcessingInstructionConstructor(exprFor(pi.getNameExpression()), exprFor(pi.getContentExpression()));
    }

    public AbstractExpression exprFor (QuantifiedExpression expr) {
        Satisfies.Quantifier quantifier = expr.getOperator() == Token.SOME ? Quantifier.SOME : Quantifier.EVERY;
        return new Satisfies(quantifier, new Variable(qnameFor(expr.getVariableQName())), exprFor(expr.getSequence()), exprFor (expr.getAction()));
    }
    
    public AbstractExpression exprFor (RootExpression expr) {
        return new Root();
    }

    public AbstractExpression exprFor (SlashExpression expr) {
        AbstractExpression lq = exprFor (expr.getControllingExpression());
        AbstractExpression rq = exprFor (expr.getControlledExpression());
        return new PathExpression(lq, rq);
    }

    public AbstractExpression exprFor (SubscriptExpression expr) {
        return new Subsequence (exprFor (expr.getBaseExpression()), exprFor(expr.getSubscriptExpression()), LiteralExpression.ONE);
    }

    public AbstractExpression exprFor (TailExpression expr) {
        return new Subsequence (exprFor (expr.getBaseExpression()), new LiteralExpression(expr.getStart()));
    }

    /*
     * just a kind of UnaryExpression?
    public AbstractExpression exprFor (TailCallLoop expr) {
        return exprFor (expr.getBaseExpression());
    }
    */

    public AbstractExpression exprFor (UnaryExpression expr) {
        return exprFor (expr.getBaseExpression());
    }
    
    public AbstractExpression exprFor (ValueOf valueOf) {
        return new TextConstructor(exprFor (valueOf.getContentExpression()));
    }
    
    public AbstractExpression exprFor (VariableReference var) {
        if (var.getBinding() != null) {
            StructuredQName varName= var.getBinding().getVariableQName();
            return new Variable (qnameFor (varName));
        } else {
            // total HACK, but Saxon provides no other public method to retrieve the constant value
            try {
                Object o = var.optimize(null, null);
                if (o instanceof Literal) {
                    return exprFor ((Literal) o);
                }
            } catch (XPathException e) {
                throw new LuxException ("Unsupported variable reference: " + var);
            } 
            throw new LuxException ("Unsupported variable reference: " + var);
        }
    }
    
    private static final StructuredQName itemAtQName = new StructuredQName("", NamespaceConstant.SAXON, "item-at");
    
    private QName qnameFor(StructuredQName name) {
        QName qname = new QName (name.getNamespaceBinding().getURI(), name.getLocalPart(), name.getNamespaceBinding().getPrefix());
        if (!(qname.getPrefix().equals ("fn") && qname.getNamespaceURI().equals(FunCall.FN_NAMESPACE) ||
                qname.getPrefix().equals("local") && qname.getNamespaceURI().equals(FunCall.LOCAL_NAMESPACE) ||
                qname.getPrefix().equals("xs") && qname.getNamespaceURI().equals(FunCall.XS_NAMESPACE))) {
            addNamespaceDeclaration(qname);
        }
        return qname;
    }

    public AbstractExpression exprFor (CompareToIntegerConstant comp) {
        Operator op = operatorFor (comp.getComparisonOperator());
        long num = comp.getComparand();
        return new BinaryOperation  (exprFor (comp.getOperand()), op, new LiteralExpression (num));
    }

    private Namespace[] namespacesFor(NamespaceBinding[] activeNamespaces) {
        if (activeNamespaces == null)
            return null;
        Namespace[] namespaces = new Namespace[activeNamespaces.length];
        int i = 0;
        for (NamespaceBinding binding : activeNamespaces) {
            namespaces[i++] = new Namespace (binding.getPrefix(), binding.getURI());
        }
        return namespaces;
    }
    
    public AbstractExpression exprFor (FLWORExpression flwor) {  
        List saxonClauses = flwor.getClauseList();
        int i = 0;
        while (i < saxonClauses.size() && saxonClauses.get(i).getClauseKey() == Clause.WHERE) {
            // Saxon optimizes constant where clauses to the left of the expression where they
            // are no longer syntactically valid as xquery
            ++i;
        }
        FLWORClause clauses[];
        int k = 0;
        if (i < saxonClauses.size()) {
            clauses = new FLWORClause[saxonClauses.size()];
            // add clauses up to but not including the first order by clause
            for (int j = i; j < saxonClauses.size() &&
                    saxonClauses.get(j).getClauseKey() != Clause.ORDERBYCLAUSE; 
                    j++) 
            {
                clauses[k++] = clauseFor (saxonClauses.get(j));
            }
        } else {
            clauses = new FLWORClause[saxonClauses.size() + 1];            
        }
        if (k == 0) {
            // Generate a dummy let clause if the where clause was the only clause??
            clauses[k++] = new LetClause(new Variable (new QName("x")), LiteralExpression.ONE);
        }
        if (i > 0) { 
            // append any of the preamble where clauses
            for (int j = 0; j < i; j++) {
                clauses[k++] = clauseFor (saxonClauses.get(j));                    
            }
        }
        // and the rest of the clauses...
        while (k < saxonClauses.size()) {
            clauses[k] = clauseFor (saxonClauses.get(k));
            ++k;
        }
        return new FLWOR(exprFor (flwor.getReturnClause()), clauses);
    }
    
    private FLWORClause clauseFor (Clause clause) {
        switch (clause.getClauseKey()) {
        case Clause.LET: return clauseFor ((net.sf.saxon.expr.flwor.LetClause) clause);
        case Clause.FOR: return clauseFor ((net.sf.saxon.expr.flwor.ForClause) clause);
        case Clause.WHERE: return clauseFor ((net.sf.saxon.expr.flwor.WhereClause) clause);
        case Clause.ORDERBYCLAUSE: return clauseFor ((net.sf.saxon.expr.flwor.OrderByClause) clause);
        default: throw new LuxException ("Unsupported FLWOR clause " + clause.getClass().getSimpleName());
        }
    }
    
    private FLWORClause clauseFor (net.sf.saxon.expr.flwor.ForClause clause) {        
        AbstractExpression seq = exprFor (clause.getSequence());
        Variable var = new Variable (qnameFor(clause.getRangeVariable().getVariableQName()));        
        LocalVariableBinding positionVariable = clause.getPositionVariable();
        Variable pos;
        if (positionVariable != null) {
            pos = new Variable (qnameFor(positionVariable.getVariableQName()));
        } else  {
            pos = null;
        }            
        return new ForClause(var, pos, seq);
    }

    private FLWORClause clauseFor (net.sf.saxon.expr.flwor.LetClause clause) {        
        AbstractExpression seq = exprFor (clause.getSequence());
        Variable var = new Variable (qnameFor(clause.getRangeVariable().getVariableQName()));
        return new LetClause(var, seq);
    }

    private FLWORClause clauseFor (net.sf.saxon.expr.flwor.WhereClause clause) {
        return new WhereClause(exprFor (clause.getPredicate()));
    }
    
    private FLWORClause clauseFor (net.sf.saxon.expr.flwor.OrderByClause clause) {
        SortKeyDefinition[] sortKeyDefs = clause.getSortKeyDefinitions();
        SortKey[] sortKeys = new SortKey[sortKeyDefs.length];
        for (int i = 0; i < sortKeyDefs.length; i++) {
            sortKeys[i] = sortKeyFor (sortKeyDefs[i]);
        }
        return new OrderByClause(sortKeys);
    }
    
    private SortKey sortKeyFor (SortKeyDefinition sortKeyDef) {
        return new SortKey (exprFor (sortKeyDef.getSortKey()), 
                (LiteralExpression) exprFor (sortKeyDef.getOrder()),
                exprFor (sortKeyDef.getCollationNameExpression()), 
                sortKeyDef.getEmptyLeast());
    }
    
    private static int cardinalityOf (SequenceType type) {
        int cardinality =type.getCardinality();
        if (cardinality == StaticProperty.ALLOWS_ONE_OR_MORE) {
            return ValueType.ONE_OR_MORE;
        } else if (cardinality == StaticProperty.ALLOWS_ZERO_OR_MORE) {
            return ValueType.ANY_NUMBER;
        } else if (cardinality == StaticProperty.ALLOWS_ZERO_OR_ONE) {
            return ValueType.ZERO_OR_ONE;
        } else if (cardinality == StaticProperty.ALLOWS_ONE) {
            return ValueType.EXACTLY_ONE;
        } else {
            return ValueType.EXACTLY_ZERO;
        }
    }
    
    public AbstractExpression exprFor (Expression expr) {
        if (expr == null) {
            return null;
        }
        ExprClass exprClass = null;        
        for (Class cls = expr.getClass(); exprClass == null && cls != Object.class; cls = cls.getSuperclass()) {
            exprClass = dispatcher.get (cls.getSimpleName());
        }
        if (exprClass == null) {
            throw new UnsupportedOperationException ("unhandled expression type: " + expr.getClass().getSimpleName() + " in " + expr.toString());
        }
        switch (exprClass) {
        case AtomicSequenceConverter:
            return exprFor ((AtomicSequenceConverter)expr);
        case Atomizer:
            return exprFor ((Atomizer) expr);
        case AxisExpression:
            return exprFor ((AxisExpression) expr);
        case BinaryExpression:
            return exprFor ((BinaryExpression) expr);
        case Block:
            return exprFor ((Block) expr);
        case CastExpression:
            return exprFor ((CastExpression) expr);
        case CastableExpression:
            return exprFor ((CastableExpression) expr);
        case Choose:
            return exprFor ((Choose) expr);
        case Comment:
            return exprFor ((Comment) expr);
        case CompareToIntegerConstant:
            return exprFor ((CompareToIntegerConstant) expr);
        case ComputedAttribute:
            return exprFor ((ComputedAttribute) expr);
        case ComputedElement:
            return exprFor ((ComputedElement) expr);
        case ContextItemExpression:
            return exprFor ((ContextItemExpression) expr);
        case CopyOf:
            return exprFor ((CopyOf) expr);
        case DocumentInstr:
            return exprFor ((DocumentInstr) expr);
        case DocumentSorter:
            return exprFor ((DocumentSorter) expr);
        case FilterExpression:
            return exprFor ((FilterExpression) expr);
        case FirstItemExpression:
            return exprFor ((FirstItemExpression) expr);
        case FixedAttribute:
            return exprFor ((FixedAttribute) expr);
        case FixedElement:
            return exprFor ((FixedElement) expr);
        case FLWORExpression:
            return exprFor ((FLWORExpression) expr);
        case ForExpression:
            return exprFor ((ForExpression) expr);
        case FunctionCall:
            return exprFor ((FunctionCall) expr);
        case InstanceOfExpression:
            return exprFor ((InstanceOfExpression) expr);
        case IntegerRangeTest:
            return exprFor ((IntegerRangeTest) expr);
        case ItemChecker:
            return exprFor ((ItemChecker) expr);
        case LastItemExpression:
            return exprFor ((LastItemExpression) expr);
        case LetExpression:
            return exprFor ((LetExpression) expr);
        case Literal:
            return exprFor ((Literal) expr);
        case NegateExpression:
            return exprFor ((NegateExpression) expr);
        case ParentNodeExpression:
            return exprFor ((ParentNodeExpression) expr);
        case ProcessingInstruction:
            return exprFor ((ProcessingInstruction) expr);
        case QuantifiedExpression:
            return exprFor ((QuantifiedExpression) expr);
        case RootExpression:
            return exprFor ((RootExpression) expr);
        case SingletonAtomizer:
            return exprFor ((SingletonAtomizer) expr);
        case SlashExpression:
            return exprFor ((SlashExpression) expr);
        case SubscriptExpression:
            return exprFor ((SubscriptExpression) expr);
        case TailExpression:
            return exprFor ((TailExpression) expr);
        case TreatAs:
            return exprFor ((ItemChecker) expr);
        case UnaryExpression:
            return exprFor ((UnaryExpression) expr);
        case ValueOf:
            return exprFor ((ValueOf) expr);
        //case LocalVariableReference:
        case VariableReference:
            return exprFor ((VariableReference) expr);
        case ErrorExpression:
           throw new LuxException ("A potential run-time error was detected during compilation: " + ((ErrorExpression) expr).getException().getMessageAndLocation(),
                   ((ErrorExpression) expr).getException());
        default:
            throw new UnsupportedOperationException("unhandled expression type: " + expr.getClass().getSimpleName() + " in " + expr.toString());
        }
    }
    
    // list the names of all the classes
    // for which we have defined overrides of exprFor
    // - probably should do this by introspection
    
    private enum ExprClass {
        AtomicSequenceConverter,
        Atomizer,
        AxisExpression,
        BinaryExpression,
        CastExpression, 
        CastableExpression,
        CompareToIntegerConstant,
        ComputedAttribute,
        ComputedElement,
        ContextItemExpression,
        CopyOf,
        DocumentSorter,
        ErrorExpression,
        Expression,
        FilterExpression,
        FirstItemExpression,
        ForExpression,
        FunctionCall,
        InstanceOfExpression,
        IntegerRangeTest,
        ItemChecker,
        LastItemExpression,
        LetExpression,
        Literal,
        NegateExpression,
        ProcessingInstruction,
        ParentNodeExpression,
        QuantifiedExpression,
        RootExpression,
        SingletonAtomizer,
        SlashExpression,
        SubscriptExpression,
        TailExpression,
        TreatAs,
        UnaryExpression,
        VariableReference,
        FLWORExpression,
        Block,
        Choose,
        Comment,
        DocumentInstr,
        FixedAttribute,
        FixedElement,
        GlobalVariable,
        ValueOf
    }

}

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */




© 2015 - 2024 Weber Informatics LLC | Privacy Policy