001: package net.sf.saxon.style;
002:
003: import net.sf.saxon.expr.*;
004: import net.sf.saxon.instruct.Executable;
005: import net.sf.saxon.instruct.ForEachGroup;
006: import net.sf.saxon.om.AttributeCollection;
007: import net.sf.saxon.om.Axis;
008: import net.sf.saxon.om.NamespaceConstant;
009: import net.sf.saxon.pattern.Pattern;
010: import net.sf.saxon.pattern.PatternSponsor;
011: import net.sf.saxon.trans.XPathException;
012: import net.sf.saxon.value.EmptySequence;
013: import net.sf.saxon.value.SequenceType;
014:
015: import java.util.Comparator;
016: import java.net.URI;
017: import java.net.URISyntaxException;
018:
019: /**
020: * Handler for xsl:for-each-group elements in stylesheet. This is a new instruction
021: * defined in XSLT 2.0
022: */
023:
024: public final class XSLForEachGroup extends StyleElement {
025:
026: private Expression select = null;
027: private Expression groupBy = null;
028: private Expression groupAdjacent = null;
029: private Pattern starting = null;
030: private Pattern ending = null;
031: private String collationName;
032:
033: /**
034: * Determine whether this node is an instruction.
035: * @return true - it is an instruction
036: */
037:
038: public boolean isInstruction() {
039: return true;
040: }
041:
042: /**
043: * Specify that xsl:sort is a permitted child
044: */
045:
046: protected boolean isPermittedChild(StyleElement child) {
047: return (child instanceof XSLSort);
048: }
049:
050: /**
051: * Determine whether this type of element is allowed to contain a template-body
052: * @return true: yes, it may contain a template-body
053: */
054:
055: public boolean mayContainSequenceConstructor() {
056: return true;
057: }
058:
059: public void prepareAttributes() throws XPathException {
060:
061: AttributeCollection atts = getAttributeList();
062:
063: String selectAtt = null;
064: String groupByAtt = null;
065: String groupAdjacentAtt = null;
066: String startingAtt = null;
067: String endingAtt = null;
068:
069: for (int a = 0; a < atts.getLength(); a++) {
070: int nc = atts.getNameCode(a);
071: String f = getNamePool().getClarkName(nc);
072: if (f == StandardNames.SELECT) {
073: selectAtt = atts.getValue(a);
074: } else if (f == StandardNames.GROUP_BY) {
075: groupByAtt = atts.getValue(a);
076: } else if (f == StandardNames.GROUP_ADJACENT) {
077: groupAdjacentAtt = atts.getValue(a);
078: } else if (f == StandardNames.GROUP_STARTING_WITH) {
079: startingAtt = atts.getValue(a);
080: } else if (f == StandardNames.GROUP_ENDING_WITH) {
081: endingAtt = atts.getValue(a);
082: } else if (f == StandardNames.COLLATION) {
083: collationName = atts.getValue(a).trim();
084: } else {
085: checkUnknownAttribute(nc);
086: }
087: }
088:
089: if (selectAtt == null) {
090: reportAbsence("select");
091: select = EmptySequence.getInstance(); // for error recovery
092: } else {
093: select = makeExpression(selectAtt);
094: }
095:
096: int c = (groupByAtt == null ? 0 : 1)
097: + (groupAdjacentAtt == null ? 0 : 1)
098: + (startingAtt == null ? 0 : 1)
099: + (endingAtt == null ? 0 : 1);
100: ;
101: if (c != 1) {
102: compileError(
103: "Exactly one of the attributes group-by, group-adjacent, group-starting-with, "
104: + "and group-ending-with must be specified",
105: "XTSE1080");
106: }
107:
108: if (groupByAtt != null) {
109: groupBy = makeExpression(groupByAtt);
110: }
111:
112: if (groupAdjacentAtt != null) {
113: groupAdjacent = makeExpression(groupAdjacentAtt);
114: }
115:
116: if (startingAtt != null) {
117: starting = makePattern(startingAtt);
118: }
119:
120: if (endingAtt != null) {
121: ending = makePattern(endingAtt);
122: }
123:
124: if (collationName != null && groupBy == null
125: && groupAdjacent == null) {
126: compileError(
127: "A collation may be specified only if group-by or group-adjacent is specified",
128: "XTSE1090");
129: }
130:
131: if (collationName != null) {
132: URI collationURI;
133: try {
134: collationURI = new URI(collationName);
135: if (!collationURI.isAbsolute()) {
136: URI base = new URI(getBaseURI());
137: collationURI = base.resolve(collationURI);
138: collationName = collationURI.toString();
139: }
140: } catch (URISyntaxException err) {
141: compileError("Collation name '" + collationName
142: + "' is not a valid URI");
143: collationName = NamespaceConstant.CODEPOINT_COLLATION_URI;
144: }
145: }
146: }
147:
148: public void validate() throws XPathException {
149: checkWithinTemplate();
150: checkSortComesFirst(false);
151: select = typeCheck("select", select);
152:
153: ExpressionLocation locator = new ExpressionLocation(this );
154: if (groupBy != null) {
155: groupBy = typeCheck("group-by", groupBy);
156: try {
157: RoleLocator role = new RoleLocator(
158: RoleLocator.INSTRUCTION,
159: "xsl:for-each-group/group-by", 0, null);
160: role.setSourceLocator(locator);
161: groupBy = TypeChecker.staticTypeCheck(groupBy,
162: SequenceType.ATOMIC_SEQUENCE, false, role,
163: getStaticContext());
164: } catch (XPathException err) {
165: compileError(err);
166: }
167: } else if (groupAdjacent != null) {
168: groupAdjacent = typeCheck("group-adjacent", groupAdjacent);
169: try {
170: RoleLocator role = new RoleLocator(
171: RoleLocator.INSTRUCTION,
172: "xsl:for-each-group/group-adjacent", 0, null);
173: role.setSourceLocator(locator);
174: role.setErrorCode("XTTE1100");
175: groupAdjacent = TypeChecker.staticTypeCheck(
176: groupAdjacent, SequenceType.SINGLE_ATOMIC,
177: false, role, getStaticContext());
178: } catch (XPathException err) {
179: compileError(err);
180: }
181: }
182:
183: starting = typeCheck("starting", starting);
184: ending = typeCheck("ending", ending);
185:
186: if (starting != null || ending != null) {
187: try {
188: RoleLocator role = new RoleLocator(
189: RoleLocator.INSTRUCTION,
190: "xsl:for-each-group/select", 0, null);
191: role.setSourceLocator(locator);
192: role.setErrorCode("XTTE1120");
193: select = TypeChecker.staticTypeCheck(select,
194: SequenceType.NODE_SEQUENCE, false, role,
195: getStaticContext());
196: } catch (XPathException err) {
197: String prefix = (starting != null ? "With group-starting-with attribute: "
198: : "With group-ending-with attribute: ");
199: compileError(prefix + err.getMessage(), err
200: .getErrorCodeLocalPart());
201: }
202: }
203: }
204:
205: public Expression compile(Executable exec) throws XPathException {
206:
207: Comparator collator = null;
208: if (collationName != null) {
209: collator = getPrincipalStylesheet().findCollation(
210: collationName);
211: if (collator == null) {
212: compileError("The collation name '" + collationName
213: + "' has not been defined", "XTDE1110");
214: }
215: }
216:
217: byte algorithm = 0;
218: Expression key = null;
219: if (groupBy != null) {
220: algorithm = ForEachGroup.GROUP_BY;
221: key = groupBy;
222: } else if (groupAdjacent != null) {
223: algorithm = ForEachGroup.GROUP_ADJACENT;
224: key = groupAdjacent;
225: } else if (starting != null) {
226: algorithm = ForEachGroup.GROUP_STARTING;
227: key = new PatternSponsor(starting);
228: } else if (ending != null) {
229: algorithm = ForEachGroup.GROUP_ENDING;
230: key = new PatternSponsor(ending);
231: }
232:
233: // Block action = new Block();
234: // compileChildren(exec, action, true);
235: Expression action = compileSequenceConstructor(exec,
236: iterateAxis(Axis.CHILD), true);
237: if (action == null) {
238: // body of for-each is empty: it's a no-op.
239: return EmptySequence.getInstance();
240: }
241: try {
242: ForEachGroup inst = new ForEachGroup(select, action
243: .simplify(getStaticContext()), algorithm, key,
244: collator, makeSortKeys());
245: ExpressionTool.makeParentReferences(inst);
246: return inst;
247: } catch (XPathException e) {
248: compileError(e);
249: return null;
250: }
251:
252: }
253:
254: }
255:
256: //
257: // The contents of this file are subject to the Mozilla Public License Version 1.0 (the "License");
258: // you may not use this file except in compliance with the License. You may obtain a copy of the
259: // License at http://www.mozilla.org/MPL/
260: //
261: // Software distributed under the License is distributed on an "AS IS" basis,
262: // WITHOUT WARRANTY OF ANY KIND, either express or implied.
263: // See the License for the specific language governing rights and limitations under the License.
264: //
265: // The Original Code is: all this file.
266: //
267: // The Initial Developer of the Original Code is Michael H. Kay.
268: //
269: // Portions created by (your name) are Copyright (C) (your legal entity). All Rights Reserved.
270: //
271: // Contributor(s): none.
272: //
|