changeset 1015:9a1a3080ad98

Bring user specific meta data service to life. flys-artifacts/trunk@2461 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 03 Aug 2011 16:40:04 +0000
parents 66473e72d321
children ff7b81cf9999
files flys-artifacts/ChangeLog flys-artifacts/doc/conf/conf.xml flys-artifacts/doc/conf/meta-data-system.xml flys-artifacts/doc/conf/meta-data-template.xml flys-artifacts/doc/conf/meta-data-user.xml flys-artifacts/src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/DBConfig.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/NoneUserSpecific.java flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java
diffstat 12 files changed, 634 insertions(+), 581 deletions(-) [+]
line wrap: on
line diff
--- a/flys-artifacts/ChangeLog	Wed Aug 03 11:23:13 2011 +0000
+++ b/flys-artifacts/ChangeLog	Wed Aug 03 16:40:04 2011 +0000
@@ -1,3 +1,42 @@
+2011-08-03  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Bring user specific meta data service to life.
+
+	* doc/conf/conf.xml: There are now two templates to configure:
+	  The system template (only the data from the backend) and the
+	  user template (the datcage db and the backend db),
+
+	* doc/conf/meta-data-template.xml: Deleted.
+	* doc/conf/meta-data-user.xml: New. The user specific template. TODO: Write it!
+	* doc/conf/meta-data-system.xml: New. The system template.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/NoneUserSpecific.java:
+	  Deleted.
+	* src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java:
+	  New. The logic to fill the templates.
+
+	* src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java:
+	  Adjusted to follow the new call signatures.
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Refactored. Removed the old code and only leave the new service. Following
+	  XPaths are evaluated on the incomming document:
+
+	  "/art:meta/art:artifact-id/@value" The UUID of the artifact. Optional.
+	                                     Used to fill the template enviroment.
+	  "art:meta/art:user-id/@value"      The UUID of the user. Optional.
+	                                     If given the user specific template is filled.
+	  "/art:meta/art:outs/@value"        The list of outs used to recommend for the
+	                                     various outputs.
+	  "/art:meta/art:parameters/@value"  A list of key/value pairs to inject more
+	                                     filters to the templating.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java:
+	  Change to follow the new recommendations semantics.
+
+	* src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java:
+	  Added symbolic constants to distinguish "user" and "system" db connections.
+
 2011-08-03  Felix Wolfsteller <felix.wolfsteller@intevation.de>
 
 	* doc/conf/conf.xml:
--- a/flys-artifacts/doc/conf/conf.xml	Wed Aug 03 11:23:13 2011 +0000
+++ b/flys-artifacts/doc/conf/conf.xml	Wed Aug 03 16:40:04 2011 +0000
@@ -98,7 +98,10 @@
     </output-generators>
 
     <!-- Path to the template file of the meta data. -->
-    <metadata template="${artifacts.config.dir}/meta-data-template.xml"/>
+    <metadata>
+        <system template="${artifacts.config.dir}/meta-data-system.xml"/>
+        <user   template="${artifacts.config.dir}/meta-data-user.xml"/>
+    </metadata>
 
     <!-- The WMS layer that is displayed as background layer in floodmaps. -->
     <floodmap>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/meta-data-system.xml	Wed Aug 03 16:40:04 2011 +0000
