Mercurial > dive4elements > river
comparison flys-artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/Builder.java @ 5831:bd047b71ab37
Repaired internal references
author | Sascha L. Teichmann <teichmann@intevation.de> |
---|---|
date | Thu, 25 Apr 2013 12:06:39 +0200 |
parents | flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java@ebec12def170 |
children |
comparison
equal
deleted
inserted
replaced
5830:160f53ee0870 | 5831:bd047b71ab37 |
---|---|
1 package org.dive4elements.river.artifacts.datacage.templating; | |
2 | |
3 import org.dive4elements.artifacts.common.utils.XMLUtils; | |
4 | |
5 import org.dive4elements.river.utils.Pair; | |
6 | |
7 import java.sql.Connection; | |
8 import java.sql.SQLException; | |
9 | |
10 import java.util.ArrayDeque; | |
11 import java.util.ArrayList; | |
12 import java.util.Deque; | |
13 import java.util.HashMap; | |
14 import java.util.List; | |
15 import java.util.Map; | |
16 | |
17 import java.util.regex.Matcher; | |
18 import java.util.regex.Pattern; | |
19 | |
20 import javax.xml.namespace.QName; | |
21 | |
22 import javax.xml.xpath.XPath; | |
23 import javax.xml.xpath.XPathConstants; | |
24 import javax.xml.xpath.XPathExpression; | |
25 import javax.xml.xpath.XPathExpressionException; | |
26 import javax.xml.xpath.XPathFactory; | |
27 | |
28 import org.apache.log4j.Logger; | |
29 | |
30 import org.w3c.dom.Attr; | |
31 import org.w3c.dom.Document; | |
32 import org.w3c.dom.Element; | |
33 import org.w3c.dom.Node; | |
34 import org.w3c.dom.NodeList; | |
35 import org.w3c.dom.NamedNodeMap; | |
36 | |
37 | |
38 /** Handles and evaluate meta-data template against dbs. */ | |
39 public class Builder | |
40 { | |
41 private static Logger log = Logger.getLogger(Builder.class); | |
42 | |
43 public static final Pattern STRIP_LINE_INDENT = | |
44 Pattern.compile("\\s*\\r?\\n\\s*"); | |
45 | |
46 public static final Pattern BRACKET_XPATH = | |
47 Pattern.compile("\\{([^}]+)\\}"); | |
48 | |
49 public static final String DC_NAMESPACE_URI = | |
50 "http://www.intevation.org/2011/Datacage"; | |
51 | |
52 private static final Document EVAL_DOCUMENT = | |
53 XMLUtils.newDocument(); | |
54 | |
55 private static final XPathFactory XPATH_FACTORY = | |
56 XPathFactory.newInstance(); | |
57 | |
58 protected Document template; | |
59 | |
60 protected Map<String, CompiledStatement> compiledStatements; | |
61 | |
62 protected Map<String, Element> macros; | |
63 | |
64 /** Connection to either of the databases. */ | |
65 public static class NamedConnection { | |
66 | |
67 protected String name; | |
68 protected Connection connection; | |
69 protected boolean cached; | |
70 | |
71 public NamedConnection() { | |
72 } | |
73 | |
74 public NamedConnection( | |
75 String name, | |
76 Connection connection | |
77 ) { | |
78 this(name, connection, true); | |
79 } | |
80 | |
81 public NamedConnection( | |
82 String name, | |
83 Connection connection, | |
84 boolean cached | |
85 ) { | |
86 this.name = name; | |
87 this.connection = connection; | |
88 this.cached = cached; | |
89 } | |
90 } // class NamedConnection | |
91 | |
92 public class BuildHelper | |
93 { | |
94 protected Node output; | |
95 protected Document owner; | |
96 protected StackFrames frames; | |
97 protected List<NamedConnection> connections; | |
98 protected Map<String, CompiledStatement.Instance> statements; | |
99 protected Deque<Pair<NamedConnection, ResultData>> connectionsStack; | |
100 protected Deque<NodeList> macroBodies; | |
101 protected FunctionResolver functionResolver; | |
102 protected Map<String, XPathExpression> expressions; | |
103 | |
104 | |
105 public BuildHelper( | |
106 Node output, | |
107 List<NamedConnection> connections, | |
108 Map<String, Object> parameters | |
109 ) { | |
110 if (connections.isEmpty()) { | |
111 throw new IllegalArgumentException("no connections given."); | |
112 } | |
113 | |
114 this.connections = connections; | |
115 connectionsStack = | |
116 new ArrayDeque<Pair<NamedConnection, ResultData>>(); | |
117 this.output = output; | |
118 frames = new StackFrames(parameters); | |
119 owner = getOwnerDocument(output); | |
120 macroBodies = new ArrayDeque<NodeList>(); | |
121 functionResolver = new FunctionResolver(this); | |
122 expressions = new HashMap<String, XPathExpression>(); | |
123 statements = | |
124 new HashMap<String, CompiledStatement.Instance>(); | |
125 } | |
126 | |
127 public void build() throws SQLException { | |
128 try { | |
129 // XXX: Thread safety is now established by the builder pool. | |
130 //synchronized (template) { | |
131 for (Node current: rootsToList()) { | |
132 build(output, current); | |
133 } | |
134 //} | |
135 } | |
136 finally { | |
137 closeStatements(); | |
138 } | |
139 } | |
140 | |
141 protected void closeStatements() { | |
142 for (CompiledStatement.Instance csi: statements.values()) { | |
143 csi.close(); | |
144 } | |
145 statements.clear(); | |
146 } | |
147 | |
148 /** | |
149 * Return first statement node in NodeList, respecting | |
150 * macros but not doing evaluation (e.g. of <dc:if>s). | |
151 */ | |
152 private Node findStatementNode(NodeList nodes) { | |
153 int S = nodes.getLength(); | |
154 | |
155 // Check direct children and take special care of macros. | |
156 for (int i = 0; i < S; ++i) { | |
157 Node node = nodes.item(i); | |
158 String ns; | |
159 // Regular statement node. | |
160 if (node.getNodeType() == Node.ELEMENT_NODE | |
161 && node.getLocalName().equals("statement") | |
162 && (ns = node.getNamespaceURI()) != null | |
163 && ns.equals(DC_NAMESPACE_URI)) { | |
164 return node; | |
165 } | |
166 // Macro node. Descend. | |
167 else if (node.getNodeType() == Node.ELEMENT_NODE | |
168 && node.getLocalName().equals("call-macro") | |
169 && (ns = node.getNamespaceURI()) != null | |
170 && ns.equals(DC_NAMESPACE_URI)) { | |
171 | |
172 String macroName = ((Element)node).getAttribute("name"); | |
173 Node inMacroNode = | |
174 findStatementNode(getMacroChildren(macroName)); | |
175 if (inMacroNode != null) { | |
176 return inMacroNode; | |
177 } | |
178 } | |
179 | |
180 } | |
181 | |
182 return null; | |
183 } | |
184 | |
185 /** | |
186 * Handle a dc:context node. | |
187 */ | |
188 protected void context(Node parent, Element current) | |
189 throws SQLException | |
190 { | |
191 log.debug("dc:context"); | |
192 | |
193 NodeList subs = current.getChildNodes(); | |
194 Node stmntNode = findStatementNode(subs); | |
195 int S = subs.getLength(); | |
196 | |
197 if (stmntNode == null) { | |
198 log.warn("dc:context: cannot find statement"); | |
199 return; | |
200 } | |
201 | |
202 String stmntText = stmntNode.getTextContent(); | |
203 | |
204 String con = current.getAttribute("connection"); | |
205 | |
206 String key = con + "-" + stmntText; | |
207 | |
208 CompiledStatement.Instance csi = statements.get(key); | |
209 | |
210 if (csi == null) { | |
211 CompiledStatement cs = compiledStatements.get(stmntText); | |
212 csi = cs.new Instance(); | |
213 statements.put(key, csi); | |
214 } | |
215 | |
216 NamedConnection connection = connectionsStack.isEmpty() | |
217 ? connections.get(0) | |
218 : connectionsStack.peek().getA(); | |
219 | |
220 if (con.length() > 0) { | |
221 for (NamedConnection nc: connections) { | |
222 if (con.equals(nc.name)) { | |
223 connection = nc; | |
224 break; | |
225 } | |
226 } | |
227 } | |
228 | |
229 ResultData rd = csi.execute( | |
230 connection.connection, | |
231 frames, | |
232 connection.cached); | |
233 | |
234 // only descent if there are results | |
235 if (!rd.isEmpty()) { | |
236 connectionsStack.push( | |
237 new Pair<NamedConnection, ResultData>(connection, rd)); | |
238 try { | |
239 for (int i = 0; i < S; ++i) { | |
240 build(parent, subs.item(i)); | |
241 } | |
242 } | |
243 finally { | |
244 connectionsStack.pop(); | |
245 } | |
246 } | |
247 } | |
248 | |
249 public boolean hasResult() { | |
250 return !connectionsStack.isEmpty() | |
251 && !connectionsStack.peek().getB().isEmpty(); | |
252 } | |
253 | |
254 protected ResultData createFilteredResultData(ResultData rd, String filter) { | |
255 if (filter == null) return rd; | |
256 | |
257 List<Object []> rows = rd.getRows(); | |
258 String [] columns = rd.getColumnLabels(); | |
259 | |
260 List<Object []> filtered = new ArrayList<Object[]>(rows.size()); | |
261 | |
262 for (Object [] row: rows) { | |
263 frames.enter(); | |
264 try { | |
265 frames.put(columns, row); | |
266 boolean traverse = filter == null; | |
267 | |
268 if (!traverse) { | |
269 Boolean b = evaluateXPathToBoolean(filter); | |
270 traverse = b != null && b; | |
271 } | |
272 if (traverse) { | |
273 filtered.add(row); | |
274 } | |
275 } | |
276 finally { | |
277 frames.leave(); | |
278 } | |
279 } | |
280 return new ResultData(rd.getColumnLabels(), filtered); | |
281 } | |
282 | |
283 protected void filter(Node parent, Element current) | |
284 throws SQLException | |
285 { | |
286 String expr = current.getAttribute("expr"); | |
287 | |
288 if ((expr = expr.trim()).length() == 0) { | |
289 expr = null; | |
290 } | |
291 | |
292 NodeList subs = current.getChildNodes(); | |
293 int S = subs.getLength(); | |
294 if (S == 0) { | |
295 log.debug("dc:filter has no children"); | |
296 return; | |
297 } | |
298 | |
299 ResultData orig = null; | |
300 Pair<Builder.NamedConnection, ResultData> pair = null; | |
301 | |
302 if (expr != null && !connectionsStack.isEmpty()) { | |
303 pair = connectionsStack.peek(); | |
304 orig = pair.getB(); | |
305 pair.setB(createFilteredResultData(orig, expr)); | |
306 } | |
307 | |
308 try { | |
309 for (int i = 0; i < S; ++i) { | |
310 build(parent, subs.item(i)); | |
311 } | |
312 } | |
313 finally { | |
314 if (orig != null) { | |
315 pair.setB(orig); | |
316 } | |
317 } | |
318 } | |
319 | |
320 /** | |
321 * Kind of foreach over results of a statement within a context. | |
322 */ | |
323 protected void foreach(Node parent, Element current) | |
324 throws SQLException | |
325 { | |
326 log.debug("dc:for-each"); | |
327 | |
328 if (connectionsStack.isEmpty()) { | |
329 log.debug("dc:for-each without having results"); | |
330 return; | |
331 } | |
332 | |
333 NodeList subs = current.getChildNodes(); | |
334 int S = subs.getLength(); | |
335 | |
336 if (S == 0) { | |
337 log.debug("dc:for-each has no children"); | |
338 return; | |
339 } | |
340 | |
341 Pair<Builder.NamedConnection, ResultData> pair = | |
342 connectionsStack.peek(); | |
343 | |
344 ResultData rd = pair.getB(); | |
345 | |
346 String [] columns = rd.getColumnLabels(); | |
347 | |
348 for (Object [] row: rd.getRows()) { | |
349 frames.enter(); | |
350 try { | |
351 frames.put(columns, row); | |
352 for (int i = 0; i < S; ++i) { | |
353 build(parent, subs.item(i)); | |
354 } | |
355 } | |
356 finally { | |
357 frames.leave(); | |
358 } | |
359 } | |
360 } | |
361 | |
362 /** | |
363 * Create element. | |
364 */ | |
365 protected void element(Node parent, Element current) | |
366 throws SQLException | |
367 { | |
368 String attr = expand(current.getAttribute("name")); | |
369 | |
370 if (log.isDebugEnabled()) { | |
371 log.debug("dc:element -> '" + attr + "'"); | |
372 } | |
373 | |
374 if (attr.length() == 0) { | |
375 log.warn("no name attribute found"); | |
376 return; | |
377 } | |
378 | |
379 Element element = owner.createElement(attr); | |
380 | |
381 NodeList children = current.getChildNodes(); | |
382 for (int i = 0, N = children.getLength(); i < N; ++i) { | |
383 build(element, children.item(i)); | |
384 } | |
385 | |
386 parent.appendChild(element); | |
387 } | |
388 | |
389 protected void text(Node parent, Element current) | |
390 throws SQLException | |
391 { | |
392 log.debug("dc:text"); | |
393 String value = expand(current.getTextContent()); | |
394 parent.appendChild(owner.createTextNode(value)); | |
395 } | |
396 | |
397 /** | |
398 * Add attribute to an element | |
399 * @see Element | |
400 */ | |
401 protected void attribute(Node parent, Element current) { | |
402 | |
403 if (parent.getNodeType() != Node.ELEMENT_NODE) { | |
404 log.warn("need element here"); | |
405 return; | |
406 } | |
407 | |
408 String name = expand(current.getAttribute("name")); | |
409 String value = expand(current.getAttribute("value")); | |
410 | |
411 Element element = (Element)parent; | |
412 | |
413 element.setAttribute(name, value); | |
414 } | |
415 | |
416 /** | |
417 * Call-Macro node. | |
418 * Evaluate child-nodes of the given macro element (not its text). | |
419 */ | |
420 protected void callMacro(Node parent, Element current) | |
421 throws SQLException | |
422 { | |
423 String name = current.getAttribute("name"); | |
424 | |
425 if (name.length() == 0) { | |
426 log.warn("missing 'name' attribute in 'call-macro'"); | |
427 return; | |
428 } | |
429 | |
430 Element macro = macros.get(name); | |
431 | |
432 if (macro != null) { | |
433 macroBodies.push(current.getChildNodes()); | |
434 try { | |
435 NodeList subs = macro.getChildNodes(); | |
436 for (int j = 0, M = subs.getLength(); j < M; ++j) { | |
437 build(parent, subs.item(j)); | |
438 } | |
439 } | |
440 finally { | |
441 macroBodies.pop(); | |
442 } | |
443 } | |
444 else { | |
445 log.warn("no macro '" + name + "' found."); | |
446 } | |
447 } | |
448 | |
449 protected void macroBody(Node parent, Element current) | |
450 throws SQLException | |
451 { | |
452 if (!macroBodies.isEmpty()) { | |
453 NodeList children = macroBodies.peek(); | |
454 for (int i = 0, N = children.getLength(); i < N; ++i) { | |
455 build(parent, children.item(i)); | |
456 } | |
457 } | |
458 else { | |
459 log.warn("no current macro"); | |
460 } | |
461 } | |
462 | |
463 /** Get macro node children, not resolving bodies. */ | |
464 protected NodeList getMacroChildren(String name) { | |
465 NodeList macros = template.getElementsByTagNameNS( | |
466 DC_NAMESPACE_URI, "macro"); | |
467 | |
468 Element macro = null; | |
469 | |
470 for (int i = 0, N = macros.getLength(); i < N; ++i) { | |
471 Element m = (Element) macros.item(i); | |
472 if (name.equals(m.getAttribute("name"))) { | |
473 macro = m; | |
474 break; | |
475 } | |
476 } | |
477 | |
478 if (macro != null) { | |
479 return macro.getChildNodes(); | |
480 } | |
481 return null; | |
482 } | |
483 | |
484 protected void ifClause(Node parent, Element current) | |
485 throws SQLException | |
486 { | |
487 String test = current.getAttribute("test"); | |
488 | |
489 if (test.length() == 0) { | |
490 log.warn("missing 'test' attribute in 'if'"); | |
491 return; | |
492 } | |
493 | |
494 Boolean result = evaluateXPathToBoolean(test); | |
495 | |
496 if (result != null && result.booleanValue()) { | |
497 NodeList subs = current.getChildNodes(); | |
498 for (int i = 0, N = subs.getLength(); i < N; ++i) { | |
499 build(parent, subs.item(i)); | |
500 } | |
501 } | |
502 } | |
503 | |
504 protected void choose(Node parent, Element current) | |
505 throws SQLException | |
506 { | |
507 Node branch = null; | |
508 | |
509 NodeList children = current.getChildNodes(); | |
510 for (int i = 0, N = children.getLength(); i < N; ++i) { | |
511 Node child = children.item(i); | |
512 String ns = child.getNamespaceURI(); | |
513 if (ns == null | |
514 || !ns.equals(DC_NAMESPACE_URI) | |
515 || child.getNodeType() != Node.ELEMENT_NODE | |
516 ) { | |
517 continue; | |
518 } | |
519 String name = child.getLocalName(); | |
520 if ("when".equals(name)) { | |
521 Element when = (Element)child; | |
522 String test = when.getAttribute("test"); | |
523 if (test.length() == 0) { | |
524 log.warn("no 'test' attribute found for when"); | |
525 continue; | |
526 } | |
527 | |
528 Boolean result = evaluateXPathToBoolean(test); | |
529 if (result != null && result.booleanValue()) { | |
530 branch = child; | |
531 break; | |
532 } | |
533 | |
534 continue; | |
535 } | |
536 else if ("otherwise".equals(name)) { | |
537 branch = child; | |
538 // No break here. | |
539 } | |
540 } | |
541 | |
542 if (branch != null) { | |
543 NodeList subs = branch.getChildNodes(); | |
544 for (int i = 0, N = subs.getLength(); i < N; ++i) { | |
545 build(parent, subs.item(i)); | |
546 } | |
547 } | |
548 } | |
549 | |
550 protected XPathExpression getXPathExpression(String expr) | |
551 throws XPathExpressionException | |
552 { | |
553 XPathExpression x = expressions.get(expr); | |
554 if (x == null) { | |
555 XPath xpath = XPATH_FACTORY.newXPath(); | |
556 xpath.setXPathVariableResolver(frames); | |
557 xpath.setXPathFunctionResolver(functionResolver); | |
558 x = xpath.compile(expr); | |
559 expressions.put(expr, x); | |
560 } | |
561 return x; | |
562 } | |
563 | |
564 protected Object evaluateXPath(String expr, QName returnType) { | |
565 | |
566 if (log.isDebugEnabled()) { | |
567 log.debug("evaluate: '" + expr + "'"); | |
568 } | |
569 | |
570 try { | |
571 XPathExpression x = getXPathExpression(expr); | |
572 return x.evaluate(EVAL_DOCUMENT, returnType); | |
573 } | |
574 catch (XPathExpressionException xpee) { | |
575 log.error("expression: " + expr, xpee); | |
576 } | |
577 return null; | |
578 } | |
579 | |
580 protected Boolean evaluateXPathToBoolean(String expr) { | |
581 | |
582 Object result = evaluateXPath(expr, XPathConstants.BOOLEAN); | |
583 | |
584 return result instanceof Boolean | |
585 ? (Boolean)result | |
586 : null; | |
587 } | |
588 | |
589 protected void convert(Element current) { | |
590 | |
591 String variable = expand(current.getAttribute("var")); | |
592 String type = expand(current.getAttribute("type")); | |
593 | |
594 Object [] result = new Object[1]; | |
595 | |
596 if (frames.getStore(variable, result)) { | |
597 Object object = TypeConverter.convert(result[0], type); | |
598 frames.put(variable.toUpperCase(), object); | |
599 } | |
600 } | |
601 | |
602 | |
603 /** Put <dc:variable> content as variable on stackframes. */ | |
604 protected void variable(Element current) { | |
605 | |
606 String varName = expand(current.getAttribute("name")); | |
607 String expr = current.getAttribute("expr"); | |
608 String type = current.getAttribute("type"); | |
609 | |
610 if (varName.length() == 0 || expr.length() == 0) { | |
611 log.error("dc:variable 'name' or 'expr' empty."); | |
612 } | |
613 else { | |
614 frames.put( | |
615 varName.toUpperCase(), | |
616 evaluateXPath(expr, typeToQName(type))); | |
617 } | |
618 } | |
619 | |
620 protected String expand(String s) { | |
621 Matcher m = CompiledStatement.VAR.matcher(s); | |
622 | |
623 Object [] result = new Object[1]; | |
624 | |
625 StringBuffer sb = new StringBuffer(); | |
626 while (m.find()) { | |
627 String key = m.group(1); | |
628 result[0] = null; | |
629 if (frames.getStore(key, result)) { | |
630 m.appendReplacement( | |
631 sb, result[0] != null ? result[0].toString() : ""); | |
632 } | |
633 else { | |
634 m.appendReplacement(sb, "\\${" + key + "}"); | |
635 } | |
636 } | |
637 m.appendTail(sb); | |
638 return sb.toString(); | |
639 } | |
640 | |
641 protected void evaluateAttributeValue(Attr attr) { | |
642 String value = attr.getValue(); | |
643 if (value.indexOf('{') >= 0) { | |
644 StringBuffer sb = new StringBuffer(); | |
645 Matcher m = BRACKET_XPATH.matcher(value); | |
646 while (m.find()) { | |
647 String expr = m.group(1); | |
648 Object result = evaluateXPath(expr, XPathConstants.STRING); | |
649 if (result instanceof String) { | |
650 m.appendReplacement(sb, (String)result); | |
651 } | |
652 else { | |
653 m.appendReplacement(sb, ""); | |
654 } | |
655 } | |
656 m.appendTail(sb); | |
657 attr.setValue(sb.toString()); | |
658 } | |
659 } | |
660 | |
661 protected void build(Node parent, Node current) | |
662 throws SQLException | |
663 { | |
664 String ns = current.getNamespaceURI(); | |
665 if (ns != null && ns.equals(DC_NAMESPACE_URI)) { | |
666 if (current.getNodeType() != Node.ELEMENT_NODE) { | |
667 log.warn("need elements here"); | |
668 } | |
669 else { | |
670 String localName = current.getLocalName(); | |
671 Element curr = (Element)current; | |
672 if ("attribute".equals(localName)) { | |
673 attribute(parent, curr); | |
674 } | |
675 else if ("context".equals(localName)) { | |
676 context(parent, curr); | |
677 } | |
678 else if ("if".equals(localName)) { | |
679 ifClause(parent, curr); | |
680 } | |
681 else if ("choose".equals(localName)) { | |
682 choose(parent, curr); | |
683 } | |
684 else if ("call-macro".equals(localName)) { | |
685 callMacro(parent, curr); | |
686 } | |
687 else if ("macro-body".equals(localName)) { | |
688 macroBody(parent, curr); | |
689 } | |
690 else if ("macro".equals(localName) | |
691 || "comment".equals(localName) | |
692 || "statement".equals(localName)) { | |
693 // Simply ignore them. | |
694 } | |
695 else if ("element".equals(localName)) { | |
696 element(parent, curr); | |
697 } | |
698 else if ("for-each".equals(localName)) { | |
699 foreach(parent, curr); | |
700 } | |
701 else if ("filter".equals(localName)) { | |
702 filter(parent, curr); | |
703 } | |
704 else if ("text".equals(localName)) { | |
705 text(parent, curr); | |
706 } | |
707 else if ("variable".equals(localName)) { | |
708 variable(curr); | |
709 } | |
710 else if ("convert".equals(localName)) { | |
711 convert(curr); | |
712 } | |
713 else { | |
714 log.warn("unknown '" + localName + "' -> ignore"); | |
715 } | |
716 } | |
717 return; | |
718 } | |
719 | |
720 if (current.getNodeType() == Node.TEXT_NODE) { | |
721 String txt = current.getNodeValue(); | |
722 if (txt != null && txt.trim().length() == 0) { | |
723 return; | |
724 } | |
725 } | |
726 | |
727 if (current.getNodeType() == Node.COMMENT_NODE) { | |
728 // Ignore XML comments | |
729 return; | |
730 } | |
731 | |
732 Node copy = owner.importNode(current, false); | |
733 | |
734 NodeList children = current.getChildNodes(); | |
735 for (int i = 0, N = children.getLength(); i < N; ++i) { | |
736 build(copy, children.item(i)); | |
737 } | |
738 if (copy.getNodeType() == Node.ELEMENT_NODE) { | |
739 NamedNodeMap nnm = ((Element)copy).getAttributes(); | |
740 for (int i = 0, N = nnm.getLength(); i < N; ++i) { | |
741 Node n = nnm.item(i); | |
742 if (n.getNodeType() == Node.ATTRIBUTE_NODE) { | |
743 evaluateAttributeValue((Attr)n); | |
744 } | |
745 } | |
746 } | |
747 parent.appendChild(copy); | |
748 } | |
749 } // class BuildHelper | |
750 | |
751 | |
752 public Builder() { | |
753 compiledStatements = new HashMap<String, CompiledStatement>(); | |
754 macros = new HashMap<String, Element>(); | |
755 } | |
756 | |
757 public Builder(Document template) { | |
758 this(); | |
759 this.template = template; | |
760 extractMacros(); | |
761 compileStatements(); | |
762 } | |
763 | |
764 protected static QName typeToQName(String type) { | |
765 if ("number" .equals(type)) return XPathConstants.NUMBER; | |
766 if ("bool" .equals(type)) return XPathConstants.BOOLEAN; | |
767 if ("node" .equals(type)) return XPathConstants.NODE; | |
768 if ("nodeset".equals(type)) return XPathConstants.NODESET; | |
769 return XPathConstants.STRING; | |
770 } | |
771 | |
772 /** Handle <dc:statement> elements. */ | |
773 protected void compileStatements() { | |
774 | |
775 NodeList nodes = template.getElementsByTagNameNS( | |
776 DC_NAMESPACE_URI, "statement"); | |
777 | |
778 for (int i = 0, N = nodes.getLength(); i < N; ++i) { | |
779 Element stmntElement = (Element)nodes.item(i); | |
780 String stmnt = trimStatement(stmntElement.getTextContent()); | |
781 if (stmnt == null || stmnt.length() == 0) { | |
782 throw new IllegalArgumentException("found empty statement"); | |
783 } | |
784 CompiledStatement cs = new CompiledStatement(stmnt); | |
785 // For faster lookup store a shortend string into the template. | |
786 stmnt = "s" + i; | |
787 stmntElement.setTextContent(stmnt); | |
788 compiledStatements.put(stmnt, cs); | |
789 } | |
790 } | |
791 | |
792 protected void extractMacros() { | |
793 NodeList ms = template.getElementsByTagNameNS( | |
794 DC_NAMESPACE_URI, "macro"); | |
795 | |
796 for (int i = 0, N = ms.getLength(); i < N; ++i) { | |
797 Element m = (Element)ms.item(i); | |
798 macros.put(m.getAttribute("name"), m); | |
799 } | |
800 } | |
801 | |
802 protected List<Node> rootsToList() { | |
803 | |
804 NodeList roots = template.getElementsByTagNameNS( | |
805 DC_NAMESPACE_URI, "template"); | |
806 | |
807 List<Node> elements = new ArrayList<Node>(); | |
808 | |
809 for (int i = 0, N = roots.getLength(); i < N; ++i) { | |
810 NodeList rootChildren = roots.item(i).getChildNodes(); | |
811 for (int j = 0, M = rootChildren.getLength(); j < M; ++j) { | |
812 Node child = rootChildren.item(j); | |
813 if (child.getNodeType() == Node.ELEMENT_NODE) { | |
814 elements.add(child); | |
815 } | |
816 } | |
817 } | |
818 | |
819 return elements; | |
820 } | |
821 | |
822 protected static final String trimStatement(String stmnt) { | |
823 if (stmnt == null) return null; | |
824 //XXX: Maybe a bit to radical for multiline strings? | |
825 return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" "); | |
826 } | |
827 | |
828 protected static Document getOwnerDocument(Node node) { | |
829 Document document = node.getOwnerDocument(); | |
830 return document != null ? document : (Document)node; | |
831 } | |
832 | |
833 public void build( | |
834 List<NamedConnection> connections, | |
835 Node output, | |
836 Map<String, Object> parameters | |
837 ) | |
838 throws SQLException | |
839 { | |
840 BuildHelper helper = new BuildHelper(output, connections, parameters); | |
841 | |
842 helper.build(); | |
843 } | |
844 } | |
845 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |