changeset 998:b81626b10cb7

Datacage: Moved templating in a better suited package. flys-artifacts/trunk@2434 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 01 Aug 2011 08:31:09 +0000
parents 4c82609824c8
children b0218f21c664
files flys-artifacts/ChangeLog flys-artifacts/src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/FunctionResolver.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/NoneUserSpecific.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/ResultData.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/StackFrames.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/TypeConverter.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/App.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/FunctionResolver.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/TypeConverter.java
diffstat 19 files changed, 1252 insertions(+), 1239 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Mon Aug 01 08:11:12 2011 +0000
+++ b/flys-artifacts/ChangeLog	Mon Aug 01 08:31:09 2011 +0000
@@ -1,3 +1,16 @@
+2011-08-01  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta,
+	  src/main/java/de/intevation/flys/artifacts/datacage/templating:
+	  Moved/renamed package to better fit the common semantics.
+	  DataCage.java is now call NoneUserSpecific.java to reflect the
+	  fact that it is the template for the user independent db
+	  analysis.
+
+	* src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java,
+	  src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Ajusted imports and calls.
+
 2011-08-01  Sascha L. Teichmann <sascha.teichmann@intevation.de>
 
 	* doc/conf/conf.xml: For documentation purposes added a out-commented 
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java	Mon Aug 01 08:11:12 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java	Mon Aug 01 08:31:09 2011 +0000
@@ -22,7 +22,7 @@
 
 import de.intevation.artifactdatabase.state.Output;
 
-import de.intevation.flys.artifacts.services.meta.DataCage;
+import de.intevation.flys.artifacts.datacage.templating.NoneUserSpecific;
 
 
 public class CollectionMonitor implements Hook {
@@ -58,9 +58,9 @@
         result.appendChild(recommended);
 
         String[] outs              = extractOutputNames(flys, context);
-        Map<String, Object> params = getDataCageParameters(flys, context);
+        Map<String, Object> params = getNoneUserSpecificParameters(flys, context);
 
-        DataCage dc = DataCage.getInstance();
+        NoneUserSpecific dc = NoneUserSpecific.getInstance();
         dc.recommend(flys, outs, params, recommended);
     }
 
@@ -80,7 +80,7 @@
     }
 
 