@@ -0,0 +1,258 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<dc:template xmlns:dc="http://www.intevation.org/2011/Datacage">
+<datacage>
+  <dc:context>
+    <dc:statement>
+      SELECT id AS river_id, name as river_name FROM rivers
+      WHERE name ILIKE ${river}
+    </dc:statement>
+    <dc:elements>
+      <river>
+        <dc:attribute name="name" value="${river_name}"/>
+        <dc:attribute name="db-id" value="${river_id}"/>
+        <dc:if test="dc:contains($artifact-outs, 'computed_discharge_curve')">
+            <discharge-table-nn>
+              <discharge-table-gauge>
+                <dc:context>
+                  <dc:statement>
+                    SELECT id   AS gauge_id,
+                           name AS gauge_name
+                    FROM gauges WHERE river_id = ${river_id}
+                  </dc:statement>
+                  <dc:elements>
+                    <gauge>
+                      <dc:attribute name="name" value="${gauge_name}"/>
+                      <dc:attribute name="db-id" value="${gauge_id}"/>
+                      <dc:context>
+                        <dc:statement>
+                          SELECT description   AS gauge_desc, 
+                                 d.id          AS discharge_id,
+                                 ti.start_time AS g_start, 
+                                 ti.stop_time  AS g_stop
+                          FROM discharge_tables d JOIN time_intervals ti 
+                          ON d.time_interval_id = ti.id
+                          WHERE d.gauge_id = ${gauge_id} AND d.kind = 1
+                        </dc:statement>
+                        <dc:elements>
+                          <historical>
+                            <dc:attribute name="name" value="${gauge_desc}"/>
+                            <dc:attribute name="from" value="${g_start}"/>
+                            <dc:attribute name="to" value="${g_stop}"/>
+                            <dc:attribute name="db-id" value="${discharge_id}"/></historical>
+                        </dc:elements>
+                      </dc:context>
+                    </gauge>
+                  </dc:elements>
+                </dc:context>
+              </discharge-table-gauge>
+              <fixations>
+                <dc:attribute name="id" value="fixations-${river_id}"/>
+                <dc:context>
+                  <dc:statement>
+                    SELECT id          AS fix_id,
+                           description AS fix_description
+                    FROM wsts WHERE kind = 2 AND river_id = ${river_id}
+                  </dc:statement>
+                  <dc:elements>
+                    <fixation>
+                      <dc:attribute name="name" value="${fix_description}"/>
+                      <dc:attribute name="db-id" value="${fix_id}"/>
+                      <columns>
+                        <dc:context>
+                          <dc:statement>
+                            SELECT id   AS fix_column_id,
+                                   name AS fix_column_name
+                            FROM wst_columns WHERE wst_id = ${fix_id}
+                            ORDER by position
+                          </dc:statement>
+                          <dc:elements>
+                            <column>
+                              <dc:attribute name="name" value="${fix_column_name}"/>
+                              <dc:attribute name="db-id" value="${fix_column_id}"/></column>
+                          </dc:elements>
+                        </dc:context>
+                      </columns>
+                    </fixation>
+                  </dc:elements>
+                </dc:context>
+              </fixations>
+              <flood-protections>
+                <dc:attribute name="id" value="flood-protections-${river_id}"/>
+                <dc:context>
+                  <dc:statement>
+                    SELECT id          AS prot_id,
+                           description AS prot_description
+                    FROM wsts WHERE kind = 5 AND river_id = ${river_id}
+                  </dc:statement>
+                  <dc:elements>
+                    <flood-protection>
+                      <dc:attribute name="name" value="${prot_description}"/>
+                      <dc:attribute name="db-id" value="${prot_id}"/>
+                      <columns>
+                        <dc:context>
+                          <dc:statement>
+                            SELECT id   AS prot_column_id,
+                                   name AS prot_column_name
+                            FROM wst_columns WHERE wst_id = ${prot_id}
+                            ORDER by position
+                          </dc:statement>
+                          <dc:elements>
+                            <column>
+                              <dc:attribute name="name" value="${prot_column_name}"/>
+                              <dc:attribute name="db-id" value="${prot_column_id}"/></column>
+                          </dc:elements>
+                        </dc:context>
+                      </columns>
+                    </flood-protection>
+                  </dc:elements>
+                </dc:context>
+              </flood-protections>
+              <flood-water-marks>
+                <dc:attribute name="id" value="flood-water-marks-${river_id}"/>
+                <dc:context>
+                  <dc:statement>
+                    SELECT id          AS fw_id,
+                           description AS fw_description
+                    FROM wsts WHERE kind = 4 AND river_id = ${river_id}
+                  </dc:statement>
+                  <dc:elements>
+                    <flood-water-mark>
+                      <dc:attribute name="name" value="${fw_description}"/>
+                      <dc:attribute name="db-id" value="${fw_id}"/>
+                      <columns>
+                        <dc:context>
+                          <dc:statement>
+                            SELECT id   AS fw_column_id,
+                                   name AS fw_column_name
+                            FROM wst_columns WHERE wst_id = ${fw_id}
+                            ORDER by position
+                          </dc:statement>
+                          <dc:elements>
+                            <column>
+                              <dc:attribute name="name" value="${fw_column_name}"/>
+                              <dc:attribute name="db-id" value="${fw_column_id}"/></column>
+                          </dc:elements>
+                        </dc:context>
+                      </columns>
+                    </flood-water-mark>
+                  </dc:elements>
+                </dc:context>
+              </flood-water-marks>
+              <water-levels>
+                <dc:attribute name="id" value="water-levels-${river_id}"/>
+                <dc:context>
+                  <dc:statement>
+                    SELECT id          AS wl_id,
+                           description AS wl_description
+                    FROM wsts WHERE kind = 0 AND river_id = ${river_id}
+                  </dc:statement>
+                  <dc:elements>
+                    <water-level>
+                      <dc:attribute name="name" value="${wl_description}"/>
+                      <dc:attribute name="db-id" value="${wl_id}"/>
+                      <columns>
+                        <dc:context>
+                          <dc:statement>
+                            SELECT id   AS wl_column_id,
+                                   name AS wl_column_name
+                            FROM wst_columns WHERE wst_id = ${wl_id}
+                            ORDER by position
+                          </dc:statement>
+                          <dc:elements>
+                            <column>
+                              <dc:attribute name="name" value="${wl_column_name}"/>
+                              <dc:attribute name="db-id" value="${wl_column_id}"/></column>
+                          </dc:elements>
+                        </dc:context>
+                      </columns>
+                    </water-level>
+                  </dc:elements>
+                </dc:context>
+              </water-levels>
+              <extra-longitudinal-sections>
+                <dc:attribute name="id" value="extra-longitudinal-sections-${river_id}"/>
+                <dc:context>
+                  <dc:statement>
+                    SELECT id          AS els_id,
+                           description AS els_description
+                    FROM wsts WHERE kind = 1 AND river_id = ${river_id}
+                  </dc:statement>
+                  <dc:elements>
+                    <extra-longitudinal-section>
+                      <dc:attribute name="name" value="${els_description}"/>
+                      <dc:attribute name="db-id" value="${els_id}"/>
+                      <columns>
+                        <dc:context>
+                          <dc:statement>
+                            SELECT id   AS els_column_id,
+                                   name AS els_column_name
+                            FROM wst_columns WHERE wst_id = ${els_id}
+                            ORDER by position
+                          </dc:statement>
+                          <dc:elements>
+                            <column>
+                                <dc:attribute name="name" value="${els_column_name}"/>
+                                <dc:attribute name="db-id" value="${els_column_id}"/></column>
+                          </dc:elements>
+                        </dc:context>
+                      </columns>
+                    </extra-longitudinal-section>
+                  </dc:elements>
+                </dc:context>
+              </extra-longitudinal-sections>
+            </discharge-table-nn>
+        </dc:if>
+        <dc:if test="dc:contains($artifact-outs, 'longitudinal_section')">
+            <longitudinal-section>
+              <fixations><dc:attribute name="ref" value="fixations-${river_id}"/></fixations>
+              <flood-protections><dc:attribute name="ref" value="flood-protections-${river_id}"/></flood-protections>
+              <flood-water-marks><dc:attribute name="ref" value="flood-water-marks-${river_id}"/></flood-water-marks>
+              <water-levels><dc:attribute name="ref" value="water-levels-${river_id}"/></water-levels>
+              <extra-longitudinal-sections><dc:attribute name="ref" value="extra-longitudinal-sections-${river_id}"/></extra-longitudinal-sections>
+            </longitudinal-section>
+        </dc:if>
+        <dc:if test="dc:contains($artifact-outs, 'floodmap')">
+            <floodmap>
+            <dc:choose>
+                <dc:when test="dc:contains($parameters, 'recommended')">
+                  <dc:call-macro name="flood-map-recommended"/>
+                </dc:when>
+                <dc:otherwise>
+                  <dc:call-macro name="flood-map-complete"/>
+                </dc:otherwise>
+            </dc:choose>
+            </floodmap>
+            <dc:macro name="flood-map-recommended">
+                <kilometrage>
+                    <riveraxis>
+                        <dc:attribute name="factory" value="riveraxis"/>
+                        <dc:attribute name="db-ids" value="${river_id}"/>
+                    </riveraxis>
+                </kilometrage>
+                <rastermap>
+                    <background>
+                        <dc:attribute name="factory" value="wmsbackground"/>
+                        <dc:attribute name="db-ids" value="${river_id}"/>
+                    </background>
+                </rastermap>
+            </dc:macro>
+            <dc:macro name="flood-map-complete">
+                <kilometrage>
+                    <riveraxis>
+                        <dc:attribute name="factory" value="riveraxis"/>
+                        <dc:attribute name="db-ids" value="${river_id}"/>
+                    </riveraxis>
+                </kilometrage>
+                <rastermap>
+                    <background>
+                        <dc:attribute name="factory" value="wmsbackground"/>
+                        <dc:attribute name="db-ids" value="${river_id}"/>
+                    </background>
+                </rastermap>
+            </dc:macro>
+        </dc:if>
+      </river>
+    </dc:elements>
+  </dc:context>
+</datacage>
+</dc:template>
--- a/flys-artifacts/doc/conf/meta-data-template.xml	Wed Aug 03 11:23:13 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,259 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<dc:template xmlns:dc="http://www.intevation.org/2011/Datacage">
-<datacage>
-  <dc:context>
-    <dc:statement>
-      SELECT id AS river_id, name as river_name FROM rivers
-      WHERE name ILIKE ${river}
-    </dc:statement>
-    <dc:elements>
-      <river>
-        <dc:attribute name="name" value="${river_name}"/>
-        <dc:attribute name="db-id" value="${river_id}"/>
-        <dc:if test="dc:contains($artifact-outs, 'computed_discharge_curve')">
-            <discharge-table-nn>
-              <discharge-table-gauge>
-                <dc:context>
-                  <dc:statement>
-                    SELECT id   AS gauge_id,
-                           name AS gauge_name
-                    FROM gauges WHERE river_id = ${river_id}
-                  </dc:statement>
-                  <dc:elements>
-                    <gauge>
-                      <dc:attribute name="name" value="${gauge_name}"/>
-                      <dc:attribute name="db-id" value="${gauge_id}"/>
-                      <dc:context>
-                        <dc:statement>
-                          SELECT description   AS gauge_desc, 
-                                 d.id          AS discharge_id,
-                                 ti.start_time AS g_start, 
-                                 ti.stop_time  AS g_stop
-                          FROM discharge_tables d JOIN time_intervals ti 
-                          ON d.time_interval_id = ti.id
-                          WHERE d.gauge_id = ${gauge_id} AND d.kind = 1
-                        </dc:statement>
-                        <dc:elements>
-                          <historical>
-                            <dc:attribute name="name" value="${gauge_desc}"/>
-                            <dc:attribute name="from" value="${g_start}"/>
-                            <dc:attribute name="to" value="${g_stop}"/>
-                            <dc:attribute name="db-id" value="${discharge_id}"/></historical>
-                        </dc:elements>
-                      </dc:context>
-                    </gauge>
-                  </dc:elements>
-                </dc:context>
-              </discharge-table-gauge>
-              <fixations>
-                <dc:attribute name="id" value="fixations-${river_id}"/>
-                <dc:context>
-                  <dc:statement>
-                    SELECT id          AS fix_id,
-                           description AS fix_description
-                    FROM wsts WHERE kind = 2 AND river_id = ${river_id}
-                  </dc:statement>
-                  <dc:elements>
-                    <fixation>
-                      <dc:attribute name="name" value="${fix_description}"/>
-                      <dc:attribute name="db-id" value="${fix_id}"/>
-                      <columns>
-                        <dc:context>
-                          <dc:statement>
-                            SELECT id   AS fix_column_id,
-                                   name AS fix_column_name
-                            FROM wst_columns WHERE wst_id = ${fix_id}
-                            ORDER by position
-                          </dc:statement>
-                          <dc:elements>
-                            <column>
-                              <dc:attribute name="name" value="${fix_column_name}"/>
-                              <dc:attribute name="db-id" value="${fix_column_id}"/></column>
-                          </dc:elements>
-                        </dc:context>
-                      </columns>
-                    </fixation>
-                  </dc:elements>
-                </dc:context>
-              </fixations>
-              <flood-protections>
-                <dc:attribute name="id" value="flood-protections-${river_id}"/>
-                <dc:context>
-                  <dc:statement>
-                    SELECT id          AS prot_id,
-                           description AS prot_description
-                    FROM wsts WHERE kind = 5 AND river_id = ${river_id}
-                  </dc:statement>
-                  <dc:elements>
-                    <flood-protection>
-                      <dc:attribute name="name" value="${prot_description}"/>
-                      <dc:attribute name="db-id" value="${prot_id}"/>
-                      <columns>
-                        <dc:context>
-                          <dc:statement>
-                            SELECT id   AS prot_column_id,
-                                   name AS prot_column_name
-                            FROM wst_columns WHERE wst_id = ${prot_id}
-                            ORDER by position
-                          </dc:statement>
-                          <dc:elements>
-                            <column>
-                              <dc:attribute name="name" value="${prot_column_name}"/>
-                              <dc:attribute name="db-id" value="${prot_column_id}"/></column>
-                          </dc:elements>
-                        </dc:context>
-                      </columns>
-                    </flood-protection>
-                  </dc:elements>
-                </dc:context>
-              </flood-protections>
-              <flood-water-marks>
-                <dc:attribute name="id" value="flood-water-marks-${river_id}"/>
-                <dc:context>
-                  <dc:statement>
-                    SELECT id          AS fw_id,
-                           description AS fw_description
-                    FROM wsts WHERE kind = 4 AND river_id = ${river_id}
-                  </dc:statement>
-                  <dc:elements>
-                    <flood-water-mark>
-                      <dc:attribute name="name" value="${fw_description}"/>
-                      <dc:attribute name="db-id" value="${fw_id}"/>
-                      <columns>
-                        <dc:context>
-                          <dc:statement>
-                            SELECT id   AS fw_column_id,
-                                   name AS fw_column_name
-                            FROM wst_columns WHERE wst_id = ${fw_id}
-                            ORDER by position
-                          </dc:statement>
-                          <dc:elements>
-                            <column>
-                              <dc:attribute name="name" value="${fw_column_name}"/>
-                              <dc:attribute name="db-id" value="${fw_column_id}"/></column>
-                          </dc:elements>
-                        </dc:context>
-                      </columns>
-                    </flood-water-mark>
-                  </dc:elements>
-                </dc:context>
-              </flood-water-marks>
-              <water-levels>
-                <dc:attribute name="id" value="water-levels-${river_id}"/>
-                <dc:context>
-                  <dc:statement>
-                    SELECT id          AS wl_id,
-                           description AS wl_description
-                    FROM wsts WHERE kind = 0 AND river_id = ${river_id}
-                  </dc:statement>
-                  <dc:elements>
-                    <water-level>
-                      <dc:attribute name="name" value="${wl_description}"/>
-                      <dc:attribute name="db-id" value="${wl_id}"/>
-                      <columns>
-                        <dc:context>
-                          <dc:statement>
-                            SELECT id   AS wl_column_id,
-                                   name AS wl_column_name
-                            FROM wst_columns WHERE wst_id = ${wl_id}
-                            ORDER by position
-                          </dc:statement>
-                          <dc:elements>
-                            <column>
-                              <dc:attribute name="name" value="${wl_column_name}"/>
-                              <dc:attribute name="db-id" value="${wl_column_id}"/></column>
-                          </dc:elements>
-                        </dc:context>
-                      </columns>
-                    </water-level>
-                  </dc:elements>
-                </dc:context>
-              </water-levels>
-              <extra-longitudinal-sections>
-                <dc:attribute name="id" value="extra-longitudinal-sections-${river_id}"/>
-                <dc:context>
-                  <dc:statement>
-                    SELECT id          AS els_id,
-                           description AS els_description
-                    FROM wsts WHERE kind = 1 AND river_id = ${river_id}
-                  </dc:statement>
-                  <dc:elements>
-                    <extra-longitudinal-section>
-                      <dc:attribute name="name" value="${els_description}"/>
-                      <dc:attribute name="db-id" value="${els_id}"/>
-                      <columns>
-                        <dc:context>
-                          <dc:statement>
-                            SELECT id   AS els_column_id,
-                                   name AS els_column_name
-                            FROM wst_columns WHERE wst_id = ${els_id}
-                            ORDER by position
-                          </dc:statement>
-                          <dc:elements>
-                            <column>
-                                <dc:attribute name="name" value="${els_column_name}"/>
-                                <dc:attribute name="db-id" value="${els_column_id}"/></column>
-                          </dc:elements>
-                        </dc:context>
-                      </columns>
-                    </extra-longitudinal-section>
-                  </dc:elements>
-                </dc:context>
-              </extra-longitudinal-sections>
-            </discharge-table-nn>
-        </dc:if>
-        <dc:if test="dc:contains($artifact-outs, 'longitudinal_section')">
-            <longitudinal-section>
-              <fixations><dc:attribute name="ref" value="fixations-${river_id}"/></fixations>
-              <flood-protections><dc:attribute name="ref" value="flood-protections-${river_id}"/></flood-protections>
-              <flood-water-marks><dc:attribute name="ref" value="flood-water-marks-${river_id}"/></flood-water-marks>
-              <water-levels><dc:attribute name="ref" value="water-levels-${river_id}"/></water-levels>
-              <extra-longitudinal-sections><dc:attribute name="ref" value="extra-longitudinal-sections-${river_id}"/></extra-longitudinal-sections>
-            </longitudinal-section>
-        </dc:if>
-        <dc:if test="dc:contains($artifact-outs, 'floodmap')">
-            <floodmap>
-            <dc:choose>
-                <dc:when test="dc:contains($parameters, 'recommended')">
-                  <dc:call-macro name="flood-map-recommended"/>
-                </dc:when>
-                <dc:otherwise>
-                  <dc:call-macro name="flood-map-complete"/>
-                </dc:otherwise>
-            </dc:choose>
-            </floodmap>
-            <dc:macro name="flood-map-recommended">
-                <kilometrage>
-                    <riveraxis>
-                        <dc:attribute name="factory" value="riveraxis"/>
-                        <dc:attribute name="db-ids" value="${river_id}"/>
-                    </riveraxis>
-                </kilometrage>
-                <rastermap>
-                    <background>
-                        <dc:attribute name="factory" value="wmsbackground"/>
-                        <dc:attribute name="db-ids" value="${river_id}"/>
-                    </background>
-                </rastermap>
-            </dc:macro>
-            <dc:macro name="flood-map-complete">
-                <kilometrage>
-                    <riveraxis>
-                        <dc:attribute name="factory" value="riveraxis"/>
-                        <dc:attribute name="db-ids" value="${river_id}"/>
-                    </riveraxis>
-                </kilometrage>
-                <rastermap>
-                    <background>
-                        <dc:attribute name="factory" value="wmsbackground"/>
-                        <dc:attribute name="db-ids" value="${river_id}"/>
-                    </background>
-                </rastermap>
-            </dc:macro>
-        </dc:if>
-      </river>
-    </dc:elements>
-  </dc:context>
-</datacage>
-</dc:template>
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/meta-data-user.xml	Wed Aug 03 16:40:04 2011 +0000
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<dc:template xmlns:dc="http://www.intevation.org/2011/Datacage">
+<datacage>
+    <dc:text>IMPLEMENT ME!</dc:text>
+</datacage>
+</dc:template>
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java	Wed Aug 03 11:23:13 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/CollectionMonitor.java	Wed Aug 03 16:40:04 2011 +0000
@@ -22,7 +22,7 @@
 
 import de.intevation.artifactdatabase.state.Output;
 
