Package client.net.sf.saxon.ce.expr.sort

Source Code of client.net.sf.saxon.ce.expr.sort.SortKeyDefinition

package client.net.sf.saxon.ce.expr.sort;

import client.net.sf.saxon.ce.Configuration;
import client.net.sf.saxon.ce.expr.*;
import client.net.sf.saxon.ce.lib.StringCollator;
import client.net.sf.saxon.ce.om.StandardNames;
import client.net.sf.saxon.ce.tree.util.URI;
import client.net.sf.saxon.ce.value.Whitespace;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.*;
import client.net.sf.saxon.ce.value.StringValue;

import java.util.HashMap;

/**
* A SortKeyDefinition defines one component of a sort key. <BR>
*
* Note that most attributes defining the sort key can be attribute value templates,
* and can therefore vary from one invocation to another. We hold them as expressions. As
* soon as they are all known (which in general is only at run-time), the SortKeyDefinition
* is replaced by a FixedSortKeyDefinition in which all these values are fixed.
*/

// TODO: optimise also for the case where the attributes depend only on global variables
// or parameters, in which case the same AtomicComparer can be used for the duration of a
// transformation.

// TODO: at present the SortKeyDefinition is evaluated to obtain a AtomicComparer, which can
// be used to compare two sort keys. It would be more efficient to use a Collator to
// obtain collation keys for all the items to be sorted, as these can be compared more
// efficiently.


public class SortKeyDefinition {

    private static StringLiteral defaultOrder = new StringLiteral("ascending");
    private static StringLiteral defaultCaseOrder = new StringLiteral("#default");
    private static StringLiteral defaultLanguage = new StringLiteral(StringValue.EMPTY_STRING);

    protected Expression sortKey;
    protected Expression order = defaultOrder;
    protected Expression dataTypeExpression = null;
                                        // used when the type is not known till run-time
    protected Expression caseOrder = defaultCaseOrder;
    protected Expression language = defaultLanguage;
    protected Expression collationName = null;
    protected Expression stable = null; // not actually used, but present so it can be validated
    protected StringCollator collation;
    protected String baseURI;           // needed in case collation URI is relative
    protected boolean backwardsCompatible = false;

    private AtomicComparer finalComparator = null;
    // Note, the "collation" defines the collating sequence for the sort key. The
    // "finalComparator" is what is actually used to do comparisons, after taking into account
    // ascending/descending, caseOrder, etc.

    /**
     * Set the expression used as the sort key
     * @param exp the sort key select expression
    */

    public void setSortKey(Expression exp) {
        sortKey = exp;
    }

    /**
     * Get the expression used as the sort key
     * @return the sort key select expression
    */

    public Expression getSortKey() {
        return sortKey;
    }


    /**
     * Set the order. This is supplied as an expression which must evaluate to "ascending"
     * or "descending". If the order is fixed, supply e.g. new StringValue("ascending").
     * Default is "ascending".
     * @param exp the expression that determines the order (always a literal in XQuery, but
     * can be defined by an AVT in XSLT)
    */

    public void setOrder(Expression exp) {
        order = exp;
    }

    /**
     * Get the expression that defines the order as ascending or descending
     * @return the expression that determines the order (always a literal in XQuery, but
     * can be defined by an AVT in XSLT)
     */

    public Expression getOrder() {
        return order;
    }

    /**
     * Set the data type. This is supplied as an expression which must evaluate to "text",
     * "number", or a QName. If the data type is fixed, the valus should be supplied using
     * setDataType() and not via this method.
     * @param exp the expression that defines the data type, as used in XSLT 1.0
    */

    public void setDataTypeExpression(Expression exp) {
        dataTypeExpression = exp;
    }

    /**
     * Get the expression that defines the data type of the sort keys
     * @return the expression that defines the data type, as used in XSLT 1.0
     */

    public Expression getDataTypeExpression() {
        return dataTypeExpression;
    }

    /**
     * Set the case order. This is supplied as an expression which must evaluate to "upper-first"
     * or "lower-first" or "#default". If the order is fixed, supply e.g. new StringValue("lower-first").
     * Default is "#default".
     * @param exp the expression that defines the case order
    */

    public void setCaseOrder(Expression exp) {
        caseOrder = exp;
    }

    /**
     * Get the expression that defines the case order of the sort keys.
     * @return the expression that defines the case order, whose run-time value will be "upper-first",
     * "lower-first", or "#default".
     */

    public Expression getCaseOrder() {
        return caseOrder;
    }

    /**
     * Set the language. This is supplied as an expression which evaluates to the language name.
     * If the order is fixed, supply e.g. new StringValue("de").
     * @param exp the expression that determines the language
    */

    public void setLanguage(Expression exp) {
        language = exp;
    }