-    protected Map<String, Object> getDataCageParameters(
+    protected Map<String, Object> getNoneUserSpecificParameters(
         FLYSArtifact flys,
         CallContext  context)
     {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java	Mon Aug 01 08:31:09 2011 +0000
@@ -0,0 +1,118 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+
+import de.intevation.flys.backend.SessionFactoryProvider;
+
+import org.hibernate.Session;
+
+import org.hibernate.jdbc.Work;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+public class App
+{
+    private static Logger log = Logger.getLogger(App.class);
+
+    public static final String template =
+        System.getProperty("meta.data.template", "meta-data-template.xml");
+
+    public static final String PARAMETERS =
+        System.getProperty("meta.data.parameters", "");
+
+    public static final String OUTPUT =
+        System.getProperty("meta.data.output");
+
+    public static Map<String, Object> getParameters() {
+        HashMap<String, Object> map = new HashMap<String, Object>();
+        String [] parts = PARAMETERS.split("\\s*;\\s*");
+        for (String part: parts) {
+            String [] kv = part.split("\\s*:\\s*");
+            if (kv.length < 2 || (kv[0] = kv[0].trim()).length() == 0) {
+                continue;
+            }
+            String [] values = kv[1].split("\\s*,\\s*");
+            map.put(kv[0], values.length == 1 ? values[0] : values);
+        }
+        return map;
+    }
+
+    public static void main(String [] args) {
+
+        NoneUserSpecific dc = new NoneUserSpecific(
+            NoneUserSpecific.createBuilder(new File(template)));
+
+        final Document result = XMLUtils.newDocument();
+        final Builder builder = dc.getBuilder();
+
+        if (builder == null) {
+            System.err.println("No builder created");
+            return;
+        }
+
+        final Map<String, Object> parameters = getParameters();
+
+        Session session = SessionFactoryProvider
+            .createSessionFactory()
+            .openSession();
+
+        try {
+            session.doWork(new Work() {
+                @Override
+                public void execute(Connection connection)
+                throws SQLException
+                {
+                    builder.build(connection, result, parameters);
+                }
+
+            });
+        }
+        finally {
+            session.close();
+        }
+
+        OutputStream out;
+
+        if (OUTPUT == null) {
+            out = System.out;
+        }
+        else {
+            try {
+                out = new FileOutputStream(OUTPUT);
+            }
+            catch (IOException ioe) {
+                log.error(ioe);
+                return;
+            }
+        }
+
+        try {
+            XMLUtils.toStream(result, out);
+        }
+        finally {
+            if (OUTPUT != null) {
+                try {
+                    out.close();
+                }
+                catch (IOException ioe) {
+                    log.error(ioe);
+                }
+            }
+        }
+        System.exit(0);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java	Mon Aug 01 08:31:09 2011 +0000
@@ -0,0 +1,469 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathFactory;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathConstants;
+
+import java.sql.SQLException;
+import java.sql.Connection;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import org.apache.log4j.Logger;
+
+public class Builder
+{
+    private static Logger log = Logger.getLogger(Builder.class);
+
+    public static final Pattern STRIP_LINE_INDENT =
+        Pattern.compile("\\s*\\r?\\n\\s*");
+
+    public static final String DC_NAMESPACE_URI =
+        "http://www.intevation.org/2011/Datacage";
+
+    private static final Document EVAL_DOCUMENT =
+        XMLUtils.newDocument();
+
+    private static final XPathFactory XPATH_FACTORY =
+        XPathFactory.newInstance();
+
+    protected Document template;
+
+    protected Map<String, CompiledStatement> compiledStatements;
+
+
+    public class BuildHelper
+    {
+        protected Node                                    output;
+        protected Document                                owner;
+        protected StackFrames                             frames;
+        protected Connection                              connection;
+        protected Map<String, CompiledStatement.Instance> statements;
+
+        public BuildHelper(
+            Node                output,
+            Connection          connection,
+            Map<String, Object> parameters
+        ) {
+            this.output     = output;
+            this.connection = connection;
+            frames          = new StackFrames(parameters);
+            statements      = new HashMap<String, CompiledStatement.Instance>();
+            owner           = getOwnerDocument(output);
+        }
+
+        public void build() throws SQLException {
+            try {
+                synchronized (template) {
+                    for (Node current: rootsToList()) {
+                        build(output, current);
+                    }
+                }
+            }
+            finally {
+                closeStatements();
+            }
+        }
+
+        protected void closeStatements() {
+            for (CompiledStatement.Instance csi: statements.values()) {
+                csi.close();
+            }
+            statements.clear();
+        }
+
+        protected void context(Node parent, Element current)
+        throws SQLException
+        {
+            NodeList elements = current.getElementsByTagNameNS(
+                DC_NAMESPACE_URI, "elements");
+
+            if (elements.getLength() < 1) {
+                log.warn("no elements found -> ignore");
+                return;
+            }
+
+            NodeList subs = elements.item(0).getChildNodes();
+            int S = subs.getLength();
+
+            if (S < 1) {
+                log.warn("elements is empty -> ignore");
+                return;
+            }
+
+            NodeList stmntNode = current.getElementsByTagNameNS(
+                DC_NAMESPACE_URI, "statement");
+
+            if (stmntNode.getLength() < 1) {
+                log.warn("dc:context: too less statements");
+                return;
+            }
+
+            String stmntText = stmntNode.item(0).getTextContent();
+
+            CompiledStatement.Instance csi = statements.get(stmntText);
+
+            if (csi == null) {
+                CompiledStatement cs = compiledStatements.get(stmntText);
+                csi = cs.new Instance();
+                statements.put(stmntText, csi);
+            }
+
+            ResultData rd = csi.execute(connection, frames);
+
+            String [] columns = rd.getColumnLabels();
+
+
+            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();
+                }
+            }
+        }
+
+        protected void element(Node parent, Element current)
+        throws SQLException
+        {
+            String attr = expand(current.getAttribute("name"));
+
+            if (log.isDebugEnabled()) {
+                log.debug("dc:element -> '" + attr + "'");
+            }
+
+            if (attr.length() == 0) {
+                log.warn("no name attribute found");
+                return;
+            }
+
+            Element element = owner.createElement(attr);
+
+            NodeList children = current.getChildNodes();
+            for (int i = 0, N = children.getLength(); i < N; ++i) {
+                build(element, children.item(i));
+            }
+
+            parent.appendChild(element);
+        }
+
+        protected void text(Node parent, Element current)
+        throws SQLException
+        {
+            log.debug("dc:text");
+            String value = expand(current.getTextContent());
+            parent.appendChild(owner.createTextNode(value));
+        }
+
+
+        protected void attribute(Node parent, Element current) {
+
+            if (parent.getNodeType() != Node.ELEMENT_NODE) {
+                log.warn("need element here");
+                return;
+            }
+
+            String name  = expand(current.getAttribute("name"));
+            String value = expand(current.getAttribute("value"));
+
+            Element element = (Element)parent;
+
+            element.setAttribute(name, value);
+        }
+
+        protected void callMacro(Node parent, Element current)
+        throws SQLException
+        {
+            String name = current.getAttribute("name");
+
+            if (name.length() == 0) {
+                log.warn("missing 'name' attribute in 'call-macro'");
+                return;
+            }
+
+            NodeList macros = template.getElementsByTagNameNS(
+                DC_NAMESPACE_URI, "macro");
+
+            for (int i = 0, N = macros.getLength(); i < N; ++i) {
+                Element macro = (Element)macros.item(i);
+                if (name.equals(macro.getAttribute("name"))) {
+                    NodeList subs = macro.getChildNodes();
+                    for (int j = 0, M = subs.getLength(); j < M; ++j) {
+                        build(parent, subs.item(j));
+                    }
+                    return;
+                }
+            }
+
+            log.warn("no macro '" + name + "' found.");
+        }
+
+        protected void ifClause(Node parent, Element current)
+        throws SQLException
+        {
+            String test = current.getAttribute("test");
+
+            if (test.length() == 0) {
+                log.warn("missing 'test' attribute in 'if'");
+                return;
+            }
+
+            Boolean result = evaluateXPath(test);
+
+            if (result != null && result.booleanValue()) {
+                NodeList subs = current.getChildNodes();
+                for (int i = 0, N = subs.getLength(); i < N; ++i) {
+                    build(parent, subs.item(i));
+                }
+            }
+        }
+
+        protected void choose(Node parent, Element current)
+        throws SQLException
+        {
+            Node branch = null;
+
+            NodeList children = current.getChildNodes();
+            for (int i = 0, N = children.getLength(); i < N; ++i) {
+                Node child = children.item(i);
+                String ns = child.getNamespaceURI();
+                if (ns == null
+                || !ns.equals(DC_NAMESPACE_URI)
+                || child.getNodeType() != Node.ELEMENT_NODE
+                ) {
+                    continue;
+                }
+                String name = child.getLocalName();
+                if ("when".equals(name)) {
+                    Element when = (Element)child;
+                    String test = when.getAttribute("test");
+                    if (test.length() == 0) {
+                        log.warn("no 'test' attribute found for when");
+                        continue;
+                    }
+
+                    Boolean result = evaluateXPath(test);
+                    if (result != null && result.booleanValue()) {
+                        branch = child;
+                        break;
+                    }
+
+                    continue;
+                }
+                else if ("otherwise".equals(name)) {
+                    branch = child;
+                    // No break here.
+                }
+            }
+
+            if (branch != null) {
+                NodeList subs = branch.getChildNodes();
+                for (int i = 0, N = subs.getLength(); i < N; ++i) {
+                    build(parent, subs.item(i));
+                }
+            }
+        }
+
+        protected Boolean evaluateXPath(String expr) {
+
+            if (log.isDebugEnabled()) {
+                log.debug("evaluate: '" + expr + "'");
+            }
+
+            try {
+                XPath xpath = XPATH_FACTORY.newXPath();
+                xpath.setXPathVariableResolver(frames);
+                xpath.setXPathFunctionResolver(FunctionResolver.FUNCTIONS);
+                Object result = xpath.evaluate(
+                    expr, EVAL_DOCUMENT, XPathConstants.BOOLEAN);
+
+                return result instanceof Boolean
+                    ? (Boolean)result
+                    : null;
+            }
+            catch (XPathExpressionException xfce) {
+                log.error(xfce);
+            }
+            return null;
+        }
+
+        protected void convert(Node parent, Element current) {
+
+            String variable = expand(current.getAttribute("var"));
+            String type     = expand(current.getAttribute("type"));
+
+            if (frames.containsKey(variable)) {
+                Object object = TypeConverter.convert(
+                    frames.get(variable),
+                    type);
+                frames.put(variable, object);
+            }
+        }
+
+        protected String expand(String s) {
+            Matcher m = CompiledStatement.VAR.matcher(s);
+
+            StringBuffer sb = new StringBuffer();
+            while (m.find()) {
+                String key = m.group(1);
+                Object value = frames.get(key);
+                m.appendReplacement(sb, value != null ? value.toString() : "");
+            }
+            m.appendTail(sb);
+            return sb.toString();
+        }
+
+        protected void build(Node parent, Node current)
+        throws SQLException
+        {
+            String ns = current.getNamespaceURI();
+            if (ns != null && ns.equals(DC_NAMESPACE_URI)) {
+                if (current.getNodeType() != Node.ELEMENT_NODE) {
+                    log.warn("need elements here");
+                }
+                else {
+                    String localName = current.getLocalName();
+                    if ("attribute".equals(localName)) {
+                        attribute(parent, (Element)current);
+                    }
+                    else if ("context".equals(localName)) {
+                        context(parent, (Element)current);
+                    }
+                    else if ("if".equals(localName)) {
+                        ifClause(parent, (Element)current);
+                    }
+                    else if ("choose".equals(localName)) {
+                        choose(parent, (Element)current);
+                    }
+                    else if ("call-macro".equals(localName)) {
+                        callMacro(parent, (Element)current);
+                    }
+                    else if ("macro".equals(localName)) {
+                        // simply ignore the definition.
+                    }
+                    else if ("element".equals(localName)) {
+                        element(parent, (Element)current);
+                    }
+                    else if ("text".equals(localName)) {
+                        text(parent, (Element)current);
+                    }
+                    else if ("convert".equals(localName)) {
+                        convert(parent, (Element)current);
+                    }
+                    else {
+                        log.warn("unknown '" + localName + "' -> ignore");
+                    }
+                }
+                return;
+            }
+
+            if (current.getNodeType() == Node.TEXT_NODE) {
+                String txt = current.getNodeValue();
+                if (txt != null && txt.trim().length() == 0) {
+                    return;
+                }
+            }
+
+            Node copy = owner.importNode(current, false);
+
+            NodeList children = current.getChildNodes();
+            for (int i = 0, N = children.getLength(); i < N; ++i) {
+                build(copy, children.item(i));
+            }
+            parent.appendChild(copy);
+        }
+    } // class BuildHelper
+
+
+    public Builder() {
+        compiledStatements = new HashMap<String, CompiledStatement>();
+    }
+
+    public Builder(Document template) {
+        this();
+        this.template = template;
+        compileStatements();
+    }
+
+    protected void compileStatements() {
+
+        NodeList nodes = template.getElementsByTagNameNS(
+            DC_NAMESPACE_URI, "statement");
+
+        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
+            Element stmntElement = (Element)nodes.item(i);
+            String stmnt = trimStatement(stmntElement.getTextContent());
+            if (stmnt == null || stmnt.length() == 0) {
+                throw new IllegalArgumentException("found empty statement");
+            }
+            CompiledStatement cs = new CompiledStatement(stmnt);
+            // for faster lookup store a shortend string into the template
+            stmnt = "s" + i;
+            stmntElement.setTextContent(stmnt);
+            compiledStatements.put(stmnt, cs);
+        }
+    }
+
+    protected List<Node> rootsToList() {
+
+        NodeList roots = template.getElementsByTagNameNS(
+            DC_NAMESPACE_URI, "template");
+
+        List<Node> elements = new ArrayList<Node>();
+
+        for (int i = 0, N = roots.getLength(); i < N; ++i) {
+            NodeList rootChildren = roots.item(i).getChildNodes();
+            for (int j = 0, M = rootChildren.getLength(); j < M; ++j) {
+                Node child = rootChildren.item(j);
+                if (child.getNodeType() == Node.ELEMENT_NODE) {
+                    elements.add(child);
+                }
+            }
+        }
+
+        return elements;
+    }
+
+    protected static final String trimStatement(String stmnt) {
+        if (stmnt == null) return null;
+        //XXX: Maybe a bit to radical for multiline strings?
+        return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" ");
+    }
+
+    protected static Document getOwnerDocument(Node node) {
+        Document document = node.getOwnerDocument();
+        return document != null ? document : (Document)node;
+    }
+
+    public void build(
+        Connection          connection,
+        Node                output,
+        Map<String, Object> parameters
+    )
+    throws SQLException
+    {
+        BuildHelper helper = new BuildHelper(output, connection, parameters);
+
+        helper.build();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/CompiledStatement.java	Mon Aug 01 08:31:09 2011 +0000
@@ -0,0 +1,188 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.ArrayList;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+public class CompiledStatement
+{
+    public static final String DATACAGE_DB_CACHE =
+        "datacage.db";
+
+    public static final Pattern VAR =
+        Pattern.compile("\\$\\{([a-zA-Z0-9_-]+)\\}");
+
+    protected String original;
+    protected String statement;
+
+    protected Map<String, List<Integer>> positions;
+
+    protected int numVars;
+
+    public class Instance {
+
+        protected PreparedStatement preparedStatement;
+
+        public Instance() {
+        }
+
+        protected ResultData executeCached(
+            Cache       cache,
+            Connection  connection,
+            StackFrames frames
+        )
+        throws SQLException
+        {
+            Object [] values = new Object[numVars];
+
+            StringBuilder sb = new StringBuilder(original);
+
+            for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
+                String key   = entry.getKey();
+                Object value = frames.get(key);
+                sb.append(';').append(key).append(':').append(value);
+                for (Integer index: entry.getValue()) {
+                    values[index] = value;
+                }
+            }
+
+            // XXX: Maybe too many collisions?
+            // String key = original + Arrays.hashCode(values);
+            String key = sb.toString();
+
+            Element element = cache.get(key);
+
+            if (element != null) {
+                return (ResultData)element.getValue();
+            }
+
+            if (preparedStatement == null) {
+                preparedStatement = connection.prepareStatement(statement);
+            }
+
+            for (int i = 0; i < values.length; ++i) {
+                preparedStatement.setObject(i+1, values[i]);
+            }
+
+            ResultData data;
+
+            ResultSet result = preparedStatement.executeQuery();
+            try {
+                data = new ResultData(preparedStatement.getMetaData())
+                    .addAll(result);
+            }
+            finally {
+                result.close();
+            }
+
+            element = new Element(key, data);
+            cache.put(element);
+
+            return data;
+        }
+
+        protected ResultData executeUncached(
+            Connection  connection,
+            StackFrames frames
+        ) 
+        throws SQLException
+        {
+            if (preparedStatement == null) {
+                preparedStatement = connection.prepareStatement(statement);
+            }
+
+            for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
+                Object value = frames.get(entry.getKey());
+                for (Integer index: entry.getValue()) {
+                    preparedStatement.setObject(index+1, value);
+                }
+            }
+
+            ResultSet result = preparedStatement.executeQuery();
+            try {
+                return new ResultData(preparedStatement.getMetaData())
+                    .addAll(result);
+            }
+            finally {
+                result.close();
+            }
+        }
+
+        public ResultData execute(Connection connection, StackFrames frames)
+        throws SQLException
+        {
+            Cache cache = CacheFactory.getCache(DATACAGE_DB_CACHE);
+
+            return cache != null
+                ? executeCached(cache, connection, frames)
+                : executeUncached(connection, frames);
+        }
+
+        public void close() {
+            if (preparedStatement != null) {
+                try {
+                    preparedStatement.close();
+                }
+                catch (SQLException sqle) {
+                }
+                preparedStatement = null;
+            }
+        }
+    } // class Instance
+
+    public CompiledStatement() {
+    }
+
+    public CompiledStatement(String original) {
+        this.original = original;
+        // TreeMap to ensure order
+        positions = new TreeMap<String, List<Integer>>();
+        compile();
+    }
+
+    protected void compile() {
+
+        StringBuffer sb = new StringBuffer();
+
+        Matcher m = VAR.matcher(original);
+
+        int index = 0;
+
+        while (m.find()) {
+            String key = m.group(1);
+            List<Integer> indices = positions.get(key);
+            if (indices == null) {
+                indices = new ArrayList<Integer>();
+                positions.put(key, indices);
+            }
+            indices.add(index);
+            m.appendReplacement(sb, "?");
+            ++index;
+        }
+
+        m.appendTail(sb);
+
+        numVars = index;
+
+        statement = sb.toString();
+    }
+
+    public String getStatement() {
+        return statement;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/FunctionResolver.java	Mon Aug 01 08:31:09 2011 +0000
@@ -0,0 +1,103 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.util.List;
+import java.util.Collection;
+import java.util.Map;
+import java.util.ArrayList;
+
+import javax.xml.xpath.XPathFunctionResolver;
+import javax.xml.xpath.XPathFunction;
+import javax.xml.xpath.XPathFunctionException;
+
+import javax.xml.namespace.QName;
+
+import org.apache.log4j.Logger;
+
+public class FunctionResolver
+implements   XPathFunctionResolver
+{
+    private static Logger log = Logger.getLogger(FunctionResolver.class);
+
+    public static final String FUNCTION_NAMESPACE_URI = "dc";
+
+    public static final class Entry {
+
+        String        name;
+        XPathFunction function;
+        int           arity;
+
+        public Entry() {
+        }
+
+        public Entry(String name, XPathFunction function, int arity) {
+            this.name     = name;
+            this.function = function;
+            this.arity    = arity;
+        }
+    } // class Entry
+
+    public static final FunctionResolver FUNCTIONS = new FunctionResolver();
+
+    static {
+        FUNCTIONS.addFunction("contains", 2, new XPathFunction() {
+            @Override
+            public Object evaluate(List args) throws XPathFunctionException {
+                Object haystack = args.get(0);
+                Object needle   = args.get(1);
+                try {
+                    if (haystack instanceof Collection) {
+                        return Boolean.valueOf(
+                            ((Collection)haystack).contains(needle));
+                    }
+
+                    if (haystack instanceof Map) {
+                        return Boolean.valueOf(
+                            ((Map)haystack).containsKey(needle));
+                    }
+
+                    if (haystack instanceof Object []) {
+                        for (Object straw: (Object [])haystack) {
+                            if (straw.equals(needle)) {
+                                return Boolean.TRUE;
+                            }
+                        }
+                    }
+
+                    return Boolean.FALSE;
+                }
+                catch (Exception e) {
+                    log.error(e);
+                    throw new XPathFunctionException(e);
+                }
+            }
+        });
+    }
+
+    protected List<Entry> functions;
+
+    public FunctionResolver() {
+        functions = new ArrayList<Entry>();
+    }
+
+    public void addFunction(String name, int arity, XPathFunction function) {
+        functions.add(new Entry(name, function, arity));
+    }
+
+    @Override
+    public XPathFunction resolveFunction(QName functionName, int arity) {
+
+        if (!functionName.getNamespaceURI().equals(FUNCTION_NAMESPACE_URI)) {
+            return null;
+        }
+
+        String name = functionName.getLocalPart();
+        for (Entry entry: functions) {
+            if (entry.arity == arity && entry.name.equals(name)) {
+                return entry.function;
+            }
+        }
+
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/NoneUserSpecific.java	Mon Aug 01 08:31:09 2011 +0000
@@ -0,0 +1,175 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+
+import java.io.FileInputStream;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.hibernate.Session;
+
+import org.hibernate.jdbc.Work;
+
+import de.intevation.artifacts.common.utils.Config;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+import de.intevation.flys.backend.SessionHolder;
+
+import de.intevation.artifactdatabase.data.StateData;
+
+
+public class NoneUserSpecific
+{
+    private static Logger log = Logger.getLogger(NoneUserSpecific.class);
+
+    public static final String XPATH_META_DATA_TEMPLATE =
+        "/artifact-database/metadata/@template";
+
+    private static NoneUserSpecific INSTANCE;
+
+    protected Builder builder;
+
+    public NoneUserSpecific() {
+    }
+
+    public NoneUserSpecific(Builder builder) {
+        this.builder = builder;
+    }
+
+    public Builder getBuilder() {
+        return builder;
+    }
+
+    protected static void artifactToParameters(
+        FLYSArtifact        artifact, 
+        Map<String, Object> parameters
+    ) {
+        parameters.put("current-state-id", artifact.getCurrentStateId());
+
+        for (StateData sd: artifact.getAllData()) {
+            Object value = sd.getValue();
+            if (value == null) {
+                continue;
+            }
+            String key = sd.getName().replace('.', '-');
+            parameters.put(key, value);
+        }
+    }
+
+    public void  recommend(
+        FLYSArtifact        artifact,
+        String []           outs,
+        Map<String, Object> extraParameters,
+        Node                result
+    ) {
+        Map<String, Object> parameters = new HashMap<String, Object>();
+
+        if (extraParameters != null) {
+            parameters.putAll(extraParameters);
+        }
+
+        if (artifact != null) {
+            artifactToParameters(artifact, parameters);
+        }
+
+        parameters.put("artifact-outs", outs);
+
+        parameters.put("parameters", parameters);
+
+        recommend(parameters, result);
+    }
+
+    public void recommend(
+        final Map<String, Object> parameters,
+        final Node                result
+    ) {
+        if (builder == null) {
+            log.error("builder not configured properly.");
+            return;
+        }
+
+        Session session = SessionHolder.HOLDER.get();
+
+        session.doWork(new Work() {
+            @Override
+            public void execute(Connection connection)
+            throws SQLException
+            {
+                builder.build(connection, result, parameters);
+            }
+        });
+    }
+
+    public static synchronized NoneUserSpecific getInstance() {
+        if (INSTANCE == null) {
+            INSTANCE = createNoneUserSpecific();
+        }
+        return INSTANCE;
+    }
+
+    protected static Builder createBuilder(File file) {
+        log.debug("NoneUserSpecific.createBuilder");
+
+        if (!file.isFile() || !file.canRead()) {
+            log.error("Cannot open template file '" + file + "'");
+            return null;
+        }
+
+        InputStream in = null;
+
+        try {
+            in = new FileInputStream(file);
+
+            Document template = XMLUtils.parseDocument(in);
+
+            if (template == null) {
+                log.error("cannot parse meta data template");
+            }
+            else {
+                return new Builder(template);
+            }
+        }
+        catch (IOException ioe) {
+            log.error(ioe);
+        }
+        finally {
+            if (in != null) {
+                try {
+                    in.close();
+                }
+                catch (IOException ioe) {
+                    log.error(ioe);
+                }
+            }
+        }
+        return null;
+    }
+
+    protected static NoneUserSpecific createNoneUserSpecific() {
+        log.debug("NoneUserSpecific.createNoneUserSpecific");
+
+        String path = Config.getStringXPath(XPATH_META_DATA_TEMPLATE);
+        if (path == null) {
+            log.error("no path to template file given");
+            return null;
+        }
+
+        path = Config.replaceConfigDir(path);
+
+        return new NoneUserSpecific(createBuilder(new File(path)));
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/ResultData.java	Mon Aug 01 08:31:09 2011 +0000
@@ -0,0 +1,60 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.io.Serializable;
+
+import java.sql.ResultSetMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class ResultData
+implements   Serializable
+{
+    protected String [] columns;
+
+    protected List<Object []> rows;
+
+    public ResultData() {
+        rows = new ArrayList<Object []>();
+    }
+
+    public ResultData(ResultSetMetaData meta)
+    throws SQLException
+    {
+        this();
+
+        int N = meta.getColumnCount();
+
+        columns = new String[N];
+
+        for (int i = 1; i <= N; ++i) {
+            columns[i-1] = meta.getColumnLabel(i);
+        }
+    }
+
+    public String [] getColumnLabels() {
+        return columns;
+    }
+
+    public ResultData addAll(ResultSet result) throws SQLException {
+        while (result.next()) {
+            add(result);
+        }
+        return this;
+    }
+
+    public void add(ResultSet result) throws SQLException {
+        Object [] row = new Object[columns.length];
+        for (int i = 0; i < columns.length; ++i) {
+            row[i] = result.getObject(i+1);
+        }
+        rows.add(row);
+    }
+
+    public List<Object []> getRows() {
+        return rows;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/StackFrames.java	Mon Aug 01 08:31:09 2011 +0000
@@ -0,0 +1,87 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+import java.util.Map;
+import java.util.List;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+import javax.xml.xpath.XPathVariableResolver;
+
+import javax.xml.namespace.QName;
+
+import org.apache.log4j.Logger;
+
+public class StackFrames
+implements   XPathVariableResolver
+{
+    private static Logger log = Logger.getLogger(StackFrames.class);
+
+    protected List<Map<String, Object>> frames;
+
+    public StackFrames() {
+        frames = new ArrayList<Map<String, Object>>();
+    }
+
+    public StackFrames(Map<String, Object> initialFrame) {
+        this();
+        if (initialFrame != null) {
+            frames.add(new HashMap<String, Object>(initialFrame));
+        }
+    }
+
+    public void enter() {
+        frames.add(new HashMap<String, Object>());
+    }
+
+    public void leave() {
+        frames.remove(frames.size()-1);
+    }
+
+    public void put(String key, Object value) {
+        int N = frames.size();
+        if (N > 0) {
+            frames.get(N-1).put(key, value);
+        }
+    }
+
+    public void put(String [] keys, Object [] values) {
+        Map<String, Object> top = frames.get(frames.size()-1);
+        for (int i = 0; i < keys.length; ++i) {
+            top.put(keys[i], values[i]);
+        }
+    }
+
+    public boolean containsKey(String key) {
+        for (int i = frames.size()-1; i >= 0; --i) {
+            if (frames.get(i).containsKey(key)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Object get(String key) {
+        return get(key, null);
+    }
+
+    public Object get(String key, Object def) {
+
+        for (int i = frames.size()-1; i >= 0; --i) {
+            Map<String, Object> frame = frames.get(i);
+            if (frame.containsKey(key)) {
+                return frame.get(key);
+            }
+        }
+
+        return def;
+    }
+
+    @Override
+    public Object resolveVariable(QName variableName) {
+        if (log.isDebugEnabled()) {
+            log.debug("resolve var: " + variableName);
+        }
+        return get(variableName.getLocalPart());
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/TypeConverter.java	Mon Aug 01 08:31:09 2011 +0000
@@ -0,0 +1,31 @@
+package de.intevation.flys.artifacts.datacage.templating;
+
+public class TypeConverter
+{
+    private TypeConverter() {
+    }
+
+    public static Object convert(Object object, String type) {
+
+        if (type == null) {
+            return object;
+        }
+
+        if ("Integer".equals(type)) {
+            return Integer.valueOf(object.toString());
+        }
+
+        if ("Double".equals(type)) {
+            return Double.valueOf(object.toString());
+        }
+
+        if ("String".equals(type)) {
+            return object.toString();
+        }
+
+        // TODO: Add more types
+
+        return object;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java	Mon Aug 01 08:11:12 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java	Mon Aug 01 08:31:09 2011 +0000
@@ -23,8 +23,8 @@
 
 import de.intevation.artifacts.common.ArtifactNamespaceContext;
 
-import de.intevation.flys.artifacts.services.meta.Builder;
-import de.intevation.flys.artifacts.services.meta.DataCage;
+import de.intevation.flys.artifacts.datacage.templating.Builder;
+import de.intevation.flys.artifacts.datacage.templating.NoneUserSpecific;
 
 import de.intevation.flys.artifacts.FLYSArtifact;
 
@@ -74,7 +74,7 @@
 
         final Document result = XMLUtils.newDocument();
 
-        final Builder builder = DataCage.getInstance().getBuilder();
+        final Builder builder = NoneUserSpecific.getInstance().getBuilder();
 
         if (builder == null) {
             log.error("MetaDataService is not setup properly.");
@@ -194,7 +194,7 @@
 
         String [] outs = outsString.split("\\s*,\\s*");
         
-        DataCage dc = DataCage.getInstance();
+        NoneUserSpecific dc = NoneUserSpecific.getInstance();
 
         dc.recommend(flysArtifact, outs, data, result);
 
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/App.java	Mon Aug 01 08:11:12 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,118 +0,0 @@
-package de.intevation.flys.artifacts.services.meta;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-import java.util.Map;
-import java.util.HashMap;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-
-import de.intevation.flys.backend.SessionFactoryProvider;
-
-import org.hibernate.Session;
-
-import org.hibernate.jdbc.Work;
-
-import org.w3c.dom.Document;
-
-import org.apache.log4j.Logger;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-public class App
-{
-    private static Logger log = Logger.getLogger(App.class);
-
-    public static final String template =
-        System.getProperty("meta.data.template", "meta-data-template.xml");
-
-    public static final String PARAMETERS =
-        System.getProperty("meta.data.parameters", "");
-
-    public static final String OUTPUT =
-        System.getProperty("meta.data.output");
-
-    public static Map<String, Object> getParameters() {
-        HashMap<String, Object> map = new HashMap<String, Object>();
-        String [] parts = PARAMETERS.split("\\s*;\\s*");
-        for (String part: parts) {
-            String [] kv = part.split("\\s*:\\s*");
-            if (kv.length < 2 || (kv[0] = kv[0].trim()).length() == 0) {
-                continue;
-            }
-            String [] values = kv[1].split("\\s*,\\s*");
-            map.put(kv[0], values.length == 1 ? values[0] : values);
-        }
-        return map;
-    }
-
-    public static void main(String [] args) {
-
-        DataCage dc = new DataCage(
-            DataCage.createBuilder(new File(template)));
-
-        final Document result = XMLUtils.newDocument();
-        final Builder builder = dc.getBuilder();
-
-        if (builder == null) {
-            System.err.println("No builder created");
-            return;
-        }
-
-        final Map<String, Object> parameters = getParameters();
-
-        Session session = SessionFactoryProvider
-            .createSessionFactory()
-            .openSession();
-
-        try {
-            session.doWork(new Work() {
-                @Override
-                public void execute(Connection connection)
-                throws SQLException
-                {
-                    builder.build(connection, result, parameters);
-                }
-
-            });
-        }
-        finally {
-            session.close();
-        }
-
-        OutputStream out;
-
-        if (OUTPUT == null) {
-            out = System.out;
-        }
-        else {
-            try {
-                out = new FileOutputStream(OUTPUT);
-            }
-            catch (IOException ioe) {
-                log.error(ioe);
-                return;
-            }
-        }
-
-        try {
-            XMLUtils.toStream(result, out);
-        }
-        finally {
-            if (OUTPUT != null) {
-                try {
-                    out.close();
-                }
-                catch (IOException ioe) {
-                    log.error(ioe);
-                }
-            }
-        }
-        System.exit(0);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java	Mon Aug 01 08:11:12 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,469 +0,0 @@
-package de.intevation.flys.artifacts.services.meta;
-
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.NodeList;
-import org.w3c.dom.Node;
-import org.w3c.dom.Element;
-
-import javax.xml.xpath.XPath;
-import javax.xml.xpath.XPathFactory;
-import javax.xml.xpath.XPathExpressionException;
-import javax.xml.xpath.XPathConstants;
-
-import java.sql.SQLException;
-import java.sql.Connection;
-
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import org.apache.log4j.Logger;
-
-public class Builder
-{
-    private static Logger log = Logger.getLogger(Builder.class);
-
-    public static final Pattern STRIP_LINE_INDENT =
-        Pattern.compile("\\s*\\r?\\n\\s*");
-
-    public static final String DC_NAMESPACE_URI =
-        "http://www.intevation.org/2011/Datacage";
-
-    private static final Document EVAL_DOCUMENT =
-        XMLUtils.newDocument();
-
-    private static final XPathFactory XPATH_FACTORY =
-        XPathFactory.newInstance();
-
-    protected Document template;
-
-    protected Map<String, CompiledStatement> compiledStatements;
-
-
-    public class BuildHelper
-    {
-        protected Node                                    output;
-        protected Document                                owner;
-        protected StackFrames                             frames;
-        protected Connection                              connection;
-        protected Map<String, CompiledStatement.Instance> statements;
-
-        public BuildHelper(
-            Node                output,
-            Connection          connection,
-            Map<String, Object> parameters
-        ) {
-            this.output     = output;
-            this.connection = connection;
-            frames          = new StackFrames(parameters);
-            statements      = new HashMap<String, CompiledStatement.Instance>();
-            owner           = getOwnerDocument(output);
-        }
-
-        public void build() throws SQLException {
-            try {
-                synchronized (template) {
-                    for (Node current: rootsToList()) {
-                        build(output, current);
-                    }
-                }
-            }
-            finally {
-                closeStatements();
-            }
-        }
-
-        protected void closeStatements() {
-            for (CompiledStatement.Instance csi: statements.values()) {
-                csi.close();
-            }
-            statements.clear();
-        }
-
-        protected void context(Node parent, Element current)
-        throws SQLException
-        {
-            NodeList elements = current.getElementsByTagNameNS(
-                DC_NAMESPACE_URI, "elements");
-
-            if (elements.getLength() < 1) {
-                log.warn("no elements found -> ignore");
-                return;
-            }
-
-            NodeList subs = elements.item(0).getChildNodes();
-            int S = subs.getLength();
-
-            if (S < 1) {
-                log.warn("elements is empty -> ignore");
-                return;
-            }
-
-            NodeList stmntNode = current.getElementsByTagNameNS(
-                DC_NAMESPACE_URI, "statement");
-
-            if (stmntNode.getLength() < 1) {
-                log.warn("dc:context: too less statements");
-                return;
-            }
-
-            String stmntText = stmntNode.item(0).getTextContent();
-
-            CompiledStatement.Instance csi = statements.get(stmntText);
-
-            if (csi == null) {
-                CompiledStatement cs = compiledStatements.get(stmntText);
-                csi = cs.new Instance();
-                statements.put(stmntText, csi);
-            }
-
-            ResultData rd = csi.execute(connection, frames);
-
-            String [] columns = rd.getColumnLabels();
-
-
-            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();
-                }
-            }
-        }
-
-        protected void element(Node parent, Element current)
-        throws SQLException
-        {
-            String attr = expand(current.getAttribute("name"));
-
-            if (log.isDebugEnabled()) {
-                log.debug("dc:element -> '" + attr + "'");
-            }
-
-            if (attr.length() == 0) {
-                log.warn("no name attribute found");
-                return;
-            }
-
-            Element element = owner.createElement(attr);
-
-            NodeList children = current.getChildNodes();
-            for (int i = 0, N = children.getLength(); i < N; ++i) {
-                build(element, children.item(i));
-            }
-
-            parent.appendChild(element);
-        }
-
-        protected void text(Node parent, Element current)
-        throws SQLException
-        {
-            log.debug("dc:text");
-            String value = expand(current.getTextContent());
-            parent.appendChild(owner.createTextNode(value));
-        }
-
-
-        protected void attribute(Node parent, Element current) {
-
-            if (parent.getNodeType() != Node.ELEMENT_NODE) {
-                log.warn("need element here");
-                return;
-            }
-
-            String name  = expand(current.getAttribute("name"));
-            String value = expand(current.getAttribute("value"));
-
-            Element element = (Element)parent;
-
-            element.setAttribute(name, value);
-        }
-
-        protected void callMacro(Node parent, Element current)
-        throws SQLException
-        {
-            String name = current.getAttribute("name");
-
-            if (name.length() == 0) {
-                log.warn("missing 'name' attribute in 'call-macro'");
-                return;
-            }
-
-            NodeList macros = template.getElementsByTagNameNS(
-                DC_NAMESPACE_URI, "macro");
-
-            for (int i = 0, N = macros.getLength(); i < N; ++i) {
-                Element macro = (Element)macros.item(i);
-                if (name.equals(macro.getAttribute("name"))) {
-                    NodeList subs = macro.getChildNodes();
-                    for (int j = 0, M = subs.getLength(); j < M; ++j) {
-                        build(parent, subs.item(j));
-                    }
-                    return;
-                }
-            }
-
-            log.warn("no macro '" + name + "' found.");
-        }
-
-        protected void ifClause(Node parent, Element current)
-        throws SQLException
-        {
-            String test = current.getAttribute("test");
-
-            if (test.length() == 0) {
-                log.warn("missing 'test' attribute in 'if'");
-                return;
-            }
-
-            Boolean result = evaluateXPath(test);
-
-            if (result != null && result.booleanValue()) {
-                NodeList subs = current.getChildNodes();
-                for (int i = 0, N = subs.getLength(); i < N; ++i) {
-                    build(parent, subs.item(i));
-                }
-            }
-        }
-
-        protected void choose(Node parent, Element current)
-        throws SQLException
-        {
-            Node branch = null;
-
-            NodeList children = current.getChildNodes();
-            for (int i = 0, N = children.getLength(); i < N; ++i) {
-                Node child = children.item(i);
-                String ns = child.getNamespaceURI();
-                if (ns == null
-                || !ns.equals(DC_NAMESPACE_URI)
-                || child.getNodeType() != Node.ELEMENT_NODE
-                ) {
-                    continue;
-                }
-                String name = child.getLocalName();
-                if ("when".equals(name)) {
-                    Element when = (Element)child;
-                    String test = when.getAttribute("test");
-                    if (test.length() == 0) {
-                        log.warn("no 'test' attribute found for when");
-                        continue;
-                    }
-
-                    Boolean result = evaluateXPath(test);
-                    if (result != null && result.booleanValue()) {
-                        branch = child;
-                        break;
-                    }
-
-                    continue;
-                }
-                else if ("otherwise".equals(name)) {
-                    branch = child;
-                    // No break here.
-                }
-            }
-
-            if (branch != null) {
-                NodeList subs = branch.getChildNodes();
-                for (int i = 0, N = subs.getLength(); i < N; ++i) {
-                    build(parent, subs.item(i));
-                }
-            }
-        }
-
-        protected Boolean evaluateXPath(String expr) {
-
-            if (log.isDebugEnabled()) {
-                log.debug("evaluate: '" + expr + "'");
-            }
-
-            try {
-                XPath xpath = XPATH_FACTORY.newXPath();
-                xpath.setXPathVariableResolver(frames);
-                xpath.setXPathFunctionResolver(FunctionResolver.FUNCTIONS);
-                Object result = xpath.evaluate(
-                    expr, EVAL_DOCUMENT, XPathConstants.BOOLEAN);
-
-                return result instanceof Boolean
-                    ? (Boolean)result
-                    : null;
-            }
-            catch (XPathExpressionException xfce) {
-                log.error(xfce);
-            }
-            return null;
-        }
-
-        protected void convert(Node parent, Element current) {
-
-            String variable = expand(current.getAttribute("var"));
-            String type     = expand(current.getAttribute("type"));
-
-            if (frames.containsKey(variable)) {
-                Object object = TypeConverter.convert(
-                    frames.get(variable),
-                    type);
-                frames.put(variable, object);
-            }
-        }
-
-        protected String expand(String s) {
-            Matcher m = CompiledStatement.VAR.matcher(s);
-
-            StringBuffer sb = new StringBuffer();
-            while (m.find()) {
-                String key = m.group(1);
-                Object value = frames.get(key);
-                m.appendReplacement(sb, value != null ? value.toString() : "");
-            }
-            m.appendTail(sb);
-            return sb.toString();
-        }
-
-        protected void build(Node parent, Node current)
-        throws SQLException
-        {
-            String ns = current.getNamespaceURI();
-            if (ns != null && ns.equals(DC_NAMESPACE_URI)) {
-                if (current.getNodeType() != Node.ELEMENT_NODE) {
-                    log.warn("need elements here");
-                }
-                else {
-                    String localName = current.getLocalName();
-                    if ("attribute".equals(localName)) {
-                        attribute(parent, (Element)current);
-                    }
-                    else if ("context".equals(localName)) {
-                        context(parent, (Element)current);
-                    }
-                    else if ("if".equals(localName)) {
-                        ifClause(parent, (Element)current);
-                    }
-                    else if ("choose".equals(localName)) {
-                        choose(parent, (Element)current);
-                    }
-                    else if ("call-macro".equals(localName)) {
-                        callMacro(parent, (Element)current);
-                    }
-                    else if ("macro".equals(localName)) {
-                        // simply ignore the definition.
-                    }
-                    else if ("element".equals(localName)) {
-                        element(parent, (Element)current);
-                    }
-                    else if ("text".equals(localName)) {
-                        text(parent, (Element)current);
-                    }
-                    else if ("convert".equals(localName)) {
-                        convert(parent, (Element)current);
-                    }
-                    else {
-                        log.warn("unknown '" + localName + "' -> ignore");
-                    }
-                }
-                return;
-            }
-
-            if (current.getNodeType() == Node.TEXT_NODE) {
-                String txt = current.getNodeValue();
-                if (txt != null && txt.trim().length() == 0) {
-                    return;
-                }
-            }
-
-            Node copy = owner.importNode(current, false);
-
-            NodeList children = current.getChildNodes();
-            for (int i = 0, N = children.getLength(); i < N; ++i) {
-                build(copy, children.item(i));
-            }
-            parent.appendChild(copy);
-        }
-    } // class BuildHelper
-
-
-    public Builder() {
-        compiledStatements = new HashMap<String, CompiledStatement>();
-    }
-
-    public Builder(Document template) {
-        this();
-        this.template = template;
-        compileStatements();
-    }
-
-    protected void compileStatements() {
-
-        NodeList nodes = template.getElementsByTagNameNS(
-            DC_NAMESPACE_URI, "statement");
-
-        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
-            Element stmntElement = (Element)nodes.item(i);
-            String stmnt = trimStatement(stmntElement.getTextContent());
-            if (stmnt == null || stmnt.length() == 0) {
-                throw new IllegalArgumentException("found empty statement");
-            }
-            CompiledStatement cs = new CompiledStatement(stmnt);
-            // for faster lookup store a shortend string into the template
-            stmnt = "s" + i;
-            stmntElement.setTextContent(stmnt);
-            compiledStatements.put(stmnt, cs);
-        }
-    }
-
-    protected List<Node> rootsToList() {
-
-        NodeList roots = template.getElementsByTagNameNS(
-            DC_NAMESPACE_URI, "template");
-
-        List<Node> elements = new ArrayList<Node>();
-
-        for (int i = 0, N = roots.getLength(); i < N; ++i) {
-            NodeList rootChildren = roots.item(i).getChildNodes();
-            for (int j = 0, M = rootChildren.getLength(); j < M; ++j) {
-                Node child = rootChildren.item(j);
-                if (child.getNodeType() == Node.ELEMENT_NODE) {
-                    elements.add(child);
-                }
-            }
-        }
-
-        return elements;
-    }
-
-    protected static final String trimStatement(String stmnt) {
-        if (stmnt == null) return null;
-        //XXX: Maybe a bit to radical for multiline strings?
-        return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" ");
-    }
-
-    protected static Document getOwnerDocument(Node node) {
-        Document document = node.getOwnerDocument();
-        return document != null ? document : (Document)node;
-    }
-
-    public void build(
-        Connection          connection,
-        Node                output,
-        Map<String, Object> parameters
-    )
-    throws SQLException
-    {
-        BuildHelper helper = new BuildHelper(output, connection, parameters);
-
-        helper.build();
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java	Mon Aug 01 08:11:12 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,188 +0,0 @@
-package de.intevation.flys.artifacts.services.meta;
-
-import java.util.regex.Pattern;
-import java.util.regex.Matcher;
-
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.ArrayList;
-
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.sql.Connection;
-import java.sql.ResultSet;
-
-import net.sf.ehcache.Cache;
-import net.sf.ehcache.Element;
-
-import de.intevation.flys.artifacts.cache.CacheFactory;
-
-public class CompiledStatement
-{
-    public static final String DATACAGE_DB_CACHE =
-        "datacage.db";
-
-    public static final Pattern VAR =
-        Pattern.compile("\\$\\{([a-zA-Z0-9_-]+)\\}");
-
-    protected String original;
-    protected String statement;
-
-    protected Map<String, List<Integer>> positions;
-
-    protected int numVars;
-
-    public class Instance {
-
-        protected PreparedStatement preparedStatement;
-
-        public Instance() {
-        }
-
-        protected ResultData executeCached(
-            Cache       cache,
-            Connection  connection,
-            StackFrames frames
-        )
-        throws SQLException
-        {
-            Object [] values = new Object[numVars];
-
-            StringBuilder sb = new StringBuilder(original);
-
-            for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
-                String key   = entry.getKey();
-                Object value = frames.get(key);
-                sb.append(';').append(key).append(':').append(value);
-                for (Integer index: entry.getValue()) {
-                    values[index] = value;
-                }
-            }
-
-            // XXX: Maybe too many collisions?
-            // String key = original + Arrays.hashCode(values);
-            String key = sb.toString();
-
-            Element element = cache.get(key);
-
-            if (element != null) {
-                return (ResultData)element.getValue();
-            }
-
-            if (preparedStatement == null) {
-                preparedStatement = connection.prepareStatement(statement);
-            }
-
-            for (int i = 0; i < values.length; ++i) {
-                preparedStatement.setObject(i+1, values[i]);
-            }
-
-            ResultData data;
-
-            ResultSet result = preparedStatement.executeQuery();
-            try {
-                data = new ResultData(preparedStatement.getMetaData())
-                    .addAll(result);
-            }
-            finally {
-                result.close();
-            }
-
-            element = new Element(key, data);
-            cache.put(element);
-
-            return data;
-        }
-
-        protected ResultData executeUncached(
-            Connection  connection,
-            StackFrames frames
-        ) 
-        throws SQLException
-        {
-            if (preparedStatement == null) {
-                preparedStatement = connection.prepareStatement(statement);
-            }
-
-            for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
-                Object value = frames.get(entry.getKey());
-                for (Integer index: entry.getValue()) {
-                    preparedStatement.setObject(index+1, value);
-                }
-            }
-
-            ResultSet result = preparedStatement.executeQuery();
-            try {
-                return new ResultData(preparedStatement.getMetaData())
-                    .addAll(result);
-            }
-            finally {
-                result.close();
-            }
-        }
-
-        public ResultData execute(Connection connection, StackFrames frames)
-        throws SQLException
-        {
-            Cache cache = CacheFactory.getCache(DATACAGE_DB_CACHE);
-
-            return cache != null
-                ? executeCached(cache, connection, frames)
-                : executeUncached(connection, frames);
-        }
-
-        public void close() {
-            if (preparedStatement != null) {
-                try {
-                    preparedStatement.close();
-                }
-                catch (SQLException sqle) {
-                }
-                preparedStatement = null;
-            }
-        }
-    } // class Instance
-
-    public CompiledStatement() {
-    }
-
-    public CompiledStatement(String original) {
-        this.original = original;
-        // TreeMap to ensure order
-        positions = new TreeMap<String, List<Integer>>();
-        compile();
-    }
-
-    protected void compile() {
-
-        StringBuffer sb = new StringBuffer();
-
-        Matcher m = VAR.matcher(original);
-
-        int index = 0;
-
-        while (m.find()) {
-            String key = m.group(1);
-            List<Integer> indices = positions.get(key);
-            if (indices == null) {
-                indices = new ArrayList<Integer>();
-                positions.put(key, indices);
-            }
-            indices.add(index);
-            m.appendReplacement(sb, "?");
-            ++index;
-        }
-
-        m.appendTail(sb);
-
-        numVars = index;
-
-        statement = sb.toString();
-    }
-
-    public String getStatement() {
-        return statement;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/DataCage.java	Mon Aug 01 08:11:12 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,175 +0,0 @@
-package de.intevation.flys.artifacts.services.meta;
-
-import java.util.Map;
-import java.util.HashMap;
-
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.File;
-
-import java.io.FileInputStream;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-import org.hibernate.Session;
-
-import org.hibernate.jdbc.Work;
-
-import de.intevation.artifacts.common.utils.Config;
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.backend.SessionHolder;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-
-public class DataCage
-{
-    private static Logger log = Logger.getLogger(DataCage.class);
-
-    public static final String XPATH_META_DATA_TEMPLATE =
-        "/artifact-database/metadata/@template";
-
-    private static DataCage INSTANCE;
-
-    protected Builder builder;
-
-    public DataCage() {
-    }
-
-    public DataCage(Builder builder) {
-        this.builder = builder;
-    }
-
-    public Builder getBuilder() {
-        return builder;
-    }
-
-    protected static void artifactToParameters(
-        FLYSArtifact        artifact, 
-        Map<String, Object> parameters
-    ) {
-        parameters.put("current-state-id", artifact.getCurrentStateId());
-
-        for (StateData sd: artifact.getAllData()) {
-            Object value = sd.getValue();
-            if (value == null) {
-                continue;
-            }
-            String key = sd.getName().replace('.', '-');
-            parameters.put(key, value);
-        }
-    }
-
-    public void  recommend(
-        FLYSArtifact        artifact,
-        String []           outs,
-        Map<String, Object> extraParameters,
-        Node                result
-    ) {
-        Map<String, Object> parameters = new HashMap<String, Object>();
-
-        if (extraParameters != null) {
-            parameters.putAll(extraParameters);
-        }
-
-        if (artifact != null) {
-            artifactToParameters(artifact, parameters);
-        }
-
-        parameters.put("artifact-outs", outs);
-
-        parameters.put("parameters", parameters);
-
-        recommend(parameters, result);
-    }
-
-    public void recommend(
-        final Map<String, Object> parameters,
-        final Node                result
-    ) {
-        if (builder == null) {
-            log.error("builder not configured properly.");
-            return;
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-
-        session.doWork(new Work() {
-            @Override
-            public void execute(Connection connection)
-            throws SQLException
-            {
-                builder.build(connection, result, parameters);
-            }
-        });
-    }
-
-    public static synchronized DataCage getInstance() {
-        if (INSTANCE == null) {
-            INSTANCE = createDataCage();
-        }
-        return INSTANCE;
-    }
-
-    protected static Builder createBuilder(File file) {
-        log.debug("DataCage.createBuilder");
-
-        if (!file.isFile() || !file.canRead()) {
-            log.error("Cannot open template file '" + file + "'");
-            return null;
-        }
-
-        InputStream in = null;
-
-        try {
-            in = new FileInputStream(file);
-
-            Document template = XMLUtils.parseDocument(in);
-
-            if (template == null) {
-                log.error("cannot parse meta data template");
-            }
-            else {
-                return new Builder(template);
-            }
-        }
-        catch (IOException ioe) {
-            log.error(ioe);
-        }
-        finally {
-            if (in != null) {
-                try {
-                    in.close();
-                }
-                catch (IOException ioe) {
-                    log.error(ioe);
-                }
-            }
-        }
-        return null;
-    }
-
-    protected static DataCage createDataCage() {
-        log.debug("DataCage.createDataCage");
-
-        String path = Config.getStringXPath(XPATH_META_DATA_TEMPLATE);
-        if (path == null) {
-            log.error("no path to template file given");
-            return null;
-        }
-
-        path = Config.replaceConfigDir(path);
-
-        return new DataCage(createBuilder(new File(path)));
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/FunctionResolver.java	Mon Aug 01 08:11:12 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,103 +0,0 @@
-package de.intevation.flys.artifacts.services.meta;
-
-import java.util.List;
-import java.util.Collection;
-import java.util.Map;
-import java.util.ArrayList;
-
-import javax.xml.xpath.XPathFunctionResolver;
-import javax.xml.xpath.XPathFunction;
-import javax.xml.xpath.XPathFunctionException;
-
-import javax.xml.namespace.QName;
-
-import org.apache.log4j.Logger;
-
-public class FunctionResolver
-implements   XPathFunctionResolver
-{
-    private static Logger log = Logger.getLogger(FunctionResolver.class);
-
-    public static final String FUNCTION_NAMESPACE_URI = "dc";
-
-    public static final class Entry {
-
-        String        name;
-        XPathFunction function;
-        int           arity;
-
-        public Entry() {
-        }
-
-        public Entry(String name, XPathFunction function, int arity) {
-            this.name     = name;
-            this.function = function;
-            this.arity    = arity;
-        }
-    } // class Entry
-
-    public static final FunctionResolver FUNCTIONS = new FunctionResolver();
-
-    static {
-        FUNCTIONS.addFunction("contains", 2, new XPathFunction() {
-            @Override
-            public Object evaluate(List args) throws XPathFunctionException {
-                Object haystack = args.get(0);
-                Object needle   = args.get(1);
-                try {
-                    if (haystack instanceof Collection) {
-                        return Boolean.valueOf(
-                            ((Collection)haystack).contains(needle));
-                    }
-
-                    if (haystack instanceof Map) {
-                        return Boolean.valueOf(
-                            ((Map)haystack).containsKey(needle));
-                    }
-
-                    if (haystack instanceof Object []) {
-                        for (Object straw: (Object [])haystack) {
-                            if (straw.equals(needle)) {
-                                return Boolean.TRUE;
-                            }
-                        }
-                    }
-
-                    return Boolean.FALSE;
-                }
-                catch (Exception e) {
-                    log.error(e);
-                    throw new XPathFunctionException(e);
-                }
-            }
-        });
-    }
-
-    protected List<Entry> functions;
-
-    public FunctionResolver() {
-        functions = new ArrayList<Entry>();
-    }
-
-    public void addFunction(String name, int arity, XPathFunction function) {
-        functions.add(new Entry(name, function, arity));
-    }
-
-    @Override
-    public XPathFunction resolveFunction(QName functionName, int arity) {
-
-        if (!functionName.getNamespaceURI().equals(FUNCTION_NAMESPACE_URI)) {
-            return null;
-        }
-
-        String name = functionName.getLocalPart();
-        for (Entry entry: functions) {
-            if (entry.arity == arity && entry.name.equals(name)) {
-                return entry.function;
-            }
-        }
-
-        return null;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java	Mon Aug 01 08:11:12 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-package de.intevation.flys.artifacts.services.meta;
-
-import java.io.Serializable;
-
-import java.sql.ResultSetMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-
-import java.util.List;
-import java.util.ArrayList;
-
-public class ResultData
-implements   Serializable
-{
-    protected String [] columns;
-
-    protected List<Object []> rows;
-
-    public ResultData() {
-        rows = new ArrayList<Object []>();
-    }
-
-    public ResultData(ResultSetMetaData meta)
-    throws SQLException
-    {
-        this();
-
-        int N = meta.getColumnCount();
-
-        columns = new String[N];
-
-        for (int i = 1; i <= N; ++i) {
-            columns[i-1] = meta.getColumnLabel(i);
-        }
-    }
-
-    public String [] getColumnLabels() {
-        return columns;
-    }
-
-    public ResultData addAll(ResultSet result) throws SQLException {
-        while (result.next()) {
-            add(result);
-        }
-        return this;
-    }
-
-    public void add(ResultSet result) throws SQLException {
-        Object [] row = new Object[columns.length];
-        for (int i = 0; i < columns.length; ++i) {
-            row[i] = result.getObject(i+1);
-        }
-        rows.add(row);
-    }
-
-    public List<Object []> getRows() {
-        return rows;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java	Mon Aug 01 08:11:12 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-package de.intevation.flys.artifacts.services.meta;
-
-import java.util.Map;
-import java.util.List;
-import java.util.HashMap;
-import java.util.ArrayList;
-
-import javax.xml.xpath.XPathVariableResolver;
-
-import javax.xml.namespace.QName;
-
-import org.apache.log4j.Logger;
-
-public class StackFrames
-implements   XPathVariableResolver
-{
-    private static Logger log = Logger.getLogger(StackFrames.class);
-
-    protected List<Map<String, Object>> frames;
-
-    public StackFrames() {
-        frames = new ArrayList<Map<String, Object>>();
-    }
-
-    public StackFrames(Map<String, Object> initialFrame) {
-        this();
-        if (initialFrame != null) {
-            frames.add(new HashMap<String, Object>(initialFrame));
-        }
-    }
-
-    public void enter() {
-        frames.add(new HashMap<String, Object>());
-    }
-
-    public void leave() {
-        frames.remove(frames.size()-1);
-    }
-
-    public void put(String key, Object value) {
-        int N = frames.size();
-        if (N > 0) {
-            frames.get(N-1).put(key, value);
-        }
-    }
-
-    public void put(String [] keys, Object [] values) {
-        Map<String, Object> top = frames.get(frames.size()-1);
-        for (int i = 0; i < keys.length; ++i) {
-            top.put(keys[i], values[i]);
-        }
-    }
-
-    public boolean containsKey(String key) {
-        for (int i = frames.size()-1; i >= 0; --i) {
-            if (frames.get(i).containsKey(key)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    public Object get(String key) {
-        return get(key, null);
-    }
-
-    public Object get(String key, Object def) {
-
-        for (int i = frames.size()-1; i >= 0; --i) {
-            Map<String, Object> frame = frames.get(i);
-            if (frame.containsKey(key)) {
-                return frame.get(key);
-            }
-        }
-
-        return def;
-    }
-
-    @Override
-    public Object resolveVariable(QName variableName) {
-        if (log.isDebugEnabled()) {
-            log.debug("resolve var: " + variableName);
-        }
-        return get(variableName.getLocalPart());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/TypeConverter.java	Mon Aug 01 08:11:12 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-package de.intevation.flys.artifacts.services.meta;
-
-public class TypeConverter
-{
-    private TypeConverter() {
-    }
-
-    public static Object convert(Object object, String type) {
-
-        if (type == null) {
-            return object;
-        }
-
-        if ("Integer".equals(type)) {
-            return Integer.valueOf(object.toString());
-        }
-
-        if ("Double".equals(type)) {
-            return Double.valueOf(object.toString());
-        }
-
-        if ("String".equals(type)) {
-            return object.toString();
-        }
-
-        // TODO: Add more types
-
-        return object;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org