package client.net.sf.saxon.ce.style;
import client.net.sf.saxon.ce.expr.*;
import client.net.sf.saxon.ce.expr.instruct.Executable;
import client.net.sf.saxon.ce.expr.instruct.ForEachGroup;
import client.net.sf.saxon.ce.lib.NamespaceConstant;
import client.net.sf.saxon.ce.lib.StringCollator;
import client.net.sf.saxon.ce.om.AttributeCollection;
import client.net.sf.saxon.ce.om.Axis;
import client.net.sf.saxon.ce.om.StandardNames;
import client.net.sf.saxon.ce.pattern.Pattern;
import client.net.sf.saxon.ce.pattern.PatternSponsor;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.tree.util.URI;
import client.net.sf.saxon.ce.value.EmptySequence;
import client.net.sf.saxon.ce.value.SequenceType;
import client.net.sf.saxon.ce.value.Whitespace;
/**
* Handler for xsl:for-each-group elements in stylesheet. This is a new instruction
* defined in XSLT 2.0
*/
public final class XSLForEachGroup extends StyleElement {
private Expression select = null;
private Expression groupBy = null;
private Expression groupAdjacent = null;
private Pattern starting = null;
private Pattern ending = null;
private Expression collationName;
/**
* Determine whether this node is an instruction.
* @return true - it is an instruction
*/
public boolean isInstruction() {
return true;
}
/**
* Specify that xsl:sort is a permitted child
*/
protected boolean isPermittedChild(StyleElement child) {
return (child instanceof XSLSort);
}
/**
* Determine whether this type of element is allowed to contain a template-body
* @return true: yes, it may contain a template-body
*/
public boolean mayContainSequenceConstructor() {
return true;
}
public void prepareAttributes() throws XPathException {
AttributeCollection atts = getAttributeList();
String selectAtt = null;
String groupByAtt = null;
String groupAdjacentAtt = null;
String startingAtt = null;
String endingAtt = null;
String collationAtt = null;
for (int a=0; a<atts.getLength(); a++) {
int nc = atts.getNameCode(a);
String f = getNamePool().getClarkName(nc);
if (f.equals(StandardNames.SELECT)) {
selectAtt = atts.getValue(a);
} else if (f.equals(StandardNames.GROUP_BY)) {
groupByAtt = atts.getValue(a);
} else if (f.equals(StandardNames.GROUP_ADJACENT)) {
groupAdjacentAtt = atts.getValue(a);
} else if (f.equals(StandardNames.GROUP_STARTING_WITH)) {
startingAtt = atts.getValue(a);
} else if (f.equals(StandardNames.GROUP_ENDING_WITH)) {
endingAtt = atts.getValue(a);
} else if (f.equals(StandardNames.COLLATION)) {
collationAtt = Whitespace.trim(atts.getValue(a));
} else {
checkUnknownAttribute(nc);
}
}
if (selectAtt==null) {
reportAbsence("select");
select = new Literal(EmptySequence.getInstance()); // for error recovery
} else {
select = makeExpression(selectAtt);
}
int c = (groupByAtt==null ? 0 : 1) +
(groupAdjacentAtt==null ? 0 : 1) +
(startingAtt==null ? 0 : 1) +
(endingAtt==null ? 0 : 1);
if (c!=1) {
compileError("Exactly one of the attributes group-by, group-adjacent, group-starting-with, " +
"and group-ending-with must be specified", "XTSE1080");
}
if (groupByAtt != null) {
groupBy = makeExpression(groupByAtt);
}
if (groupAdjacentAtt != null) {
groupAdjacent = makeExpression(groupAdjacentAtt);
}
if (startingAtt != null) {
starting = makePattern(startingAtt);
}
if (endingAtt != null) {
ending = makePattern(endingAtt);
}
if (collationAtt != null) {
if (groupBy==null && groupAdjacent==null) {
compileError("A collation may be specified only if group-by or group-adjacent is specified", "XTSE1090");
} else {
collationName = makeAttributeValueTemplate(collationAtt);
if (collationName instanceof StringLiteral) {
String collation = ((StringLiteral)collationName).getStringValue();
URI collationURI;
try {
collationURI = new URI(collation, true);
if (!collationURI.isAbsolute()) {
URI base = new URI(getBaseURI());
collationURI = base.resolve(collationURI.toString());
collationName = new StringLiteral(collationURI.toString());
}
} catch (URI.URISyntaxException err) {
compileError("Collation name '" + collationName + "' is not a valid URI", "XTDE1110");
collationName = new StringLiteral(NamespaceConstant.CODEPOINT_COLLATION_URI);
}
}
}
} else {
String defaultCollation = getDefaultCollationName();
if (defaultCollation != null) {
collationName = new StringLiteral(defaultCollation);
}
}
}
public void validate(Declaration decl) throws XPathException {
checkSortComesFirst(false);
select = typeCheck(select);
ExpressionVisitor visitor = makeExpressionVisitor();
if (groupBy != null) {
groupBy = typeCheck(groupBy);
try {
RoleLocator role =
new RoleLocator(RoleLocator.INSTRUCTION, "xsl:for-each-group/group-by", 0);
//role.setSourceLocator(locator);
groupBy = TypeChecker.staticTypeCheck(groupBy,
SequenceType.ATOMIC_SEQUENCE,
false, role, visitor);
} catch (XPathException err) {
compileError(err);
}
} else if (groupAdjacent != null) {
groupAdjacent = typeCheck(groupAdjacent);
try {
RoleLocator role =
new RoleLocator(RoleLocator.INSTRUCTION, "xsl:for-each-group/group-adjacent", 0);
//role.setSourceLocator(locator);
role.setErrorCode("XTTE1100");
groupAdjacent = TypeChecker.staticTypeCheck(groupAdjacent,
SequenceType.SINGLE_ATOMIC,
false, role, visitor);
} catch (XPathException err) {
compileError(err);
}
}
starting = typeCheck("starting", starting);
ending = typeCheck("ending", ending);
if (starting != null || ending != null) {
try {
RoleLocator role =
new RoleLocator(RoleLocator.INSTRUCTION, "xsl:for-each-group/select", 0);
//role.setSourceLocator(locator);
role.setErrorCode("XTTE1120");
select = TypeChecker.staticTypeCheck(select,
SequenceType.NODE_SEQUENCE,
false, role, visitor);
} catch (XPathException err) {
String prefix = (starting != null ?
"With group-starting-with attribute: " :
"With group-ending-with attribute: ");
compileError(prefix + err.getMessage(), err.getErrorCodeQName());
}
}
}
public Expression compile(Executable exec, Declaration decl) throws XPathException {
StringCollator collator = null;
if (collationName instanceof StringLiteral) {
// if the collation name is constant, then we've already resolved it against the base URI
final String uri = ((StringLiteral)collationName).getStringValue();
collator = getConfiguration().getNamedCollation(uri);
if (collator==null) {
compileError("The collation name '" + collationName + "' has not been defined", "XTDE1110");
}
}
byte algorithm = 0;
Expression key = null;
if (groupBy != null) {
algorithm = ForEachGroup.GROUP_BY;
key = groupBy;
} else if (groupAdjacent != null) {
algorithm = ForEachGroup.GROUP_ADJACENT;
key = groupAdjacent;
} else if (starting != null) {
algorithm = ForEachGroup.GROUP_STARTING;
key = new PatternSponsor(starting);
} else if (ending != null) {
algorithm = ForEachGroup.GROUP_ENDING;
key = new PatternSponsor(ending);
}
// Block action = new Block();
// compileChildren(exec, action, true);
Expression action = compileSequenceConstructor(exec, decl, iterateAxis(Axis.CHILD));
if (action == null) {
// body of for-each is empty: it's a no-op.
return new Literal(EmptySequence.getInstance());
}
try {
return new ForEachGroup( select,
makeExpressionVisitor().simplify(action),
algorithm,
key,
collator,
collationName,
getBaseURI(),
makeSortKeys(decl) );
} catch (XPathException e) {
compileError(e);
return null;
}
}
}
// 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.