-import de.intevation.flys.artifacts.datacage.templating.NoneUserSpecific;
+import de.intevation.flys.artifacts.datacage.Recommendations;
 
 
 public class CollectionMonitor implements Hook {
@@ -60,8 +60,8 @@
         String[] outs              = extractOutputNames(flys, context);
         Map<String, Object> params = getNoneUserSpecificParameters(flys, context);
 
-        NoneUserSpecific dc = NoneUserSpecific.getInstance();
-        dc.recommend(flys, outs, params, recommended);
+        Recommendations rec = Recommendations.getInstance();
+        rec.recommend(flys, null, outs, params, recommended);
     }
 
 
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/DBConfig.java	Wed Aug 03 11:23:13 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/DBConfig.java	Wed Aug 03 16:40:04 2011 +0000
@@ -9,7 +9,7 @@
 
 public class DBConfig
 { 
-    private static Logger logger = Logger.getLogger(DBConnection.class);
+    private static Logger logger = Logger.getLogger(DBConfig.class);
 
      /**
      * XPath to access the database driver within the global configuration.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/Recommendations.java	Wed Aug 03 16:40:04 2011 +0000
@@ -0,0 +1,252 @@
+package de.intevation.flys.artifacts.datacage;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.File;
+
+import java.io.FileInputStream;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.sql.DataSource;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.hibernate.Session;
+
+import org.hibernate.jdbc.Work;
+
+import de.intevation.artifacts.common.utils.Config;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+import de.intevation.flys.backend.SessionHolder;
+
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.flys.artifacts.datacage.templating.Builder;
+
+public class Recommendations
+{
+    private static Logger log = Logger.getLogger(Recommendations.class);
+
+    public static final String XPATH_SYSTEM_TEMPLATE =
+        "/artifact-database/metadata/system/@template";
+
+    public static final String XPATH_USER_TEMPLATE =
+        "/artifact-database/metadata/user/@template";
+
+    private static Recommendations INSTANCE;
+
+    protected Builder systemBuilder;
+    protected Builder userBuilder;
+
+    public Recommendations() {
+    }
+
+    public Recommendations(Builder systemBuilder, Builder userBuilder) {
+        this.systemBuilder = systemBuilder;
+        this.userBuilder   = userBuilder;
+    }
+
+    public Builder getUserBuilder() {
+        return userBuilder;
+    }
+
+    public Builder getSystemBuilder() {
+        return systemBuilder;
+    }
+
+    protected static void artifactToParameters(
+        FLYSArtifact        artifact, 
+        Map<String, Object> parameters
+    ) {
+        parameters.put("current-state-id", artifact.getCurrentStateId());
+
+        for (StateData sd: artifact.getAllData()) {
+            Object value = sd.getValue();
+            if (value == null) {
+                continue;
+            }
+            String key = sd.getName().replace('.', '-');
+            parameters.put(key, value);
+        }
+    }
+
+    public void  recommend(
+        FLYSArtifact        artifact,
+        String              userId,
+        String []           outs,
+        Map<String, Object> extraParameters,
+        Node                result
+    ) {
+        Map<String, Object> parameters = new HashMap<String, Object>();
+
+        if (extraParameters != null) {
+            parameters.putAll(extraParameters);
+        }
+
+        if (userId != null) {
+            parameters.put("user-id", userId);
+        }
+
+        if (artifact != null) {
+            artifactToParameters(artifact, parameters);
+        }
+
+        parameters.put("artifact-outs", outs);
+
+        parameters.put("parameters", parameters);
+
+        recommend(parameters, userId, result);
+    }
+
+    public void recommend(
+        Map<String, Object> parameters,
+        String              userId,
+        Node                result
+    ) {
+        recommend(parameters, userId, result, SessionHolder.HOLDER.get());
+    }
+
+    public void recommend(
+        final Map<String, Object> parameters,
+        final String              userId,
+        final Node                result,
+        Session                   session
+    ) {
+        if (systemBuilder == null || userBuilder == null) {
+            log.error("builder not configured properly.");
+            return;
+        }
+
+        session.doWork(new Work() {
+            @Override
+            public void execute(Connection systemConnection)
+            throws SQLException
+            {
+                List<Builder.NamedConnection> connections =
+                    new ArrayList<Builder.NamedConnection>(2);
+
+                if (userId != null) { // system and user templates
+                    connections.add(new Builder.NamedConnection(
+                        Builder.CONNECTION_USER, systemConnection));
+
+                    // get connection to datacage db
+                    DataSource dataSource = DBConfig
+                        .getInstance()
+                        .getDBConnection()
+                        .getDataSource();
+
+                    Connection userConnection = dataSource.getConnection();
+                    try {
+                        connections.add(new Builder.NamedConnection(
+                            Builder.CONNECTION_SYSTEM, userConnection, false));
+
+                        userBuilder.build(connections, result, parameters);
+                    }
+                    finally {
+                        userConnection.close();
+                    }
+                }
+                else { // system template only
+                    connections.add(new Builder.NamedConnection(
+                        Builder.CONNECTION_SYSTEM, systemConnection));
+
+                    systemBuilder.build(connections, result, parameters);
+                }
+            }
+        });
+    }
+
+    public static synchronized Recommendations getInstance() {
+        if (INSTANCE == null) {
+            INSTANCE = createRecommendations();
+        }
+        return INSTANCE;
+    }
+
+    protected static Document loadTemplate(File file) throws IOException {
+        InputStream in = null;
+
+        try {
+            in = new FileInputStream(file);
+
+            Document template = XMLUtils.parseDocument(in);
+
+            if (template == null) {
+                throw new IOException("cannot load template");
+            }
+            return template;
+        }
+        finally {
+            if (in != null) {
+                try {
+                    in.close();
+                }
+                catch (IOException ioe) {
+                    log.error(ioe);
+                }
+            }
+        }
+    }
+
+    public static Recommendations createRecommendations(
+        File systemFile,
+        File userFile
+    ) {
+        log.debug("Recommendations.createBuilder");
+
+        if (!systemFile.isFile() || !systemFile.canRead()) {
+            log.error("Cannot open template file '" + systemFile + "'");
+            return null;
+        }
+
+        if (!userFile.isFile() || !userFile.canRead()) {
+            log.error("Cannot open template file '" + userFile + "'");
+            return null;
+        }
+
+        try {
+            Document systemTemplate = loadTemplate(systemFile);
+            Document userTemplate   = loadTemplate(userFile);
+            return new Recommendations(
+                new Builder(systemTemplate),
+                new Builder(userTemplate));
+        }
+        catch (IOException ioe) {
+            log.error(ioe);
+            return null;
+        }
+    }
+
+    protected static Recommendations createRecommendations() {
+        log.debug("Recommendations.createRecommendations");
+
+        String systemPath = Config.getStringXPath(XPATH_SYSTEM_TEMPLATE);
+        String userPath   = Config.getStringXPath(XPATH_USER_TEMPLATE);
+
+        if (systemPath == null || userPath == null) {
+            log.error("no path to template file given");
+            return null;
+        }
+
+        systemPath = Config.replaceConfigDir(systemPath);
+        userPath   = Config.replaceConfigDir(userPath);
+
+        return createRecommendations(
+            new File(systemPath),
+            new File(userPath));
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java	Wed Aug 03 11:23:13 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/App.java	Wed Aug 03 16:40:04 2011 +0000
@@ -1,8 +1,5 @@
 package de.intevation.flys.artifacts.datacage.templating;
 
-import java.sql.Connection;
-import java.sql.SQLException;
-
 import java.util.Map;
 import java.util.HashMap;
 
@@ -15,20 +12,26 @@
 
 import org.hibernate.Session;
 
-import org.hibernate.jdbc.Work;
-
 import org.w3c.dom.Document;
 
 import org.apache.log4j.Logger;
 
 import de.intevation.artifacts.common.utils.XMLUtils;
 
+import de.intevation.flys.artifacts.datacage.Recommendations;
+
 public class App
 {
     private static Logger log = Logger.getLogger(App.class);
 
-    public static final String template =
-        System.getProperty("meta.data.template", "meta-data-template.xml");
+    public static final String systemTemplate =
+        System.getProperty("meta.data.template", "meta-data-system.xml");
+
+    public static final String userTemplate =
+        System.getProperty("meta.data.template", "meta-data-user.xml");
+
+    public static final String userId =
+        System.getProperty("user.id");
 
     public static final String PARAMETERS =
         System.getProperty("meta.data.parameters", "");
@@ -52,16 +55,16 @@
 
     public static void main(String [] args) {
 
-        NoneUserSpecific dc = new NoneUserSpecific(
-            NoneUserSpecific.createBuilder(new File(template)));
+        Recommendations rec = Recommendations.createRecommendations(
+            new File(systemTemplate),
+            new File(userTemplate));
+
+        if (rec == null) {
+            System.err.println("No recommendations created");
+            return;
+        }
 
         final Document result = XMLUtils.newDocument();
-        final Builder builder = dc.getBuilder();
-
-        if (builder == null) {
-            System.err.println("No builder created");
-            return;
-        }
 
         final Map<String, Object> parameters = getParameters();
 
@@ -70,15 +73,7 @@
             .openSession();
 
         try {
-            session.doWork(new Work() {
-                @Override
-                public void execute(Connection connection)
-                throws SQLException
-                {
-                    builder.build(connection, result, parameters);
-                }
-
-            });
+            rec.recommend(parameters, userId, result, session);
         }
         finally {
             session.close();
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java	Wed Aug 03 11:23:13 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/Builder.java	Wed Aug 03 16:40:04 2011 +0000
@@ -31,6 +31,10 @@
 {
     private static Logger log = Logger.getLogger(Builder.class);
 
+    public static final String CONNECTION_USER   = "user";
+    public static final String CONNECTION_SYSTEM = "system";
+    public static final String DEFAULT_CONNECTION_NAME = CONNECTION_SYSTEM;
+
     public static final Pattern STRIP_LINE_INDENT =
         Pattern.compile("\\s*\\r?\\n\\s*");
 
@@ -517,7 +521,7 @@
 
     private static final List<NamedConnection> wrap(Connection connection) {
         List<NamedConnection> list = new ArrayList<NamedConnection>(1);
-        list.add(new NamedConnection("", connection));
+        list.add(new NamedConnection(DEFAULT_CONNECTION_NAME, connection));
         return list;
     }
 
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/datacage/templating/NoneUserSpecific.java	Wed Aug 03 11:23:13 2011 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,175 +0,0 @@
-package de.intevation.flys.artifacts.datacage.templating;
-
-import java.util.Map;
-import java.util.HashMap;
-
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.File;
-
-import java.io.FileInputStream;
-
-import java.sql.Connection;
-import java.sql.SQLException;
-
-import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
-import org.w3c.dom.Node;
-
-import org.hibernate.Session;
-
-import org.hibernate.jdbc.Work;
-
-import de.intevation.artifacts.common.utils.Config;
-import de.intevation.artifacts.common.utils.XMLUtils;
-
-import de.intevation.flys.artifacts.FLYSArtifact;
-
-import de.intevation.flys.backend.SessionHolder;
-
-import de.intevation.artifactdatabase.data.StateData;
-
-
-public class NoneUserSpecific
-{
-    private static Logger log = Logger.getLogger(NoneUserSpecific.class);
-
-    public static final String XPATH_META_DATA_TEMPLATE =
-        "/artifact-database/metadata/@template";
-
-    private static NoneUserSpecific INSTANCE;
-
-    protected Builder builder;
-
-    public NoneUserSpecific() {
-    }
-
-    public NoneUserSpecific(Builder builder) {
-        this.builder = builder;
-    }
-
-    public Builder getBuilder() {
-        return builder;
-    }
-
-    protected static void artifactToParameters(
-        FLYSArtifact        artifact, 
-        Map<String, Object> parameters
-    ) {
-        parameters.put("current-state-id", artifact.getCurrentStateId());
-
-        for (StateData sd: artifact.getAllData()) {
-            Object value = sd.getValue();
-            if (value == null) {
-                continue;
-            }
-            String key = sd.getName().replace('.', '-');
-            parameters.put(key, value);
-        }
-    }
-
-    public void  recommend(
-        FLYSArtifact        artifact,
-        String []           outs,
-        Map<String, Object> extraParameters,
-        Node                result
-    ) {
-        Map<String, Object> parameters = new HashMap<String, Object>();
-
-        if (extraParameters != null) {
-            parameters.putAll(extraParameters);
-        }
-
-        if (artifact != null) {
-            artifactToParameters(artifact, parameters);
-        }
-
-        parameters.put("artifact-outs", outs);
-
-        parameters.put("parameters", parameters);
-
-        recommend(parameters, result);
-    }
-
-    public void recommend(
-        final Map<String, Object> parameters,
-        final Node                result
-    ) {
-        if (builder == null) {
-            log.error("builder not configured properly.");
-            return;
-        }
-
-        Session session = SessionHolder.HOLDER.get();
-
-        session.doWork(new Work() {
-            @Override
-            public void execute(Connection connection)
-            throws SQLException
-            {
-                builder.build(connection, result, parameters);
-            }
-        });
-    }
-
-    public static synchronized NoneUserSpecific getInstance() {
-        if (INSTANCE == null) {
-            INSTANCE = createNoneUserSpecific();
-        }
-        return INSTANCE;
-    }
-
-    protected static Builder createBuilder(File file) {
-        log.debug("NoneUserSpecific.createBuilder");
-
-        if (!file.isFile() || !file.canRead()) {
-            log.error("Cannot open template file '" + file + "'");
-            return null;
-        }
-
-        InputStream in = null;
-
-        try {
-            in = new FileInputStream(file);
-
-            Document template = XMLUtils.parseDocument(in);
-
-            if (template == null) {
-                log.error("cannot parse meta data template");
-            }
-            else {
-                return new Builder(template);
-            }
-        }
-        catch (IOException ioe) {
-            log.error(ioe);
-        }
-        finally {
-            if (in != null) {
-                try {
-                    in.close();
-                }
-                catch (IOException ioe) {
-                    log.error(ioe);
-                }
-            }
-        }
-        return null;
-    }
-
-    protected static NoneUserSpecific createNoneUserSpecific() {
-        log.debug("NoneUserSpecific.createNoneUserSpecific");
-
-        String path = Config.getStringXPath(XPATH_META_DATA_TEMPLATE);
-        if (path == null) {
-            log.error("no path to template file given");
-            return null;
-        }
-
-        path = Config.replaceConfigDir(path);
-
-        return new NoneUserSpecific(createBuilder(new File(path)));
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java	Wed Aug 03 11:23:13 2011 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java	Wed Aug 03 16:40:04 2011 +0000
@@ -1,8 +1,5 @@
 package de.intevation.flys.artifacts.services;
 
-import java.sql.Connection;
-import java.sql.SQLException;
-
 import org.w3c.dom.Document;
 
 import org.apache.log4j.Logger;
@@ -23,29 +20,19 @@
 
 import de.intevation.artifacts.common.ArtifactNamespaceContext;
 
-import de.intevation.flys.artifacts.datacage.templating.Builder;
-import de.intevation.flys.artifacts.datacage.templating.NoneUserSpecific;
+import de.intevation.flys.artifacts.datacage.Recommendations;
 
 import de.intevation.flys.artifacts.FLYSArtifact;
 
-import de.intevation.flys.backend.SessionHolder;
-
-import org.hibernate.Session;
-
-import org.hibernate.jdbc.Work;
-
 public class MetaDataService
 extends      DefaultService
 {
     private static Logger log = Logger.getLogger(MetaDataService.class);
 
-    // old service. To be removed
-    public static final String XPATH_RIVER = "/art:river/text()";
-
-    public static final String XPATH_UUID       = "/art:meta/art:uuid/@value";
-    public static final String XPATH_OUTS       = "/art:meta/art:outs/@value";
-    public static final String XPATH_PARAMETERS = "/art:meta/art:parameters/@value";
-    public static final String XPATH_FILTERS    = "/art:meta/art:filters/@value";
+    public static final String XPATH_ARTIFACT_ID = "/art:meta/art:artifact-id/@value";
+    public static final String XPATH_USER_ID     = "/art:meta/art:user-id/@value";
+    public static final String XPATH_OUTS        = "/art:meta/art:outs/@value";
+    public static final String XPATH_PARAMETERS  = "/art:meta/art:parameters/@value";
 
     /** The global context key of the artifact database */
     public static final String ARTIFACT_DATA_BASE_KEY =
@@ -54,56 +41,31 @@
     public MetaDataService() {
     }
 
-    protected static Map<String, Object> extractParameters(Document data) {
-        HashMap<String, Object> parameters = new HashMap<String, Object>();
-
-        String river = XMLUtils.xpathString(
-            data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE);
+    @Override
+    public Document process(
+        Document      data,
+        GlobalContext globalContext,
+        CallMeta      callMeta
+    ) {
+        log.debug("MetaDataService.process");
 
-        if (river == null || (river = river.trim()).length() == 0) {
-            river = "%"; // matches all rivers
-        }
+        String artifactId = XMLUtils.xpathString(
+            data, XPATH_ARTIFACT_ID, ArtifactNamespaceContext.INSTANCE);
 
-        parameters.put("river", river);
+        String userId = XMLUtils.xpathString(
+            data, XPATH_USER_ID, ArtifactNamespaceContext.INSTANCE);
 
-        return parameters;
+        String outs = XMLUtils.xpathString(
+            data, XPATH_OUTS, ArtifactNamespaceContext.INSTANCE);
+
+        String parameters = XMLUtils.xpathString(
+            data, XPATH_PARAMETERS, ArtifactNamespaceContext.INSTANCE);
+
+        return doService(
+            artifactId, userId, outs, parameters, globalContext);
     }
 
-    /** The old service. To be removed. */
-    protected Document oldService(Document data) {
-
-        final Document result = XMLUtils.newDocument();
-
-        final Builder builder = NoneUserSpecific.getInstance().getBuilder();
-
-        if (builder == null) {
-            log.error("MetaDataService is not setup properly.");
-            return result;
-        }
-
-        final Map<String, Object> parameters = extractParameters(data);
-
-        Session session = SessionHolder.acquire();
-        try {
-            session.doWork(new Work() {
-                @Override
-                public void execute(Connection connection)
-                throws SQLException
-                {
-                    log.debug("MetaDataService.execute");
-                    builder.build(connection, result, parameters);
-                }
-            });
-        }
-        finally {
-            session.close();
-            SessionHolder.release();
-        }
-
-        return result;
-    }
-
-    protected static Map<String, Object> parameters(
+    protected static Map<String, Object> splitParameters(
         String              parameters,
         Map<String, Object> data
     ) {
@@ -121,23 +83,11 @@
         return data;
     }
 
-    protected static Map<String, Object> filters(
-        String              filters,
-        Map<String, Object> data
-    ) {
-        if (filters != null) {
-            for (String filter: filters.split("\\s*,\\*s")) {
-                data.put(filter, "true");
-            }
-        }
-        return data;
-    }
-
-    protected Document newService(
-        String        uuid,
+    protected Document doService(
+        String        artifactId,
+        String        userId,
         String        outsString,
         String        parameters,
-        String        filters,
         GlobalContext globalContext
     ) {
         Document result = XMLUtils.newDocument();
@@ -145,14 +95,20 @@
         FLYSArtifact flysArtifact;
 
         if (log.isDebugEnabled()) {
-            log.debug("uuid: " + uuid);
-            log.debug("outs: " + outsString);
-            log.debug("filters: " + filters);
+            log.debug("artifact  : " + artifactId);
+            log.debug("user      : " + userId);
+            log.debug("outs      : " + outsString);
+            log.debug("parameters: " + parameters);
         }
 
-        if (uuid != null && uuid.length() != 0) {
-            if (!StringUtils.checkUUID(uuid)) {
-                log.warn("'" + uuid + "' is not a UUID");
+        if (userId != null && !StringUtils.checkUUID(userId)) {
+            log.warn("'" + userId + "' is not a UUID");
+            return result;
+        }
+
+        if (artifactId != null) {
+            if (!StringUtils.checkUUID(artifactId)) {
+                log.warn("'" + artifactId + "' is not a UUID");
                 return result;
             }
 
@@ -169,7 +125,7 @@
             Artifact artifact;
 
             try {
-                artifact = db.getRawArtifact(uuid);
+                artifact = db.getRawArtifact(artifactId);
             }
             catch (ArtifactDatabaseException adbe) {
                 log.warn("fetching artifact failed", adbe);
@@ -187,43 +143,17 @@
             flysArtifact = null;
         }
 
-        Map<String, Object> data =
-            filters(filters,
-                parameters(parameters, 
-                    new HashMap<String, Object>()));
+
+        Map<String, Object> data = splitParameters(
+            parameters, new HashMap<String, Object>());
 
         String [] outs = outsString.split("\\s*,\\s*");
         
-        NoneUserSpecific dc = NoneUserSpecific.getInstance();
-
-        dc.recommend(flysArtifact, outs, data, result);
+        Recommendations rec = Recommendations.getInstance();
+        rec.recommend(
+            flysArtifact, userId, outs, data, result);
 
         return result;
     }
-
-    @Override
-    public Document process(
-        Document      data,
-        GlobalContext globalContext,
-        CallMeta      callMeta
-    ) {
-        log.debug("MetaDataService.process");
-
-        String uuid = XMLUtils.xpathString(
-            data, XPATH_UUID, ArtifactNamespaceContext.INSTANCE);
-
-        String outs = XMLUtils.xpathString(
-            data, XPATH_OUTS, ArtifactNamespaceContext.INSTANCE);
-
-        String parameters = XMLUtils.xpathString(
-            data, XPATH_PARAMETERS, ArtifactNamespaceContext.INSTANCE);
-
-        String filters = XMLUtils.xpathString(
-            data, XPATH_FILTERS, ArtifactNamespaceContext.INSTANCE);
-
-        return outs != null
-            ? newService(uuid, outs, parameters, filters, globalContext)
-            : oldService(data);
-    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org