    /**
     * Get the expression that defines the language of the sort keys
     * @return exp the expression that determines the language
     */

    public Expression getLanguage() {
        return language;
    }

    /**
     * Set the collation name (specifically, an expression which when evaluated returns the collation URI).
     * @param collationName the expression that determines the collation name
    */

    public void setCollationNameExpression(Expression collationName) {
        this.collationName = collationName;
    }

    /**
     * Get the selected collation name
     * (specifically, an expression which when evaluated returns the collation URI).
     * @return the expression that determines the collation name
    */

    public Expression getCollationNameExpression() {
        return collationName;
    }

    /**
     * Set the collation to be used
     * @param collation A StringCollator, which encapsulates both the collation URI and the collating function
     */

    public void setCollation(StringCollator collation) {
        this.collation = collation;
    }

    /**
     * Get the collation to be used
     * @return A StringCollator, which encapsulates both the collation URI and the collating function
     */

    public StringCollator getCollation() {
        return collation;
    }

    /**
     * Set the base URI of the expression. This is needed to handle the case where a collation URI
     * evaluated at run-time turns out to be a relative URI.
     * @param baseURI the static base URI of the expression
     */

    public void setBaseURI(String baseURI) {
        this.baseURI = baseURI;
    }

    /**
     * Get the static base URI of the expression. This is needed to handle the case where a collation URI
     * evaluated at run-time turns out to be a relative URI.
     * @return the static base URI of the expression
     */

    public String getBaseURI() {
        return baseURI;
    }

    /**
     * Set whether this sort key definition is stable
     * @param stable the expression that determines whether the sort key definition is stable
     * (it evaluates to the string "yes" or "no".
     */

    public void setStable(Expression stable) {
        this.stable = stable;
    }

    /**
     * Ask whether this sort key definition is stable
     * @return the expression that determines whether the sort key definition is stable
     * (it evaluates to the string "yes" or "no".
     */

    public Expression getStable() {
        return stable;
    }

    /**
     * Set whether this sort key is evaluated in XSLT 1.0 backwards compatibility mode
     * @param compatible true if backwards compatibility mode is selected
     */

    public void setBackwardsCompatible(boolean compatible) {
        backwardsCompatible = compatible;
    }

    /**
     * Ask whether this sort key is evaluated in XSLT 1.0 backwards compatibility mode
     * @return true if backwards compatibility mode was selected
     */

    public boolean isBackwardsCompatible() {
        return backwardsCompatible;
    }

     /**
     * Ask whether the sort key definition is fixed, that is, whether all the information needed
     * to create a Comparator is known statically
     * @return true if all information needed to create a Comparator is known statically
     */

    public boolean isFixed() {
        return (order instanceof Literal &&
                (dataTypeExpression == null ||
                    dataTypeExpression instanceof Literal) &&
                caseOrder instanceof Literal &&
                language instanceof Literal &&
                (stable == null || stable instanceof Literal) &&
                (collationName == null || collationName instanceof Literal));
    }

    /**
     * Simplify this sort key definition
     * @param visitor the expression visitor
     * @return the simplified sort key definition
     * @throws XPathException if any failure occurs
     */

    public SortKeyDefinition simplify(ExpressionVisitor visitor) throws XPathException {
        sortKey = visitor.simplify(sortKey);
        order = visitor.simplify(order);
        dataTypeExpression = visitor.simplify(dataTypeExpression);
        caseOrder = visitor.simplify(caseOrder);
        language = visitor.simplify(language);
        stable = visitor.simplify(stable);
        collationName = visitor.simplify(collationName);
        return this;
    }


    /**
     * Type-check this sort key definition (all properties other than the sort key
     * select expression, which has a different dynamic context)
     * @param visitor the expression visitor
     * @param contextItemType the type of the context item
     * @throws XPathException if any failure occurs
     */

    public void typeCheck(ExpressionVisitor visitor, ItemType contextItemType) throws XPathException {
        order = visitor.typeCheck(order, contextItemType);
        dataTypeExpression = visitor.typeCheck(dataTypeExpression, contextItemType);
        caseOrder = visitor.typeCheck(caseOrder, contextItemType);
        language = visitor.typeCheck(language, contextItemType);
        stable = visitor.typeCheck(stable, contextItemType);
        collationName = visitor.typeCheck(collationName, contextItemType);

        if (language instanceof StringLiteral && ((StringLiteral)language).getStringValue().length() != 0) {
            if (!StringValue.isValidLanguageCode(((StringLiteral)language).getStringValue())) {
                throw new XPathException("The lang attribute of xsl:sort must be a valid language code", "XTDE0030");
            }
        }
    }   

