net.sf.saxon.functions.CollatingFunction Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of saxon-he Show documentation
Show all versions of saxon-he Show documentation
An OSGi bundle for Saxon-HE
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2013 Saxonica Limited.
// 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.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
package net.sf.saxon.functions;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.parser.ExpressionTool;
import net.sf.saxon.expr.parser.ExpressionVisitor;
import net.sf.saxon.expr.sort.AtomicComparer;
import net.sf.saxon.expr.sort.AtomicSortComparer;
import net.sf.saxon.expr.sort.GenericAtomicComparer;
import net.sf.saxon.lib.StringCollator;
import net.sf.saxon.om.GroundedValue;
import net.sf.saxon.om.Sequence;
import net.sf.saxon.trans.Err;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.type.AtomicType;
import net.sf.saxon.type.BuiltInAtomicType;
import net.sf.saxon.value.AtomicValue;
import net.sf.saxon.value.StringValue;
import java.net.URI;
import java.net.URISyntaxException;
/**
* Abstract superclass for all functions that take an optional collation argument
*/
// Supports string comparison using a collation
public abstract class CollatingFunction extends SystemFunctionCall {
// The default collation, or if it is known statically, the actual collation requested in the call
protected StringCollator staticCollation = null;
// The base URI from the static context
private URI expressionBaseURI = null;
// The AtomicComparer to be used if known statically (not used by all collating functions)
private AtomicComparer atomicComparer = null;
/**
* Bind aspects of the static context on which the particular function depends
*
* @param env the static context of the function call
* @throws net.sf.saxon.trans.XPathException
* if execution with this static context will inevitably fail
*/
@Override
public void bindStaticContext(StaticContext env) throws XPathException {
expressionBaseURI = ExpressionTool.getBaseURI(env, this, false);
staticCollation = env.getCollation(env.getDefaultCollationName());
}
/**
* Get the argument position (0-based) containing the collation name
* @return the position of the argument containing the collation URI
*/
protected abstract int getCollationArgument();
@Override
public void checkArguments(ExpressionVisitor visitor) throws XPathException {
super.checkArguments(visitor);
preEvaluateCollation(visitor.getStaticContext());
}
/**
* Get the saved static base URI
* @return the static base URI
*/
public URI getExpressionBaseURI() {
return expressionBaseURI;
}
/**
* Get the collation if known statically, as a StringCollator object
* @return a StringCollator. Return null if the collation is not known statically.
*/
public StringCollator getStaticCollation() {
return staticCollation;
}
/**
* Pre-evaluate the collation argument if its value is known statically
* @throws XPathException if execution of the function is bound to fail
*/
private void preEvaluateCollation(StaticContext env) throws XPathException {
if ((getDetails().properties & StandardFunction.DCOLL) == 0) {
final Expression collationExp = argument[getNumberOfArguments() - 1];
final GroundedValue collationVal = (collationExp instanceof Literal ? ((Literal)collationExp).getValue() : null);
if (collationVal instanceof AtomicValue) {
// Collation is supplied as a constant
String collationName = ((AtomicValue)collationVal).getStringValue();
URI collationURI;
try {
collationURI = new URI(collationName);
if (!collationURI.isAbsolute()) {
if (expressionBaseURI == null) {
XPathException err = new XPathException("The collation name is a relative URI, but the base URI is unknown");
err.setErrorCode("XPST0001");
err.setIsStaticError(true);
err.setLocator(this);
throw err;
}
URI base = expressionBaseURI;
collationURI = base.resolve(collationURI);
}
collationName = collationURI.toString();
StringCollator selectedCollation = env.getCollation(collationName);
if (selectedCollation == null) {
XPathException err = new XPathException("Unknown collation " + Err.wrap(collationName, Err.URI));
err.setErrorCode("FOCH0002");
//err.setIsStaticError(true);
err.setLocator(this);
throw err;
} else {
staticCollation = selectedCollation;
}
// following lines removed 28/1/2013: breaks JUnit test CzechCollation
// Expression[] args = new Expression[argument.length - 1];
// System.arraycopy(argument, 0, args, 0, args.length);
// argument = args;
} catch (URISyntaxException e) {
XPathException err = new XPathException("Collation name '" + collationName + "' is not a valid URI");
err.setErrorCode("FOCH0002");
//err.setIsStaticError(true);
err.setLocator(this);
throw err;
}
} else {
staticCollation = null;
// collation isn't known until run-time
}
} else {
// Use the default collation : no action needed
}
}
/**
* Copy an expression. This makes a deep copy.
* @return the copy of the original expression
*/
/*@NotNull*/
public Expression copy() {
CollatingFunction d = (CollatingFunction)super.copy();
d.expressionBaseURI = expressionBaseURI;
d.staticCollation = staticCollation;
d.atomicComparer = atomicComparer;
return d;
}
/**
* Determine whether two expressions are equivalent
*/
@Override
public boolean equals(Object o) {
return o instanceof CollatingFunction &&
super.equals(o) &&
equalOrNull(expressionBaseURI, ((CollatingFunction)o).expressionBaseURI) &&
equalOrNull(staticCollation, ((CollatingFunction)o).staticCollation);
}
/**
* Get a collator suitable for comparing strings. Returns the collator specified in the
* given function argument if present, otherwise returns the default collator. This method is
* called by subclasses at run time.
*
* This method is used only when evaluating static function calls.
*
*
* @param context The dynamic context
* @return a StringCollator
* @throws XPathException if a failure occurs evaluating the collation argument, or if the
* specified collation is not recognized
*/
public StringCollator getCollator(XPathContext context) throws XPathException {
if (staticCollation != null) {
return staticCollation;
}
int arg = getCollationArgument();
AtomicValue av = (AtomicValue) argument[arg].evaluateItem(context);
StringValue collationValue = (StringValue) av;
String collationName = collationValue.getStringValue();
try {
collationName = expandCollationURI(collationName, expressionBaseURI);
} catch (XPathException err) {
err.setLocator(this);
}
StringCollator collator = context.getCollation(collationName);
if (collator == null) {
throw new XPathException("Unrecognized collation: " + collationName, "FOCH0002");
}
return collator;
}
/**
* Get a collator suitable for comparing strings. Returns the collator specified in the
* given function argument if present, otherwise returns the default collator. This method is
* called by subclasses at run time.
*
* This method is used only when evaluating dynamic function calls.
*
* @param arguments The arguments supplied to the function call
* @param arg The position of the argument (base 0) containing the collation name
* @param context The dynamic context
* @return a StringCollator
* @throws XPathException if a failure occurs evaluating the collation argument, or if the
* specified collation is not recognized
*/
protected StringCollator getCollatorFromLastArgument(Sequence[] arguments, int arg, XPathContext context)
throws XPathException {
if (arguments.length > arg) {
// collation is specified explicitly in the call
String collationName = arguments[arg].head().getStringValue();
try {
collationName = expandCollationURI(collationName, expressionBaseURI);
} catch (XPathException err) {
err.setLocator(this);
}
return context.getCollation(collationName);
} else {
// use the default collation
return staticCollation;
}
}
/**
* Expand a collation URI, which may be a relative URI reference
*
* @param collationName the collation URI as provided
* @param expressionBaseURI the base URI against which the collation URI will be resolved if it is relative
* @return the resolved (expanded) absolute collation URI
* @throws XPathException if the collation URI cannot be resolved
*/
public static String expandCollationURI(String collationName, URI expressionBaseURI) throws XPathException {
try {
URI collationURI = new URI(collationName);
if (!collationURI.isAbsolute()) {
if (expressionBaseURI == null) {
throw new XPathException("Cannot resolve relative collation URI '" + collationName +
"': unknown or invalid base URI", "FOCH0002");
}
collationURI = expressionBaseURI.resolve(collationURI);
collationName = collationURI.toString();
}
} catch (URISyntaxException e) {
throw new XPathException("Collation name '" + collationName + "' is not a valid URI", "FOCH0002");
}
return collationName;
}
/**
* During static analysis, if types are known and the collation is known, pre-allocate a comparer
* for comparing atomic values. Called by some collating functions during type-checking
* @param type0 the type of the first comparand
* @param type1 the type of the second comparand
* @param env the static context
* @param NaNequalsNaN true if two NaN values are to be considered equal
*/
protected void preAllocateComparer(AtomicType type0, AtomicType type1, StaticContext env, boolean NaNequalsNaN) {
StringCollator collation;
if (argument.length <= getCollationArgument()) {
// use the default collation
collation = staticCollation;
} else if (argument[argument.length-1] instanceof StringLiteral) {
// use the collation given as a string literal in the last argument
String collationName = ((StringLiteral)argument[argument.length-1]).getStringValue();
try {
collationName = expandCollationURI(collationName, expressionBaseURI);
collation = env.getCollation(collationName);
if (collation == null) {
throw new XPathException("Unrecognized collation: " + collationName, "FOCH0002");
}
} catch (XPathException err) {
err.setLocator(this);
argument[argument.length-1] = new ErrorExpression(err);
return;
}
} else {
return;
}
if (NaNequalsNaN) {
atomicComparer = AtomicSortComparer.makeSortComparer(
collation, type0.getPrimitiveType(), env.makeEarlyEvaluationContext());
} else {
atomicComparer = GenericAtomicComparer.makeAtomicComparer(
(BuiltInAtomicType)type0.getBuiltInBaseType(), (BuiltInAtomicType)type1.getBuiltInBaseType(),
staticCollation, env.makeEarlyEvaluationContext());
}
}
/**
* Get the pre-allocated atomic comparer, if available
* @return the preallocated atomic comparer, or null
*/
public AtomicComparer getPreAllocatedAtomicComparer() {
return atomicComparer;
}
/**
* During evaluation, get the pre-allocated atomic comparer if available, or allocate a new one otherwise
* @return the pre-allocated comparer if one is available; otherwise, a newly allocated one, using the specified
* StringCollator for comparing strings
*/
protected AtomicComparer getAtomicComparer(StringCollator collator, XPathContext context) {
if (atomicComparer != null) {
return atomicComparer.provideContext(context);
} else {
return new GenericAtomicComparer(collator, context);
}
}
}