Mercurial > dive4elements > river
diff flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java @ 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 | |
children | aca3b46160cb |
line wrap: on
line diff
--- /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 :