[Dive4elements-commits] [PATCH 1 of 2] Datacage: Introduced <dc:group epxr="xpath" type="type"> ... </dc:group> and XPath function dc:group-key()
Wald Commits
scm-commit at wald.intevation.org
Thu May 2 20:55:12 CEST 2013
# HG changeset patch
# User Sascha L. Teichmann <teichmann at intevation.de>
# Date 1367520738 -7200
# Node ID 6ea004d512032606fe548fa6cda34299c1165a97
# Parent a763fb7aa2e526e9a74e77a2ae52df10557809bc
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>
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<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);
}
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<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) {
More information about the Dive4elements-commits
mailing list