
net.sf.saxon.expr.instruct.NumberInstruction Maven / Gradle / Ivy
Show all versions of Saxon-HE Show documentation
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2015 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.expr.instruct;
import net.sf.saxon.Configuration;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.number.NumberFormatter;
import net.sf.saxon.expr.parser.ContextItemStaticInfo;
import net.sf.saxon.expr.parser.ExpressionTool;
import net.sf.saxon.expr.parser.ExpressionVisitor;
import net.sf.saxon.expr.parser.PromotionOffer;
import net.sf.saxon.functions.Number_1;
import net.sf.saxon.lib.ConversionRules;
import net.sf.saxon.lib.Numberer;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.SequenceIterator;
import net.sf.saxon.pattern.Pattern;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.util.Navigator;
import net.sf.saxon.type.*;
import net.sf.saxon.value.*;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* An xsl:number element in the stylesheet. Although this is an XSLT instruction, it is compiled
* into an expression, evaluated using xsl:value-of to create the resulting text node.
*/
public class NumberInstruction extends Expression {
public static final int SINGLE = 0;
public static final int MULTI = 1;
public static final int ANY = 2;
public static final int SIMPLE = 3;
public static final String[] LEVEL_NAMES = new String[]{"single", "multi", "any", "simple"};
private Operand selectOp;
private Operand valueOp;
private Operand formatOp;
private Operand groupSizeOp;
private Operand groupSeparatorOp;
private Operand letterValueOp;
private Operand ordinalOp;
private Operand startAtOp;
private Operand langOp;
private int level;
/*@Nullable*/ private Pattern count = null;
private Pattern from = null;
private NumberFormatter formatter = null;
private Numberer numberer = null;
private boolean hasVariablesInPatterns;
private boolean backwardsCompatible;
/**
* Construct a NumberInstruction
*
* @param select the expression supplied in the select attribute
* @param level one of "single", "level", "multi"
* @param count the pattern supplied in the count attribute
* @param from the pattern supplied in the from attribute
* @param value the expression supplied in the value attribute
* @param format the expression supplied in the format attribute
* @param groupSize the expression supplied in the group-size attribute
* @param groupSeparator the expression supplied in the grouping-separator attribute
* @param letterValue the expression supplied in the letter-value attribute
* @param ordinal the expression supplied in the ordinal attribute
* @param startAt the expression supplied in the start-at attribute
* @param lang the expression supplied in the lang attribute
* @param formatter A NumberFormatter to be used
* @param hasVariablesInPatterns true if one or more of the patterns contains variable references
* @param backwardsCompatible true if running in 1.0 compatibility mode
*/
public NumberInstruction(Expression select,
int level,
Pattern count,
Pattern from,
Expression value,
Expression format,
Expression groupSize,
Expression groupSeparator,
Expression letterValue,
Expression ordinal,
Expression startAt,
Expression lang,
NumberFormatter formatter,
boolean hasVariablesInPatterns,
boolean backwardsCompatible) {
if (select != null) {
selectOp = new Operand(this, select, OperandRole.SINGLE_ATOMIC);
}
if (value != null) {
valueOp = new Operand(this, value, OperandRole.SINGLE_ATOMIC);
}
if (format != null) {
formatOp = new Operand(this, format, OperandRole.SINGLE_ATOMIC);
}
if (groupSize != null) {
groupSizeOp = new Operand(this, groupSize, OperandRole.SINGLE_ATOMIC);
}
if (groupSeparator != null) {
groupSeparatorOp = new Operand(this, groupSeparator, OperandRole.SINGLE_ATOMIC);
}
if (letterValue != null) {
letterValueOp = new Operand(this, letterValue, OperandRole.SINGLE_ATOMIC);
}
if (ordinal != null) {
ordinalOp = new Operand(this, ordinal, OperandRole.SINGLE_ATOMIC);
}
if (startAt != null) {
startAtOp = new Operand(this, startAt, OperandRole.SINGLE_ATOMIC);
}
if (lang != null) {
langOp = new Operand(this, lang, OperandRole.SINGLE_ATOMIC);
}
this.level = level;
this.count = count;
this.from = from;
this.formatter = formatter;
this.hasVariablesInPatterns = hasVariablesInPatterns;
this.backwardsCompatible = backwardsCompatible;
}
/**
* Simplify an expression. This performs any static optimization (by rewriting the expression
* as a different expression). The default implementation simplifies its operands.
*
* @return the simplified expression (or the original if unchanged, or if modified in-situ)
* @throws net.sf.saxon.trans.XPathException if an error is discovered during expression
* rewriting
*/
@Override
public Expression simplify() throws XPathException {
if (valueOp != null && !valueOp.getChildExpression().getItemType().isPlainType()) {
valueOp.setChildExpression(Atomizer.makeAtomizer(valueOp.getChildExpression()));
}
preallocateNumberer(getConfiguration());
return super.simplify();
}
public void preallocateNumberer(Configuration config) throws XPathException {
if (langOp == null) {
numberer = config.makeNumberer(null, null);
} else {
if (langOp.getChildExpression() instanceof StringLiteral) {
String language = ((StringLiteral) langOp.getChildExpression()).getStringValue();
if (language.length() != 0) {
ValidationFailure vf = StringConverter.STRING_TO_LANGUAGE.validate(language);
if (vf != null) {
langOp.setChildExpression(new StringLiteral(StringValue.EMPTY_STRING));
throw new XPathException("The lang attribute must be a valid language code", "XTDE0030");
}
}
numberer = config.makeNumberer(language, null);
} // else we allocate a numberer at run-time
}
}
/**
* Perform static analysis of an expression and its subexpressions.
*
* This checks statically that the operands of the expression have
* the correct type; if necessary it generates code to do run-time type checking or type
* conversion. A static type error is reported only if execution cannot possibly succeed, that
* is, if a run-time type error is inevitable. The call may return a modified form of the expression.
*
* This method is called after all references to functions and variables have been resolved
* to the declaration of the function or variable. However, the types of such functions and
* variables will only be accurately known if they have been explicitly declared.
*
*
param visitor an expression visitor
* @param contextInfo information about the type of the context item
* @return the original expression, rewritten to perform necessary
* run-time type checks, and to perform other type-related
* optimizations
* @throws XPathException if an error is discovered during this phase
* (typically a type error)
*/
/*@NotNull*/
public Expression typeCheck(ExpressionVisitor visitor, ContextItemStaticInfo contextInfo) throws XPathException {
typeCheckChildren(visitor, contextInfo);
if (selectOp == null && valueOp == null) {
// we are numbering the context node
XPathException err = null;
if (contextInfo == null || contextInfo.getItemType() == null) {
err = new XPathException(
"xsl:number requires a select attribute, a value attribute, or a context item");
} else if (contextInfo.getItemType().isPlainType()) {
err = new XPathException(
"xsl:number requires the context item to be a node, but it is an atomic value");
}
if (err != null) {
err.setIsTypeError(true);
err.setErrorCode("XTTE0990");
err.setLocation(getLocation());
throw err;
}
}
// if (count != null) {
// new count.typeCheck(visitor, contextInfo);
// }
// if (from != null) {
// new PatternSponsor(from).typeCheck(visitor, contextInfo);
// }
return this;
}
/**
* Perform optimisation of an expression and its subexpressions.
*
* This method is called after all references to functions and variables have been resolved
* to the declaration of the function or variable, and after all type checking has been done.
*
* @param visitor an expression visitor
* @param contextInfo the static type of "." at the point where this expression is invoked.
* The parameter is set to null if it is known statically that the context item will be undefined.
* If the type of the context item is not known statically, the argument is set to
* {@link net.sf.saxon.type.Type#ITEM_TYPE}
* @return the original expression, rewritten if appropriate to optimize execution
* @throws XPathException if an error is discovered during this phase
* (typically a type error)
*/
/*@NotNull*/
public Expression optimize(ExpressionVisitor visitor, ContextItemStaticInfo contextInfo) throws XPathException {
optimizeChildren(visitor, contextInfo);
return this;
}
@Override
public Iterable operands() {
List sub = new ArrayList(9);
for (Operand o : operandSparseList(selectOp, valueOp, formatOp, groupSizeOp,
groupSeparatorOp, letterValueOp, ordinalOp, startAtOp, langOp)) {
sub.add(o);
}
if (count != null) {
sub.add(new Operand(this, count, OperandRole.INSPECT));
}
if (from != null) {
sub.add(new Operand(this, from, OperandRole.INSPECT));
}
return sub;
}
/**
* Copy an expression. This makes a deep copy.
*
* @return the copy of the original expression
*/
/*@NotNull*/
public Expression copy() {
NumberInstruction exp = new NumberInstruction(
copy(selectOp), level, count, from, copy(valueOp), copy(formatOp),
copy(groupSizeOp), copy(groupSeparatorOp), copy(letterValueOp),
copy(ordinalOp), copy(startAtOp),
copy(langOp), formatter, hasVariablesInPatterns, backwardsCompatible);
ExpressionTool.copyLocationInfo(this, exp);
return exp;
// TODO: copy the patterns (level and count)
}
private Expression copy(Operand op) {
return op == null ? null : op.getChildExpression().copy();
}
/**
* Determine the intrinsic dependencies of an expression, that is, those which are not derived
* from the dependencies of its subexpressions. For example, position() has an intrinsic dependency
* on the context position, while (position()+1) does not. The default implementation
* of the method returns 0, indicating "no dependencies".
*
* @return a set of bit-significant flags identifying the "intrinsic"
* dependencies. The flags are documented in class net.sf.saxon.value.StaticProperty
*/
public int getIntrinsicDependencies() {
return selectOp == null ? StaticProperty.DEPENDS_ON_CONTEXT_ITEM : 0;
}
/*@NotNull*/
public ItemType getItemType() {
return BuiltInAtomicType.STRING;
}
public int computeCardinality() {
return StaticProperty.EXACTLY_ONE;
}
/**
* An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
* This method indicates which of these methods is provided directly. The other methods will always be available
* indirectly, using an implementation that relies on one of the other methods.
*
* @return the implementation method, for example {@link #ITERATE_METHOD} or {@link #EVALUATE_METHOD} or
* {@link #PROCESS_METHOD}
*/
@Override
public int getImplementationMethod() {
return EVALUATE_METHOD;
}
/**
* Offer promotion for this subexpression. The offer will be accepted if the subexpression
* is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
* By default the offer is not accepted - this is appropriate in the case of simple expressions
* such as constant values and variable references where promotion would give no performance
* advantage. This method is always called at compile time.
*
* @param offer details of the offer, for example the offer to move
* expressions that don't depend on the context to an outer level in
* the containing expression
* @throws net.sf.saxon.trans.XPathException
* if any error is detected
*/
public void promoteChildren(PromotionOffer offer) throws XPathException {
super.promoteChildren(offer);
if (count != null) {
count.promote(offer, this);
}
if (from != null) {
from.promote(offer, this);
}
}
public Item evaluateItem(XPathContext context) throws XPathException {
long value = -1;
List