package client.net.sf.saxon.ce.style;
import com.google.gwt.logging.client.LogConfiguration;
import client.net.sf.saxon.ce.LogController;
import client.net.sf.saxon.ce.expr.Expression;
import client.net.sf.saxon.ce.expr.Literal;
import client.net.sf.saxon.ce.expr.instruct.Choose;
import client.net.sf.saxon.ce.expr.instruct.Executable;
import client.net.sf.saxon.ce.om.AttributeCollection;
import client.net.sf.saxon.ce.om.Axis;
import client.net.sf.saxon.ce.tree.iter.AxisIterator;
import client.net.sf.saxon.ce.om.NodeInfo;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.type.ItemType;
import client.net.sf.saxon.ce.value.BooleanValue;
/**
* An xsl:choose elements in the stylesheet. <br>
*/
public class XSLChoose extends StyleElement {
private StyleElement otherwise;
private int numberOfWhens = 0;
/**
* Determine whether this node is an instruction.
* @return true - it is an instruction
*/
public boolean isInstruction() {
return true;
}
/**
* Determine the type of item returned by this instruction (only relevant if
* it is an instruction).
* @return the item type returned
*/
protected ItemType getReturnedItemType() {
return getCommonChildItemType();
}
public void prepareAttributes() throws XPathException {
AttributeCollection atts = getAttributeList();
for (int a=0; a<atts.getLength(); a++) {
int nc = atts.getNameCode(a);
checkUnknownAttribute(nc);
}
}
public void validate(Declaration decl) throws XPathException {
AxisIterator kids = iterateAxis(Axis.CHILD);
while(true) {
NodeInfo curr = (NodeInfo)kids.next();
if (curr == null) {
break;
}
if (curr instanceof XSLWhen) {
if (otherwise!=null) {
otherwise.compileError("xsl:otherwise must come last", "XTSE0010");
}
numberOfWhens++;
} else if (curr instanceof XSLOtherwise) {
if (otherwise!=null) {
((XSLOtherwise)curr).compileError("Only one xsl:otherwise is allowed in an xsl:choose", "XTSE0010");
} else {
otherwise = (StyleElement)curr;
}
} else if (curr instanceof StyleElement) {
((StyleElement)curr).compileError("Only xsl:when and xsl:otherwise are allowed here", "XTSE0010");
} else {
compileError("Only xsl:when and xsl:otherwise are allowed within xsl:choose", "XTSE0010");
}
}
if (numberOfWhens==0) {
compileError("xsl:choose must contain at least one xsl:when", "XTSE0010");
}
}
/**
* Mark tail-recursive calls on templates and functions.
*/
public boolean markTailCalls() {
boolean found = false;
AxisIterator kids = iterateAxis(Axis.CHILD);
while(true) {
NodeInfo curr = (NodeInfo)kids.next();
if (curr == null) {
return found;
}
if (curr instanceof StyleElement) {
found |= ((StyleElement)curr).markTailCalls();
}
}
}
public Expression compile(Executable exec, Declaration decl) throws XPathException {
int entries = numberOfWhens + (otherwise==null ? 0 : 1);
Expression[] conditions = new Expression[entries];
Expression[] actions = new Expression[entries];
String[] conditionTests = null;
if (LogConfiguration.loggingIsEnabled() && LogController.traceIsEnabled()){
conditionTests = new String[entries];
}
int w = 0;
AxisIterator kids = iterateAxis(Axis.CHILD);
while(true) {
NodeInfo curr = (NodeInfo)kids.next();
if (curr == null) {
break;
}
if (curr instanceof XSLWhen) {
conditions[w] = ((XSLWhen)curr).getCondition();
Expression b = ((XSLWhen)curr).compileSequenceConstructor(
exec, decl, curr.iterateAxis(Axis.CHILD));
if (b == null) {
b = Literal.makeEmptySequence();
}
try {
b = makeExpressionVisitor().simplify(b);
if (LogConfiguration.loggingIsEnabled() && LogController.traceIsEnabled()) {
String test = XSLIf.getTestAttribute((XSLWhen)curr);
conditionTests[w] = test;
}
actions[w] = b;
} catch (XPathException e) {
compileError(e);
}
// Optimize for constant conditions (true or false)
if (conditions[w] instanceof Literal && ((Literal)conditions[w]).getValue() instanceof BooleanValue) {
if (((BooleanValue)((Literal)conditions[w]).getValue()).getBooleanValue()) {
// constant true: truncate the tests here
entries = w+1;
break;
} else {
// constant false: omit this test
w--;
entries--;
}
}
w++;
} else if (curr instanceof XSLOtherwise) {
conditions[w] = Literal.makeLiteral(BooleanValue.TRUE);
Expression b = ((XSLOtherwise)curr).compileSequenceConstructor(
exec, decl, curr.iterateAxis(Axis.CHILD));
if (b == null) {
b = Literal.makeEmptySequence();
}
try {
b = makeExpressionVisitor().simplify(b);
if (LogConfiguration.loggingIsEnabled() && LogController.traceIsEnabled()) {
conditionTests[w] = "";
}
actions[w] = b;
} catch (XPathException e) {
compileError(e);
}
w++;
} else {
// Ignore: problem has already been reported.
}
}
if (conditions.length != entries) {
// we've optimized some entries away
if (entries==0) {
return null; // return a no-op
}
if (entries==1 && (conditions[0] instanceof Literal) &&
((Literal)conditions[0]).getValue() instanceof BooleanValue) {
if (((BooleanValue)((Literal)conditions[0]).getValue()).getBooleanValue()) {
// only one condition left, and it's known to be true: return the corresponding action
return actions[0];
} else {
// but if it's false, return a no-op
return null;
}
}
Expression[] conditions2 = new Expression[entries];
System.arraycopy(conditions, 0, conditions2, 0, entries);
Expression[] actions2 = new Expression[entries];
System.arraycopy(actions, 0, actions2, 0, entries);
conditions = conditions2;
actions = actions2;
}
if (LogConfiguration.loggingIsEnabled() && LogController.traceIsEnabled()) {
Choose ch = new Choose(conditions, actions);
ch.setConditionTests(conditionTests);
return ch;
} else {
return new Choose(conditions, actions);
}
}
}
// 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.