Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java @ 1072:80aecb01d79a
Let MainValuesArtifact have a state, outputmode and facet.
flys-artifacts/trunk@2556 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Felix Wolfsteller <felix.wolfsteller@intevation.de> |
---|---|
date | Wed, 24 Aug 2011 12:07:33 +0000 |
parents | 3f3988bb6284 |
children | 3ca999f507b7 |
line wrap: on
line source
package de.intevation.flys.artifacts.datacage.templating; import java.util.regex.Pattern; import java.util.regex.Matcher; import java.util.Arrays; import java.util.ArrayList; import java.util.List; import java.util.HashMap; import java.util.Map; import java.util.Deque; import java.util.ArrayDeque; 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 String CONNECTION_USER = "user"; public static final String CONNECTION_SYSTEM = "system"; public static final String DEFAULT_CONNECTION_NAME = CONNECTION_SYSTEM; 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 static class NamedConnection { protected String name; protected Connection connection; protected boolean cached; public NamedConnection() { } public NamedConnection( String name, Connection connection ) { this(name, connection, true); } public NamedConnection( String name, Connection connection, boolean cached ) { this.name = name; this.connection = connection; this.cached = cached; } } // class NamedConnection public class BuildHelper { protected Node output; protected Document owner; protected StackFrames frames; protected List<NamedConnection> connections; protected Map<String, CompiledStatement.Instance> statements; protected Deque<NamedConnection> connectionsStack; protected Deque<ResultData> resultsStack; public BuildHelper( Node output, List<NamedConnection> connections, Map<String, Object> parameters ) { if (connections.isEmpty()) { throw new IllegalArgumentException("no connections given."); } this.connections = connections; connectionsStack = new ArrayDeque<NamedConnection>(); resultsStack = new ArrayDeque<ResultData>(); this.output = output; frames = new StackFrames(parameters); owner = getOwnerDocument(output); statements = new HashMap<String, CompiledStatement.Instance>(); } 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 { log.debug("dc:context"); NodeList subs = current.getChildNodes(); int S = subs.getLength(); // check only direct children Node stmntNode = null; for (int i = 0; i < S; ++i) { Node node = subs.item(i); String ns; if (node.getNodeType() == Node.ELEMENT_NODE && node.getLocalName().equals("statement") && (ns = node.getNamespaceURI()) != null && ns.equals(DC_NAMESPACE_URI)) { stmntNode = node; break; } } if (stmntNode == null) { log.warn("dc:context: cannot find statement"); return; } String con = current.getAttribute("connection"); String stmntText = stmntNode.getTextContent(); String key = con + "-" + stmntText; CompiledStatement.Instance csi = statements.get(key); if (csi == null) { CompiledStatement cs = compiledStatements.get(stmntText); csi = cs.new Instance(); statements.put(key, csi); } NamedConnection connection = connectionsStack.isEmpty() ? connections.get(0) : connectionsStack.peek(); if (con.length() > 0) { for (NamedConnection nc: connections) { if (con.equals(nc.name)) { connection = nc; break; } } } ResultData rd = csi.execute( connection.connection, frames, connection.cached); // only descent if there are results if (!rd.isEmpty()) { resultsStack.push(rd); connectionsStack.push(connection); try { for (int i = 0; i < S; ++i) { build(parent, subs.item(i)); } } finally { connectionsStack.pop(); resultsStack.pop(); } } } protected void elements(Node parent, Element current) throws SQLException { log.debug("dc:elements"); if (resultsStack.isEmpty()) { log.warn("dc:elements without having results"); return; } NodeList subs = current.getChildNodes(); int S = subs.getLength(); if (S == 0) { log.debug("dc:elements has no children"); return; } ResultData rd = resultsStack.peek(); String [] columns = rd.getColumnLabels(); //if (log.isDebugEnabled()) { // log.debug("pushing vars: " // + java.util.Arrays.toString(columns)); //} for (Object [] row: rd.getRows()) { frames.enter(); try { frames.put(columns, row); //if (log.isDebugEnabled()) { // log.debug("current vars: " + frames.dump()); //} 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 ("elements".equals(localName)) { elements(parent, (Element)current); } else if ("text".equals(localName)) { text(parent, (Element)current); } else if ("comment".equals(localName) || "statement".equals(localName)) { // ignore comments and statements in output } 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; } private static final List<NamedConnection> wrap(Connection connection) { List<NamedConnection> list = new ArrayList<NamedConnection>(1); list.add(new NamedConnection(DEFAULT_CONNECTION_NAME, connection)); return list; } public void build( Connection connection, Node output, Map<String, Object> parameters ) throws SQLException { build(wrap(connection), output, parameters); } public void build( List<NamedConnection> connections, Node output, Map<String, Object> parameters ) throws SQLException { BuildHelper helper = new BuildHelper(output, connections, parameters); helper.build(); } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :