All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.saxon.expr.instruct.ValueOf Maven / Gradle / Ivy

There is a newer version: 10.5
Show newest version
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 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