    /**
     * Allocate an AtomicComparer to perform the comparisons described by this sort key component. This method
     * is called at run-time. The AtomicComparer takes into account not only the collation, but also parameters
     * such as order=descending and handling of empty sequence and NaN (the result of the compare()
     * method of the comparator is +1 if the second item is to sort after the first item)
     * @param context the dynamic evaluation context
     * @return an AtomicComparer suitable for making the sort comparisons
    */

    public AtomicComparer makeComparator(XPathContext context) throws XPathException {

        String orderX = order.evaluateAsString(context).toString();

        final Configuration config = context.getConfiguration();
        final TypeHierarchy th = config.getTypeHierarchy();

        AtomicComparer atomicComparer;
        StringCollator stringCollator;
        if (collation != null) {
            stringCollator = collation;
        } else if (collationName != null) {
            String cname = collationName.evaluateAsString(context).toString();
            URI collationURI;
            try {
                collationURI = new URI(cname, true);
                if (!collationURI.isAbsolute()) {
                    if (baseURI == null) {
                        throw new XPathException("Collation URI is relative, and base URI is unknown");
                    } else {
                        URI base = new URI(baseURI);
                        collationURI = base.resolve(collationURI.toString());
                    }
                }
            } catch (URI.URISyntaxException err) {
                throw new XPathException("Collation name " + cname + " is not a valid URI: " + err);
            }
            stringCollator = context.getConfiguration().getNamedCollation(collationURI.toString());
            if (stringCollator == null) {
                throw new XPathException("Unknown collation " + collationURI.toString(), "XTDE1035");
            }
        } else {
            String caseOrderX = caseOrder.evaluateAsString(context).toString();
            String languageX = language.evaluateAsString(context).toString();
            HashMap props = new HashMap();
            if (languageX.length() != 0 && !(language instanceof StringLiteral)) {
                if (!StringValue.isValidLanguageCode(((StringLiteral)language).getStringValue())) {
                    throw new XPathException("The lang attribute of xsl:sort must be a valid language code", "XTDE0030");
                }
                props.put("lang", languageX);
            }
            if (!caseOrderX.equals("#default")) {
                props.put("case-order", caseOrderX);
            }
            //stringCollator = Configuration.getPlatform().makeCollation(config, props, "");
            stringCollator = null;
        }



        if (dataTypeExpression==null) {
            atomicComparer = AtomicSortComparer.makeSortComparer(stringCollator,
                sortKey.getItemType(th).getAtomizedItemType().getPrimitiveType(), context);
        } else {
            String dataType = dataTypeExpression.evaluateAsString(context).toString();
            if (dataType.equals("text")) {
                atomicComparer = AtomicSortComparer.makeSortComparer(stringCollator,
                    StandardNames.XS_STRING, context);
                atomicComparer = new TextComparer(atomicComparer);
            } else if (dataType.equals("number")) {
                atomicComparer = NumericComparer.getInstance();
            } else {
                XPathException err = new XPathException("data-type on xsl:sort must be 'text' or 'number'");
                err.setErrorCode("XTDE0030");
                throw err;
            }
        }

        if (stable != null) {
            StringValue stableVal = (StringValue)stable.evaluateItem(context);
            String s = Whitespace.trim(stableVal.getStringValue());
            if (s.equals("yes") || s.equals("no")) {
                // no action
            } else {
                XPathException err = new XPathException("Value of 'stable' on xsl:sort must be 'yes' or 'no'");
                err.setErrorCode("XTDE0030");
                throw err;
            }
        }

        if (orderX.equals("ascending")) {
            return atomicComparer;
        } else if (orderX.equals("descending")) {
            return new DescendingComparer(atomicComparer);
        } else {
            XPathException err1 = new XPathException("order must be 'ascending' or 'descending'");
            err1.setErrorCode("XTDE0030");
            throw err1;
        }
    }

    /**
     * Set the comparator which is used to compare two values according to this sort key. The comparator makes the final
     * decision whether one value sorts before or after another: this takes into account the data type, the collation,
     * whether empty comes first or last, whether the sort order is ascending or descending.
     *
     * <p>This method is called at compile time if all these factors are known at compile time.
     * It must not be called at run-time, except to reconstitute a finalComparator that has been
     * lost by virtue of serialization .</p>
     * @param comp the Atomic Comparer to be used
    */

    public void setFinalComparator(AtomicComparer comp) {
        finalComparator = comp;
    }

    /**
     * Get the comparator which is used to compare two values according to this sort key. This method
     * may be called either at compile time or at run-time. If no comparator has been allocated,
     * it returns null. It is then necessary to allocate a comparator using the {@link #makeComparator}
     * method.
     * @return the Atomic Comparer to be used
    */

    public AtomicComparer getFinalComparator() {
        return finalComparator;
    }



}


// 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/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.
TOP

Related Classes of client.net.sf.saxon.ce.expr.sort.SortKeyDefinition

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.