Mercurial > dive4elements > river
comparison 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 |
comparison
equal
deleted
inserted
replaced
997:4c82609824c8 | 998:b81626b10cb7 |
---|---|
1 package de.intevation.flys.artifacts.datacage.templating; | |
2 | |
3 import java.util.regex.Pattern; | |
4 import java.util.regex.Matcher; | |
5 | |
6 import java.util.ArrayList; | |
7 import java.util.List; | |
8 import java.util.HashMap; | |
9 import java.util.Map; | |
10 | |
11 import org.w3c.dom.Document; | |
12 import org.w3c.dom.NodeList; | |
13 import org.w3c.dom.Node; | |
14 import org.w3c.dom.Element; | |
15 | |
16 import javax.xml.xpath.XPath; | |
17 import javax.xml.xpath.XPathFactory; | |
18 import javax.xml.xpath.XPathExpressionException; | |
19 import javax.xml.xpath.XPathConstants; | |
20 | |
21 import java.sql.SQLException; | |
22 import java.sql.Connection; | |
23 | |
24 import de.intevation.artifacts.common.utils.XMLUtils; | |
25 | |
26 import org.apache.log4j.Logger; | |
27 | |
28 public class Builder | |
29 { | |
30 private static Logger log = Logger.getLogger(Builder.class); | |
31 | |
32 public static final Pattern STRIP_LINE_INDENT = | |
33 Pattern.compile("\\s*\\r?\\n\\s*"); | |
34 | |
35 public static final String DC_NAMESPACE_URI = | |
36 "http://www.intevation.org/2011/Datacage"; | |
37 | |
38 private static final Document EVAL_DOCUMENT = | |
39 XMLUtils.newDocument(); | |
40 | |
41 private static final XPathFactory XPATH_FACTORY = | |
42 XPathFactory.newInstance(); | |
43 | |
44 protected Document template; | |
45 | |
46 protected Map<String, CompiledStatement> compiledStatements; | |
47 | |
48 | |
49 public class BuildHelper | |
50 { | |
51 protected Node output; | |
52 protected Document owner; | |
53 protected StackFrames frames; | |
54 protected Connection connection; | |
55 protected Map<String, CompiledStatement.Instance> statements; | |
56 | |
57 public BuildHelper( | |
58 Node output, | |
59 Connection connection, | |
60 Map<String, Object> parameters | |
61 ) { | |
62 this.output = output; | |
63 this.connection = connection; | |
64 frames = new StackFrames(parameters); | |
65 statements = new HashMap<String, CompiledStatement.Instance>(); | |
66 owner = getOwnerDocument(output); | |
67 } | |
68 | |
69 public void build() throws SQLException { | |
70 try { | |
71 synchronized (template) { | |
72 for (Node current: rootsToList()) { | |
73 build(output, current); | |
74 } | |
75 } | |
76 } | |
77 finally { | |
78 closeStatements(); | |
79 } | |
80 } | |
81 | |
82 protected void closeStatements() { | |
83 for (CompiledStatement.Instance csi: statements.values()) { | |
84 csi.close(); | |
85 } | |
86 statements.clear(); | |
87 } | |
88 | |
89 protected void context(Node parent, Element current) | |
90 throws SQLException | |
91 { | |
92 NodeList elements = current.getElementsByTagNameNS( | |
93 DC_NAMESPACE_URI, "elements"); | |
94 | |
95 if (elements.getLength() < 1) { | |
96 log.warn("no elements found -> ignore"); | |
97 return; | |
98 } | |
99 | |
100 NodeList subs = elements.item(0).getChildNodes(); | |
101 int S = subs.getLength(); | |
102 | |
103 if (S < 1) { | |
104 log.warn("elements is empty -> ignore"); | |
105 return; | |
106 } | |
107 | |
108 NodeList stmntNode = current.getElementsByTagNameNS( | |
109 DC_NAMESPACE_URI, "statement"); | |
110 | |
111 if (stmntNode.getLength() < 1) { | |
112 log.warn("dc:context: too less statements"); | |
113 return; | |
114 } | |
115 | |
116 String stmntText = stmntNode.item(0).getTextContent(); | |
117 | |
118 CompiledStatement.Instance csi = statements.get(stmntText); | |
119 | |
120 if (csi == null) { | |
121 CompiledStatement cs = compiledStatements.get(stmntText); | |
122 csi = cs.new Instance(); | |
123 statements.put(stmntText, csi); | |
124 } | |
125 | |
126 ResultData rd = csi.execute(connection, frames); | |
127 | |
128 String [] columns = rd.getColumnLabels(); | |
129 | |
130 | |
131 for (Object [] row: rd.getRows()) { | |
132 frames.enter(); | |
133 try { | |
134 frames.put(columns, row); | |
135 for (int i = 0; i < S; ++i) { | |
136 build(parent, subs.item(i)); | |
137 } | |
138 } | |
139 finally { | |
140 frames.leave(); | |
141 } | |
142 } | |
143 } | |
144 | |
145 protected void element(Node parent, Element current) | |
146 throws SQLException | |
147 { | |
148 String attr = expand(current.getAttribute("name")); | |
149 | |
150 if (log.isDebugEnabled()) { | |
151 log.debug("dc:element -> '" + attr + "'"); | |
152 } | |
153 | |
154 if (attr.length() == 0) { | |
155 log.warn("no name attribute found"); | |
156 return; | |
157 } | |
158 | |
159 Element element = owner.createElement(attr); | |
160 | |
161 NodeList children = current.getChildNodes(); | |
162 for (int i = 0, N = children.getLength(); i < N; ++i) { | |
163 build(element, children.item(i)); | |
164 } | |
165 | |
166 parent.appendChild(element); | |
167 } | |
168 | |
169 protected void text(Node parent, Element current) | |
170 throws SQLException | |
171 { | |
172 log.debug("dc:text"); | |
173 String value = expand(current.getTextContent()); | |
174 parent.appendChild(owner.createTextNode(value)); | |
175 } | |
176 | |
177 | |
178 protected void attribute(Node parent, Element current) { | |
179 | |
180 if (parent.getNodeType() != Node.ELEMENT_NODE) { | |
181 log.warn("need element here"); | |
182 return; | |
183 } | |
184 | |
185 String name = expand(current.getAttribute("name")); | |
186 String value = expand(current.getAttribute("value")); | |
187 | |
188 Element element = (Element)parent; | |
189 | |
190 element.setAttribute(name, value); | |
191 } | |
192 | |
193 protected void callMacro(Node parent, Element current) | |
194 throws SQLException | |
195 { | |
196 String name = current.getAttribute("name"); | |
197 | |
198 if (name.length() == 0) { | |
199 log.warn("missing 'name' attribute in 'call-macro'"); | |
200 return; | |
201 } | |
202 | |
203 NodeList macros = template.getElementsByTagNameNS( | |
204 DC_NAMESPACE_URI, "macro"); | |
205 | |
206 for (int i = 0, N = macros.getLength(); i < N; ++i) { | |
207 Element macro = (Element)macros.item(i); | |
208 if (name.equals(macro.getAttribute("name"))) { | |
209 NodeList subs = macro.getChildNodes(); | |
210 for (int j = 0, M = subs.getLength(); j < M; ++j) { | |
211 build(parent, subs.item(j)); | |
212 } | |
213 return; | |
214 } | |
215 } | |
216 | |
217 log.warn("no macro '" + name + "' found."); | |
218 } | |
219 | |
220 protected void ifClause(Node parent, Element current) | |
221 throws SQLException | |
222 { | |
223 String test = current.getAttribute("test"); | |
224 | |
225 if (test.length() == 0) { | |
226 log.warn("missing 'test' attribute in 'if'"); | |
227 return; | |
228 } | |
229 | |
230 Boolean result = evaluateXPath(test); | |
231 | |
232 if (result != null && result.booleanValue()) { | |
233 NodeList subs = current.getChildNodes(); | |
234 for (int i = 0, N = subs.getLength(); i < N; ++i) { | |
235 build(parent, subs.item(i)); | |
236 } | |
237 } | |
238 } | |
239 | |
240 protected void choose(Node parent, Element current) | |
241 throws SQLException | |
242 { | |
243 Node branch = null; | |
244 | |
245 NodeList children = current.getChildNodes(); | |
246 for (int i = 0, N = children.getLength(); i < N; ++i) { | |
247 Node child = children.item(i); | |
248 String ns = child.getNamespaceURI(); | |
249 if (ns == null | |
250 || !ns.equals(DC_NAMESPACE_URI) | |
251 || child.getNodeType() != Node.ELEMENT_NODE | |
252 ) { | |
253 continue; | |
254 } | |
255 String name = child.getLocalName(); | |
256 if ("when".equals(name)) { | |
257 Element when = (Element)child; | |
258 String test = when.getAttribute("test"); | |
259 if (test.length() == 0) { | |
260 log.warn("no 'test' attribute found for when"); | |
261 continue; | |
262 } | |
263 | |
264 Boolean result = evaluateXPath(test); | |
265 if (result != null && result.booleanValue()) { | |
266 branch = child; | |
267 break; | |
268 } | |
269 | |
270 continue; | |
271 } | |
272 else if ("otherwise".equals(name)) { | |
273 branch = child; | |
274 // No break here. | |
275 } | |
276 } | |
277 | |
278 if (branch != null) { | |
279 NodeList subs = branch.getChildNodes(); | |
280 for (int i = 0, N = subs.getLength(); i < N; ++i) { | |
281 build(parent, subs.item(i)); | |
282 } | |
283 } | |
284 } | |
285 | |
286 protected Boolean evaluateXPath(String expr) { | |
287 | |
288 if (log.isDebugEnabled()) { | |
289 log.debug("evaluate: '" + expr + "'"); | |
290 } | |
291 | |
292 try { | |
293 XPath xpath = XPATH_FACTORY.newXPath(); | |
294 xpath.setXPathVariableResolver(frames); | |
295 xpath.setXPathFunctionResolver(FunctionResolver.FUNCTIONS); | |
296 Object result = xpath.evaluate( | |
297 expr, EVAL_DOCUMENT, XPathConstants.BOOLEAN); | |
298 | |
299 return result instanceof Boolean | |
300 ? (Boolean)result | |
301 : null; | |
302 } | |
303 catch (XPathExpressionException xfce) { | |
304 log.error(xfce); | |
305 } | |
306 return null; | |
307 } | |
308 | |
309 protected void convert(Node parent, Element current) { | |
310 | |
311 String variable = expand(current.getAttribute("var")); | |
312 String type = expand(current.getAttribute("type")); | |
313 | |
314 if (frames.containsKey(variable)) { | |
315 Object object = TypeConverter.convert( | |
316 frames.get(variable), | |
317 type); | |
318 frames.put(variable, object); | |
319 } | |
320 } | |
321 | |
322 protected String expand(String s) { | |
323 Matcher m = CompiledStatement.VAR.matcher(s); | |
324 | |
325 StringBuffer sb = new StringBuffer(); | |
326 while (m.find()) { | |
327 String key = m.group(1); | |
328 Object value = frames.get(key); | |
329 m.appendReplacement(sb, value != null ? value.toString() : ""); | |
330 } | |
331 m.appendTail(sb); | |
332 return sb.toString(); | |
333 } | |
334 | |
335 protected void build(Node parent, Node current) | |
336 throws SQLException | |
337 { | |
338 String ns = current.getNamespaceURI(); | |
339 if (ns != null && ns.equals(DC_NAMESPACE_URI)) { | |
340 if (current.getNodeType() != Node.ELEMENT_NODE) { | |
341 log.warn("need elements here"); | |
342 } | |
343 else { | |
344 String localName = current.getLocalName(); | |
345 if ("attribute".equals(localName)) { | |
346 attribute(parent, (Element)current); | |
347 } | |
348 else if ("context".equals(localName)) { | |
349 context(parent, (Element)current); | |
350 } | |
351 else if ("if".equals(localName)) { | |
352 ifClause(parent, (Element)current); | |
353 } | |
354 else if ("choose".equals(localName)) { | |
355 choose(parent, (Element)current); | |
356 } | |
357 else if ("call-macro".equals(localName)) { | |
358 callMacro(parent, (Element)current); | |
359 } | |
360 else if ("macro".equals(localName)) { | |
361 // simply ignore the definition. | |
362 } | |
363 else if ("element".equals(localName)) { | |
364 element(parent, (Element)current); | |
365 } | |
366 else if ("text".equals(localName)) { | |
367 text(parent, (Element)current); | |
368 } | |
369 else if ("convert".equals(localName)) { | |
370 convert(parent, (Element)current); | |
371 } | |
372 else { | |
373 log.warn("unknown '" + localName + "' -> ignore"); | |
374 } | |
375 } | |
376 return; | |
377 } | |
378 | |
379 if (current.getNodeType() == Node.TEXT_NODE) { | |
380 String txt = current.getNodeValue(); | |
381 if (txt != null && txt.trim().length() == 0) { | |
382 return; | |
383 } | |
384 } | |
385 | |
386 Node copy = owner.importNode(current, false); | |
387 | |
388 NodeList children = current.getChildNodes(); | |
389 for (int i = 0, N = children.getLength(); i < N; ++i) { | |
390 build(copy, children.item(i)); | |
391 } | |
392 parent.appendChild(copy); | |
393 } | |
394 } // class BuildHelper | |
395 | |
396 | |
397 public Builder() { | |
398 compiledStatements = new HashMap<String, CompiledStatement>(); | |
399 } | |
400 | |
401 public Builder(Document template) { | |
402 this(); | |
403 this.template = template; | |
404 compileStatements(); | |
405 } | |
406 | |
407 protected void compileStatements() { | |
408 | |
409 NodeList nodes = template.getElementsByTagNameNS( | |
410 DC_NAMESPACE_URI, "statement"); | |
411 | |
412 for (int i = 0, N = nodes.getLength(); i < N; ++i) { | |
413 Element stmntElement = (Element)nodes.item(i); | |
414 String stmnt = trimStatement(stmntElement.getTextContent()); | |
415 if (stmnt == null || stmnt.length() == 0) { | |
416 throw new IllegalArgumentException("found empty statement"); | |
417 } | |
418 CompiledStatement cs = new CompiledStatement(stmnt); | |
419 // for faster lookup store a shortend string into the template | |
420 stmnt = "s" + i; | |
421 stmntElement.setTextContent(stmnt); | |
422 compiledStatements.put(stmnt, cs); | |
423 } | |
424 } | |
425 | |
426 protected List<Node> rootsToList() { | |
427 | |
428 NodeList roots = template.getElementsByTagNameNS( | |
429 DC_NAMESPACE_URI, "template"); | |
430 | |
431 List<Node> elements = new ArrayList<Node>(); | |
432 | |
433 for (int i = 0, N = roots.getLength(); i < N; ++i) { | |
434 NodeList rootChildren = roots.item(i).getChildNodes(); | |
435 for (int j = 0, M = rootChildren.getLength(); j < M; ++j) { | |
436 Node child = rootChildren.item(j); | |
437 if (child.getNodeType() == Node.ELEMENT_NODE) { | |
438 elements.add(child); | |
439 } | |
440 } | |
441 } | |
442 | |
443 return elements; | |
444 } | |
445 | |
446 protected static final String trimStatement(String stmnt) { | |
447 if (stmnt == null) return null; | |
448 //XXX: Maybe a bit to radical for multiline strings? | |
449 return STRIP_LINE_INDENT.matcher(stmnt.trim()).replaceAll(" "); | |
450 } | |
451 | |
452 protected static Document getOwnerDocument(Node node) { | |
453 Document document = node.getOwnerDocument(); | |
454 return document != null ? document : (Document)node; | |
455 } | |
456 | |
457 public void build( | |
458 Connection connection, | |
459 Node output, | |
460 Map<String, Object> parameters | |
461 ) | |
462 throws SQLException | |
463 { | |
464 BuildHelper helper = new BuildHelper(output, connection, parameters); | |
465 | |
466 helper.build(); | |
467 } | |
468 } | |
469 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 : |