package client.net.sf.saxon.ce.functions;
import client.net.sf.saxon.ce.expr.*;
import client.net.sf.saxon.ce.lib.NamespaceConstant;
import client.net.sf.saxon.ce.om.StructuredQName;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.AnyItemType;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.type.TypeHierarchy;
import client.net.sf.saxon.ce.value.SequenceType;
/**
* Abstract superclass for system-defined and user-defined functions
*/
public abstract class SystemFunction extends FunctionCall {
/**
* Make a system function call (one in the standard function namespace).
* @param name The local name of the function.
* @param arguments the arguments to the function call
* @return a FunctionCall that implements this function, if it
* exists, or null if the function is unknown.
*/
public static FunctionCall makeSystemFunction(String name, Expression[] arguments) {
StandardFunction.Entry entry = StandardFunction.getFunction(name, arguments.length);
if (entry==null) {
return null;
}
SystemFunction f = entry.skeleton.newInstance();
f.setDetails(entry);
f.setFunctionName(new StructuredQName("", NamespaceConstant.FN, name));
f.setArguments(arguments);
return f;
}
private StandardFunction.Entry details;
protected int operation;
/**
* Set the details of this type of function
* @param entry information giving details of the function signature
*/
public void setDetails(StandardFunction.Entry entry) {
details = entry;
}
/**
* Get the details of the function signature
* @return information about the function signature
*/
public StandardFunction.Entry getDetails() {
return details;
}
/**
* Method called during static type checking
*/
public void checkArguments(ExpressionVisitor visitor) throws XPathException {
checkArgumentCount(details.minArguments, details.maxArguments, visitor);
for (int i=0; i<argument.length; i++) {
checkArgument(i, visitor);
}
}
/**
* Perform static type checking on an argument to a function call, and add
* type conversion logic where necessary.
* @param arg argument number, zero-based
* @param visitor an expression visitor
*/
private void checkArgument(int arg, ExpressionVisitor visitor) throws XPathException {
RoleLocator role = new RoleLocator(RoleLocator.FUNCTION,
getFunctionName(), arg);
//role.setSourceLocator(this);
role.setErrorCode(getErrorCodeForTypeErrors());
argument[arg] = TypeChecker.staticTypeCheck(
argument[arg],
getRequiredType(arg),
visitor.getStaticContext().isInBackwardsCompatibleMode(),
role, visitor);
}
/**
* Copy an expression. This makes a deep copy.
*
* @return the copy of the original expression
*/
public abstract SystemFunction newInstance();
/**
* Return the error code to be used for type errors. This is overridden for functions
* such as exactly-one(), one-or-more(), ...
* @return the error code to be used for type errors in the function call. Normally XPTY0004,
* but different codes are used for functions such as exactly-one()
*/
public String getErrorCodeForTypeErrors() {
return "XPTY0004";
}
/**
* Get the required type of the nth argument
* @param arg the number of the argument whose type is requested, zero-based
* @return the required type of the argument as defined in the function signature
*/
protected SequenceType getRequiredType(int arg) {
if (details == null) {
return SequenceType.ANY_SEQUENCE;
}
return details.argumentTypes[arg];
// this is overridden for concat()
}
/**
* Determine the item type of the value returned by the function
* @param th the type hierarchy cache
*/
public ItemType getItemType(TypeHierarchy th) {
ItemType type = details.resultType.getPrimaryType();
if (details.sameItemTypeAsFirstArgument) {
if (argument.length > 0) {
return argument[0].getItemType(th);
} else {
return AnyItemType.getInstance();
// if there is no first argument, an error will be reported
}
} else {
return type;
}
}
/**
* Determine the cardinality of the function.
*/
public int computeCardinality() {
return details.resultType.getCardinality();
}
/**
* Determine the special properties of this expression. The general rule
* is that a system function call is non-creative if its return type is
* atomic, or if all its arguments are non-creative. This is overridden
* for the generate-id() function, which is considered creative if
* its operand is creative (because the result depends on the
* identity of the operand)
*/
public int computeSpecialProperties() {
int p = super.computeSpecialProperties();
if (details == null) {
return p;
}
if (details.resultType.getPrimaryType().isAtomicType()) {
return p | StaticProperty.NON_CREATIVE;
}
for (int i=0; i<argument.length; i++) {
if ((argument[i].getSpecialProperties() & StaticProperty.NON_CREATIVE) == 0) {
// the argument is creative
return p;
}
}
return p | StaticProperty.NON_CREATIVE;
}
/**
* Set "." as the default value for the first and only argument. Called from subclasses.
*/
protected final void useContextItemAsDefault() {
if (argument.length==0) {
argument = new Expression[1];
argument[0] = new ContextItemExpression();
ExpressionTool.copyLocationInfo(this, argument[0]);
resetLocalStaticProperties();
}
// Note that the extra argument is added before type-checking takes place. The
// type-checking will add any necessary checks to ensure that the context item
// is a node, in cases where this is required.
}
/**
* Add an implicit argument referring to the context document. Called by functions such as
* id() and key() that take the context document as an implicit argument
* @param pos the position of the argument whose default value is ".", zero-based
* @param augmentedName the name to be used for the function call with its extra argument.
* There are some cases where user function calls cannot supply the argument directly (notably
* unparsed-entity-uri() and unparsed-entity-public-id()) and in these cases a synthesized
* function name is used for the new function call.
*/
protected final void addContextDocumentArgument(int pos, String augmentedName)
throws XPathException {
if (argument.length > pos) {
return;
// this can happen during optimization, if the extra argument is already present
}
if (argument.length != pos) {
throw new XPathException("Too few arguments in call to " + augmentedName + "() function");
}
Expression[] newArgs = new Expression[pos+1];
System.arraycopy(argument, 0, newArgs, 0, argument.length);
RootExpression rootExpression = new RootExpression();
ExpressionTool.copyLocationInfo(this, rootExpression);
newArgs[pos] = rootExpression;
argument = newArgs;
setDetails(StandardFunction.getFunction(augmentedName, newArgs.length));
}
}
// 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.