# HG changeset patch # User Sascha L. Teichmann # Date 1367520738 -7200 # Node ID 6ea004d512032606fe548fa6cda34299c1165a97 # Parent a763fb7aa2e526e9a74e77a2ae52df10557809bc Datacage: Introduced ... and XPath function dc:group-key(). This splits the current result set into groups formed by expr. The type defaults to string. Afterwards all these groups are iterated by there natural order. The dc:group-key() gives access to the result of the grouping expression that forms a group. Say, you have a result set like this: name | description -----+------------ a | foo a | bar b | baz b | bla c | blub you can use: to create: diff -r a763fb7aa2e5 -r 6ea004d51203 artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/Builder.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/Builder.java Thu May 02 17:58:16 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/Builder.java Thu May 02 20:52:18 2013 +0200 @@ -17,10 +17,12 @@ import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -106,6 +108,7 @@ protected Map statements; protected Deque> connectionsStack; protected Deque macroBodies; + protected Deque groupExprStack; protected FunctionResolver functionResolver; protected Map expressions; @@ -126,6 +129,7 @@ frames = new StackFrames(parameters); owner = getOwnerDocument(output); macroBodies = new ArrayDeque(); + groupExprStack = new ArrayDeque(); functionResolver = new FunctionResolver(this); expressions = new HashMap(); statements = @@ -325,6 +329,121 @@ } } + protected Map createGroupedResultData( + ResultData rd, + String expr, + String type + ) { + + List rows = rd.getRows(); + String [] columns = rd.getColumnLabels(); + + XPathExpression x; + try { + x = getXPathExpression(expr); + } + catch (XPathExpressionException xee) { + log.warn("Invalid expression '" + expr + "'."); + return Collections.emptyMap(); + } + + QName returnType = typeToQName(type); + + Map groups = new TreeMap(); + + for (Object [] row: rows) { + frames.enter(); + try { + frames.put(columns, row); + + Object key = x.evaluate(EVAL_DOCUMENT, returnType); + + ResultData group = groups.get(key); + + if (group == null) { + group = new ResultData(rd.getColumnLabels()); + groups.put(key, group); + } + + group.add(row); + } + catch (XPathExpressionException xxe) { + log.warn("unable to apply expression '" + + expr + "' to dataset."); + } + finally { + frames.leave(); + } + } + return groups; + } + + protected void group(Node parent, Element current) + throws SQLException + { + log.debug("dc:group"); + + if (connectionsStack.isEmpty()) { + log.debug("dc:group without having results"); + return; + } + + NodeList subs = current.getChildNodes(); + int S = subs.getLength(); + + if (S == 0) { + log.debug("dc:group has no children"); + return; + } + + String expr = current.getAttribute("expr").trim(); + String type = current.getAttribute("type").trim(); + + Pair pair = + connectionsStack.peek(); + + ResultData orig = connectionsStack.peek().getB(); + + Map groups = + createGroupedResultData(orig, expr, type); + + String [] columns = orig.getColumnLabels(); + + try { + for (Map.Entry entry: groups.entrySet()) { + ResultData rd = entry.getValue(); + pair.setB(rd); + groupExprStack.push(entry.getKey()); + try { + for (Object [] row: rd.getRows()) { + frames.enter(); + try { + frames.put(columns, row); + for (int i = 0; i < S; ++i) { + build(parent, subs.item(i)); + } + } + finally { + frames.leave(); + } + } + } + finally { + groupExprStack.pop(); + } + } + } + finally { + pair.setB(orig); + } + } + + public Object getGroupKey() { + return groupExprStack.isEmpty() + ? null + : groupExprStack.peek(); + } + /** * Kind of foreach over results of a statement within a context. */ @@ -709,6 +828,9 @@ else if ("filter".equals(localName)) { filter(parent, curr); } + else if ("group".equals(localName)) { + group(parent, curr); + } else if ("text".equals(localName)) { text(parent, curr); } diff -r a763fb7aa2e5 -r 6ea004d51203 artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/FunctionResolver.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/FunctionResolver.java Thu May 02 17:58:16 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/FunctionResolver.java Thu May 02 20:52:18 2013 +0200 @@ -104,6 +104,13 @@ } }); + addFunction("group-key", 0, new XPathFunction() { + @Override + public Object evaluate(List args) throws XPathFunctionException { + return FunctionResolver.this.buildHelper.getGroupKey(); + } + }); + addFunction("date-format", 2, new XPathFunction() { @Override public Object evaluate(List args) throws XPathFunctionException { diff -r a763fb7aa2e5 -r 6ea004d51203 artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/ResultData.java --- a/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/ResultData.java Thu May 02 17:58:16 2013 +0200 +++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/ResultData.java Thu May 02 20:52:18 2013 +0200 @@ -34,6 +34,10 @@ rows = new ArrayList(); } + public ResultData(String [] columns) { + this(columns, new ArrayList()); + } + public ResultData(String [] columns, List rows) { this.columns = columns; this.rows = rows; @@ -73,6 +77,10 @@ return this; } + public void add(Object [] result) { + rows.add(result); + } + public void add(ResultSet result) throws SQLException { Object [] row = new Object[columns.length]; for (int i = 0; i < columns.length; ++i) {