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) {

http://dive4elements.wald.intevation.org