sascha@998: package de.intevation.flys.artifacts.datacage.templating; sascha@998: sascha@998: import de.intevation.artifacts.common.utils.XMLUtils; sascha@998: sascha@1818: import de.intevation.flys.utils.Pair; sascha@1818: teichmann@4740: import java.sql.Connection; teichmann@4740: import java.sql.SQLException; teichmann@4740: teichmann@4740: import java.util.ArrayDeque; teichmann@4740: import java.util.ArrayList; teichmann@4740: import java.util.Deque; teichmann@4740: import java.util.HashMap; teichmann@4740: import java.util.List; teichmann@4740: import java.util.Map; teichmann@4740: teichmann@4740: import java.util.regex.Matcher; teichmann@4740: import java.util.regex.Pattern; teichmann@4740: teichmann@4740: import javax.xml.namespace.QName; teichmann@4740: teichmann@4740: import javax.xml.xpath.XPath; teichmann@4740: import javax.xml.xpath.XPathConstants; teichmann@5449: import javax.xml.xpath.XPathExpression; teichmann@4740: import javax.xml.xpath.XPathExpressionException; teichmann@4740: import javax.xml.xpath.XPathFactory; teichmann@4740: sascha@998: import org.apache.log4j.Logger; sascha@998: teichmann@5485: import org.w3c.dom.Attr; teichmann@4740: import org.w3c.dom.Document; teichmann@4740: import org.w3c.dom.Element; teichmann@4740: import org.w3c.dom.Node; teichmann@4740: import org.w3c.dom.NodeList; aheinecke@5494: import org.w3c.dom.NamedNodeMap; teichmann@4740: felix@4526: felix@4897: /** Handles and evaluate meta-data template against dbs. */ sascha@998: public class Builder sascha@998: { sascha@998: private static Logger log = Logger.getLogger(Builder.class); sascha@998: sascha@998: public static final Pattern STRIP_LINE_INDENT = sascha@998: Pattern.compile("\\s*\\r?\\n\\s*"); sascha@998: teichmann@5485: public static final Pattern BRACKET_XPATH = teichmann@5485: Pattern.compile("\\{([^}]+)\\}"); teichmann@5485: sascha@998: public static final String DC_NAMESPACE_URI = sascha@998: "http://www.intevation.org/2011/Datacage"; sascha@998: sascha@998: private static final Document EVAL_DOCUMENT = sascha@998: XMLUtils.newDocument(); sascha@998: sascha@998: private static final XPathFactory XPATH_FACTORY = sascha@998: XPathFactory.newInstance(); sascha@998: sascha@998: protected Document template; sascha@998: sascha@998: protected Map compiledStatements; sascha@998: teichmann@5443: protected Map macros; teichmann@5443: felix@4526: /** Connection to either of the databases. */ sascha@1011: public static class NamedConnection { sascha@1011: sascha@1011: protected String name; sascha@1011: protected Connection connection; sascha@1011: protected boolean cached; sascha@1011: sascha@1011: public NamedConnection() { sascha@1011: } sascha@1011: sascha@1011: public NamedConnection( sascha@3076: String name, sascha@1011: Connection connection sascha@1011: ) { sascha@1011: this(name, connection, true); sascha@1011: } sascha@1011: sascha@1011: public NamedConnection( sascha@3076: String name, sascha@1011: Connection connection, sascha@1011: boolean cached sascha@1011: ) { sascha@1011: this.name = name; sascha@1011: this.connection = connection; sascha@1011: this.cached = cached; sascha@1011: } sascha@1011: } // class NamedConnection sascha@1011: sascha@998: public class BuildHelper sascha@998: { sascha@1818: protected Node output; sascha@1818: protected Document owner; sascha@1818: protected StackFrames frames; sascha@1818: protected List connections; sascha@1818: protected Map statements; sascha@1818: protected Deque> connectionsStack; teichmann@4890: protected Deque macroBodies; teichmann@5442: protected FunctionResolver functionResolver; teichmann@5449: protected Map expressions; teichmann@5449: sascha@998: sascha@998: public BuildHelper( sascha@1011: Node output, sascha@1011: List connections, sascha@1011: Map parameters sascha@998: ) { sascha@1011: if (connections.isEmpty()) { sascha@1011: throw new IllegalArgumentException("no connections given."); sascha@1011: } sascha@1011: sascha@1011: this.connections = connections; sascha@1818: connectionsStack = sascha@1818: new ArrayDeque>(); sascha@1011: this.output = output; sascha@1011: frames = new StackFrames(parameters); sascha@1011: owner = getOwnerDocument(output); teichmann@5443: macroBodies = new ArrayDeque(); teichmann@5443: functionResolver = new FunctionResolver(this); teichmann@5449: expressions = new HashMap(); teichmann@5443: statements = sascha@1011: new HashMap(); sascha@998: } sascha@998: sascha@998: public void build() throws SQLException { sascha@998: try { teichmann@5779: // XXX: Thread safety is now established by the builder pool. teichmann@5779: //synchronized (template) { sascha@998: for (Node current: rootsToList()) { sascha@998: build(output, current); sascha@998: } teichmann@5779: //} sascha@998: } sascha@998: finally { sascha@998: closeStatements(); sascha@998: } sascha@998: } sascha@998: sascha@998: protected void closeStatements() { sascha@998: for (CompiledStatement.Instance csi: statements.values()) { sascha@998: csi.close(); sascha@998: } sascha@998: statements.clear(); sascha@998: } sascha@998: felix@1890: /** felix@4896: * Return first statement node in NodeList, respecting felix@4897: * macros but not doing evaluation (e.g. of s). felix@4896: */ felix@4896: private Node findStatementNode(NodeList nodes) { felix@4896: int S = nodes.getLength(); felix@4896: felix@4896: // Check direct children and take special care of macros. felix@4896: for (int i = 0; i < S; ++i) { felix@4896: Node node = nodes.item(i); felix@4896: String ns; felix@4896: // Regular statement node. felix@4896: if (node.getNodeType() == Node.ELEMENT_NODE felix@4896: && node.getLocalName().equals("statement") felix@4896: && (ns = node.getNamespaceURI()) != null felix@4896: && ns.equals(DC_NAMESPACE_URI)) { felix@4896: return node; felix@4896: } felix@4896: // Macro node. Descend. felix@4896: else if (node.getNodeType() == Node.ELEMENT_NODE felix@4896: && node.getLocalName().equals("call-macro") felix@4896: && (ns = node.getNamespaceURI()) != null felix@4896: && ns.equals(DC_NAMESPACE_URI)) { felix@4896: felix@4896: String macroName = ((Element)node).getAttribute("name"); felix@4896: Node inMacroNode = felix@4896: findStatementNode(getMacroChildren(macroName)); felix@4896: if (inMacroNode != null) { felix@4896: return inMacroNode; felix@4896: } felix@4896: } felix@4896: felix@4896: } felix@4896: felix@4896: return null; felix@4896: } felix@4896: felix@4896: /** teichmann@5509: * Handle a dc:context node. felix@1890: */ sascha@998: protected void context(Node parent, Element current) sascha@998: throws SQLException sascha@998: { sascha@1023: log.debug("dc:context"); sascha@998: sascha@1023: NodeList subs = current.getChildNodes(); felix@4896: Node stmntNode = findStatementNode(subs); sascha@1023: int S = subs.getLength(); sascha@1023: sascha@1023: if (stmntNode == null) { sascha@1023: log.warn("dc:context: cannot find statement"); sascha@998: return; sascha@998: } sascha@998: felix@1890: String stmntText = stmntNode.getTextContent(); felix@1890: sascha@1011: String con = current.getAttribute("connection"); sascha@1011: sascha@1011: String key = con + "-" + stmntText; sascha@1011: sascha@1011: CompiledStatement.Instance csi = statements.get(key); sascha@998: sascha@998: if (csi == null) { sascha@998: CompiledStatement cs = compiledStatements.get(stmntText); sascha@998: csi = cs.new Instance(); sascha@1011: statements.put(key, csi); sascha@998: } sascha@998: sascha@1011: NamedConnection connection = connectionsStack.isEmpty() sascha@1011: ? connections.get(0) sascha@1818: : connectionsStack.peek().getA(); sascha@998: sascha@1011: if (con.length() > 0) { sascha@1011: for (NamedConnection nc: connections) { sascha@1011: if (con.equals(nc.name)) { sascha@1011: connection = nc; sascha@1011: break; sascha@998: } sascha@998: } sascha@1011: } sascha@1011: sascha@1023: ResultData rd = csi.execute( sascha@1023: connection.connection, sascha@1023: frames, sascha@1023: connection.cached); sascha@1011: sascha@1023: // only descent if there are results sascha@1023: if (!rd.isEmpty()) { teichmann@4051: connectionsStack.push( teichmann@4051: new Pair(connection, rd)); sascha@1023: try { sascha@1023: for (int i = 0; i < S; ++i) { sascha@1023: build(parent, subs.item(i)); sascha@1011: } sascha@998: } sascha@1023: finally { sascha@1023: connectionsStack.pop(); sascha@1023: } sascha@998: } sascha@1023: } sascha@1023: teichmann@5433: public boolean hasResult() { teichmann@5433: return !connectionsStack.isEmpty() teichmann@5433: && !connectionsStack.peek().getB().isEmpty(); teichmann@5433: } rrenkert@5305: rrenkert@5305: protected ResultData createFilteredResultData(ResultData rd, String filter) { rrenkert@5305: if (filter == null) return rd; rrenkert@5305: rrenkert@5305: List rows = rd.getRows(); rrenkert@5305: String [] columns = rd.getColumnLabels(); rrenkert@5305: rrenkert@5305: List filtered = new ArrayList(rows.size()); rrenkert@5305: rrenkert@5305: for (Object [] row: rows) { rrenkert@5305: frames.enter(); rrenkert@5305: try { rrenkert@5305: frames.put(columns, row); rrenkert@5305: boolean traverse = filter == null; rrenkert@5305: rrenkert@5305: if (!traverse) { rrenkert@5305: Boolean b = evaluateXPathToBoolean(filter); rrenkert@5305: traverse = b != null && b; rrenkert@5305: } rrenkert@5305: if (traverse) { rrenkert@5305: filtered.add(row); rrenkert@5305: } rrenkert@5305: } rrenkert@5305: finally { rrenkert@5305: frames.leave(); rrenkert@5305: } rrenkert@5305: } rrenkert@5305: return new ResultData(rd.getColumnLabels(), filtered); rrenkert@5305: } rrenkert@5305: teichmann@5509: protected void filter(Node parent, Element current) teichmann@5509: throws SQLException teichmann@5509: { teichmann@5509: String expr = current.getAttribute("expr"); teichmann@5509: teichmann@5509: if ((expr = expr.trim()).length() == 0) { teichmann@5509: expr = null; teichmann@5509: } teichmann@5509: teichmann@5509: NodeList subs = current.getChildNodes(); teichmann@5509: int S = subs.getLength(); teichmann@5509: if (S == 0) { teichmann@5509: log.debug("dc:filter has no children"); teichmann@5509: return; teichmann@5509: } teichmann@5509: teichmann@5509: ResultData orig = null; teichmann@5509: Pair pair = null; teichmann@5509: teichmann@5509: if (expr != null && !connectionsStack.isEmpty()) { teichmann@5509: pair = connectionsStack.peek(); teichmann@5509: orig = pair.getB(); teichmann@5509: pair.setB(createFilteredResultData(orig, expr)); teichmann@5509: } teichmann@5509: teichmann@5509: try { teichmann@5509: for (int i = 0; i < S; ++i) { teichmann@5509: build(parent, subs.item(i)); teichmann@5509: } teichmann@5509: } teichmann@5509: finally { teichmann@5509: if (orig != null) { teichmann@5509: pair.setB(orig); teichmann@5509: } teichmann@5509: } teichmann@5509: } teichmann@5509: felix@1890: /** felix@1890: * Kind of foreach over results of a statement within a context. felix@1890: */ teichmann@5522: protected void foreach(Node parent, Element current) sascha@1023: throws SQLException sascha@1023: { teichmann@5522: log.debug("dc:for-each"); sascha@1023: sascha@1818: if (connectionsStack.isEmpty()) { teichmann@5525: log.debug("dc:for-each without having results"); sascha@1023: return; sascha@1023: } sascha@1023: sascha@1023: NodeList subs = current.getChildNodes(); sascha@1023: int S = subs.getLength(); sascha@1023: sascha@1023: if (S == 0) { teichmann@5522: log.debug("dc:for-each has no children"); sascha@1023: return; sascha@1023: } sascha@1023: rrenkert@5305: Pair pair = rrenkert@5305: connectionsStack.peek(); sascha@1023: teichmann@5509: ResultData rd = pair.getB(); rrenkert@5305: teichmann@5525: String [] columns = rd.getColumnLabels(); teichmann@5525: teichmann@5525: for (Object [] row: rd.getRows()) { teichmann@5525: frames.enter(); teichmann@5525: try { teichmann@5525: frames.put(columns, row); teichmann@5525: for (int i = 0; i < S; ++i) { teichmann@5525: build(parent, subs.item(i)); sascha@1023: } sascha@1023: } teichmann@5525: finally { teichmann@5525: frames.leave(); teichmann@5525: } sascha@1011: } sascha@998: } sascha@998: felix@1890: /** felix@1890: * Create element. felix@1890: */ sascha@998: protected void element(Node parent, Element current) sascha@998: throws SQLException sascha@998: { sascha@998: String attr = expand(current.getAttribute("name")); sascha@998: sascha@998: if (log.isDebugEnabled()) { sascha@998: log.debug("dc:element -> '" + attr + "'"); sascha@998: } sascha@998: sascha@998: if (attr.length() == 0) { sascha@998: log.warn("no name attribute found"); sascha@998: return; sascha@998: } sascha@998: sascha@998: Element element = owner.createElement(attr); sascha@998: sascha@998: NodeList children = current.getChildNodes(); sascha@998: for (int i = 0, N = children.getLength(); i < N; ++i) { sascha@998: build(element, children.item(i)); sascha@998: } sascha@998: sascha@998: parent.appendChild(element); sascha@998: } sascha@998: sascha@998: protected void text(Node parent, Element current) sascha@998: throws SQLException sascha@998: { sascha@998: log.debug("dc:text"); sascha@998: String value = expand(current.getTextContent()); sascha@998: parent.appendChild(owner.createTextNode(value)); sascha@998: } sascha@998: felix@1890: /** felix@1890: * Add attribute to an element felix@3571: * @see Element felix@1890: */ sascha@998: protected void attribute(Node parent, Element current) { sascha@998: sascha@998: if (parent.getNodeType() != Node.ELEMENT_NODE) { sascha@998: log.warn("need element here"); sascha@998: return; sascha@998: } sascha@998: sascha@998: String name = expand(current.getAttribute("name")); sascha@998: String value = expand(current.getAttribute("value")); sascha@998: sascha@998: Element element = (Element)parent; sascha@998: sascha@998: element.setAttribute(name, value); sascha@998: } sascha@998: felix@4893: /** felix@4893: * Call-Macro node. felix@4893: * Evaluate child-nodes of the given macro element (not its text). felix@4893: */ sascha@998: protected void callMacro(Node parent, Element current) sascha@998: throws SQLException sascha@998: { sascha@998: String name = current.getAttribute("name"); sascha@998: sascha@998: if (name.length() == 0) { sascha@998: log.warn("missing 'name' attribute in 'call-macro'"); sascha@998: return; sascha@998: } sascha@998: teichmann@5443: Element macro = macros.get(name); teichmann@4890: teichmann@4890: if (macro != null) { teichmann@4890: macroBodies.push(current.getChildNodes()); teichmann@4890: try { sascha@998: NodeList subs = macro.getChildNodes(); sascha@998: for (int j = 0, M = subs.getLength(); j < M; ++j) { sascha@998: build(parent, subs.item(j)); sascha@998: } teichmann@4890: } teichmann@4890: finally { teichmann@4890: macroBodies.pop(); sascha@998: } sascha@998: } teichmann@4890: else { teichmann@4890: log.warn("no macro '" + name + "' found."); teichmann@4890: } teichmann@4890: } sascha@998: teichmann@4890: protected void macroBody(Node parent, Element current) teichmann@4890: throws SQLException teichmann@4890: { teichmann@4890: if (!macroBodies.isEmpty()) { teichmann@4890: NodeList children = macroBodies.peek(); teichmann@4890: for (int i = 0, N = children.getLength(); i < N; ++i) { teichmann@4890: build(parent, children.item(i)); teichmann@4890: } teichmann@4890: } teichmann@4890: else { teichmann@4890: log.warn("no current macro"); teichmann@4890: } sascha@998: } sascha@998: felix@4896: /** Get macro node children, not resolving bodies. */ felix@4896: protected NodeList getMacroChildren(String name) { felix@4896: NodeList macros = template.getElementsByTagNameNS( felix@4896: DC_NAMESPACE_URI, "macro"); felix@4896: felix@4896: Element macro = null; felix@4896: felix@4896: for (int i = 0, N = macros.getLength(); i < N; ++i) { felix@4896: Element m = (Element) macros.item(i); felix@4896: if (name.equals(m.getAttribute("name"))) { felix@4896: macro = m; felix@4896: break; felix@4896: } felix@4896: } felix@4896: felix@4896: if (macro != null) { felix@4896: return macro.getChildNodes(); felix@4896: } felix@4896: return null; felix@4896: } felix@4896: sascha@998: protected void ifClause(Node parent, Element current) sascha@998: throws SQLException sascha@998: { sascha@998: String test = current.getAttribute("test"); sascha@998: sascha@998: if (test.length() == 0) { sascha@998: log.warn("missing 'test' attribute in 'if'"); sascha@998: return; sascha@998: } sascha@998: teichmann@4737: Boolean result = evaluateXPathToBoolean(test); sascha@998: sascha@998: if (result != null && result.booleanValue()) { sascha@998: NodeList subs = current.getChildNodes(); sascha@998: for (int i = 0, N = subs.getLength(); i < N; ++i) { sascha@998: build(parent, subs.item(i)); sascha@998: } sascha@998: } sascha@998: } sascha@998: sascha@998: protected void choose(Node parent, Element current) sascha@998: throws SQLException sascha@998: { sascha@998: Node branch = null; sascha@998: sascha@998: NodeList children = current.getChildNodes(); sascha@998: for (int i = 0, N = children.getLength(); i < N; ++i) { sascha@998: Node child = children.item(i); sascha@998: String ns = child.getNamespaceURI(); sascha@998: if (ns == null sascha@998: || !ns.equals(DC_NAMESPACE_URI) sascha@998: || child.getNodeType() != Node.ELEMENT_NODE sascha@998: ) { sascha@998: continue; sascha@998: } sascha@998: String name = child.getLocalName(); sascha@998: if ("when".equals(name)) { sascha@998: Element when = (Element)child; sascha@998: String test = when.getAttribute("test"); sascha@998: if (test.length() == 0) { sascha@998: log.warn("no 'test' attribute found for when"); sascha@998: continue; sascha@998: } sascha@998: teichmann@4737: Boolean result = evaluateXPathToBoolean(test); sascha@998: if (result != null && result.booleanValue()) { sascha@998: branch = child; sascha@998: break; sascha@998: } sascha@998: sascha@998: continue; sascha@998: } sascha@998: else if ("otherwise".equals(name)) { sascha@998: branch = child; sascha@998: // No break here. sascha@998: } sascha@998: } sascha@998: sascha@998: if (branch != null) { sascha@998: NodeList subs = branch.getChildNodes(); sascha@998: for (int i = 0, N = subs.getLength(); i < N; ++i) { sascha@998: build(parent, subs.item(i)); sascha@998: } sascha@998: } sascha@998: } sascha@998: teichmann@5449: protected XPathExpression getXPathExpression(String expr) teichmann@5449: throws XPathExpressionException teichmann@5449: { teichmann@5449: XPathExpression x = expressions.get(expr); teichmann@5449: if (x == null) { teichmann@5449: XPath xpath = XPATH_FACTORY.newXPath(); teichmann@5449: xpath.setXPathVariableResolver(frames); teichmann@5449: xpath.setXPathFunctionResolver(functionResolver); teichmann@5449: x = xpath.compile(expr); teichmann@5449: expressions.put(expr, x); teichmann@5449: } teichmann@5449: return x; teichmann@5449: } teichmann@5449: teichmann@4740: protected Object evaluateXPath(String expr, QName returnType) { sascha@998: sascha@998: if (log.isDebugEnabled()) { sascha@998: log.debug("evaluate: '" + expr + "'"); sascha@998: } sascha@998: sascha@998: try { teichmann@5449: XPathExpression x = getXPathExpression(expr); teichmann@5449: return x.evaluate(EVAL_DOCUMENT, returnType); sascha@998: } teichmann@4740: catch (XPathExpressionException xpee) { teichmann@4740: log.error("expression: " + expr, xpee); sascha@998: } sascha@998: return null; sascha@998: } sascha@998: teichmann@4737: protected Boolean evaluateXPathToBoolean(String expr) { teichmann@4737: teichmann@4740: Object result = evaluateXPath(expr, XPathConstants.BOOLEAN); teichmann@4737: teichmann@4737: return result instanceof Boolean teichmann@4737: ? (Boolean)result teichmann@4737: : null; teichmann@4737: } teichmann@4737: teichmann@4737: protected void convert(Element current) { sascha@998: sascha@998: String variable = expand(current.getAttribute("var")); sascha@998: String type = expand(current.getAttribute("type")); sascha@998: sascha@1716: Object [] result = new Object[1]; sascha@1716: sascha@1716: if (frames.getStore(variable, result)) { sascha@1716: Object object = TypeConverter.convert(result[0], type); sascha@1716: frames.put(variable.toUpperCase(), object); sascha@998: } sascha@998: } sascha@998: teichmann@4740: felix@4891: /** Put content as variable on stackframes. */ teichmann@4737: protected void variable(Element current) { teichmann@4737: teichmann@4737: String varName = expand(current.getAttribute("name")); teichmann@4737: String expr = current.getAttribute("expr"); teichmann@4740: String type = current.getAttribute("type"); teichmann@4737: teichmann@4737: if (varName.length() == 0 || expr.length() == 0) { teichmann@4737: log.error("dc:variable 'name' or 'expr' empty."); teichmann@4737: } teichmann@4737: else { teichmann@4740: frames.put( teichmann@4740: varName.toUpperCase(), teichmann@4740: evaluateXPath(expr, typeToQName(type))); teichmann@4737: } teichmann@4737: } teichmann@4737: sascha@998: protected String expand(String s) { sascha@998: Matcher m = CompiledStatement.VAR.matcher(s); sascha@998: sascha@1716: Object [] result = new Object[1]; sascha@1716: sascha@998: StringBuffer sb = new StringBuffer(); sascha@998: while (m.find()) { sascha@998: String key = m.group(1); sascha@1716: result[0] = null; sascha@1716: if (frames.getStore(key, result)) { sascha@1716: m.appendReplacement( sascha@1716: sb, result[0] != null ? result[0].toString() : ""); sascha@1716: } sascha@1716: else { sascha@1716: m.appendReplacement(sb, "\\${" + key + "}"); sascha@1716: } sascha@998: } sascha@998: m.appendTail(sb); sascha@998: return sb.toString(); sascha@998: } sascha@998: teichmann@5485: protected void evaluateAttributeValue(Attr attr) { teichmann@5485: String value = attr.getValue(); teichmann@5485: if (value.indexOf('{') >= 0) { teichmann@5485: StringBuffer sb = new StringBuffer(); teichmann@5485: Matcher m = BRACKET_XPATH.matcher(value); teichmann@5485: while (m.find()) { teichmann@5485: String expr = m.group(1); teichmann@5485: Object result = evaluateXPath(expr, XPathConstants.STRING); teichmann@5485: if (result instanceof String) { teichmann@5485: m.appendReplacement(sb, (String)result); teichmann@5485: } teichmann@5485: else { rrenkert@5532: m.appendReplacement(sb, ""); teichmann@5485: } teichmann@5485: } teichmann@5485: m.appendTail(sb); teichmann@5485: attr.setValue(sb.toString()); teichmann@5485: } teichmann@5485: } teichmann@5485: sascha@998: protected void build(Node parent, Node current) sascha@998: throws SQLException sascha@998: { sascha@998: String ns = current.getNamespaceURI(); sascha@998: if (ns != null && ns.equals(DC_NAMESPACE_URI)) { sascha@998: if (current.getNodeType() != Node.ELEMENT_NODE) { sascha@998: log.warn("need elements here"); sascha@998: } sascha@998: else { sascha@998: String localName = current.getLocalName(); aheinecke@5517: Element curr = (Element)current; sascha@998: if ("attribute".equals(localName)) { teichmann@5509: attribute(parent, curr); sascha@998: } sascha@998: else if ("context".equals(localName)) { teichmann@5509: context(parent, curr); sascha@998: } sascha@998: else if ("if".equals(localName)) { teichmann@5509: ifClause(parent, curr); sascha@998: } sascha@998: else if ("choose".equals(localName)) { teichmann@5509: choose(parent, curr); sascha@998: } sascha@998: else if ("call-macro".equals(localName)) { teichmann@5509: callMacro(parent, curr); sascha@998: } teichmann@4890: else if ("macro-body".equals(localName)) { teichmann@5509: macroBody(parent, curr); teichmann@4890: } teichmann@5509: else if ("macro".equals(localName) teichmann@5509: || "comment".equals(localName) teichmann@5509: || "statement".equals(localName)) { teichmann@5509: // Simply ignore them. sascha@998: } sascha@998: else if ("element".equals(localName)) { teichmann@5509: element(parent, curr); sascha@998: } teichmann@5522: else if ("for-each".equals(localName)) { teichmann@5522: foreach(parent, curr); teichmann@5509: } teichmann@5509: else if ("filter".equals(localName)) { teichmann@5509: filter(parent, curr); sascha@1023: } sascha@998: else if ("text".equals(localName)) { teichmann@5509: text(parent, curr); sascha@998: } teichmann@4737: else if ("variable".equals(localName)) { teichmann@5509: variable(curr); sascha@1017: } sascha@998: else if ("convert".equals(localName)) { teichmann@5509: convert(curr); sascha@998: } sascha@998: else { sascha@998: log.warn("unknown '" + localName + "' -> ignore"); sascha@998: } sascha@998: } sascha@998: return; sascha@998: } sascha@998: sascha@998: if (current.getNodeType() == Node.TEXT_NODE) { sascha@998: String txt = current.getNodeValue(); sascha@998: if (txt != null && txt.trim().length() == 0) { sascha@998: return; sascha@998: } sascha@998: } sascha@998: bjoern@4353: if (current.getNodeType() == Node.COMMENT_NODE) { bjoern@4353: // Ignore XML comments bjoern@4353: return; bjoern@4353: } bjoern@4353: sascha@998: Node copy = owner.importNode(current, false); sascha@998: aheinecke@5494: NodeList children = current.getChildNodes(); aheinecke@5494: for (int i = 0, N = children.getLength(); i < N; ++i) { aheinecke@5494: build(copy, children.item(i)); teichmann@5485: } aheinecke@5494: if (copy.getNodeType() == Node.ELEMENT_NODE) { aheinecke@5494: NamedNodeMap nnm = ((Element)copy).getAttributes(); aheinecke@5494: for (int i = 0, N = nnm.getLength(); i < N; ++i) { aheinecke@5494: Node n = nnm.item(i); aheinecke@5494: if (n.getNodeType() == Node.ATTRIBUTE_NODE) { aheinecke@5494: evaluateAttributeValue((Attr)n); aheinecke@5494: } teichmann@5485: } sascha@998: } sascha@998: parent.appendChild(copy); sascha@998: } sascha@998: } // class BuildHelper sascha@998: sascha@998: sascha@998: public Builder() { sascha@998: compiledStatements = new HashMap(); teichmann@5443: macros = new HashMap(); sascha@998: } sascha@998: sascha@998: public Builder(Document template) { sascha@998: this(); sascha@998: this.template = template; teichmann@5443: extractMacros(); sascha@998: compileStatements(); sascha@998: } sascha@998: teichmann@4740: protected static QName typeToQName(String type) { teichmann@4740: if ("number" .equals(type)) return XPathConstants.NUMBER; teichmann@4740: if ("bool" .equals(type)) return XPathConstants.BOOLEAN; teichmann@4740: if ("node" .equals(type)) return XPathConstants.NODE; teichmann@4740: if ("nodeset".equals(type)) return XPathConstants.NODESET; teichmann@4740: return XPathConstants.STRING; teichmann@4740: } teichmann@4740: felix@4614: /** Handle elements. */ sascha@998: protected void compileStatements() { sascha@998: sascha@998: NodeList nodes = template.getElementsByTagNameNS( sascha@998: DC_NAMESPACE_URI, "statement"); sascha@998: sascha@998: for (int i = 0, N = nodes.getLength(); i < N; ++i) { sascha@998: Element stmntElement = (Element)nodes.item(i); sascha@998: String stmnt = trimStatement(stmntElement.getTextContent()); sascha@998: if (stmnt == null || stmnt.length() == 0) { sascha@998: throw new IllegalArgumentException("found empty statement"); sascha@998: } sascha@998: CompiledStatement cs = new CompiledStatement(stmnt); felix@1029: // For faster lookup store a shortend string into the template. sascha@998: stmnt = "s" + i; sascha@998: stmntElement.setTextContent(stmnt); sascha@998: compiledStatements.put(stmnt, cs); sascha@998: } sascha@998: } sascha@998: teichmann@5443: protected void extractMacros() { teichmann@5443: NodeList ms = template.getElementsByTagNameNS( teichmann@5443: DC_NAMESPACE_URI, "macro"); teichmann@5443: teichmann@5443: for (int i = 0, N = ms.getLength(); i < N; ++i) { teichmann@5443: Element m = (Element)ms.item(i); teichmann@5443: macros.put(m.getAttribute("name"), m); teichmann@5443: } teichmann@5443: } teichmann@5443: sascha@998: protected List rootsToList() { sascha@998: sascha@998: NodeList roots = template.getElementsByTagNameNS( sascha@998: DC_NAMESPACE_URI, "template"); sascha@998: sascha@998: List elements = new ArrayList(); sascha@998: sascha@998: for (int i = 0, N = roots.getLength(); i < N; ++i) { sascha@998: NodeList rootChildren = roots.item(i).getChildNodes(); sascha@998: for (int j = 0, M = rootChildren.getLength(); j < M; ++j) { sascha@998: Node child = rootChildren.item(j); sascha@998: if (child.getNodeType() == Node.ELEMENT_NODE) { sascha@998: elements.add(child); sascha@998: } sascha@998: } sascha@998: } sascha@998: sascha@998: return elements; sascha@998: } sascha@998: sascha@998: protected static final String trimStatement(String stmnt) { sascha@998: if (stmnt == null) return null; sascha@998: //XXX: Maybe a bit to radical for multiline strings? sascha@998: return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" "); sascha@998: } sascha@998: sascha@998: protected static Document getOwnerDocument(Node node) { sascha@998: Document document = node.getOwnerDocument(); sascha@998: return document != null ? document : (Document)node; sascha@998: } sascha@998: sascha@1011: public void build( sascha@1011: List connections, sascha@1011: Node output, sascha@1011: Map parameters sascha@1011: ) sascha@1011: throws SQLException sascha@1011: { sascha@1011: BuildHelper helper = new BuildHelper(output, connections, parameters); sascha@998: sascha@998: helper.build(); sascha@998: } sascha@998: } sascha@998: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :