001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.Err;
004: import net.sf.saxon.event.ReceiverOptions;
005: import net.sf.saxon.event.SequenceReceiver;
006: import net.sf.saxon.expr.*;
007: import net.sf.saxon.om.Item;
008: import net.sf.saxon.om.NamePool;
009: import net.sf.saxon.om.Orphan;
010: import net.sf.saxon.pattern.NodeKindTest;
011: import net.sf.saxon.style.StandardNames;
012: import net.sf.saxon.trans.StaticError;
013: import net.sf.saxon.trans.XPathException;
014: import net.sf.saxon.type.*;
015: import net.sf.saxon.value.StringValue;
016: import net.sf.saxon.value.Value;
017: import net.sf.saxon.value.Whitespace;
018:
019: import java.io.PrintStream;
020:
021: /**
022: * An xsl:value-of element in the stylesheet. <br>
023: * The xsl:value-of element takes attributes:<ul>
024: * <li>a mandatory attribute select="expression".
025: * This must be a valid String expression</li>
026: * <li>an optional disable-output-escaping attribute, value "yes" or "no"</li>
027: * <li>an optional separator attribute. This is handled at compile-time: if the separator attribute
028: * is present, the select expression passed in here will be a call to the string-join() function.</li>
029: * </ul>
030: */
031:
032: public final class ValueOf extends SimpleNodeConstructor {
033:
034: private int options;
035: private boolean isNumberingInstruction = false; // set to true if generated by xsl:number
036: private boolean noNodeIfEmpty;
037:
038: public ValueOf(Expression select, boolean disable,
039: boolean noNodeIfEmpty) {
040: this .select = select;
041: this .options = (disable ? ReceiverOptions.DISABLE_ESCAPING : 0);
042: this .noNodeIfEmpty = noNodeIfEmpty;
043: adoptChildExpression(select);
044:
045: // If value is fixed, test whether there are any special characters that might need to be
046: // escaped when the time comes for serialization
047: if (select instanceof StringValue) {
048: boolean special = false;
049: CharSequence val = ((StringValue) select)
050: .getStringValueCS();
051: for (int k = 0; k < val.length(); k++) {
052: char c = val.charAt(k);
053: if ((int) c < 33 || (int) c > 126 || c == '<'
054: || c == '>' || c == '&') {
055: special = true;
056: break;
057: }
058: }
059: if (!special) {
060: this .options |= ReceiverOptions.NO_SPECIAL_CHARS;
061: }
062: }
063: }
064:
065: /**
066: * Indicate that this is really an xsl:nunber instruction
067: */
068:
069: public void setIsNumberingInstruction() {
070: isNumberingInstruction = true;
071: }
072:
073: /**
074: * Get the name of this instruction for diagnostic and tracing purposes
075: */
076:
077: public int getInstructionNameCode() {
078: if (isNumberingInstruction) {
079: return StandardNames.XSL_NUMBER;
080: } else if (select instanceof StringValue) {
081: return StandardNames.XSL_TEXT;
082: } else {
083: return StandardNames.XSL_VALUE_OF;
084: }
085: }
086:
087: /**
088: * Offer promotion for subexpressions. The offer will be accepted if the subexpression
089: * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
090: * By default the offer is not accepted - this is appropriate in the case of simple expressions
091: * such as constant values and variable references where promotion would give no performance
092: * advantage. This method is always called at compile time.
093: *
094: * @param offer details of the offer, for example the offer to move
095: * expressions that don't depend on the context to an outer level in
096: * the containing expression
097: * @exception XPathException if any error is detected
098: */
099:
100: protected void promoteInst(PromotionOffer offer)
101: throws XPathException {
102: super .promoteInst(offer);
103: }
104:
105: public ItemType getItemType(TypeHierarchy th) {
106: return NodeKindTest.TEXT;
107: }
108:
109: public int computeCardinality() {
110: if (noNodeIfEmpty) {
111: return StaticProperty.ALLOWS_ZERO_OR_ONE;
112: } else {
113: return StaticProperty.EXACTLY_ONE;
114: }
115: }
116:
117: public void localTypeCheck(StaticContext env,
118: ItemType contextItemType) {
119:
120: }
121:
122: /**
123: * Check statically that the results of the expression are capable of constructing the content
124: * of a given schema type.
125: *
126: * @param parentType The schema type
127: * @param env the static context
128: * @param whole
129: * @throws net.sf.saxon.trans.XPathException
130: * if the expression doesn't match the required content type
131: */
132:
133: public void checkPermittedContents(SchemaType parentType,
134: StaticContext env, boolean whole) throws XPathException {
135: // if the expression is a constant value, check that it is valid for the type
136: if (select instanceof Value) {
137: SimpleType stype = null;
138: if (parentType instanceof SimpleType && whole) {
139: stype = (SimpleType) parentType;
140: } else if (parentType instanceof ComplexType
141: && ((ComplexType) parentType).isSimpleContent()) {
142: stype = ((ComplexType) parentType)
143: .getSimpleContentType();
144: }
145: if (whole && stype != null && !stype.isNamespaceSensitive()) {
146: // Can't validate namespace-sensitive content statically
147: XPathException err = stype.validateContent(
148: ((Value) select).getStringValue(), null, env
149: .getConfiguration().getNameChecker());
150: if (err != null) {
151: err.setLocator(this );
152: throw err;
153: }
154: return;
155: }
156: if (parentType instanceof ComplexType
157: && !((ComplexType) parentType).isSimpleContent()
158: && !((ComplexType) parentType).isMixedContent()
159: && !Whitespace.isWhite(((Value) select)
160: .getStringValue())) {
161: StaticError err = new StaticError("Complex type "
162: + parentType.getDescription()
163: + " does not allow text content "
164: + Err.wrap(((Value) select).getStringValue()));
165: err.setLocator(this );
166: err.setIsTypeError(true);
167: throw err;
168: }
169: }
170: }
171:
172: public TailCall processLeavingTail(XPathContext context)
173: throws XPathException {
174: SequenceReceiver out = context.getReceiver();
175: Item item = select.evaluateItem(context);
176: if (item != null) {
177: out
178: .characters(item.getStringValueCS(), locationId,
179: options);
180: }
181: return null;
182: }
183:
184: public Item evaluateItem(XPathContext context)
185: throws XPathException {
186: try {
187: CharSequence val;
188: Item item = select.evaluateItem(context);
189: if (item == null) {
190: if (noNodeIfEmpty) {
191: return null;
192: } else {
193: val = "";
194: }
195: } else {
196: val = item.getStringValueCS();
197: }
198: Orphan o = new Orphan(context.getController()
199: .getConfiguration());
200: o.setNodeKind(Type.TEXT);
201: o.setStringValue(val);
202: return o;
203: } catch (XPathException err) {
204: if (err.getLocator() == null) {
205: err.setLocator(this );
206: }
207: throw err;
208: }
209: }
210:
211: /**
212: * Display this instruction as an expression, for diagnostics
213: */
214:
215: public void display(int level, NamePool pool, PrintStream out) {
216: out.println(ExpressionTool.indent(level) + "value-of");
217: select.display(level + 1, pool, out);
218: }
219: }
220:
221: //
222: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
223: // you may not use this file except in compliance with the License. You may obtain a copy of the
224: // License at http://www.mozilla.org/MPL/
225: //
226: // Software distributed under the License is distributed on an "AS IS" basis,
227: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
228: // See the License for the specific language governing rights and limitations under the License.
229: //
230: // The Original Code is: all this file.
231: //
232: // The Initial Developer of the Original Code is Michael H. Kay.
233: //
234: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
235: //
236: // Contributor(s): none.
237: //
|