001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.expr.Expression;
004: import net.sf.saxon.expr.ExpressionTool;
005: import net.sf.saxon.instruct.Choose;
006: import net.sf.saxon.instruct.Executable;
007: import net.sf.saxon.instruct.TraceWrapper;
008: import net.sf.saxon.om.AttributeCollection;
009: import net.sf.saxon.om.Axis;
010: import net.sf.saxon.om.AxisIterator;
011: import net.sf.saxon.om.NodeInfo;
012: import net.sf.saxon.trans.XPathException;
013: import net.sf.saxon.type.ItemType;
014: import net.sf.saxon.value.BooleanValue;
015: import net.sf.saxon.value.EmptySequence;
016:
017: /**
018: * An xsl:choose elements in the stylesheet. <br>
019: */
020:
021: public class XSLChoose extends StyleElement {
022:
023: private StyleElement otherwise;
024: private int numberOfWhens = 0;
025:
026: /**
027: * Determine whether this node is an instruction.
028: * @return true - it is an instruction
029: */
030:
031: public boolean isInstruction() {
032: return true;
033: }
034:
035: /**
036: * Determine the type of item returned by this instruction (only relevant if
037: * it is an instruction).
038: * @return the item type returned
039: */
040:
041: protected ItemType getReturnedItemType() {
042: return getCommonChildItemType();
043: }
044:
045: public void prepareAttributes() throws XPathException {
046: AttributeCollection atts = getAttributeList();
047: for (int a = 0; a < atts.getLength(); a++) {
048: int nc = atts.getNameCode(a);
049: checkUnknownAttribute(nc);
050: }
051: }
052:
053: public void validate() throws XPathException {
054: checkWithinTemplate();
055:
056: AxisIterator kids = iterateAxis(Axis.CHILD);
057: while (true) {
058: NodeInfo curr = (NodeInfo) kids.next();
059: if (curr == null) {
060: break;
061: }
062: if (curr instanceof XSLWhen) {
063: if (otherwise != null) {
064: compileError("xsl:otherwise must come last",
065: "XTSE0010");
066: }
067: numberOfWhens++;
068: } else if (curr instanceof XSLOtherwise) {
069: if (otherwise != null) {
070: compileError(
071: "Only one xsl:otherwise allowed in an xsl:choose",
072: "XTSE0010");
073: } else {
074: otherwise = (StyleElement) curr;
075: }
076: } else {
077: compileError(
078: "Only xsl:when and xsl:otherwise are allowed here",
079: "XTSE0010");
080: }
081: }
082:
083: if (numberOfWhens == 0) {
084: compileError(
085: "xsl:choose must contain at least one xsl:when",
086: "XTSE0010");
087: }
088: }
089:
090: /**
091: * Mark tail-recursive calls on templates and functions.
092: */
093:
094: public void markTailCalls() {
095: AxisIterator kids = iterateAxis(Axis.CHILD);
096: while (true) {
097: NodeInfo curr = (NodeInfo) kids.next();
098: if (curr == null) {
099: return;
100: }
101: if (curr instanceof StyleElement) {
102: ((StyleElement) curr).markTailCalls();
103: }
104: }
105: }
106:
107: public Expression compile(Executable exec) throws XPathException {
108:
109: int entries = numberOfWhens + (otherwise == null ? 0 : 1);
110: Expression[] conditions = new Expression[entries];
111: Expression[] actions = new Expression[entries];
112:
113: int w = 0;
114: AxisIterator kids = iterateAxis(Axis.CHILD);
115: while (true) {
116: NodeInfo curr = (NodeInfo) kids.next();
117: if (curr == null) {
118: break;
119: }
120: if (curr instanceof XSLWhen) {
121: conditions[w] = ((XSLWhen) curr).getCondition();
122: Expression b = ((XSLWhen) curr)
123: .compileSequenceConstructor(exec, curr
124: .iterateAxis(Axis.CHILD), true);
125: if (b == null) {
126: b = EmptySequence.getInstance();
127: }
128: try {
129: b = b.simplify(((XSLWhen) curr).getStaticContext());
130: actions[w] = b;
131: } catch (XPathException e) {
132: compileError(e);
133: }
134:
135: if (getConfiguration().getTraceListener() != null) {
136: TraceWrapper trace = makeTraceInstruction(
137: (XSLWhen) curr, actions[w]);
138: trace.setParentExpression((XSLWhen) curr);
139: actions[w] = trace;
140: }
141:
142: // Optimize for constant conditions (true or false)
143: if (conditions[w] instanceof BooleanValue) {
144: if (((BooleanValue) conditions[w])
145: .getBooleanValue()) {
146: // constant true: truncate the tests here
147: entries = w + 1;
148: break;
149: } else {
150: // constant false: omit this test
151: w--;
152: entries--;
153: }
154: }
155: w++;
156: } else if (curr instanceof XSLOtherwise) {
157: conditions[w] = BooleanValue.TRUE;
158: Expression b = ((XSLOtherwise) curr)
159: .compileSequenceConstructor(exec, curr
160: .iterateAxis(Axis.CHILD), true);
161: if (b == null) {
162: b = EmptySequence.getInstance();
163: }
164: try {
165: b = b.simplify(((XSLOtherwise) curr)
166: .getStaticContext());
167: actions[w] = b;
168: } catch (XPathException e) {
169: compileError(e);
170: }
171: if (getConfiguration().getTraceListener() != null) {
172: TraceWrapper trace = makeTraceInstruction(
173: (XSLOtherwise) curr, actions[w]);
174: trace.setParentExpression((XSLOtherwise) curr);
175: actions[w] = trace;
176: }
177: w++;
178: } else {
179: new AssertionError("Expected xsl:when or xsl:otherwise");
180: }
181: }
182:
183: if (conditions.length != entries) {
184: // we've optimized some entries away
185: if (entries == 0) {
186: return null; // return a no-op
187: }
188: if (entries == 1 && (conditions[0] instanceof BooleanValue)) {
189: if (((BooleanValue) conditions[0]).getBooleanValue()) {
190: // only one condition left, and it's known to be true: return the corresponding action
191: return actions[0];
192: } else {
193: // but if it's false, return a no-op
194: return null;
195: }
196: }
197: Expression[] conditions2 = new Expression[entries];
198: System.arraycopy(conditions, 0, conditions2, 0, entries);
199: Expression[] actions2 = new Expression[entries];
200: System.arraycopy(actions, 0, actions2, 0, entries);
201: conditions = conditions2;
202: actions = actions2;
203: }
204:
205: Choose ch = new Choose(conditions, actions);
206: ExpressionTool.makeParentReferences(ch);
207: return ch;
208: }
209:
210: }
211:
212: //
213: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
214: // you may not use this file except in compliance with the License. You may obtain a copy of the
215: // License at http://www.mozilla.org/MPL/
216: //
217: // Software distributed under the License is distributed on an "AS IS" basis,
218: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
219: // See the License for the specific language governing rights and limitations under the License.
220: //
221: // The Original Code is: all this file.
222: //
223: // The Initial Developer of the Original Code is Michael H. Kay.
224: //
225: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
226: //
227: // Contributor(s):
228: // Portions marked "e.g." are from Edwin Glaser (edwin@pannenleiter.de)
229: //
|