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 :

http://dive4elements.wald.intevation.org