You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@xalan.apache.org by mu...@apache.org on 2023/05/09 17:21:59 UTC
[xalan-java] branch xalan-j_xslt3.0 updated: committing implementations of xsl:for-each-group attributes group-starting-with and group-ending-with
This is an automated email from the ASF dual-hosted git repository.
mukulg pushed a commit to branch xalan-j_xslt3.0
in repository https://gitbox.apache.org/repos/asf/xalan-java.git
The following commit(s) were added to refs/heads/xalan-j_xslt3.0 by this push:
new 04e0b1c5 committing implementations of xsl:for-each-group attributes group-starting-with and group-ending-with
04e0b1c5 is described below
commit 04e0b1c584201c4df1413a2aec20959197e09dc2
Author: Mukul Gandhi <ga...@gmail.com>
AuthorDate: Tue May 9 22:51:35 2023 +0530
committing implementations of xsl:for-each-group attributes group-starting-with and group-ending-with
---
src/org/apache/xalan/processor/XSLTSchema.java | 15 +-
.../apache/xalan/templates/ElemForEachGroup.java | 193 ++++++++++++++++++---
2 files changed, 184 insertions(+), 24 deletions(-)
diff --git a/src/org/apache/xalan/processor/XSLTSchema.java b/src/org/apache/xalan/processor/XSLTSchema.java
index 62171bbc..4190f290 100644
--- a/src/org/apache/xalan/processor/XSLTSchema.java
+++ b/src/org/apache/xalan/processor/XSLTSchema.java
@@ -209,6 +209,18 @@ public class XSLTSchema extends XSLTElementDef
XSLTAttributeDef groupAdjacentAttrOpt = new XSLTAttributeDef(null,
"group-adjacent",
XSLTAttributeDef.T_EXPR, false, false, XSLTAttributeDef.ERROR);
+
+ // Optional.
+ // xsl:for-each-group
+ XSLTAttributeDef groupStartingWithAttrOpt = new XSLTAttributeDef(null,
+ "group-starting-with",
+ XSLTAttributeDef.T_EXPR, false, false, XSLTAttributeDef.ERROR);
+
+ // Optional.
+ // xsl:for-each-group
+ XSLTAttributeDef groupEndingWithAttrOpt = new XSLTAttributeDef(null,
+ "group-ending-with",
+ XSLTAttributeDef.T_EXPR, false, false, XSLTAttributeDef.ERROR);
// Optional.
// xsl:variable, xsl:param, xsl:with-param
@@ -462,7 +474,8 @@ public class XSLTSchema extends XSLTElementDef
XSLTElementDef xslForEachGroup = new XSLTElementDef(this,
Constants.S_XSLNAMESPACEURL, "for-each-group",
null /*alias */, templateElementsAndSort,
- new XSLTAttributeDef[]{ selectAttrRequired, groupByAttrOpt, groupAdjacentAttrOpt, spaceAttr },
+ new XSLTAttributeDef[]{ selectAttrRequired, groupByAttrOpt, groupAdjacentAttrOpt,
+ groupStartingWithAttrOpt, groupEndingWithAttrOpt, spaceAttr },
new ProcessorTemplateElem(),
ElemForEachGroup.class /* class object */, true, false, true, 20, true);
diff --git a/src/org/apache/xalan/templates/ElemForEachGroup.java b/src/org/apache/xalan/templates/ElemForEachGroup.java
index 3ac050cc..64f9d638 100644
--- a/src/org/apache/xalan/templates/ElemForEachGroup.java
+++ b/src/org/apache/xalan/templates/ElemForEachGroup.java
@@ -45,8 +45,8 @@ import org.apache.xpath.objects.XObject;
import org.apache.xpath.objects.XString;
/**
- * Implementation of the xsl:for-each-group XSLT 3.0 instruction.
- *
+ * XSLT 3.0 for-each-group element.
+ *
* <xsl:for-each-group
select = expression
group-by? = expression
@@ -71,9 +71,7 @@ import org.apache.xpath.objects.XString;
There are following two XSLT 3.0 grouping functions,
1) fn:current-group()
- 2) fn:current-grouping-key()
- *
- * (xsl:for-each-group instruction was first introduced in XSLT 2.0 language)
+ 2) fn:current-grouping-key()
*
* @author Mukul Gandhi <mu...@apache.org>
*
@@ -81,18 +79,16 @@ import org.apache.xpath.objects.XString;
*/
/*
- Currently, xsl:for-each-group's attributes "group-by" and "group-adjacent" have been
- implemented.
-
+ Implementation of the XSLT 3.0 xsl:for-each-group instruction.
+
The xsl:for-each-group's attributes are as described below (and are further defined within
XSLT 3.0 spec),
The xsl:for-each-group's attributes "group-by", "group-adjacent", "group-starting-with",
"group-ending-with" are mutually exclusive. i.e one of these is required and two or more
of these is an error within the XSLT stylesheet.
-
- Notes: To make this implementation more compliant to XSLT 3.0 spec.
*/
-public class ElemForEachGroup extends ElemTemplateElement implements ExpressionOwner
+public class ElemForEachGroup extends ElemTemplateElement
+ implements ExpressionOwner
{
private static final long serialVersionUID = -6682554013978812260L;
@@ -118,6 +114,16 @@ public class ElemForEachGroup extends ElemTemplateElement implements ExpressionO
*/
protected Expression m_GroupAdjacentExpression = null;
+ /**
+ * The "group-starting-with" expression.
+ */
+ protected Expression m_GroupStartingWithExpression = null;
+
+ /**
+ * The "group-ending-with" expression.
+ */
+ protected Expression m_GroupEndingWithExpression = null;
+
/**
* Vector containing the xsl:sort elements associated with this element.
*/
@@ -162,6 +168,26 @@ public class ElemForEachGroup extends ElemTemplateElement implements ExpressionO
{
return m_GroupAdjacentExpression;
}
+
+ public void setGroupStartingWith(XPath xpath)
+ {
+ m_GroupStartingWithExpression = xpath.getExpression();
+ }
+
+ public Expression getGroupStartingWith()
+ {
+ return m_GroupStartingWithExpression;
+ }
+
+ public void setGroupEndingWith(XPath xpath)
+ {
+ m_GroupEndingWithExpression = xpath.getExpression();
+ }
+
+ public Expression getGroupEndingWith()
+ {
+ return m_GroupEndingWithExpression;
+ }
/**
* This function is called after everything else has been
@@ -169,7 +195,7 @@ public class ElemForEachGroup extends ElemTemplateElement implements ExpressionO
* values that may be based on some other property that
* depends on recomposition.
*
- * NEEDSDOC @param sroot
+ * @param sroot
*
* @throws TransformerException
*/
@@ -347,16 +373,18 @@ public class ElemForEachGroup extends ElemTemplateElement implements ExpressionO
XPathContext xctxt = transformer.getXPathContext();
- if (m_GroupByExpression == null && m_GroupAdjacentExpression == null) {
+ int forEachGroupGroupingAttributesCount = getForEachGroupGroupingAttributesCount();
+
+ if (forEachGroupGroupingAttributesCount == 0) {
throw new TransformerException("XTSE1080 : None of the attributes 'group-by', 'group-adjacent', "
+ "'group-starting-with', 'group-ending-with' is present on "
+ "xsl:for-each-group element.", xctxt.getSAXLocator());
}
-
- if (m_GroupByExpression != null && m_GroupAdjacentExpression != null) {
+
+ if (forEachGroupGroupingAttributesCount > 1) {
throw new TransformerException("XTSE1080 : Only one of the attributes 'group-by', 'group-adjacent', "
- + "'group-starting-with', 'group-ending-with' is allowed on "
- + "xsl:for-each-group element.", xctxt.getSAXLocator());
+ + "'group-starting-with', 'group-ending-with' is allowed to be "
+ + "present on xsl:for-each-group element.", xctxt.getSAXLocator());
}
final int sourceNode = xctxt.getCurrentNode();
@@ -364,17 +392,22 @@ public class ElemForEachGroup extends ElemTemplateElement implements ExpressionO
// form xsl:for-each-group raw groups data from the 'sourceNodes' iterator
- // grouping keys, can have data types as defined by XPath data model.
+ // grouping keys (only available when attributes 'group-by' or 'group-adjacent' are used),
+ // can have data types as defined by XPath data model.
- // groups data is formed, within a hashmap.
+ // groups data is formed, within a hashmap (when attributes 'group-by' or
+ // 'group-adjacent' are used).
// hashmap item's key is the grouping key, and value of that hashmap item
// is a list of nodes dtm integer handles representing the group for that
// grouping key.
Map<Object, List<Integer>> xslForEachGroupMap = new HashMap<Object, List<Integer>>();
- int nextNode;
+ // List to store groups formed for, either 'group-starting-with' or 'group-ending-with'
+ List<List<Integer>> xslForEachGroupStartingWithEndingWith = new ArrayList<List<Integer>>();
if (m_GroupByExpression != null) {
+ int nextNode;
+
while (DTM.NULL != (nextNode = sourceNodes.nextNode())) {
xctxt.pushCurrentNode(nextNode);
@@ -394,6 +427,8 @@ public class ElemForEachGroup extends ElemTemplateElement implements ExpressionO
else if (m_GroupAdjacentExpression != null) {
Object prevValue = null;
int idx = 0;
+ int nextNode;
+
while (DTM.NULL != (nextNode = sourceNodes.nextNode())) {
xctxt.pushCurrentNode(nextNode);
@@ -425,12 +460,74 @@ public class ElemForEachGroup extends ElemTemplateElement implements ExpressionO
idx++;
}
}
+ else if (m_GroupStartingWithExpression != null) {
+ int nextNode;
+
+ while (DTM.NULL != (nextNode = sourceNodes.nextNode())) {
+ xctxt.pushCurrentNode(nextNode);
+
+ XObject xpathEvalResult = m_GroupStartingWithExpression.execute(xctxt);
+ boolean patternEvalResult = xpathEvalResult.bool();
+ if (patternEvalResult) {
+ List<Integer> group = new ArrayList<Integer>();
+ group.add(nextNode);
+ xslForEachGroupStartingWithEndingWith.add(group);
+ }
+ else {
+ int groupsCountSoFar = xslForEachGroupStartingWithEndingWith.size();
+ if (groupsCountSoFar > 0) {
+ List<Integer> group = xslForEachGroupStartingWithEndingWith.get(
+ groupsCountSoFar - 1);
+ group.add(nextNode);
+ }
+ }
+ }
+ }
+ else if (m_GroupEndingWithExpression != null) {
+ int nextNode;
+
+ List<Integer> nodesList = new ArrayList<Integer>();
+ while (DTM.NULL != (nextNode = sourceNodes.nextNode())) {
+ nodesList.add(Integer.valueOf(nextNode));
+ }
+
+ Collections.reverse(nodesList);
+
+ for (int idx = 0; idx < nodesList.size(); idx++) {
+ xctxt.pushCurrentNode((nodesList.get(idx)).intValue());
+
+ XObject xpathEvalResult = m_GroupEndingWithExpression.execute(xctxt);
+ boolean patternEvalResult = xpathEvalResult.bool();
+ if (patternEvalResult) {
+ List<Integer> group = new ArrayList<Integer>();
+ group.add((nodesList.get(idx)).intValue());
+ xslForEachGroupStartingWithEndingWith.add(group);
+ }
+ else {
+ int groupsCountSoFar = xslForEachGroupStartingWithEndingWith.size();
+ if (groupsCountSoFar > 0) {
+ List<Integer> group = xslForEachGroupStartingWithEndingWith.get(
+ groupsCountSoFar - 1);
+ group.add((nodesList.get(idx)).intValue());
+ }
+ }
+ }
+
+ Collections.reverse(xslForEachGroupStartingWithEndingWith);
+
+ for (int idx = 0; idx < xslForEachGroupStartingWithEndingWith.size(); idx++) {
+ List<Integer> group = xslForEachGroupStartingWithEndingWith.get(idx);
+ Collections.reverse(group);
+ }
+ }
// end, form xsl:for-each-group raw groups data from the 'sourceNodes' iterator
- // reorder xsl:for-each-group raw groups content known as 'order of first appearance'
+ // Reorder xsl:for-each-group raw groups content known as 'order of first appearance'
// as defined by XSLT 3.0 spec (this is the default XSLT transformation xsl:for-each-group
- // element's output order in absence of xsl:for-each-group's xsl:sort elements).
+ // element's output order in absence of xsl:for-each-group's xsl:sort elements).
+ // The groups reordering logic performed below, is only applicable when attributes
+ // 'group-by' or 'group-adjacent' are used.
List<GroupingKeyAndNodeHandlePair> groupingKeyAndNodeHandlePairList = new ArrayList
<GroupingKeyAndNodeHandlePair>();
if (xslForEachGroupMap.size() > 0) {
@@ -457,7 +554,8 @@ public class ElemForEachGroup extends ElemTemplateElement implements ExpressionO
Collections.sort(groupingKeyAndNodeHandlePairList); // sort the xsl:for-each-group's groups
// loop through all the, groups formed by xsl:for-each-group instruction,
- // and process the XSLT contents of each group.
+ // and process the XSLT contents of each group. This is done when,
+ // attributes 'group-by' or 'group-adjacent' are used.
for (int idx = 0; idx < groupingKeyAndNodeHandlePairList.size(); idx++) {
GroupingKeyAndNodeHandlePair groupingKeyNodeLevelPair = groupingKeyAndNodeHandlePairList.get(idx);
@@ -473,6 +571,26 @@ public class ElemForEachGroup extends ElemTemplateElement implements ExpressionO
templateElem.execute(transformer);
}
}
+
+ // loop through all the, groups formed by xsl:for-each-group instruction,
+ // and process the XSLT contents of each group.
+ // This is done when, attributes 'group-starting-with' or 'group-ending-with'
+ // are used.
+ for (int idx = 0; idx < xslForEachGroupStartingWithEndingWith.size(); idx++) {
+ List<Integer> groupNodesDtmHandles = xslForEachGroupStartingWithEndingWith.get(idx);
+ for (ElemTemplateElement templateElem = this.m_firstChild; templateElem != null;
+ templateElem = templateElem.m_nextSibling) {
+ // grouping key is absent when, attributes 'group-starting-with' or 'group-ending-with'
+ // are used.
+ // to handle this more correctly TO DO
+ templateElem.setGroupingKey(null);
+
+ templateElem.setGroupNodesDtmHandles(groupNodesDtmHandles);
+ xctxt.setSAXLocator(templateElem);
+ transformer.setCurrentElement(templateElem);
+ templateElem.execute(transformer);
+ }
+ }
}
finally
{
@@ -556,6 +674,35 @@ public class ElemForEachGroup extends ElemTemplateElement implements ExpressionO
m_selectExpression = exp;
}
+ /*
+ * Method to support, validating the presence and count of xsl:for-each-group
+ * attributes "group-by", "group-adjacent", "group-starting-with",
+ * "group-ending-with".
+ */
+ private int getForEachGroupGroupingAttributesCount()
+ throws TransformerException {
+
+ int forEachGroupGroupingAttributesCount = 0;
+
+ if (m_GroupByExpression != null) {
+ forEachGroupGroupingAttributesCount++;
+ }
+
+ if (m_GroupAdjacentExpression != null) {
+ forEachGroupGroupingAttributesCount++;
+ }
+
+ if (m_GroupStartingWithExpression != null) {
+ forEachGroupGroupingAttributesCount++;
+ }
+
+ if (m_GroupEndingWithExpression != null) {
+ forEachGroupGroupingAttributesCount++;
+ }
+
+ return forEachGroupGroupingAttributesCount;
+ }
+
/*
* For the purpose of, evaluating grouping key XPath expressions for xsl:for-each-group,
* the grouping keys are treated as of type String, Number or Boolean. Any other data
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@xalan.apache.org
For additional commands, e-mail: commits-help@xalan.apache.org