Mercurial > dive4elements > river
view flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java @ 4143:58864f4f6e3b
Fix issue946 (own style for wkms annotation facets in wq diagrams).
author | Felix Wolfsteller <felix.wolfsteller@intevation.de> |
---|---|
date | Tue, 16 Oct 2012 11:15:12 +0200 |
parents | 58bdf95df5e4 |
children | daf0919df76d |
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.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 de.intevation.flys.utils.Pair; 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<Pair<NamedConnection, ResultData>> connectionsStack; 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<Pair<NamedConnection, 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(); } /** * Handle a \<context\> node. */ 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 stmntText = stmntNode.getTextContent(); String con = current.getAttribute("connection"); 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().getA(); 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()) { connectionsStack.push( new Pair<NamedConnection, ResultData>(connection, rd)); try { for (int i = 0; i < S; ++i) { build(parent, subs.item(i)); } } finally { connectionsStack.pop(); } } } /** * Kind of foreach over results of a statement within a context. */ protected void elements(Node parent, Element current) throws SQLException { log.debug("dc:elements"); if (connectionsStack.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 = connectionsStack.peek().getB(); 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(); } } } /** * Create element. */ 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)); } /** * Add attribute to an element * @see Element */ 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("expression: " + expr, xfce); } return null; } protected void convert(Node parent, Element current) { String variable = expand(current.getAttribute("var")); String type = expand(current.getAttribute("type")); Object [] result = new Object[1]; if (frames.getStore(variable, result)) { Object object = TypeConverter.convert(result[0], type); frames.put(variable.toUpperCase(), object); } } protected String expand(String s) { Matcher m = CompiledStatement.VAR.matcher(s); Object [] result = new Object[1]; StringBuffer sb = new StringBuffer(); while (m.find()) { String key = m.group(1); result[0] = null; if (frames.getStore(key, result)) { m.appendReplacement( sb, result[0] != null ? result[0].toString() : ""); } else { m.appendReplacement(sb, "\\${" + key + "}"); } } 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 :