Mercurial > dive4elements > river
changeset 5890:6ea004d51203
Datacage: Introduced <dc:group epxr="xpath" type="type"> ... </dc:group> 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:
<dc:group expr="$name">
<group name="{dc:group-key()}">
<dc:for-each>
<description value="{$description}"/>
</dc:for-each>
</group>
</dc:group>
to create:
<group name="a">
<description name="foo"/>
<description name="bar"/>
</group>
<group name="b">
<description name="baz"/>
<description name="bla"/>
</group>
<group name="c">
<description name="blub"/>
</group>
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Thu, 02 May 2013 20:52:18 +0200 |
parents | a763fb7aa2e5 |
children | f59ff0ddc004 |
files | artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/Builder.java artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/FunctionResolver.java artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/ResultData.java |
diffstat | 3 files changed, 137 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- 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<String, CompiledStatement.Instance> statements; protected Deque<Pair<NamedConnection, ResultData>> connectionsStack; protected Deque<NodeList> macroBodies; + protected Deque<Object> groupExprStack; protected FunctionResolver functionResolver; protected Map<String, XPathExpression> expressions; @@ -126,6 +129,7 @@ frames = new StackFrames(parameters); owner = getOwnerDocument(output); macroBodies = new ArrayDeque<NodeList>(); + groupExprStack = new ArrayDeque<Object>(); functionResolver = new FunctionResolver(this); expressions = new HashMap<String, XPathExpression>(); statements = @@ -325,6 +329,121 @@ } } + protected Map<Object, ResultData> createGroupedResultData( + ResultData rd, + String expr, + String type + ) { + + List<Object []> rows = rd.getRows(); + String [] columns = rd.getColumnLabels(); + + XPathExpression x; + try { + x = getXPathExpression(expr); + } + catch (XPathExpressionException xee) { + log.warn("Invalid expression '" + expr + "'."); + return Collections.<Object, ResultData>emptyMap(); + } + + QName returnType = typeToQName(type); + + Map<Object, ResultData> groups = new TreeMap<Object, ResultData>(); + + 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<Builder.NamedConnection, ResultData> pair = + connectionsStack.peek(); + + ResultData orig = connectionsStack.peek().getB(); + + Map<Object, ResultData> groups = + createGroupedResultData(orig, expr, type); + + String [] columns = orig.getColumnLabels(); + + try { + for (Map.Entry<Object, ResultData> 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); }
--- 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 {
--- 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<Object []>(); } + public ResultData(String [] columns) { + this(columns, new ArrayList<Object []>()); + } + public ResultData(String [] columns, List<Object []> 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) {