
net.sf.saxon.expr.instruct.ValueOf 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.expr.instruct;
import net.sf.saxon.Controller;
import net.sf.saxon.event.ReceiverOptions;
import net.sf.saxon.event.SequenceReceiver;
import net.sf.saxon.expr.*;
import net.sf.saxon.expr.parser.ExpressionVisitor;
import net.sf.saxon.functions.StringFn;
import net.sf.saxon.functions.SystemFunctionCall;
import net.sf.saxon.om.GroundedValue;
import net.sf.saxon.om.Item;
import net.sf.saxon.om.NodeInfo;
import net.sf.saxon.om.StandardNames;
import net.sf.saxon.pattern.NodeKindTest;
import net.sf.saxon.trace.ExpressionPresenter;
import net.sf.saxon.trans.Err;
import net.sf.saxon.trans.XPathException;
import net.sf.saxon.tree.util.Orphan;
import net.sf.saxon.type.*;
import net.sf.saxon.value.Cardinality;
import net.sf.saxon.value.StringValue;
import net.sf.saxon.value.Whitespace;
/**
* An xsl:value-of element in the stylesheet.
* The xsl:value-of element takes attributes:
* - a mandatory attribute select="expression".
* This must be a valid String expression
* - an optional disable-output-escaping attribute, value "yes" or "no"
* - an optional separator attribute. This is handled at compile-time: if the separator attribute
* is present, the select expression passed in here will be a call to the string-join() function.
*
*/
public final class ValueOf extends SimpleNodeConstructor {
private int options;
private boolean isNumberingInstruction = false; // set to true if generated by xsl:number
private boolean noNodeIfEmpty;
/**
* Create a new ValueOf expression
* @param select the select expression
* @param disable true if disable-output-escaping is in force
* @param noNodeIfEmpty true if the instruction is to return () if the select expression is (),
* false if it is to return an empty text node
*/
public ValueOf(Expression select, boolean disable, boolean noNodeIfEmpty) {
this.select = select;
options = (disable ? ReceiverOptions.DISABLE_ESCAPING : 0);
this.noNodeIfEmpty = noNodeIfEmpty;
adoptChildExpression(select);
// If value is fixed, test whether there are any special characters that might need to be
// escaped when the time comes for serialization
if (select instanceof StringLiteral) {
boolean special = false;
CharSequence val = ((StringLiteral)select).getStringValue();
for (int k=0; k126 ||
c=='<' || c=='>' || c=='&') {
special = true;
break;
}
}
if (!special) {
options |= ReceiverOptions.NO_SPECIAL_CHARS;
}
}
}
/**
* Indicate that this is really an xsl:nunber instruction
*/
public void setIsNumberingInstruction() {
isNumberingInstruction = true;
}
/**
* Determine whether this is really an xsl:number instruction
* @return true if this derives from xsl:number
*/
public boolean isNumberingInstruction() {
return isNumberingInstruction;
}
public boolean isNoNodeIfEmpty(){
return noNodeIfEmpty;
}
/**
* Get the name of this instruction for diagnostic and tracing purposes
* @return the namecode of the instruction name
*/
public int getInstructionNameCode() {
if (isNumberingInstruction) {
return StandardNames.XSL_NUMBER;
} else if (select instanceof StringLiteral) {
return StandardNames.XSL_TEXT;
} else {
return StandardNames.XSL_VALUE_OF;
}
}
/**
* Test for any special options such as disable-output-escaping
* @return any special options
*/
public int getOptions() {
return options;
}
/**
* Test whether disable-output-escaping was requested
* @return true if disable-output-escaping was requested
*/
public boolean isDisableOutputEscaping() {
return (options & ReceiverOptions.DISABLE_ESCAPING) != 0;
}
/*@NotNull*/
public ItemType getItemType(TypeHierarchy th) {
return NodeKindTest.TEXT;
}
public int computeCardinality() {
if (noNodeIfEmpty) {
return StaticProperty.ALLOWS_ZERO_OR_ONE;
} else {
return StaticProperty.EXACTLY_ONE;
}
}
public void localTypeCheck(ExpressionVisitor visitor, ExpressionVisitor.ContextItemType contextItemType) {
//
}
/**
* Copy an expression. This makes a deep copy.
*
* @return the copy of the original expression
*/
/*@NotNull*/
public Expression copy() {
ValueOf exp = new ValueOf(select.copy(), (options&ReceiverOptions.DISABLE_ESCAPING) != 0 , noNodeIfEmpty);
if (isNumberingInstruction) {
exp.setIsNumberingInstruction();
}
return exp;
}
/**
* Check statically that the results of the expression are capable of constructing the content
* of a given schema type.
*
* @param parentType The schema type
* @param env the static context
* @param whole true if this expression is to account for the whole value of the type
* @throws net.sf.saxon.trans.XPathException
* if the expression doesn't match the required content type
*/
public void checkPermittedContents(SchemaType parentType, StaticContext env, boolean whole) throws XPathException {
// if the expression is a constant value, check that it is valid for the type
if (select instanceof Literal) {
GroundedValue selectValue = ((Literal)select).getValue();
SimpleType stype = null;
if (parentType instanceof SimpleType && whole) {
stype = (SimpleType)parentType;
} else if (parentType instanceof ComplexType && ((ComplexType)parentType).isSimpleContent()) {
stype = ((ComplexType)parentType).getSimpleContentType();
}
if (whole && stype != null && !stype.isNamespaceSensitive()) {
// Can't validate namespace-sensitive content statically
ValidationFailure err = stype.validateContent(
selectValue.getStringValue(), null, env.getConfiguration().getConversionRules());
if (err != null) {
err.setLocator(this);
err.setErrorCode(isXSLT() ? "XTTE1540" : "XQDY0027");
throw err.makeException();
}
return;
}
if (parentType instanceof ComplexType &&
!((ComplexType)parentType).isSimpleContent() &&
!((ComplexType)parentType).isMixedContent() &&
!Whitespace.isWhite(selectValue.getStringValue())) {
XPathException err = new XPathException("The containing element must be of type " + parentType.getDescription() +
", which does not allow text content " +
Err.wrap(selectValue.getStringValue()));
err.setLocator(this);
err.setIsTypeError(true);
throw err;
}
} else {
// check that the type allows text nodes. If not, this is a warning condition, since the text
// node might turn out to be whitespace
if (parentType instanceof ComplexType &&
!((ComplexType)parentType).isSimpleContent() &&
!((ComplexType)parentType).isMixedContent()) {
env.issueWarning("The containing element must be of type " + parentType.getDescription() +
", which does not allow text content other than whitespace", this);
}
}
}
/**
* Convert this value-of instruction to an expression that delivers the string-value of the resulting
* text node as an untyped atomic value.
* @return the converted expression
*/
public Expression convertToCastAsString() {
if (noNodeIfEmpty || !Cardinality.allowsZero(select.getCardinality())) {
return new CastExpression(select, BuiltInAtomicType.UNTYPED_ATOMIC, true);
} else {
// must return zero-length string rather than () if empty
StringFn sf = (StringFn) SystemFunctionCall.makeSystemFunction("string", new Expression[]{select});
return new CastExpression(sf, BuiltInAtomicType.UNTYPED_ATOMIC, false);
}
}
/**
* Process this instruction
* @param context the dynamic context of the transformation
* @return a TailCall to be executed by the caller, always null for this instruction
*/
/*@Nullable*/ public TailCall processLeavingTail(XPathContext context) throws XPathException {
if (noNodeIfEmpty) {
StringValue value = (StringValue)select.evaluateItem(context);
if (value != null) {
processValue(value.getStringValueCS(), context);
}
return null;
} else {
return super.processLeavingTail(context);
}
}
/**
* Process the value of the node, to create the new node.
* @param value the string value of the new node
* @param context the dynamic evaluation context
* @throws net.sf.saxon.trans.XPathException
*
*/
public void processValue(CharSequence value, XPathContext context) throws XPathException {
SequenceReceiver out = context.getReceiver();
out.characters(value, locationId, options);
}
/**
* Evaluate this expression, returning the resulting text node to the caller
* @param context the dynamic evaluation context
* @return the parentless text node that results from evaluating this instruction, or null to
* represent an empty sequence
* @throws XPathException
*/
public NodeInfo evaluateItem(XPathContext context) throws XPathException {
try {
CharSequence val;
Item item = select.evaluateItem(context);
if (item == null) {
if (noNodeIfEmpty) {
return null;
} else {
val = "";
}
} else {
val = item.getStringValueCS();
}
Controller controller = context.getController();
assert controller != null;
Orphan o = new Orphan(controller.getConfiguration());
o.setNodeKind(Type.TEXT);
o.setStringValue(val);
return o;
} catch (XPathException err) {
err.maybeSetLocation(this);
throw err;
}
}
/**
* Diagnostic print of expression structure. The abstract expression tree
* is written to the supplied output destination.
*/
public void explain(ExpressionPresenter out) {
out.startElement("valueOf");
getContentExpression().explain(out);
out.endElement();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy