001: package net.sf.saxon.instruct;
002:
003: import net.sf.saxon.expr.*;
004: import net.sf.saxon.om.*;
005: import net.sf.saxon.trans.XPathException;
006: import net.sf.saxon.type.ItemType;
007: import net.sf.saxon.type.Type;
008: import net.sf.saxon.type.TypeHierarchy;
009: import net.sf.saxon.value.AtomicValue;
010: import net.sf.saxon.value.Cardinality;
011: import net.sf.saxon.value.StringValue;
012:
013: import java.io.PrintStream;
014: import java.util.Iterator;
015:
016: /**
017: * This class implements the rules for an XSLT simple content constructor, which are used in constructing
018: * the string value of an attribute node, text node, comment node, etc, from the value of the select
019: * expression or the contained sequence constructor.
020: */
021:
022: public class SimpleContentConstructor extends ComputedExpression {
023:
024: Expression select;
025: Expression separator;
026: boolean isSingleton = false;
027: boolean isAtomic = false;
028:
029: public SimpleContentConstructor(Expression select,
030: Expression separator) {
031: this .select = select;
032: this .separator = separator;
033: adoptChildExpression(select);
034: adoptChildExpression(separator);
035: }
036:
037: /**
038: * Compute the cardinality of the result of the expression.
039: * @return the cardinality, @link {StaticProperty.EXACTLY_ONE}
040: */
041:
042: protected int computeCardinality() {
043: return StaticProperty.EXACTLY_ONE;
044:
045: }
046:
047: public Expression typeCheck(StaticContext env,
048: ItemType contextItemType) throws XPathException {
049: select = select.typeCheck(env, contextItemType);
050: separator = separator.typeCheck(env, contextItemType);
051: if (!Cardinality.allowsMany(select.getCardinality())) {
052: isSingleton = true;
053: }
054: final TypeHierarchy th = env.getNamePool().getTypeHierarchy();
055: if (select.getItemType(th).isAtomicType()) {
056: isAtomic = true;
057: }
058: return this ;
059: }
060:
061: public Expression optimize(Optimizer opt, StaticContext env,
062: ItemType contextItemType) throws XPathException {
063: select = select.optimize(opt, env, contextItemType);
064: separator = separator.optimize(opt, env, contextItemType);
065: return this ;
066: }
067:
068: /**
069: * Determine the data type of the expression, if possible. All expression return
070: * sequences, in general; this method determines the type of the items within the
071: * sequence, assuming that (a) this is known in advance, and (b) it is the same for
072: * all items in the sequence.
073: * <p/>
074: * <p>This method should always return a result, though it may be the best approximation
075: * that is available at the time.</p>
076: *
077: * @return a value such as Type.STRING, Type.BOOLEAN, Type.NUMBER,
078: * Type.NODE, or Type.ITEM (meaning not known at compile time)
079: * @param th
080: */
081:
082: public ItemType getItemType(TypeHierarchy th) {
083: return Type.STRING_TYPE;
084: }
085:
086: /**
087: * Diagnostic print of expression structure. The expression is written to the System.err
088: * output stream
089: *
090: * @param level indentation level for this expression
091: * @param pool NamePool used to expand any names appearing in the expression
092: * @param out Output destination
093: */
094:
095: public void display(int level, NamePool pool, PrintStream out) {
096: out.println(ExpressionTool.indent(level)
097: + "construct simple content");
098: select.display(level + 1, pool, out);
099: separator.display(level + 1, pool, out);
100: }
101:
102: /**
103: * Simplify an expression. This performs any static optimization (by rewriting the expression
104: * as a different expression). The default implementation does nothing.
105: *
106: * @return the simplified expression
107: * @throws net.sf.saxon.trans.XPathException
108: * if an error is discovered during expression
109: * rewriting
110: */
111:
112: public Expression simplify(StaticContext env) throws XPathException {
113: select = select.simplify(env);
114: if (select instanceof AtomicValue) {
115: return select;
116: }
117: separator = separator.simplify(env);
118: return this ;
119: }
120:
121: /**
122: * Get the immediate sub-expressions of this expression. Default implementation
123: * returns a zero-length array, appropriate for an expression that has no
124: * sub-expressions.
125: *
126: * @return an iterator containing the sub-expressions of this expression
127: */
128:
129: public Iterator iterateSubExpressions() {
130: return new PairIterator(select, separator);
131: }
132:
133: /**
134: * Offer promotion for this subexpression. The offer will be accepted if the subexpression
135: * is not dependent on the factors (e.g. the context item) identified in the PromotionOffer.
136: * By default the offer is not accepted - this is appropriate in the case of simple expressions
137: * such as constant values and variable references where promotion would give no performance
138: * advantage. This method is always called at compile time.
139: *
140: * @param offer details of the offer, for example the offer to move
141: * expressions that don't depend on the context to an outer level in
142: * the containing expression
143: * @return if the offer is not accepted, return this expression unchanged.
144: * Otherwise return the result of rewriting the expression to promote
145: * this subexpression
146: * @throws net.sf.saxon.trans.XPathException
147: * if any error is detected
148: */
149:
150: public Expression promote(PromotionOffer offer)
151: throws XPathException {
152: Expression exp = offer.accept(this );
153: if (exp != null) {
154: return exp;
155: } else {
156: select = doPromotion(select, offer);
157: separator = doPromotion(separator, offer);
158: return this ;
159: }
160: }
161:
162: /**
163: * Evaluate an expression as a single item. This always returns either a single Item or
164: * null (denoting the empty sequence). No conversion is done. This method should not be
165: * used unless the static type of the expression is a subtype of "item" or "item?": that is,
166: * it should not be called if the expression may return a sequence. There is no guarantee that
167: * this condition will be detected.
168: *
169: * @param context The context in which the expression is to be evaluated
170: * @return the node or atomic value that results from evaluating the
171: * expression; or null to indicate that the result is an empty
172: * sequence
173: * @throws net.sf.saxon.trans.XPathException
174: * if any dynamic error occurs evaluating the
175: * expression
176: */
177:
178: public Item evaluateItem(XPathContext context)
179: throws XPathException {
180: SequenceIterator iter;
181: if (isSingleton) {
182: // optimize for this case
183: Item item = select.evaluateItem(context);
184: if (item == null || item instanceof StringValue) {
185: return item;
186: } else if (item instanceof AtomicValue) {
187: return ((AtomicValue) item).convert(Type.STRING,
188: context);
189: } else {
190: iter = SingletonIterator.makeIterator(item);
191: }
192: } else {
193: iter = select.iterate(context);
194: }
195: FastStringBuffer sb = new FastStringBuffer(1024);
196: boolean prevText = false;
197: boolean first = true;
198: CharSequence sep = null;
199: while (true) {
200: Item item = iter.next();
201: if (item == null) {
202: break;
203: }
204: if (item instanceof NodeInfo) {
205: if (((NodeInfo) item).getNodeKind() == Type.TEXT) {
206: CharSequence s = item.getStringValueCS();
207: if (s.length() > 0) {
208: if (!first && !prevText) {
209: if (sep == null) {
210: sep = separator.evaluateItem(context)
211: .getStringValueCS();
212: }
213: sb.append(sep);
214: }
215: first = false;
216: sb.append(s);
217: prevText = true;
218: }
219: } else {
220: prevText = false;
221: SequenceIterator iter2 = item.getTypedValue();
222: while (true) {
223: Item item2 = iter2.next();
224: if (item2 == null) {
225: break;
226: }
227: if (!first) {
228: if (sep == null) {
229: sep = separator.evaluateItem(context)
230: .getStringValueCS();
231: }
232: sb.append(sep);
233: }
234: first = false;
235: sb.append(item2.getStringValueCS());
236: }
237: }
238: } else {
239: if (!first) {
240: if (sep == null) {
241: sep = separator.evaluateItem(context)
242: .getStringValueCS();
243: }
244: sb.append(sep);
245: }
246: first = false;
247: sb.append(item.getStringValueCS());
248: }
249: }
250: return StringValue.makeStringValue(sb.condense());
251: }
252:
253: /**
254: * An implementation of Expression must provide at least one of the methods evaluateItem(), iterate(), or process().
255: * This method indicates which of these methods is prefered.
256: */
257:
258: public int getImplementationMethod() {
259: return Expression.EVALUATE_METHOD;
260: }
261:
262: }
263:
264: //
265: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
266: // you may not use this file except in compliance with the License. You may obtain a copy of the
267: // License at http://www.mozilla.org/MPL/
268: //
269: // Software distributed under the License is distributed on an "AS IS" basis,
270: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
271: // See the License for the specific language governing rights and limitations under the License.
272: //
273: // The Original Code is: all this file.
274: //
275: // The Initial Developer of the Original Code is Michael H. Kay.
276: //
277: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
278: //
279: // Contributor(s): none.
280: //
|