[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