sascha@372: package de.intevation.flys.artifacts.services.meta; sascha@372: sascha@972: import java.util.regex.Pattern; sascha@372: import java.util.regex.Matcher; sascha@372: sascha@372: import java.util.ArrayList; sascha@372: import java.util.List; sascha@372: import java.util.HashMap; sascha@372: import java.util.Map; sascha@372: sascha@372: import org.w3c.dom.Document; sascha@372: import org.w3c.dom.NodeList; sascha@372: import org.w3c.dom.Node; sascha@372: import org.w3c.dom.Element; sascha@372: sascha@957: import javax.xml.xpath.XPath; sascha@957: import javax.xml.xpath.XPathFactory; sascha@957: import javax.xml.xpath.XPathExpressionException; sascha@957: import javax.xml.xpath.XPathConstants; sascha@957: sascha@372: import java.sql.SQLException; sascha@372: import java.sql.Connection; sascha@372: sascha@372: import de.intevation.artifacts.common.utils.XMLUtils; sascha@372: sascha@372: import org.apache.log4j.Logger; sascha@372: sascha@372: public class Builder sascha@372: { sascha@372: private static Logger log = Logger.getLogger(Builder.class); sascha@372: sascha@972: public static final Pattern STRIP_LINE_INDENT = sascha@972: Pattern.compile("\\s*\\r?\\n\\s*"); sascha@972: sascha@372: public static final String DC_NAMESPACE_URI = sascha@372: "http://www.intevation.org/2011/Datacage"; sascha@372: sascha@957: private static final Document EVAL_DOCUMENT = sascha@957: XMLUtils.newDocument(); sascha@957: sascha@957: private static final XPathFactory XPATH_FACTORY = sascha@957: XPathFactory.newInstance(); sascha@957: sascha@415: protected Document template; sascha@372: sascha@963: sascha@372: public class BuildHelper sascha@372: { sascha@963: protected Node output; sascha@963: protected Document owner; sascha@415: protected StackFrames frames; sascha@415: protected Connection connection; sascha@372: protected Map statements; sascha@372: sascha@415: public BuildHelper( sascha@963: Node output, sascha@415: Connection connection, sascha@415: Map parameters sascha@415: ) { sascha@372: this.output = output; sascha@372: this.connection = connection; sascha@415: frames = new StackFrames(parameters); sascha@415: statements = new HashMap(); sascha@963: owner = getOwnerDocument(output); sascha@372: } sascha@372: sascha@372: public void build(List elements) throws SQLException { sascha@372: try { sascha@372: for (Node current: elements) { sascha@372: build(output, current); sascha@372: } sascha@372: } sascha@372: finally { sascha@372: for (CompiledStatement cs: statements.values()) { sascha@372: cs.close(); sascha@372: } sascha@372: statements.clear(); sascha@372: } sascha@372: } sascha@372: sascha@372: protected void context(Node parent, Element current) sascha@372: throws SQLException sascha@372: { sascha@372: NodeList elements = current.getElementsByTagNameNS( sascha@372: DC_NAMESPACE_URI, "elements"); sascha@372: sascha@372: if (elements.getLength() < 1) { sascha@372: log.warn("no elements found -> ignore"); sascha@372: return; sascha@372: } sascha@372: sascha@372: NodeList subs = elements.item(0).getChildNodes(); sascha@372: int S = subs.getLength(); sascha@372: sascha@372: if (S < 1) { sascha@372: log.warn("elements is empty -> ignore"); sascha@372: return; sascha@372: } sascha@372: sascha@372: NodeList stmntNode = current.getElementsByTagNameNS( sascha@372: DC_NAMESPACE_URI, "statement"); sascha@372: sascha@372: if (stmntNode.getLength() < 1) { sascha@372: log.warn("dc:context: too less statements"); sascha@372: return; sascha@372: } sascha@372: sascha@372: String stmntText = stmntNode.item(0).getTextContent(); sascha@372: sascha@415: if (stmntText == null sascha@972: || (stmntText = trimStatement(stmntText)).length() == 0) { sascha@415: log.warn("dc:context: no sql statement found -> ignored"); sascha@415: return; sascha@372: } sascha@372: sascha@372: CompiledStatement cs = statements.get(stmntText); sascha@372: sascha@372: if (cs == null) { sascha@372: cs = new CompiledStatement(stmntText); sascha@372: statements.put(stmntText, cs); sascha@372: } sascha@372: sascha@372: ResultData rd = cs.execute(connection, frames); sascha@372: sascha@372: String [] columns = rd.getColumnLabels(); sascha@372: sascha@372: sascha@372: for (Object [] row: rd.getRows()) { sascha@372: frames.enter(); sascha@372: try { sascha@372: frames.put(columns, row); sascha@372: for (int i = 0; i < S; ++i) { sascha@372: build(parent, subs.item(i)); sascha@372: } sascha@372: } sascha@372: finally { sascha@372: frames.leave(); sascha@372: } sascha@372: } sascha@372: } sascha@372: sascha@372: protected void element(Node parent, Element current) sascha@372: throws SQLException sascha@372: { sascha@372: String attr = expand(current.getAttribute("name")); sascha@372: sascha@372: if (log.isDebugEnabled()) { sascha@372: log.debug("dc:element -> '" + attr + "'"); sascha@372: } sascha@372: sascha@372: if (attr.length() == 0) { sascha@372: log.warn("no name attribute found"); sascha@372: return; sascha@372: } sascha@372: sascha@963: Element element = owner.createElement(attr); sascha@372: sascha@372: NodeList children = current.getChildNodes(); sascha@372: for (int i = 0, N = children.getLength(); i < N; ++i) { sascha@372: build(element, children.item(i)); sascha@372: } sascha@372: sascha@372: parent.appendChild(element); sascha@372: } sascha@372: sascha@372: protected void text(Node parent, Element current) sascha@372: throws SQLException sascha@372: { sascha@372: log.debug("dc:text"); sascha@372: String value = expand(current.getTextContent()); sascha@963: parent.appendChild(owner.createTextNode(value)); sascha@372: } sascha@372: sascha@372: sascha@372: protected void attribute(Node parent, Element current) { sascha@372: sascha@372: if (parent.getNodeType() != Node.ELEMENT_NODE) { sascha@372: log.warn("need element here"); sascha@372: return; sascha@372: } sascha@372: sascha@372: String name = expand(current.getAttribute("name")); sascha@372: String value = expand(current.getAttribute("value")); sascha@372: sascha@372: Element element = (Element)parent; sascha@372: sascha@372: element.setAttribute(name, value); sascha@372: } sascha@372: sascha@962: protected void callMacro(Node parent, Element current) sascha@962: throws SQLException sascha@962: { sascha@962: String name = current.getAttribute("name"); sascha@962: sascha@962: if (name.length() == 0) { sascha@962: log.warn("missing 'name' attribute in 'call-macro'"); sascha@962: return; sascha@962: } sascha@962: sascha@962: NodeList macros = template.getElementsByTagNameNS( sascha@962: DC_NAMESPACE_URI, "macro"); sascha@962: sascha@962: for (int i = 0, N = macros.getLength(); i < N; ++i) { sascha@962: Element macro = (Element)macros.item(i); sascha@962: if (name.equals(macro.getAttribute("name"))) { sascha@962: NodeList subs = macro.getChildNodes(); sascha@962: for (int j = 0, M = subs.getLength(); j < M; ++j) { sascha@962: build(parent, subs.item(j)); sascha@962: } sascha@962: return; sascha@962: } sascha@962: } sascha@962: sascha@962: log.warn("no macro '" + name + "' found."); sascha@962: } sascha@962: sascha@961: protected void ifClause(Node parent, Element current) sascha@961: throws SQLException sascha@961: { sascha@961: String test = current.getAttribute("test"); sascha@961: sascha@961: if (test.length() == 0) { sascha@961: log.warn("missing 'test' attribute in 'if'"); sascha@961: return; sascha@961: } sascha@961: sascha@961: Boolean result = evaluateXPath(test); sascha@961: sascha@961: if (result != null && result.booleanValue()) { sascha@961: NodeList subs = current.getChildNodes(); sascha@961: for (int i = 0, N = subs.getLength(); i < N; ++i) { sascha@961: build(parent, subs.item(i)); sascha@961: } sascha@961: } sascha@961: } sascha@961: sascha@957: protected void choose(Node parent, Element current) sascha@957: throws SQLException sascha@957: { sascha@957: Node branch = null; sascha@957: sascha@957: NodeList children = current.getChildNodes(); sascha@957: for (int i = 0, N = children.getLength(); i < N; ++i) { sascha@957: Node child = children.item(i); sascha@957: String ns = child.getNamespaceURI(); sascha@957: if (ns == null sascha@957: || !ns.equals(DC_NAMESPACE_URI) sascha@957: || child.getNodeType() != Node.ELEMENT_NODE sascha@957: ) { sascha@957: continue; sascha@957: } sascha@957: String name = child.getLocalName(); sascha@957: if ("when".equals(name)) { sascha@957: Element when = (Element)child; sascha@957: String test = when.getAttribute("test"); sascha@957: if (test.length() == 0) { sascha@957: log.warn("no 'test' attribute found for when"); sascha@957: continue; sascha@957: } sascha@957: sascha@959: Boolean result = evaluateXPath(test); sascha@959: if (result != null && result.booleanValue()) { sascha@959: branch = child; sascha@959: break; sascha@959: } sascha@957: sascha@957: continue; sascha@957: } sascha@957: else if ("otherwise".equals(name)) { sascha@957: branch = child; sascha@957: // No break here. sascha@957: } sascha@957: } sascha@957: sascha@957: if (branch != null) { sascha@957: NodeList subs = branch.getChildNodes(); sascha@957: for (int i = 0, N = subs.getLength(); i < N; ++i) { sascha@957: build(parent, subs.item(i)); sascha@957: } sascha@957: } sascha@957: } sascha@957: sascha@959: protected Boolean evaluateXPath(String expr) { sascha@959: try { sascha@959: XPath xpath = XPATH_FACTORY.newXPath(); sascha@959: xpath.setXPathVariableResolver(frames); sascha@959: xpath.setXPathFunctionResolver(FunctionResolver.FUNCTIONS); sascha@959: Object result = xpath.evaluate( sascha@959: expr, EVAL_DOCUMENT, XPathConstants.BOOLEAN); sascha@959: sascha@959: return result instanceof Boolean sascha@959: ? (Boolean)result sascha@959: : null; sascha@959: } sascha@959: catch (XPathExpressionException xfce) { sascha@959: log.error(xfce); sascha@959: } sascha@959: return null; sascha@959: } sascha@959: sascha@415: protected void convert(Node parent, Element current) { sascha@415: sascha@415: String variable = expand(current.getAttribute("var")); sascha@415: String type = expand(current.getAttribute("type")); sascha@415: sascha@415: if (frames.containsKey(variable)) { sascha@415: Object object = TypeConverter.convert( sascha@415: frames.get(variable), sascha@415: type); sascha@415: frames.put(variable, object); sascha@415: } sascha@415: } sascha@415: sascha@372: protected String expand(String s) { sascha@372: Matcher m = CompiledStatement.VAR.matcher(s); sascha@372: sascha@372: StringBuffer sb = new StringBuffer(); sascha@372: while (m.find()) { sascha@372: String key = m.group(1); sascha@372: Object value = frames.get(key); sascha@372: m.appendReplacement(sb, value != null ? value.toString() : ""); sascha@372: } sascha@372: m.appendTail(sb); sascha@372: return sb.toString(); sascha@372: } sascha@372: sascha@742: protected void build(Node parent, Node current) sascha@372: throws SQLException sascha@372: { sascha@372: String ns = current.getNamespaceURI(); sascha@372: if (ns != null && ns.equals(DC_NAMESPACE_URI)) { sascha@372: if (current.getNodeType() != Node.ELEMENT_NODE) { sascha@372: log.warn("need elements here"); sascha@372: } sascha@372: else { sascha@372: String localName = current.getLocalName(); sascha@972: if ("attribute".equals(localName)) { sascha@972: attribute(parent, (Element)current); sascha@972: } sascha@972: else if ("context".equals(localName)) { sascha@372: context(parent, (Element)current); sascha@372: } sascha@972: else if ("if".equals(localName)) { sascha@972: ifClause(parent, (Element)current); sascha@972: } sascha@957: else if ("choose".equals(localName)) { sascha@957: choose(parent, (Element)current); sascha@957: } sascha@972: else if ("call-macro".equals(localName)) { sascha@972: callMacro(parent, (Element)current); sascha@961: } sascha@972: else if ("macro".equals(localName)) { sascha@972: // simply ignore the definition. sascha@372: } sascha@372: else if ("element".equals(localName)) { sascha@372: element(parent, (Element)current); sascha@372: } sascha@372: else if ("text".equals(localName)) { sascha@372: text(parent, (Element)current); sascha@372: } sascha@415: else if ("convert".equals(localName)) { sascha@415: convert(parent, (Element)current); sascha@415: } sascha@372: else { sascha@372: log.warn("unknown '" + localName + "' -> ignore"); sascha@372: } sascha@372: } sascha@372: return; sascha@372: } sascha@372: sascha@410: if (current.getNodeType() == Node.TEXT_NODE) { sascha@410: String txt = current.getNodeValue(); sascha@410: if (txt != null && txt.trim().length() == 0) { sascha@410: return; sascha@410: } sascha@410: } sascha@410: sascha@963: Node copy = owner.importNode(current, false); sascha@372: sascha@372: NodeList children = current.getChildNodes(); sascha@372: for (int i = 0, N = children.getLength(); i < N; ++i) { sascha@372: build(copy, children.item(i)); sascha@372: } sascha@372: parent.appendChild(copy); sascha@372: } sascha@372: } // class BuildHelper sascha@372: sascha@372: sascha@372: public Builder() { sascha@372: } sascha@372: sascha@372: public Builder(Document template) { sascha@372: this.template = template; sascha@372: } sascha@372: sascha@963: protected static List rootsToList(NodeList roots) { sascha@372: sascha@372: List elements = new ArrayList(); sascha@372: sascha@372: for (int i = 0, N = roots.getLength(); i < N; ++i) { sascha@372: NodeList rootChildren = roots.item(i).getChildNodes(); sascha@372: for (int j = 0, M = rootChildren.getLength(); j < M; ++j) { sascha@372: Node child = rootChildren.item(j); sascha@372: if (child.getNodeType() == Node.ELEMENT_NODE) { sascha@372: elements.add(child); sascha@372: } sascha@372: } sascha@372: } sascha@372: sascha@963: return elements; sascha@963: } sascha@963: sascha@972: protected static final String trimStatement(String stmnt) { sascha@972: //XXX: Maybe a bit to radical for multiline strings? sascha@972: return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" "); sascha@972: } sascha@972: sascha@972: protected static Document getOwnerDocument(Node node) { sascha@972: Document document = node.getOwnerDocument(); sascha@972: return document != null ? document : (Document)node; sascha@972: } sascha@972: sascha@963: public void build( sascha@963: Connection connection, sascha@963: Node output, sascha@963: Map parameters sascha@963: ) sascha@963: throws SQLException sascha@963: { sascha@963: BuildHelper helper = new BuildHelper(output, connection, parameters); sascha@963: sascha@963: NodeList roots = template.getElementsByTagNameNS( sascha@963: DC_NAMESPACE_URI, "template"); sascha@963: sascha@963: helper.build(rootsToList(roots)); sascha@372: } sascha@372: } sascha@372: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :