changeset 430:7ab81ff32111 2.3

merged flys-artifacts/2.3
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:10 +0200
parents a6f56ed9238b (current diff) b8377caf4515 (diff)
children 29a15392e229
files
diffstat 86 files changed, 14399 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/ChangeLog	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,1734 @@
+2011-05-17  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 0.1
+
+2011-05-16  Hans Plum <hans@intevation.de>
+
+	* NEWS:
+	New. Giving some user specific perspective to new functionality and
+	changes. This file references releases dates only; details can be find
+	in the client module at flys-client.
+
+2011-05-10	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	First step to calculate "W fuer ungleichwertige Abfluesse" correctly.
+	flys/issue55
+
+	* src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java:
+	  New. Remaps "gleichwertige" Q values to the corresponding
+	  "ungleichwertige" Q values depending on km.
+	  
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Remap the Q values "ungleichwertig" depending on the 
+	  "gleichwertige" ones.
+
+2011-05-10	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	First step to fix flys/issue69
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  New code path to implement the calculation of "Wasserstand/Wasspiegellage"
+	  correctly. TODO 1: Use new path in UI. TODO 2: Remove unused old code.
+
+2011-05-13  Ingo Weinzierl <ingo@intevation.de>
+
+	* Changes: Prepared Changes for the upcoming release 2.3 - see Changes
+	  file to get to know about the changes of the version numbers.
+
+2011-05-13  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-37
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java: This
+	  state would be happy if there is a UI provider called "river_panel".
+
+2011-05-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added new
+	  methods that return the requested chart size as integer array [width,
+	  height]. The requested size is read from the incomding request document.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: The size
+	  of a chart is no longer static. The requested size is fetched using
+	  ChartGenerator.getSize().
+
+2011-05-11  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-52
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  The X-Axis of such a chart is inverted, if the head of the river is not
+	  at kilometer 0. This type of charts always have the head of the river at
+	  the left side.
+
+2011-05-10	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Make incoming XML symmetric to DistanceInfoService.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-47
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Adjusted temporarily the color of the W, Q and corrected W curves to
+	  distinguish each other.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Adjusted the plot of xy charts - the gridlines are displayed now.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-53
+
+	* src/main/java/de/intevation/flys/artifacts/resources/Resources.java:
+	  Added a method that returns the preferred locale based on the available
+	  locales of the server and the desired locales of the request (CallMeta).
+
+	* src/main/java/de/intevation/flys/exports/AbstractExporter.java: Added a
+	  method that creates a number formatter with minimum and maximum digits.
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java,
+	  src/main/java/de/intevation/flys/exports/WaterlevelExporter.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveExporter.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java:
+	  Formatted the number values of the CSV exports.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/RangeWithValues.java:
+	  New. A data structure that enables us to save a data triple: a range
+	  that consist of lower and upper double value and a set of values that
+	  belong to this range.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java:
+	  Implemented the validation of W/Q values.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/AbstractExporter.java: New
+	  method to retrieve i18n messages based on keys.
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java,
+	  src/main/java/de/intevation/flys/exports/WaterlevelExporter.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveExporter.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java:
+	  Added headers for CSV exports.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Added new i18n strings for
+	  CSV headers.
+
+2011-05-10	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Fetches river name from incoming XML document. If no river is given all 
+	  infos about all rivers are listed.
+
+	* src/main/resources/metadata/template.xml: Templates honors the 'river'
+	  parameter.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java:
+	  Extended to pass parameters to the templating. Added support for
+	  type conversion.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java:
+	  Take parameters as an initial stack frame.
+	  
+	* src/main/java/de/intevation/flys/artifacts/services/meta/TypeConverter.java:
+	  New. Converts types off stacked variables.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Added a
+	  method to add subtitles to charts. The implementation in this class does
+	  not add any subtitle. Concrete subclasses may override this method to
+	  add some.
+
+	* src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Add subtitles to charts.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Added i18n templates for
+	  compound messages (chart subtitles).
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/resources/Resources.java:
+	  Added new methods to retrieve translated compound messages.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/OutGenerator.java: Added a
+	  method to set the master artifact that should be used for some special
+	  operations.
+
+	* src/main/java/de/intevation/flys/exports/ChartGenerator.java,
+	  src/main/java/de/intevation/flys/exports/AbstractExporter.java:
+	  Implement the setMasterArtifact() method of the interface.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Call OutGenerator.setMasterArtifact().
+
+	  NOTE: The determination of the master artifact needs to be implemented!
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Removed
+	  hard coded dev code that defined a WQ mode.
+
+2011-05-09	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java:
+	  Stripped ugly extra whitespace from output introduced by
+	  templating.
+
+2011-05-09	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/resources/metadata/template.xml: Added forgotten
+	  columns of fixation WSTs.
+
+2011-05-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added a
+	  method to retrieve i18n strings.
+
+	* src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Enabled i18n support for chart title and axes labels.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Added i18n strings for the
+	  chart types above.
+
+2011-05-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java:
+	  New. This OutGenerator exports the data of a discharge longitudinal
+	  section computation.
+
+	* doc/conf/conf.xml: Added the DischargeLongitudinalSectionExporter.
+
+	* doc/conf/artifacts/winfo.xml: Added the exporter with CSV facet to the
+	  discharge_longitudinal_section state.
+
+2011-05-07	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java:
+	  Added code to make back jump correction work with both
+	  potential flow directions.
+
+2011-05-06	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/resources/metadata/template.xml:
+	  Added data cage configuration for 'Längsschnitt'.
+
+2011-05-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Added the missing Q facet for discharge
+	  longitudinal sections.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQCKms.java: New. A
+	  derived dataset to store W/Q values with corrected Ws for a kilometer
+	  range.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Some new
+	  methods and a new constructor to initialize this data object with a
+	  predefined set of values.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New
+	  methods to retrieve the W/Q values for the 'discharge longitudinal
+	  section' computation.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: New
+	  methods to retrieve and compute data used for the 'discharge
+	  longitudinal section' computation.
+
+2011-05-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java:
+	  Bugfix: just write the ranges of gauges into the DESCRIBE if the
+	  'wq_values' data item is required.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Added i18n messages used in
+	  the DESCRIBE of the WQAdapted state.
+
+2011-05-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Changed the title of the y-axis (now 'W [NN+m]').
+
+2011-05-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Improved the transition model to reach the
+	  output state for creating 'discharge longitudinal section' charts.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a
+	  method that returns all gauges of the selected river based on a the
+	  given kilometer range.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java: New.
+	  This state creates a set of elements for the DESCRIBE that consist of a
+	  tuple of kilometer values. The number of elements depend on the number
+	  of gauges intersected by the given kilometer range.
+
+	* src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java:
+	  New. This state is the output state that is reached after the 'discharge
+	  longitudinal section' computation has been chosen.
+
+2011-05-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Default
+	  step width between two kilometers added - if no step width is given,
+	  this default width is used.
+
+2011-05-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Fixed a potential NullPointerException if there is just a single
+	  kilometer given to create a longitudinal section.
+
+2011-05-04	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* pom.xml: Added http://repository.jboss.org/maven2 repo
+	  to fix flys/issue30
+
+2011-05-04	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  To make diagram generation possible ws are now generated from qs
+	  because they are many ws having different qs.
+
+2011-05-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java:
+	  Fetch the WQ data from WINFO artifact and write those values into the
+	  CSV export.
+
+2011-05-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added
+	  methods to compute and retrieve the data for discharge curves (computed).
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Fetch the computed discharge curve data from WINFOArtifact and add the
+	  values into the JFreeChart dataset.
+
+2011-05-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Added new transitions and states to enable
+	  the WINFO artifact for computing discharge curves.
+
+	* doc/conf/conf.xml: Added OutGenerators that generate computed discharge
+	  curves and exports for its data.
+
+	* src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java:
+	  New. This state is reached if the user chose the computed discharge
+	  curve.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  New. This is only a stub implementation at the moment. This
+	  OutGenerator should create computed discharge curves later. It extends
+	  the DischargeCurveGenerator which should do the same stuff for discharge
+	  curves for gauges.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java:
+	  New. This is only a stub implementation at the moment. This OutGenerator
+	  should create the exports of the discharge curve computation.
+
+2011-05-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Added a new output mode for the duration
+	  curve state (CSV export).
+
+	* doc/conf/conf.xml: Added a new OutGenerator to export duration curve
+	  computations.
+
+	* src/main/java/de/intevation/flys/exports/AbstractExporter.java: New.
+	  This abstract OutGenerator represents the base class for exporting
+	  computed data. Currently, the CSV export is supported.
+
+	* src/main/java/de/intevation/flys/exports/WaterlevelExporter.java: Moved
+	  the most code to export to CSV into the AbstractExporter.
+
+	* src/main/java/de/intevation/flys/exports/DurationCurveExporter.java:
+	  New. This exporter exports the computed data of a duration computation.
+
+2011-05-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Modified a wrong debug statement which would confuse the user.
+
+2011-05-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Added a new output mode for the waterlevel
+	  state (CSV export).
+
+	* doc/conf/conf.xml: Added a new OutGenerator to export waterlevels.
+
+	* src/main/java/de/intevation/flys/exports/WaterlevelExporter.java: New.
+	  This OutGenerator exports the data of a waterlevel computation. Note:
+	  It is necessary to specify the desired facet (e.g.
+	  'waterlevel_export.csv').
+
+	* pom.xml: Added a dependency to OpenCSV.
+
+2011-05-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Introduced an "export" output target. If the target is "export", a
+	  facet (read from the incoming xml document) is a necessary parameter
+	  that determines which facets are written to the output.
+
+2011-05-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/resources/messages_en.properties: Bugfix: replaced german
+	  string (copy & paste mistake).
+
+2011-05-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Added new i18n strings for
+	  for the location selection.
+
+2011-05-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java:
+	  New. An OutGenerator for creating duration curves.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQDay.java: New. A
+	  model class to store necessary data for creating W and Q facets of a
+	  duration curve. This model stores W, Q and Days.
+
+	* src/main/java/de/intevation/flys/artifacts/model/MainValuesFactory.java:
+	  Added a function to retrieve tuples of (day, q) based on a given gauge -
+	  these tuples are necessary for creating duration curves.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added
+	  methods to compute and retrieve the data necessary for creating duration
+	  curves.
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java:
+	  Bugfix: improved the access to the location array (avoid
+	  NullPointerException).
+
+	* doc/conf/conf.xml: Registered the new OutGenerator for duration curves.
+
+2011-05-03	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* contrib/visualize-transitions.xsl: State quoting was done wrong.
+
+2011-05-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Enhanced the transition model to reach the
+	  final state for creating duration curves.
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java:
+	  New. This state should be reached to just insert an array of locations.
+
+	* src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java:
+	  New. This state is reached if the duration curve calculation is
+	  selected.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Adjusted
+	  getDistance() so that it takes care on inserted locations - not just
+	  inserted ranges.
+
+2011-05-02	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java:
+	  Flush/persist caches at program exists.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Commented out too eloquent debug output.
+
+2011-05-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  Uses the methods of FLYSArtifact to retrieve the necessary information
+	  rivername and selected distance.
+
+2011-05-02	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Added interpolateW method to take reference to result ws array
+	  as an argument to avoid expensive array allocations in km iterating
+	  loops.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java:
+	  Added a constructor to create backing trove datastructure
+	  with the right capacity.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Move allocation of result ws out of km loop.
+
+2011-05-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Added two further data items in the WQ
+	  selection state. Those items are necessary to store the information
+	  about the selected mode (range/single selection) and  the values of the
+	  single selection.
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: If
+	  there is no value for a data item, this item is not written into the
+	  static DESCRIBE part.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RangeState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java:
+	  Renamed the method to validate upper and lower values.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: This
+	  state can now handle values inserted in the single selection. Therefore,
+	  new validate methods has been added.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: The
+	  methods getWs() and getWs() take care on the values inserted in the
+	  single insert mode of the client which enables the user to insert single
+	  W and Q.
+
+2011-05-02	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Repaired getQForW() by calculating indices on right dimension.
+
+2011-05-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New
+	  methods for retrieving selected W values (getWs()).
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The
+	  method for retrieving waterlevel data takes care on selected Ws, now.
+	  The selected Ws are transformed using the DischargeTables.getQForW()
+	  into Q values.
+
+2011-05-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Removed an unused parameter 'result' of getQForW().
+
+2011-05-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Removed
+	  the getDataset() method and replaced it with a addDataset() method.
+	  On this way, concrete subclasses of this OutGenerator can have multiple
+	  datasets (e.g. different datasets for W and Q). This abstract method is
+	  called after the chart generation is finished.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  Both classes implement the addDataset() method. The
+	  LongitudinalSectionGenerator has already multiple datasets for W and Q.
+	  Both are added to the chart - both have an own range axis.
+
+2011-05-01	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Use the cache for the wst value table if configured.
+
+	* doc/conf/cache.xml: Choose a more precise name for the 
+	  wst value table cache.
+
+2011-04-29	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* pom.xml: Added dependency to Ehcache. Apache 2.0 license.
+
+	* doc/conf/conf.xml: Added configuration of ehcache.
+
+	* doc/conf/cache.xml: New. Cache configurations.
+
+	* src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java:
+	  New. Factory to access caches.
+
+2011-04-29	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Forgot to add.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java:
+	  New. Given a database connection and a XML template it generates
+	  an output with meta data about the database.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java:
+	  New. Holds prepared statements optimized to be run in the stack of
+	  contextes.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java:
+	  New. Model to hold a hierarchical scope of variables.
+
+	* src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java:
+	  New. Stores data set fetched from a sql select to be iterated in
+	  a context.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java,
+	  src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Removed superfluous imports.
+
+2011-04-29	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* doc/conf/conf.xml: Added meta data service.
+
+	* src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java:
+	  Stub for the meta data service.
+
+	* src/main/resources/metadata/template.xml: Initial template for
+	  the meta data service.
+
+2011-04-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Bugfix: Now, curves for Q values are drawn into a longitudinal section
+	  chart as well. Therefore, it was necessary to change the datastructure
+	  of the inner class ThemeList that stores all themes included in a chart
+	  in an ordered list (stored in a java.util.Vector now).
+
+2011-04-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: New. An
+	  abstract base class for ChartGenerators that create XY charts.
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  Sourced the generate() method out to the XYChartGenerator.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Implemented the methods to add W and Q facets to the chart.
+
+2011-04-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Improved
+	  the calculation of the step with for ranges.
+
+2011-04-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Added a
+	  method that returns the number of elements stored in the data pool.
+
+2011-04-29	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java:
+	  Added a get() method which takes destination array as an
+	  argument.
+
+2011-04-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Changed the
+	  data structure to store w, q and kms values from List<Double> to
+	  TDoubleArrayList which stores native double values instead of big
+	  Double values.
+
+	* pom.xml: Added the GNU Trove dependency.
+
+2011-04-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/OutGenerator.java: Added the
+	  name a the requested facet to doOut(). Concrete generators should just
+	  create output for this facet now.
+
+	* src/main/java/de/intevation/flys/exports/ChartGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  Adapted the method signature of doOut().
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Adapted the method call of OutGenerator.doOut().
+
+2011-04-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/themes.xml: Added mappings for the facets
+	  longitudinal.section.w and longitudinal.section.q.
+
+2011-04-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added some
+	  methods to retrieve necessary information for computing the data of a
+	  waterlevel.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added
+	  methods to compute and retrieve the data of a waterlevel computation.
+
+2011-04-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java: New. This
+	  model class represents a pool of data triples that consist of W, Q and
+	  Kms information. This class might be used to compute data for creating
+	  longitudinal section curves (which are based on those W, Q and Kms
+	  values).
+
+2011-04-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Bugfix: Removed endless loop and a bug while iterating over Hibernate
+	  results.
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  New. An OutGenerator that creates longitudinal section curves.
+
+	  NOTE: This is just the stub - the out creation needs to be implemented!
+
+	* doc/conf/conf.xml: Added the LongitudinalSectionGenerator.
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/conf.xml: Bugfix: Added missing <output-generators> section.
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WaterlevelState.java: New.
+	  This state should be reached if the 'calc.surface.curve' calculation
+	  method has been chosen.
+
+	* doc/conf/artifacts/winfo.xml: Modified a transition and added the
+	  WaterlevelState. This state is reached if the 'calc.surface.curve'
+	  calculation method has been chosen. It currently has 1 output - a
+	  longitudinal section that is not implemented yet!
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/transitions/ValueCompareTransition.java:
+	  New. This transition is valid if the a data object of the current
+	  artifact equals/notequals a configured value in the transition model.
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Bugfixes:
+	  - Adapted the parameters of the isStateReachable() call - added the
+	    artifact and the current state.
+	  - Append the outputs of a current state if the state is filled with
+	    valid data.
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java:
+	  Added the missing init() method that has been introduced in the
+	  interface some commits ealier.
+
+	* src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java:
+	  Call init() after a Transition has been created.
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java:
+	  Bugfix: Introduced a <art:outputs> node in the attribute document of a
+	  Collection that contains further <art:output> nodes - instead of having
+	  multiple <art:output> nodes at toplevel of the document.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Adapted the XPath to retrieve the outputs in the attribute document of a
+	  Collection.
+
+2011-04-28	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* contrib/visualize-transitions.xsl: Added to create a 
+	  Graphviz digraph out of the config.xml. Usage:
+
+	  $ xsltproc --stringparam base-dir ../doc/conf/ \
+	    contrib/visualize-transitions.xsl \
+	    doc/conf/conf.xml > transitions.dot
+
+	  $ dot -Tsvg -o transitions.svg transitions.dot
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WQSelect.java:
+	  If no data has been inserted so far, an IllegalArgumentException is
+	  thrown.
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java::
+	  A lot of new methods to retrieve the theme of a facet - used while
+	  creating the output of a facet/artifact. If a facet has no theme yet, it
+	  is initialized.
+
+2011-04-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/AttributeParser.java,
+	  src/main/java/de/intevation/flys/collections/OutputParser.java: Removed
+	  useless imports.
+
+2011-04-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ChartGenerator.java: New. An
+	  abstract OutGenerator that might be used to create chart output. Some
+	  basic things that are equal in all charts should be done here!
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  This OutGenerator no longer implements the OutGenerator directly, but it
+	  extends the ChartGenerator now.
+
+2011-04-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Restructured the out() operation of a collection: Now, we collect a list
+	  of artifacts/facets and use this for the OutGenerator. Its doOut()
+	  method gets the attribute of an artifact - the position and the active
+	  state is managed by the Collection itself.
+
+2011-04-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java: New.
+	  a specialized facet that stores information about its position and its
+	  state (active/inactive) in an output of a collection.
+
+	* src/main/java/de/intevation/flys/collections/AttributeParser.java: New.
+	  This parser takes the attributes (XML) of a collection and extracts the
+	  contained outputs with its facets. The result is a Map<String, Output>.
+
+	* src/main/java/de/intevation/flys/collections/OutputParser.java: New.
+	  This parser is used to query the artifact's DESCRIBE and to extract the
+	  supported outputs. The result is a Map<String, Output>.
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java: New.
+	  This writer merges the outputs contained in an attribute of a collection
+	  with the outputs of a collection's artifacts.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  The attributes of a collection are written into its DESCRIBE document
+	  now. The OutputParser and AttributeParser are used to read the supported
+	  attributes by the collection and its artifacts - the AttributeWriter is
+	  used to merge both attributes and create a final attribute document.
+
+2011-04-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java,
+	  src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  The facet-2-theme mappings are initialized at startup and stored in the
+	  FLYSContext.
+
+	* src/main/java/de/intevation/flys/themes/ThemeFactory.java: Added a
+	  function that retrieves a theme from FLYSContext based on its name.
+
+2011-04-22	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Corrected a silly c&p mistake.
+
+2011-04-22	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java:
+
+	  Implementation of the "Ruecksprungkorrektur" to be done in
+	  "W fuer angepassten Abflusslaengschnitt".
+
+	  All tests show the expected results. In some corner cases the
+	  algorithm described in the "Anwenderhandbuch" chapter 3.3.4.3 "Korrektur"
+	  has some definition shortcomings:
+
+	  a - What should happend when you cannot find point 2 because
+	      you cannot step back one quarter from point 3 because there
+	      is no data there any more (river too short in this direction)?
+	      The implemented algorithm raises point 3' only to an
+	      according factor. E.g. If you can step back the whole quarter
+	      distance the elevation is the full quarter. If you can
+	      step back only the half of the quarter the elevation is
+	      only an eighth.
+
+	  b - If the water heights between point 2 and 3 are constant then
+	      the algorithm will produce a spline interpolation that
+	      lowers those values. Is this intended?
+
+	  For real data the back jumps are expected to be more in the middle
+	  of the distance ranges so the corner cases are maybe not so
+	  important.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java:
+	  Removed superfluous import.
+
+2011-04-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/themes/ThemeFactory.java: Removed debug
+	  code that has been commited by accident.
+
+2011-04-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/themes.xml: New. A first small theme configuration.
+
+	* doc/conf/conf.xml: Added a link to the theme configuration.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java:
+	  Defined a key that is used to store a themes map in the FLYSContext.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  The theme configuration is read at startup and the themes are stores in
+	  the FLYSContext.
+
+2011-04-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/themes/Theme.java,
+	  src/main/java/de/intevation/flys/themes/DefaultTheme.java:
+	  New. The interface and its default implementation that represents themes
+	  used to style charts and maps.
+
+	* src/main/java/de/intevation/flys/themes/ThemeField.java,
+	  src/main/java/de/intevation/flys/themes/DefaultThemeField.java:
+	  New. The interface and its default implementation that represents fields
+	  in themes. A theme might be "Lines" and one of its field might be
+	  "Color" or "Size".
+
+	* src/main/java/de/intevation/flys/themes/ThemeFactory.java: A factory
+	  that creates new themes based on a theme configuration.
+
+2011-04-20	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Implementation of "Abflusskurve/Abflusstafel" calculation.
+
+	  Added method interpolateWQ() which takes an km and results in a
+	  tuple of two double arrays containing the w/q values interpolated
+	  between the surrounding w/q values of the table.
+	  w values are interpolated linear, q values with a cubic spline.
+
+	  Drawing w over q gives you the discharge table at the given km.
+
+	  !!! This code needs testing !!!
+
+2011-04-20	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* pom.xml: Added dependency to Apache Commons Math 2.2 (Apache License 2.0)
+
+2011-04-20	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Fix problem when sorting by q (copied w instead of q).
+
+2011-04-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Fixed broken HQL statement.
+
+2011-04-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  add a method interpolateW() which takes an array of
+	  q values and returns an equal sized array of w values.
+	  This is essentially the "Wasserstand/Wasserspiegellagen" calculation
+	  of desktop FLYS.
+
+	  If you want to do a calculation with given w values you have
+	  to convert the w values with DischargeTables.getQForW() first.
+
+	  !!! This code needs heavy testing !!!
+
+2011-04-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  We need a getQForW() method and not getWForQ() because when
+	  doing a "Wasserstand/Wasserspiegellagen" calculation with given
+	  w values these values need to be translated to q values with
+	  the master discharge table.
+
+2011-04-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Sorting of q values was done wrong.
+
+2011-04-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java:
+	  Ordered the list of annotations returned by this factory based on its
+	  range.
+
+2011-04-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java:
+	  New. This service returns an XML document that includes the main values
+	  of a gauge based on a river name, a start and an end point.
+
+	* doc/conf/conf.xml: Registered the MainValuesService.
+
+2011-04-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/MainValuesFactory.java:
+	  A factory that provides methods to return MainValues.
+
+2011-04-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Added static method getWForQ() to interpolate a w value for
+	  a given q value based on a given discharge table.
+	
+2011-04-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Added convenience constructors/methods to ease the access to the master
+	  discharge table of a gauge.
+
+2011-04-18	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Moved the query complexity into view 'wst_value_table' and
+	  used this instead.
+
+2011-04-18	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Fetches w/q value tables from the backend. TODO: Move this
+	  to the backend and use a view.
+
+2011-04-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Changed
+	  the error key that is thrown if no input data was found so that the key
+	  is usable for GWT's i18n mechanism.
+
+2011-04-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java:
+	  The inserted river is validated now (overrides validate() of
+	  DefaultState).
+
+2011-04-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java:
+	  The inserted calculation method is validated now (overrides validate()
+	  of DefaultState).
+
+2011-04-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  The input data of feed() are validated using the DefaultStates
+	  validate() method.
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  New method validate() that needs to be overidden by concrete subclasses.
+	  It should return true, if the data of the State is fine, otherwise it
+	  should raise an exception.
+
+	  NOTE: The exceptions are not translated in the server but in the client!
+
+	* src/main/java/de/intevation/flys/artifacts/states/RangeState.java: New.
+	  This abstract class exists to provide some methods for handling ranges.
+	  Currently, there is a method that validates a given range based on
+	  min/max values.
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WQSelect.java:
+	  Implemented input data validation for ranges.
+
+	  NOTE: The input validation of concrete values has not been implemented
+	  yet!
+
+2011-04-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Removed debug code that has been commited by accident :-/
+
+2011-04-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: The
+	  getGauge() method returns the first gauge based on the given start and
+	  end point of the river.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQSelect.java:
+	  Fixed potential bugs: if no gauge could be determined, the default
+	  values for W and Q are the minimum and maximum double values.
+
+2011-04-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQSelect.java: Fills
+	  the DESCRIBE with default values for W and Q.
+
+2011-04-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstFactory.java:
+	  New. Returns Wst object - based on a river.
+
+2011-04-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Now
+	  provides some methods that return some basic objects inserted while
+	  parameterization: River, Gauge and so on.
+
+2011-04-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java,
+	  src/main/java/de/intevation/flys/artifacts/services/RiverService.java:
+	  Bugfix: Repaired broken imports of the SessionHolder.
+
+2011-04-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/SessionHolder.java:
+	  Moved this class to flys-backend.
+
+	* src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java,
+	  src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java,
+	  src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java,
+	  src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java,
+	  src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java:
+	  Adapted imports of the SessionHolder.
+
+2011-04-14	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* trunk/src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java,
+	  trunk/src/main/java/de/intevation/flys/artifacts/services/RiverService.java:
+	  Acquire/release sessions in services to avoid db connection leaks.
+
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Changed some method signatures - added a reference to the owner
+	  Artifact.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WQSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java:
+	  Modified method signatures based on the changes in DefaultState.
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java:
+	  Added default values to the dynamic part of DESCRIBE.
+
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java:
+	  Added a function that returns a River object based on a given river
+	  name.
+
+2011-04-14	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java,
+	  src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java,
+	  src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java,
+	  src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java:
+	  Static methods are using the SessionHolder, too.
+
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java:
+	  Added the setup() method that has been added to the interface
+	  description in the last commits.
+
+	* doc/conf/conf.xml: Registered the SessionCallContextListener as
+	  CallContext.Listener.
+
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/SessionHolder.java:
+	  Bugfix: Call correct method to retrieve an instance of
+	  SessionFactoryProvider.
+
+2011-04-14	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/SessionHolder.java:
+	  New. Contains thread local session holder for hibernate sessions.
+
+	* src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java:
+	  Uses session from SessionHolder.
+
+	* src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java:
+	  Interacts with SessionHolder now.
+	
+	* src/main/java/de/intevation/flys/exports/ChartExportHelper.java:
+	  Removed superfluous import.
+	
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java:
+	  Implementation of a CallContext.Listener to open/close Hibernate
+	  Sessions for each request.
+
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Some modifications related to the last commit - modification of the
+	  describe() signature of a State.
+
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/GaugeSelect.java:
+	  Removed. We do not need a state to select a gauge - the selection takes
+	  place by choosing a start and an end point.
+
+2011-04-13  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java:
+	  Bugfix: added missing label node to root node and a namespace to the
+	  data node.
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  The name of the artifact 'winfo' is written into the DESCRIBE document
+	  now. We need this to have a proper way to distinguish between different
+	  artifacts in the UI.
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Bugfix:
+	  States are filled with data before they describe themself.
+
+2011-04-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java:
+	  New. A factory that returns the annotations of a specific river.
+
+	* src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java:
+	  New. This service provides a document that contains information about
+	  distances of a river.
+
+	* doc/conf/conf.xml: Registered the DistanceInfoService.
+
+2011-04-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  This collection overrides the out() operation now. The incoming request
+	  document is read and the related OutGenerator is used to create the
+	  output.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Removed
+	  the code to create discharge curves. It has moved to the
+	  DischargeCurveGenerator which now does this work.
+
+2011-04-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/OutGenerator.java: The
+	  generate() method throws an IOException now.
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  New. This OutGenerator creates discharge curves.
+
+2011-04-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Changed
+	  the visibility of the getData() method. It's now public, because the
+	  OutGenerator needs an artifact's data.
+
+2011-04-03	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/**/*.java: Removed trailing whitespace.
+
+2011-04-03	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Make project compilable again by
+	  commenting out a not existing XMLDebug reference.
+
+2011-03-31  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  Added code to parse the configured OutGenerators and to save them (in a
+	  map) in the FLYSContext.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java:
+	  Added a key that is used to save the OutGenerators Map in the context.
+
+2011-03-31  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/OutGenerator.java:
+	  New. This interface is used to generator different types of output.
+	  ArtifactCollections will make use of this interface to create a
+	  collected output of all its artifacts.
+
+2011-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Removed the Backend from FLYSArtifactCollection - used ArtifactDatabase
+	  operations instead.
+
+	* TODO: Removed 'remove Backend reference' TODO.
+
+2011-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Enhanced the configuration of the
+	  discharge curve output mode. This output now provides three facets - W,
+	  Q and the curve itself.
+
+2011-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 0.1
+
+2011-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Renamed an output mode in the WINFO
+	  artifact configuration.
+
+2011-03-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_de.properties: Changed a german string.
+
+2011-03-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java:
+	  Modified the available calculation modes and its order in the DESCRIBE
+	  document.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Adapted the names of
+	  calculation modes.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Added the hash code of an artifact to the artifact part of the
+	  collection's DESCRIBE document.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Changed
+	  the background color of discharge curves to white.
+
+2011-03-28	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Cache the scale, too. Otherwise two calls to getValues() with
+	  different arguments will result in the same output.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Introduced a 'scale' parameter in the getValues() method.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Filled
+	  the out() operation with code that draws a discharge table of one or
+	  more gauges specified by the given range in entered in a previous state.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ChartExportHelper.java:
+	  New. A helper class to exports charts.
+
+	* pom.xml: Added dependencies to iText, Batik and JFreeChart.
+
+2011-03-28	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/importer/PegelGltParser.java:
+	  Fixed swap of operands.
+
+2011-03-28	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java:
+	  New. Load gauges for a river and filter them for given
+	  ranges.
+
+2011-03-25	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Store data in a double [][] instead of interleaved double []
+	  to be compatible with org.jfree.data.xy.DefaultXYDataset.
+
+2011-03-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* TODO: Removed 'i18n' and 'step-back' TODOs and added an issue to remove
+	  the Backend reference from FLYSArtifactCollection.
+
+2011-03-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Added missing artifact namespace of an attribute in the DESCRIBE
+	  document.
+
+2011-03-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  New. This ArtifactCollection overrides the DefaultArtifactCollection to
+	  implement FLYS specific describe() and out() operations.
+
+	* doc/conf/conf.xml: Use the FLYSArtifactCollection instead of the
+	  DefaultArtifactCollection for this application.
+
+2011-03-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  New. Fetches values of discharge tables in form of packed
+	  w/q double arrays for given gauges.
+
+	* src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java:
+	  Removed needless import.
+
+2011-03-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/resources/messages_de_DE.properties: Added a german resource
+	  bundle to avoid exceptions in the flys artifacts. Sometimes, the
+	  Resources class is not able to find a 'de_DE' bundle and throws an
+	  exception. This is really strange, because it should use the 'de' bundle
+	  in that case, but it doesn't.
+
+2011-03-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Added
+	  some code to append the output modes of previous states to the DESCRIBE
+	  document.
+
+	  TODO: Determine if the current state is already filled with data and
+	  append its output modes as well!
+
+2011-03-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Enhanced the location_distance state with
+	  an output mode 'discharge_table'.
+
+2011-03-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Write human readable strings as label attribute into the DESCRIBE
+	  output. Those labels are used to be displayed in the GUI.
+
+2011-03-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java:
+	  Added some further calculation types.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Added i18n strings for the
+	  calculation types
+
+2011-03-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Implemented the step-back part of the advance() operation.
+
+2011-03-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/resources/messages_de.properties: Fixed a german umlaut.
+
+2011-03-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Added new states for entering a
+	  location/distance and w/q.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: New string for the
+	  location/distance and w/q input states.
+
+2011-03-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQSelect.java:
+	  New. A state for the W/Q input of the WINFO parameterization.
+
+2011-03-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Append an attribute 'uiprovider' to the dynamic UI node.
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java:
+	  New. A state for the location/distance selection of the WINFO
+	  parameterization.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Added a new method getUIProvider() that might be overriden by concreted
+	  subclasses that should be rendered with a specific UIProvider.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  The static UI part is created by the previous states now. This makes it
+	  possible to group the data objects (which is necessary to group the
+	  objects in the ui).
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Added a describeStatic() method that creates a node that contains the
+	  data of that state.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/RiverService.java:
+	  Removed TODO: the document contains the rivers provided by the backend
+	  now.
+
+2011-03-17	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* pom.xml: Added dependency to flys backend.
+
+	* src/main/java/de/intevation/flys/artifacts/model/River.java:
+	  Removed. We are using the backend model now.
+
+	* src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java:
+	  Fetches the rivers from the backend.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java:
+	  Import fixes.
+
+2011-03-15	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* doc/conf/conf.xml: Added section for database backend configuration.
+
+2011-03-15	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  Fixed build error coming from different import of XMLUtils.
+
+2011-03-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Appended the missing label node that contains the human readable name of
+	  the data item.
+
+2011-03-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  I18N of strings for the DESCRIBE document.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: I18N strings for the
+	  calculcation mode state.
+
+2011-03-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Modified the winfo states.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Adapted
+	  the artifact regarding the changes of the last commit. The states
+	  describe() method creates the dynamic UI node - the artifact needs to
+	  apply this node.
+
+	* src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java:
+	  New. The state for choosing the calculation mode.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Added i18n strings for the
+	  calculation mode state.
+
+2011-03-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  New. This is the base state for the FLYS application. It provides a
+	  method that creates the dynamic ui node for the DESCRIBE.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/GaugeSelect.java: Both
+	  classes extend the abstract base class DefaultState.
+
+2011-03-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/resources/Resources.java:
+	  New. This class retrieves the i18n strings from a ResourceBundle.
+
+	* src/main/resources/messages.properties,
+	  src/main/resources/messages_en.properties,
+	  src/main/resources/messages_de.properties: Resource files for german and
+	  english translation.
+
+2011-03-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Appended
+	  the data that have been inserted in former states into the static node
+	  of the DESCRIBE.
+
+2011-03-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: The
+	  operations feed() and advance() return the description of the artifact
+	  using the describe() operation. This avoids additional server round trips
+	  in the client - the clients gets to know about the new state of the
+	  artifact immediately.
+
+2011-03-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Implemented a part (step forward) of the advance operation.
+
+	* TODO: Implement Step-Back in advance operation.
+
+2011-03-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Implemented the abstract method getName(). It returns the constant
+	  'winfo' string.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Implemented the feed action. The data of an incoming feed() operation is
+	  stored in StateData objects that are saved in a map in the artifact.
+
+	  NOTE: There is no input validation and no i18n of error messages (see
+	  TODO).
+
+2011-03-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* TODO: This file contains some open points that need to be done.
+
+2011-03-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: New. This
+	  artifact serves as the default artifact for the FLYS application.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: This
+	  artifact now inherits from FLYSArtifact. Furthermore, there is one big
+	  change: we don't store the State objects itself in the artifact, but
+	  just the identifier of those. This makes the artifact smaller and more
+	  compatible agains previous versions of the software.
+
+2011-03-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/RiverService.java:
+	  New. This service will retrieve a list of provided rivers.
+
+	* doc/conf/conf.xml: Added a configuration for the RiverService.
+
+2011-03-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/conf.xml: Added a section user-factory and collection-factory in
+	  the factories part of the configuration.
+
+2011-03-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java:
+	  Replaced the 'special' attribute from DESCRIBE with a 'uiprovider'
+	  attribute.
+
+2011-02-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The
+	  RiverSelect state is called to create the UI part of the describe
+	  document.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java:
+	  Implemented the dynamic UI part of describe(). The static part is not
+	  inserted into the describe document at the moment. We need a reference to
+	  the previous states for this.
+
+2011-02-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java,
+	  src/main/java/de/intevation/flys/artifacts/model/River.java: New. A model
+	  class that represents a river and its factory to create concrete river
+	  instances.
+	  NOTE: Currently, this is just a mockup. The factory just returns two
+	  static rivers "Mosel" and "Saar" without a connection to a backend.
+
+2011-02-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/StateFactory.java: The
+	  input data of a state is initialized with empty StateData objects after
+	  the State has been created.
+
+	* doc/conf/artifacts/winfo.xml: Renamed the input data nodes of the states
+	  which now fits better to the class name of the implementation.
+
+2011-02-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The
+	  describe document returned by this artifact now contains the current state
+	  and the reachable states.
+
+2011-02-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  The transitions are put into the TransitionEngine with the ID of the state
+	  - not longer with the artifact name. On this way, we are able to fetch
+	  just the transitions for a specific state, instead of all the transitions
+	  of an artifact.
+
+2011-02-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Describe()
+	  returns the artifact's uuid and hash value. The whole implementation of
+	  describe() is still outstanding.
+
+2011-02-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Removed useless config stuff.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Removed
+	  useless methods, and improved the init process - the first state is set as
+	  the current state for this artifact.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/GaugeSelect.java: New.
+	  The states are used in the first two steps of the WINFOArtifact.
+	  Currently, they just implement stubs of the necessary methods setup() and
+	  describe().
+
+2011-02-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/State.java,
+	  src/main/java/de/intevation/flys/artifacts/transitions/TransitionEngine.java,
+	  src/main/java/de/intevation/flys/artifacts/transitions/Transition.java:
+	  Removed. These classes are placed in the artifact-database now.
+
+	* src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java,
+	  src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java:
+	  Adapted imports of Transition.
+
+	* src/main/java/de/intevation/flys/artifacts/states/StateFactory.java: New.
+	  This factory should be used to create concrete State objects.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: Added
+	  a constant key to store the StateEngine in the context.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  New method that initializes the states at application start.
+
+2011-02-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/State.java: New. The
+	  interface description of a state.
+
+	* src/main/java/de/intevation/flys/artifacts/transitions/Transition.java,
+	  src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java:
+	  New. The interface description and a default implementation of a
+	  transition.
+
+	* src/main/java/de/intevation/flys/artifacts/transitions/TransitionEngine.java:
+	  New. The TransitionEngine stores all transitions for each artifact and
+	  should be used to determine, if an artifact can advance from one state to
+	  another.
+
+	* src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java:
+	  New. Transitions should be created by using this class.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java: New.
+	  The Flys context. It currently defines keys to store important components
+	  in the context.
+
+	* src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java:
+	  New. The context factory initializes the basic components of the
+	  application. Currently, the TransitionEngine is created and all artifacts
+	  with its transitions are read from the global configuration and stored in
+	  the FLYSContext.
+
+	* pom.xml: Added a dependency to the 'artifacts-common' package.
+
+	* doc/conf/artifacts/winfo.xml: Corrected the classname of the
+	  DefaultTransition.
+
+	* doc/conf/conf.xml: Added FLYSContextFactory as context-factory.
+
+2011-02-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/conf.xml: An initial configuration file for the FLYS artifact
+	  server.
+
+	* doc/conf/artifacts/winfo.xml: An initial transition configuration of an
+	  WINFO artifact.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: A stub of
+	  an artifact for a WINFO parameterization.
+
+	* pom.xml: Set the source code version to 1.5.
+
+2011-02-01	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/**, pom.xml: Added initial maven project.
+	* ChangeLog: new.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/Changes	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,59 @@
+2011-05-13  RELEASE 2.3
+
+    NEW:
+
+        * Initial release of the artifacts for FLYS. Currently there is a single
+          WINFO artifact for the following computations:
+          - waterlevels
+          - discharge curves
+          - duration curves
+          - discharge longitudinal section curves
+
+        * Configuration is placed in doc/conf/conf.xml
+
+        * WINFO Artifact specific configuration is placed in
+          doc/conf/artifacts/winfo.xml
+
+        * So called 'OutGenerators' produce different types of output.
+          Currently, the flys-artifacts are able to produce charts and exports.
+          Each output type has to be configured in conf.xml.
+
+        * New chart types:
+          - discharge curves (dt. 'Abflusskurven am Pegel')
+          - computed discharge curves (dt. 'Abflusskurve')
+          - longitudinal section curves (dt. 'Längsschnitte')
+          - duration curves (dt. 'Dauerlinie')
+          - discharge longitudinal section curve (dt. 'W bei
+            ungleichmäßigem Abflusslängsschnitt')
+
+        * New exports:
+          - csv of waterlevels
+          - csv of duration curves
+          - csv of computed discharge curves
+          - csv of discharge longitudinal section
+
+        * New services that provides:
+          - supported rivers
+          - main values of a gauge
+          - range information of a river
+          - meta information of a river
+
+        * Caching of computation relevant values
+
+        * Initial model to support chart specific themes (theme.xml)
+
+
+    LIMITATIONS:
+
+        * Charts are not rendered using the themes in theme.xml
+
+
+    !!!
+
+    The version number of this release depends on an existing desktop variant of
+    this software that is in version 2.1.
+
+    !!!
+
+
+2011-03-30  RELEASE 0.1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/NEWS	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,9 @@
+NEWS 
+
+for enduser-specific information, c.f. the NEWS -file in the module flys-client
+
+2011-05-16  Release V 2.3
+
+2011-03-27  Release V 2.2
+
+This file starts with V. 2.2. Earlier versions are based on a Desktop-version until V 2.1.3.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/TODO	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,1 @@
+- Validation of the input values of an incoming feed() call
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/contrib/visualize-transitions.xsl	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2010 by Intevation GmbH
+
+ This program is free software under the LGPL (>=v2.1)
+ Read the file LGPL.txt coming with the software for details
+ or visit http://www.gnu.org/licenses/ if it does not exist.
+
+ Author: Sascha L. Teichmann (sascha.teichmann@intevation.de)
+-->
+
+<xsl:stylesheet 
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns:xlink="http://www.w3.org/1999/xlink"
+    version="1.0">
+
+    <xsl:output method="text" encoding="UTF-8"/>
+
+    <xsl:param name="base-dir">.</xsl:param>
+
+    <xsl:template match="/">
+        <xsl:text>digraph transition_model {&#xa;</xsl:text>
+        <xsl:apply-templates />
+        <xsl:text>}&#xa;</xsl:text>
+    </xsl:template>
+
+    <xsl:template match="artifact">
+        <xsl:choose>
+            <xsl:when test="@xlink:href != ''">
+                <!-- handle external artifacts -->
+                <xsl:variable name="path">
+                    <xsl:call-template name="string-replace-all">
+                    <xsl:with-param name="text" select="@xlink:href" />
+                    <xsl:with-param name="replace">${artifacts.config.dir}</xsl:with-param>
+                    <xsl:with-param name="by" select="$base-dir" />
+                    </xsl:call-template>
+                </xsl:variable>
+                <xsl:for-each select="document($path)">
+                    <xsl:apply-templates select="/artifact"/>
+                </xsl:for-each>
+            </xsl:when>
+            <xsl:otherwise>
+                <!-- handle internal artifacts -->
+                <xsl:text>subgraph </xsl:text><xsl:value-of select="@name"/>
+                <xsl:text> {&#xa;</xsl:text>
+                <xsl:text>    label = "Artefakt: </xsl:text>
+                <xsl:value-of select="@name"/>
+                <xsl:text>";&#xa;</xsl:text>
+                <xsl:apply-templates mode="inside-artifact" select="./states/state"/>
+                <xsl:apply-templates mode="inside-artifact" select="./states/transition"/>
+                <xsl:text>}&#xa;</xsl:text>
+            </xsl:otherwise>
+        </xsl:choose>
+    </xsl:template>
+
+    <xsl:template match="state" mode="inside-artifact">
+        <xsl:text>    "</xsl:text>
+        <xsl:value-of select="@id"/>
+        <xsl:text disable-output-escaping="yes"
+        >" [ shape = "record" label=&lt;&lt;table border="0" cellborder="0" cellpadding="3"&gt;
+        &lt;tr&gt;&lt;td align="center" colspan="2" bgcolor="black"&gt;&lt;font color="white"&gt;</xsl:text>
+        <xsl:value-of select="@id"/>
+        <xsl:text disable-output-escaping="yes"
+        >&lt;/font&gt;&lt;/td&gt;&lt;/tr&gt;</xsl:text>
+            <xsl:apply-templates mode="inside-artifact" />
+        <xsl:text disable-output-escaping="yes"
+        >&lt;/table&gt;&gt;]</xsl:text>
+        <xsl:text>;&#xa;</xsl:text>
+    </xsl:template>
+
+    <xsl:template match="data" mode="inside-artifact">
+        <xsl:text disable-output-escaping="yes"
+        >&lt;tr&gt;&lt;td align="right"&gt;</xsl:text>
+        <xsl:value-of select="@name"/>
+        <xsl:text disable-output-escaping="yes"
+        >&lt;/td&gt;&lt;td align="left"&gt;</xsl:text>
+        <xsl:value-of select="@type"/>
+        <xsl:text disable-output-escaping="yes"
+        >&lt;/td&gt;&lt;/tr&gt;</xsl:text>
+    </xsl:template>
+
+    <xsl:template match="transition" mode="inside-artifact">
+        <xsl:text>    "</xsl:text>
+        <xsl:value-of select="from/@state"/>
+        <xsl:text disable-output-escaping="yes">" -&gt; "</xsl:text>
+        <xsl:value-of select="to/@state"/>
+        <xsl:text>"</xsl:text>
+        <xsl:apply-templates mode="inside-artifact"/>
+        <xsl:text>;&#xa;</xsl:text>
+    </xsl:template>
+
+    <xsl:template match="condition" mode="inside-artifact">
+        <xsl:text> [ label="</xsl:text>
+        <xsl:value-of select="@inputvalue"/>
+        <xsl:text> </xsl:text>
+        <xsl:value-of select="@operator"/>
+        <xsl:text> </xsl:text>
+        <xsl:value-of select="@value"/>
+        <xsl:text>" ]</xsl:text>
+    </xsl:template>
+
+    <xsl:template match="text()" mode="inside-artifact"/>
+    <xsl:template match="text()"/>
+
+     <xsl:template name="string-replace-all">
+        <xsl:param name="text" />
+        <xsl:param name="replace" />
+        <xsl:param name="by" />
+        <xsl:choose>
+          <xsl:when test="contains($text, $replace)">
+            <xsl:value-of select="substring-before($text,$replace)" />
+            <xsl:value-of select="$by" />
+            <xsl:call-template name="string-replace-all">
+              <xsl:with-param name="text"
+                  select="substring-after($text,$replace)" />
+              <xsl:with-param name="replace" select="$replace" />
+              <xsl:with-param name="by" select="$by" />
+            </xsl:call-template>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:value-of select="$text" />
+          </xsl:otherwise>
+        </xsl:choose>
+      </xsl:template>
+    
+</xsl:stylesheet>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/artifacts/winfo.xml	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,187 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<artifact name="winfo">
+    <states>
+
+        <state id="state.winfo.river" description="state.winfo.river" state="de.intevation.flys.artifacts.states.RiverSelect">
+            <data name="river" type="String" />
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition">
+            <from state="state.winfo.river"/>
+            <to state="state.winfo.calculation_mode"/>
+        </transition>
+
+        <state id="state.winfo.calculation_mode" description="state.winfo.calculation_mode" state="de.intevation.flys.artifacts.states.CalculationSelect">
+            <data name="calculation_mode"  type="String" />
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.calculation_mode"/>
+            <to state="state.winfo.location_distance"/>
+            <condition data="calculation_mode" value="calc.surface.curve" operator="equal"/>
+        </transition>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.calculation_mode"/>
+            <to state="state.winfo.location"/>
+            <condition data="calculation_mode" value="calc.discharge.curve" operator="equal"/>
+        </transition>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.calculation_mode"/>
+            <to state="state.winfo.location"/>
+            <condition data="calculation_mode" value="calc.duration.curve" operator="equal"/>
+        </transition>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.calculation_mode"/>
+            <to state="state.winfo.location_distance"/>
+            <condition data="calculation_mode" value="calc.discharge.longitudinal.section" operator="equal"/>
+        </transition>
+
+        <state id="state.winfo.location" description="state.winfo.location" state="de.intevation.flys.artifacts.states.LocationSelect">
+            <data name="ld_locations" type="Double[]" />
+
+            <outputmodes>
+                <outputmode name="discharge_curve" description="output.discharge_curve" mime-type="image/png">
+                    <facets>
+                        <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/>
+                        <facet name="discharge_curve.w" description="facet.discharge_curve.w"/>
+                        <facet name="discharge_curve.q" description="facet.discharge_curve.q"/>
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </state>
+
+        <state id="state.winfo.location_distance" description="state.winfo.location_distance" state="de.intevation.flys.artifacts.states.LocationDistanceSelect">
+            <data name="ld_from" type="Double" />
+            <data name="ld_to" type="Double" />
+            <data name="ld_step" type="Double" />
+
+            <outputmodes>
+                <outputmode name="discharge_curve" description="output.discharge_curve" mime-type="image/png">
+                    <facets>
+                        <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/>
+                        <facet name="discharge_curve.w" description="facet.discharge_curve.w"/>
+                        <facet name="discharge_curve.q" description="facet.discharge_curve.q"/>
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.location_distance"/>
+            <to state="state.winfo.wq"/>
+            <condition data="calculation_mode" value="calc.surface.curve" operator="equal"/>
+        </transition>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.location_distance"/>
+            <to state="state.winfo.wq_adapted"/>
+            <condition data="calculation_mode" value="calc.discharge.longitudinal.section" operator="equal"/>
+        </transition>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.location"/>
+            <to state="state.winfo.durationcurve"/>
+            <condition data="calculation_mode" value="calc.duration.curve" operator="equal"/>
+        </transition>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.location"/>
+            <to state="state.winfo.computeddischargecurve"/>
+            <condition data="calculation_mode" value="calc.discharge.curve" operator="equal"/>
+        </transition>
+
+        <state id="state.winfo.wq" description="state.winfo.wq" state="de.intevation.flys.artifacts.states.WQSelect">
+            <data name="wq_mode" type="String" />
+            <data name="wq_selection" type="String" />
+            <data name="wq_from" type="Double" />
+            <data name="wq_to" type="Double" />
+            <data name="wq_step" type="Double" />
+            <data name="wq_single" type="Double[]" />
+        </state>
+
+        <state id="state.winfo.wq_adapted" description="state.winfo.wq_adapted" state="de.intevation.flys.artifacts.states.WQAdapted">
+            <!-- TODO Add data objects -->
+            <data name="wq_mode" type="String" />
+            <data name="wq_values" type="WQTriple" />
+        </state>
+
+        <state id="state.winfo.durationcurve" description="state.winfo.durationcurve" state="de.intevation.flys.artifacts.states.DurationCurveState">
+            <outputmodes>
+                <outputmode name="duration_curve" description="output.duration_curve" mime-type="image/png">
+                    <facets>
+                        <facet name="duration_curve.w" description="facet.duration_curve.w"/>
+                        <facet name="duration_curve.q" description="facet.duration_curve.q"/>
+                    </facets>
+                </outputmode>
+                <outputmode name="durationcurve_export" description="output.waterlevel_export" mime-type="text/plain">
+                    <facets>
+                        <facet name="csv" description="facet.durationcurve_export.csv" />
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </state>
+
+        <state id="state.winfo.computeddischargecurve" description="state.winfo.computeddischargecurve" state="de.intevation.flys.artifacts.states.ComputedDischargeCurveState">
+            <outputmodes>
+                <outputmode name="computed_discharge_curve" description="output.computed_discharge_curve" mime-type="image/png">
+                    <facets>
+                        <facet name="computed_discharge_curve.w" description="facet.computed_discharge_curve.w"/>
+                        <facet name="computed_discharge_curve.q" description="facet.computed_discharge_curve.q"/>
+                    </facets>
+                </outputmode>
+                <outputmode name="computed_dischargecurve_export" description="output.computed_dischargecurve_export" mime-type="text/plain">
+                    <facets>
+                        <facet name="csv" description="facet.computed_dischargecurve_export.csv" />
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </state>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.ValueCompareTransition">
+            <from state="state.winfo.wq"/>
+            <to state="state.winfo.waterlevel"/>
+            <condition data="calculation_mode" value="calc.surface.curve" operator="equal"/>
+        </transition>
+
+        <transition transition="de.intevation.flys.artifacts.transitions.DefaultTransition">
+            <from state="state.winfo.wq_adapted"/>
+            <to state="state.winfo.discharge_longitudinal_section"/>
+        </transition>
+
+        <state id="state.winfo.waterlevel" description="state.winfo.waterlevel" state="de.intevation.flys.artifacts.states.WaterlevelState">
+            <outputmodes>
+                <outputmode name="longitudinal_section" description="output.longitudinal_section" mime-type="image/png">
+                    <facets>
+                        <facet name="longitudinal_section.w" description="facet.longitudinal_section.w"/>
+                        <facet name="longitudinal_section.q" description="facet.longitudinal_section.q"/>
+                    </facets>
+                </outputmode>
+                <outputmode name="waterlevel_export" description="output.waterlevel_export" mime-type="text/plain">
+                    <facets>
+                        <facet name="csv" description="facet.waterlevel_export.csv" />
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </state>
+
+        <state id="state.winfo.discharge_longitudinal_section" description="state.winfo.discharge_longitudinal_section" state="de.intevation.flys.artifacts.states.DischargeLongitudinalSection">
+            <outputmodes>
+                <outputmode name="discharge_longitudinal_section" description="output.discharge_longitudinal_section" mime-type="image/png">
+                    <facets>
+                        <facet name="discharge_longitudinal_section.w"/>
+                        <facet name="discharge_longitudinal_section.q"/>
+                    </facets>
+                </outputmode>
+                <outputmode name="discharge_longitudinal_section_export" description="output.discharge_longitudinal_section_export" mime-type="text/plain">
+                    <facets>
+                        <facet name="csv" description="facet.discharge_longitudinal_section_export.csv" />
+                    </facets>
+                </outputmode>
+            </outputmodes>
+        </state>
+
+    </states>
+</artifact>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/cache.xml	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ehcache>
+
+    <diskStore path="java.io.tmpdir"/>
+
+    <defaultCache
+            maxElementsInMemory="1000"
+            eternal="false"
+            timeToIdleSeconds="120"
+            timeToLiveSeconds="3600"
+            overflowToDisk="true"
+            maxElementsOnDisk="100000"
+            diskPersistent="false"
+            diskExpiryThreadIntervalSeconds="120"
+            memoryStoreEvictionPolicy="LRU"
+            />
+
+    <!-- This one is used for the WST value tables -->
+
+    <cache name="wst-value-table"
+           maxElementsInMemory="20"
+           maxElementsOnDisk="100"
+           eternal="false"
+           timeToIdleSeconds="360"
+           overflowToDisk="true"
+           timeToLiveSeconds="14400"
+           diskPersistent="true"
+           memoryStoreEvictionPolicy="LRU"
+           />
+</ehcache>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/conf.xml	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,100 @@
+<artifact-database>
+    <export-secret>YOUR_SECRET</export-secret>
+    <factories>
+        <context-factory>de.intevation.flys.artifacts.context.FLYSContextFactory</context-factory>
+
+        <collection-factory
+            name="DefaultArtifactCollectionFactory"
+            description="The default artifact collection factory"
+            ttl="60000"
+            artifact-collection="de.intevation.flys.collections.FLYSArtifactCollection">de.intevation.artifactdatabase.DefaultArtifactCollectionFactory</collection-factory>
+
+        <artifact-factories>
+            <!-- All Artifactfactories which are available in this Database.
+                 Each Artifactfactory represents one river. -->
+            <artifact-factory name="winfo" description="Factory to create an artifact to be used in WINFO"
+                ttl="3600000"
+                artifact="de.intevation.flys.artifacts.WINFOArtifact">de.intevation.artifactdatabase.DefaultArtifactFactory</artifact-factory>
+        </artifact-factories>
+
+        <user-factory name="default" description="Factory to create new users">de.intevation.artifactdatabase.DefaultUserFactory</user-factory>
+
+        <service-factories>
+            <service-factory
+                name="rivers"
+                service="de.intevation.flys.artifacts.services.RiverService"
+                description="This service returns a list of provided rivers by the artifact server.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory>
+            <service-factory
+                name="distanceinfo"
+                service="de.intevation.flys.artifacts.services.DistanceInfoService"
+                description="Returns a list of distances supported by a specific river.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory>
+            <service-factory
+                name="mainvalues"
+                service="de.intevation.flys.artifacts.services.MainValuesService"
+                description="Returns the main values of a river's gauge based on a start and end point of the river.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory>
+            <service-factory
+                name="metadata"
+                service="de.intevation.flys.artifacts.services.MetaDataService"
+                description="The service provides some introspection into the database content.">de.intevation.artifactdatabase.DefaultServiceFactory</service-factory>
+        </service-factories>
+
+    </factories>
+
+    <callcontext-listener
+        name="SessionCallContextListener"
+        description="A CallContext.Listener to open and close Hibernatesessions">de.intevation.flys.artifacts.context.SessionCallContextListener</callcontext-listener>
+
+    <artifacts>
+        <artifact name="winfo" xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="${artifacts.config.dir}/artifacts/winfo.xml" />
+    </artifacts>
+
+    <output-generators>
+        <output-generator name="discharge_curve">de.intevation.flys.exports.DischargeCurveGenerator</output-generator>
+        <output-generator name="computed_discharge_curve">de.intevation.flys.exports.ComputedDischargeCurveGenerator</output-generator>
+        <output-generator name="longitudinal_section">de.intevation.flys.exports.LongitudinalSectionGenerator</output-generator>
+        <output-generator name="duration_curve">de.intevation.flys.exports.DurationCurveGenerator</output-generator>
+        <output-generator name="discharge_longitudinal_section">de.intevation.flys.exports.DischargeLongitudinalSectionGenerator</output-generator>
+        <output-generator name="waterlevel_export">de.intevation.flys.exports.WaterlevelExporter</output-generator>
+        <output-generator name="durationcurve_export">de.intevation.flys.exports.DurationCurveExporter</output-generator>
+        <output-generator name="computed_dischargecurve_export">de.intevation.flys.exports.ComputedDischargeCurveExporter</output-generator>
+        <output-generator name="discharge_longitudinal_section_export">de.intevation.flys.exports.DischargeLongitudinalSectionExporter</output-generator>
+    </output-generators>
+
+    <rest-server>
+        <!--  at this Point it is possible to Change the Port the ArtifactDatabase will bind to.-->
+        <port>8181</port>
+        <listen>localhost</listen>
+    </rest-server>
+    <!-- garbage collection of outdated artifacts -->
+    <cleaner>
+        <sleep-time>60000</sleep-time>
+    </cleaner>
+
+    <cache>
+        <config-file>${artifacts.config.dir}/cache.xml</config-file>
+    </cache>
+
+    <database>
+        <!-- This Section configures the Settings for connecting to the 
+             Artifact-Database instance. e.g. SQLite -->
+        <user>SA</user>
+        <password></password>
+        <url>jdbc:h2:${artifacts.config.dir}/../artifactdb/artifacts.db</url>
+    </database>
+    <!-- This is the default backend db configuration -->
+    <!--
+    <backend-database>
+        <user>flys</user>
+        <password>flys</password>
+        <dialect>org.hibernate.dialect.PostgreSQLDialect</dialect>
+        <driver>org.postgresql.Driver</driver>
+        <url>jdbc:postgresql://localhost:5432/flys</url>
+    </backend-database>
+    -->
+
+    <flys>
+        <themes>
+            <configuration>${artifacts.config.dir}/themes.xml</configuration>
+        </themes>
+    </flys>
+</artifact-database>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/doc/conf/themes.xml	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,62 @@
+<themes>
+
+    <!-- Concrete themes are following now! -->
+    <theme name="DischargeCurve">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+    </theme>
+
+    <theme name="LongitudinalSectionW">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+    </theme>
+
+    <theme name="LongitudinalSectionQ">
+        <inherits>
+            <inherit from="HiddenColorLines"/>
+        </inherits>
+    </theme>
+
+
+
+    <!-- Virtual themes are following now! -->
+    <theme name="Lines" type="virtual">
+        <fields>
+            <field name="showlines" type="boolean" display="Linie anzeigen" default="true"/>
+            <field name="linesize"  type="int"     display="Liniendicke"    default="1"/>
+            <field name="linetype"  type="Dash"    display="Linienart"      default="new Dash()"/>
+        </fields>
+    </theme>
+
+    <theme name="ColorLines" type="virtual">
+        <inherits>
+            <inherit from="Lines"/>
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe" default="Color.BLACK"/>
+        </fields>
+    </theme>
+
+    <theme name="HiddenColorLines" type="virtual">
+        <inherits>
+            <inherit from="ColorLines"/>
+        </inherits>
+        <fields>
+            <field name="showlines" type="boolean" display="Linie anzeigen" default="true"        hints="h"/>
+            <field name="linesize"  type="int"     display="Liniendicke"    default="1"           hints="h"/>
+            <field name="linetype"  type="Dash"    display="Linienart"      default="new Dash()"  hints="h"/>
+        </fields>
+    </theme>
+
+
+
+    <!-- Mappings are following now. A mapping maps between a name of a facet
+         and a theme. -->
+    <mappings>
+        <mapping from="discharge_curve.curve" to="DischargeCurve"/>
+        <mapping from="longitudinal_section.w" to="LongitudinalSectionW"/>
+        <mapping from="longitudinal_section.q" to="LongitudinalSectionQ"/>
+    </mappings>
+</themes>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/pom.xml	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,106 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+
+  <groupId>de.intevation.flys.artifacts</groupId>
+  <artifactId>flys-artifacts</artifactId>
+  <version>1.0-SNAPSHOT</version>
+  <packaging>jar</packaging>
+
+  <name>flys-artifacts</name>
+  <url>http://maven.apache.org</url>
+
+  <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+  </properties>
+
+  <build>
+    <plugins>
+      <plugin>
+          <groupId>org.apache.maven.plugins</groupId>
+          <artifactId>maven-compiler-plugin</artifactId>
+          <version>2.0.2</version>
+          <configuration>
+              <source>1.5</source>
+              <target>1.5</target>
+          </configuration>
+      </plugin>
+     </plugins>
+  </build>
+
+  <dependencies>
+    <dependency>
+      <groupId>net.sf.ehcache</groupId>
+      <artifactId>ehcache-core</artifactId>
+      <version>2.4.2</version>
+    </dependency>
+    <dependency>
+      <groupId>jfree</groupId>
+      <artifactId>jfreechart</artifactId>
+      <version>1.0.13</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>3.8.1</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>trove</groupId>
+      <artifactId>trove</artifactId>
+      <version>2.1.1</version>
+    </dependency>
+    <dependency>
+      <groupId>net.sf.opencsv</groupId>
+      <artifactId>opencsv</artifactId>
+      <version>2.0</version>
+    </dependency>
+    <dependency>
+      <groupId>de.intevation.bsh.artifact-database</groupId>
+      <artifactId>artifact-database</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>de.intevation.artifacts.common</groupId>
+      <artifactId>artifacts-common</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>de.intevation.flys</groupId>
+      <artifactId>flys-backend</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>jfree</groupId>
+      <artifactId>jfreechart</artifactId>
+      <version>1.0.13</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.xmlgraphics</groupId>
+      <artifactId>batik-dom</artifactId>
+      <version>1.7</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.xmlgraphics</groupId>
+      <artifactId>batik-svggen</artifactId>
+      <version>1.7</version>
+    </dependency>
+    <dependency>
+      <groupId>com.lowagie</groupId>
+      <artifactId>itext</artifactId>
+      <version>2.1.7</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.commons</groupId>
+      <artifactId>commons-math</artifactId>
+      <version>2.2</version>
+    </dependency>
+  </dependencies>
+  <repositories>
+    <repository>
+        <id>jboss-repo2</id>
+        <name>JBoss repo2</name>
+        <url>http://repository.jboss.org/maven2</url>
+    </repository>
+  </repositories>
+</project>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/App.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,13 @@
+package de.intevation.flys.artifacts;
+
+/**
+ * Hello world!
+ *
+ */
+public class App
+{
+    public static void main( String[] args )
+    {
+        System.out.println( "Hello World!" );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,926 @@
+package de.intevation.flys.artifacts;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Vector;
+
+import javax.xml.xpath.XPathConstants;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import de.intevation.artifacts.ArtifactFactory;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.DefaultArtifact;
+import de.intevation.artifactdatabase.data.DefaultStateData;
+import de.intevation.artifactdatabase.data.StateData;
+import de.intevation.artifactdatabase.state.State;
+import de.intevation.artifactdatabase.state.StateEngine;
+import de.intevation.artifactdatabase.transition.TransitionEngine;
+
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.Range;
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.context.FLYSContext;
+import de.intevation.flys.artifacts.model.DischargeTables;
+import de.intevation.flys.artifacts.model.RiverFactory;
+import de.intevation.flys.artifacts.states.DefaultState;
+
+
+/**
+ * The defaul FLYS artifact.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class FLYSArtifact extends DefaultArtifact {
+
+    /** The logger that is used in this artifact.*/
+    private static Logger logger = Logger.getLogger(FLYSArtifact.class);
+
+
+    /** The XPath that points to the input data elements of the FEED document.*/
+    public static final String XPATH_FEED_INPUT =
+        "/art:action/art:data/art:input";
+
+    /** The XPath that points to the name of the target state of ADVANCE.*/
+    public static final String XPATH_ADVANCE_TARGET =
+        "/art:action/art:target/@art:name";
+
+    /** The constant string that shows that an operation was successful.*/
+    public static final String OPERATION_SUCCESSFUL = "SUCCESS";
+
+    /** The constant string that shows that an operation failed.*/
+    public static final String OPERATION_FAILED = "FAILURE";
+
+    /** The default number of steps between the start end end of a selected Q
+     * range.*/
+    public static final int DEFAULT_Q_STEPS = 30;
+
+    /** The default step width between the start end end kilometer.*/
+    public static final double DEFAULT_KM_STEPS = 0.1;
+
+
+    /** The identifier of the current state. */
+    protected String currentStateId;
+
+    /** The identifiers of previous states on a stack.*/
+    protected Vector<String> previousStateIds;
+
+    /** The name of the artifact.*/
+    protected String name;
+
+    /** The data that have been inserted into this artifact.*/
+    protected Map<String, StateData> data;
+
+
+    /**
+     * The default constructor that creates an empty FLYSArtifact.
+     */
+    public FLYSArtifact() {
+        data             = new HashMap<String, StateData>();
+        previousStateIds = new Vector<String>();
+    }
+
+
+    /**
+     * Returns the name of the concrete artifact.
+     *
+     * @return the name of the concrete artifact.
+     */
+    public abstract String getName();
+
+
+    /**
+     * Returns the FLYSContext from context object.
+     *
+     * @param context The CallContext or the FLYSContext.
+     *
+     * @return the FLYSContext.
+     */
+    protected FLYSContext getFlysContext(Object context) {
+        return context instanceof FLYSContext
+            ? (FLYSContext) context
+            : (FLYSContext) ((CallContext) context).globalContext();
+    }
+
+
+    /**
+     * Initialize the artifact and insert new data if <code>data</code> contains
+     * information necessary for this artifact.
+     *
+     * @param identifier The UUID.
+     * @param factory The factory that is used to create this artifact.
+     * @param context The CallContext.
+     * @param data Some optional data.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        Document        data)
+    {
+        logger.debug("Setup this artifact with the uuid: " + identifier);
+
+        super.setup(identifier, factory, context, data);
+
+        FLYSContext flysContext = (FLYSContext) context;
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        String name = getName();
+
+        logger.debug("Set initial state for artifact '" + name + "'");
+        List<State> states = engine.getStates(name);
+
+        setCurrentState(states.get(0));
+    }
+
+
+    /**
+     * Insert new data included in <code>input</code> into the current state.
+     *
+     * @param target XML document that contains new data.
+     * @param context The CallContext.
+     *
+     * @return a document that contains a SUCCESS or FAILURE message.
+     */
+    @Override
+    public Document feed(Document target, CallContext context) {
+        logger.info("FLYSArtifact.feed()");
+
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element result = creator.create("result");
+        doc.appendChild(result);
+
+        try {
+            saveData(target, XPATH_FEED_INPUT, context);
+            return describe(target, context);
+        }
+        catch (IllegalArgumentException iae) {
+            creator.addAttr(result, "type", OPERATION_FAILED, true);
+
+            result.setTextContent(iae.getMessage());
+        }
+
+        return doc;
+    }
+
+
+    /**
+     * This method handles request for changing the current state of an
+     * artifact. It is possible to step forward or backward.
+     *
+     * @param target The incoming ADVANCE document.
+     * @param context The CallContext.
+     *
+     * @return a document that contains a SUCCESS or FAILURE message.
+     */
+    public Document advance(Document target, CallContext context) {
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element result = ec.create("result");
+
+        String targetState = XMLUtils.xpathString(
+            target, XPATH_ADVANCE_TARGET, ArtifactNamespaceContext.INSTANCE);
+
+        logger.info("FLYSArtifact.advance() to '" + targetState + "'");
+
+        if (isStateReachable(targetState, context)) {
+            logger.info("Advance: Step forward");
+
+            Vector<String> prev = getPreviousStateIds();
+            prev.add(getCurrentStateId());
+
+            setCurrentStateId(targetState);
+
+            return describe(target, context);
+        }
+        else if (isPreviousState(targetState, context)) {
+            logger.info("Advance: Step back to");
+
+            Vector<String> prevs = getPreviousStateIds();
+            int targetIdx        = prevs.indexOf(targetState);
+            int start            = prevs.size() - 1;
+
+            for (int i = start; i >= targetIdx; i--) {
+                String prev = prevs.get(i);
+                logger.debug("Remove state id '" + prev + "'");
+                prevs.remove(prev);
+            }
+
+            setCurrentStateId(targetState);
+
+            return describe(target, context);
+        }
+
+        logger.warn("Advance: Cannot advance to '" + targetState + "'");
+        ec.addAttr(result, "type", OPERATION_FAILED, true);
+
+        doc.appendChild(result);
+
+        return doc;
+    }
+
+
+    /**
+     * Returns the identifier of the current state.
+     *
+     * @return the identifier of the current state.
+     */
+    protected String getCurrentStateId() {
+        return currentStateId;
+    }
+
+
+    /**
+     * Sets the identifier of the current state.
+     *
+     * @param id the identifier of a state.
+     */
+    protected void setCurrentStateId(String id) {
+        currentStateId = id;
+    }
+
+
+    /**
+     * Set the current state of this artifact. <b>NOTE</b>We don't store the
+     * State object itself - which is not necessary - but its identifier. So
+     * this method will just call the setCurrentStateId() method with the
+     * identifier of <i>state</i>.
+     *
+     * @param state The new current state.
+     */
+    protected void setCurrentState(State state) {
+        setCurrentStateId(state.getID());
+    }
+
+
+    /**
+     * Returns the current state of the artifact.
+     *
+     * @return the current State of the artifact.
+     */
+    protected State getCurrentState(Object context) {
+        FLYSContext flysContext = getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        return engine.getState(getCurrentStateId());
+    }
+
+
+    /**
+     * Returns the vector of previous state identifiers.
+     *
+     * @return the vector of previous state identifiers.
+     */
+    protected Vector<String> getPreviousStateIds() {
+        return previousStateIds;
+    }
+
+
+    /**
+     * Adds a new StateData item to the data pool of this artifact.
+     *
+     * @param name the name of the data object.
+     * @param data the data object itself.
+     */
+    protected void addData(String name, StateData data) {
+        this.data.put(name, data);
+    }
+
+
+    /**
+     * This method returns a specific StateData object that is stored in the
+     * data pool of this artifact.
+     *
+     * @param name The name of the data object.
+     *
+     * @return the StateData object if existing, otherwise null.
+     */
+    public StateData getData(String name) {
+        return data.get(name);
+    }
+
+
+    /**
+     * This method stores the data that is contained in the FEED document.
+     *
+     * @param feed The FEED document.
+     * @param xpath The XPath that points to the data nodes.
+     */
+    public void saveData(Document feed, String xpath, CallContext context)
+    throws IllegalArgumentException
+    {
+        if (feed == null || xpath == null || xpath.length() == 0) {
+            throw new IllegalArgumentException("error_feed_no_data");
+        }
+
+        NodeList nodes = (NodeList) XMLUtils.xpath(
+            feed,
+            xpath,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (nodes == null || nodes.getLength() == 0) {
+            throw new IllegalArgumentException("error_feed_no_data");
+        }
+
+        int count = nodes.getLength();
+        logger.debug("Try to save " + count + " data items.");
+
+        for (int i = 0; i < count; i++) {
+            Node node = nodes.item(i);
+
+            String name = XMLUtils.xpathString(
+                node, "@art:name", ArtifactNamespaceContext.INSTANCE);
+            String value = XMLUtils.xpathString(
+                node, "@art:value", ArtifactNamespaceContext.INSTANCE);
+
+            if (name != null && value != null) {
+                logger.debug("Save data item for '" + name + "' : " + value);
+
+                addData(name, new DefaultStateData(name, null, null, value));
+            }
+        }
+
+        State current           = getCurrentState(context);
+        DefaultState toValidate = (DefaultState) fillState(current);
+
+        toValidate.validate(this, context);
+    }
+
+
+    /**
+     * This method fills a state object with the data that have been inserted to
+     * this artifact. This is necessary to use the isStateReachable() method,
+     * because the Transitions need to know about the inserted data.
+     *
+     * @param state The state that needs to be filled with data.
+     *
+     * @return the filled state.
+     */
+    protected State fillState(State state) {
+        Map<String, StateData> stateData = state.getData();
+
+        if (stateData == null) {
+            return state;
+        }
+
+        Set<String>                 keys = stateData.keySet();
+
+        for (String key: keys) {
+            StateData tmp = getData(key);
+
+            if (tmp != null) {
+                StateData data = stateData.get(key);
+                data.setValue(tmp.getValue());
+            }
+        }
+
+        return state;
+    }
+
+
+    /**
+     * Determines if the state with the identifier <i>stateId</i> is reachable
+     * from the current state. The determination itself takes place in the
+     * TransitionEngine.
+     *
+     * @param stateId The identifier of a state.
+     * @param context The context object.
+     *
+     * @return true, if the state specified by <i>stateId</i> is reacahble,
+     * otherwise false.
+     */
+    protected boolean isStateReachable(String stateId, Object context) {
+        logger.debug("Determine if the state '" + stateId + "' is reachable.");
+
+        FLYSContext flysContext = getFlysContext(context);
+
+        State currentState  = fillState(getCurrentState(context));
+        StateEngine sEngine = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        TransitionEngine tEngine = (TransitionEngine) flysContext.get(
+            FLYSContext.TRANSITION_ENGINE_KEY);
+
+        return tEngine.isStateReachable(this, stateId, currentState, sEngine);
+    }
+
+
+    /**
+     * Determines if the state with the identifier <i>stateId</i> is a previous
+     * state of the current state.
+     *
+     * @param stateId The target state identifier.
+     * @param context The context object.
+     */
+    protected boolean isPreviousState(String stateId, Object context) {
+        logger.debug("Determine if the state '" + stateId + "' is old.");
+
+        Vector<String> prevs = getPreviousStateIds();
+        if (prevs.contains(stateId)) {
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns the selected River object based on the 'river' data that might
+     * have been inserted by the user.
+     *
+     * @return the selected River or null if no river has been chosen yet.
+     */
+    public River getRiver() {
+        StateData dRiver = getData("river");
+
+        return dRiver != null
+            ? RiverFactory.getRiver((String) dRiver.getValue())
+            : null;
+    }
+
+
+    /**
+     * Returns the selected distance of points.
+     *
+     * @return the selected distance or points.
+     */
+    public double[] getDistance() {
+        StateData dFrom      = getData("ld_from");
+        StateData dTo        = getData("ld_to");
+        StateData dLocations = getData("ld_locations");
+
+        if (dFrom != null && dTo != null) {
+            return getDistanceByRange(dFrom, dTo);
+        }
+        else if (dLocations != null) {
+            double[] locations = getLocations();
+            return new double[] { locations[0], locations[locations.length-1] };
+        }
+
+        logger.warn("No data found for distance determination!");
+
+        return null;
+    }
+
+
+    /**
+     * This method returns the given distance 
+     *
+     * @return an array with lower and upper kilometer range for each
+     * intersected gauge.
+     */
+    public double[][] getSplittedDistance() {
+        double[]    dist   = getDistance();
+        List<Gauge> gauges = getGauges();
+
+        int num = gauges != null ? gauges.size() : 0;
+
+        double[][] res = new double[num][2];
+
+        for (int i = 0; i < num; i++) {
+            Range range = gauges.get(i).getRange();
+
+            double lower = range.getA().doubleValue();
+            double upper = range.getB().doubleValue();
+
+            res[i][0] = dist[0] < lower ? lower : dist[0];
+            res[i][1] = dist[1] > upper ? upper : dist[1];
+        }
+
+        return res;
+    }
+
+
+    /**
+     * Returns the selected locations based on a given array of locations.
+     *
+     * @param locations The StateData that contains the locations.
+     *
+     * @return the selected locations.
+     */
+    public double[] getLocations() {
+        StateData dLocations  = getData("ld_locations");
+        String    locationStr = dLocations != null
+            ? (String) dLocations.getValue()
+            : "";
+
+        if (locationStr == null || locationStr.length() == 0) {
+            logger.warn("No valid location string found!");
+            return null;
+        }
+
+        String[] tmp               = locationStr.split(" ");
+        TDoubleArrayList locations = new TDoubleArrayList();
+
+        for (String l: tmp) {
+            try {
+                locations.add(Double.parseDouble(l));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        locations.sort();
+
+        return locations.toNativeArray();
+    }
+
+
+    /**
+     * Returns the selected distance based on a given range (from, to).
+     *
+     * @param dFrom The StateData that contains the lower value.
+     * @param dTo The StateData that contains the upper value.
+     *
+     * @return the selected distance.
+     */
+    protected double[] getDistanceByRange(StateData dFrom, StateData dTo) {
+        double from = Double.parseDouble((String) dFrom.getValue());
+        double to   = Double.parseDouble((String) dTo.getValue());
+
+        return new double[] { from, to };
+    }
+
+
+    /**
+     * Returns the selected Kms.
+     *
+     * @param distance An 2dim array with [lower, upper] values.
+     *
+     * @return the selected Kms.
+     */
+    public double[] getKms(double[] distance) {
+        StateData dStep = getData("ld_step");
+
+        if (dStep == null) {
+            logger.warn("No step width given. Cannot compute Kms.");
+            return null;
+        }
+
+        double step = Double.parseDouble((String) dStep.getValue());
+
+        // transform step from 'm' into 'km'
+        step = step / 1000;
+
+        if (step == 0d) {
+            step = DEFAULT_KM_STEPS;
+        }
+
+        return getExplodedValues(distance[0], distance[1], step);
+    }
+
+
+    /**
+     * Returns the selected Kms.
+     *
+     * @return the selected kms.
+     */
+    public double[] getKms() {
+        double[] distance = getDistance();
+        return getKms(distance);
+    }
+
+
+    /**
+     * Returns the gauge based on the current distance and river.
+     *
+     * @return the gauge.
+     */
+    public Gauge getGauge() {
+        River    river = getRiver();
+        double[] dist  = getDistance();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Determine gauge for:");
+            logger.debug("... river: " + river.getName());
+            logger.debug("... distance: " + dist[0] + " - " + dist[1]);
+        }
+
+        Gauge gauge = river.determineGauge(dist[0], dist[1]);
+
+        String name = gauge != null ? gauge.getName() : "'n/a";
+        logger.debug("Found gauge: " + name);
+
+        return gauge;
+    }
+
+
+    /**
+     * Returns the gauges that match the selected kilometer range.
+     *
+     * @return the gauges based on the selected kilometer range.
+     */
+    public List<Gauge> getGauges() {
+        River    river = getRiver();
+        double[] dist  = getDistance();
+
+        return river.determineGauges(dist[0], dist[1]);
+    }
+
+
+    /**
+     * This method returns the Q values.
+     *
+     * @return the selected Q values or null, if no Q values are selected.
+     */
+    public double[] getQs() {
+        StateData dMode   = getData("wq_mode");
+        StateData dSingle = getData("wq_single");
+
+        String mode = dMode != null ? (String) dMode.getValue() : "";
+
+        if (mode.equals("Q")) {
+            if (dSingle != null) {
+                return getSingleWQValues();
+            }
+            else {
+                return getWQTriple();
+            }
+        }
+        else {
+            logger.warn("You try to get Qs, but W has been inserted.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Returns the Q values based on a specified kilometer range.
+     *
+     * @param range A 2dim array with lower and upper kilometer range.
+     *
+     * @return an array of Q values.
+     */
+    public double[] getQs(double[] range) {
+        StateData dMode   = getData("wq_mode");
+        StateData dValues = getData("wq_values");
+
+        String mode = dMode != null ? (String) dMode.getValue() : "";
+
+        if (mode.equals("Q")) {
+            return getWQForDist(range);
+        }
+
+        logger.warn("You try to get Qs, but Ws has been inserted.");
+        return null;
+    }
+
+
+    /**
+     * Returns the W values based on a specified kilometer range.
+     *
+     * @param range A 2dim array with lower and upper kilometer range.
+     *
+     * @return an array of W values.
+     */
+    public double[] getWs(double[] range) {
+        StateData dMode   = getData("wq_mode");
+        StateData dValues = getData("wq_values");
+
+        String mode = dMode != null ? (String) dMode.getValue() : "";
+
+        if (mode.equals("W")) {
+            return getWQForDist(range);
+        }
+
+        logger.warn("You try to get Ws, but Qs has been inserted.");
+        return null;
+    }
+
+
+    /**
+     * This method returns the W values.
+     *
+     * @return the selected W values or null, if no W values are selected.
+     */
+    public double[] getWs() {
+        StateData dMode   = getData("wq_mode");
+        StateData dSingle = getData("wq_single");
+
+        String mode = dMode != null ? (String) dMode.getValue() : "";
+
+        if (mode.equals("W")) {
+            if (dSingle != null) {
+                return getSingleWQValues();
+            }
+            else {
+                return getWQTriple();
+            }
+        }
+        else {
+            logger.warn("You try to get Qs, but W has been inserted.");
+            return null;
+        }
+    }
+
+
+    /**
+     * Returns the Qs for a number of Ws. This method makes use of
+     * DischargeTables.getQForW().
+     *
+     * @param ws An array of W values.
+     *
+     * @return an array of Q values.
+     */
+    public double[] getQsForWs(double[] ws) {
+        logger.debug("FLYSArtifact.getQsForWs");
+
+        River r = getRiver();
+        Gauge g = getGauge();
+
+        DischargeTables dt = new DischargeTables(r.getName(), g.getName());
+        Map<String, double [][]>  tmp = dt.getValues();
+
+        double[][] values = tmp.get(g.getName());
+        double[]   qs     = new double[ws.length];
+
+        for (int i = 0; i < ws.length; i++) {
+            qs[i] = dt.getQForW(values, ws[i]);
+        }
+
+        return qs;
+    }
+
+
+    /**
+     * This method returns the given W or Q values for a specific range
+     * (inserted in the WQ input panel for discharge longitudinal sections).
+     *
+     * @param dist A 2dim array with lower und upper kilometer values.
+     *
+     * @return an array of W or Q values.
+     */
+    protected double[] getWQForDist(double[] dist) {
+        logger.debug("Search wq values for range: " + dist[0] + " - " + dist[1]);
+        StateData data = getData("wq_values");
+
+        if (data == null) {
+            logger.warn("Missing wq values!");
+            return null;
+        }
+
+        String dataString = (String) data.getValue();
+        String[]   ranges = dataString.split(":");
+
+        for (String range: ranges) {
+            String[] parts = range.split(";");
+
+            double lower = Double.parseDouble(parts[0]);
+            double upper = Double.parseDouble(parts[1]);
+
+            if (lower <= dist[0] && upper >= dist[1]) {
+                String[] values = parts[2].split(",");
+
+                int      num = values.length;
+                double[] res = new double[num];
+
+                for (int i = 0; i < num; i++) {
+                    try {
+                        res[i] = Double.parseDouble(values[i]);
+                    }
+                    catch (NumberFormatException nfe) {
+                        logger.warn(nfe, nfe);
+                    }
+                }
+
+                return res;
+            }
+        }
+
+        logger.warn("Specified range for WQ not found!");
+
+        return null;
+    }
+
+
+    /**
+     * This method returns an array of inserted WQ triples that consist of from,
+     * to and the step width.
+     *
+     * @return an array of from, to and step width.
+     */
+    protected double[] getWQTriple() {
+        StateData dFrom = getData("wq_from");
+        StateData dTo   = getData("wq_to");
+
+        if (dFrom == null || dTo == null) {
+            logger.warn("Missing start or end value for range.");
+            return null;
+        }
+
+        double from = Double.parseDouble((String) dFrom.getValue());
+        double to   = Double.parseDouble((String) dTo.getValue());
+
+        StateData dStep = getData("wq_step");
+
+        if (dStep == null) {
+            logger.warn("No step width given. Cannot compute Qs.");
+            return null;
+        }
+
+        double step  = Double.parseDouble((String) dStep.getValue());
+
+        // if no width is given, the DEFAULT_Q_STEPS is used to compute the step
+        // width. Maybe, we should round the value to a number of digits.
+        if (step == 0d) {
+            double diff = to - from;
+            step = diff / DEFAULT_Q_STEPS;
+        }
+
+        return getExplodedValues(from, to, step);
+    }
+
+
+    /**
+     * Returns an array of inserted WQ double values stored as whitespace
+     * separated list.
+     *
+     * @return an array of W or Q values.
+     */
+    protected double[] getSingleWQValues() {
+        StateData dSingle = getData("wq_single");
+
+        if (dSingle == null) {
+            logger.warn("Cannot determine single WQ values. No data given.");
+            return null;
+        }
+
+        String   tmp       = (String) dSingle.getValue();
+        String[] strValues = tmp.split(" ");
+
+        TDoubleArrayList values = new TDoubleArrayList();
+
+        for (String strValue: strValues) {
+            try {
+                values.add(Double.parseDouble(strValue));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        values.sort();
+
+        return values.toNativeArray();
+    }
+
+
+    /**
+     * Returns an array of double values. The values contained in this array
+     * begin with the value <i>from</i> and end with the value <i>to</i>. The
+     * number of values in the result array depends on the <i>step</i> width.
+     *
+     * @param from The lower value.
+     * @param to The upper value.
+     * @param step The step width between two values in the result array.
+     *
+     * @return an array of double values.
+     */
+    public double[] getExplodedValues(double from, double to, double step) {
+        double lower = from;
+
+        double diff = to - from;
+        double tmp  = diff / step;
+        int    num = (int) Math.ceil(tmp) ;
+
+        double[] values = new double[num];
+
+        for (int idx = 0; idx < num; idx++) {
+            values[idx] = lower;
+            lower      += step;
+        }
+
+        return values;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,537 @@
+package de.intevation.flys.artifacts;
+
+import java.util.List;
+import java.util.Vector;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+import de.intevation.artifactdatabase.state.Output;
+import de.intevation.artifactdatabase.state.State;
+import de.intevation.artifactdatabase.state.StateEngine;
+import de.intevation.artifactdatabase.transition.TransitionEngine;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.states.DefaultState;
+import de.intevation.flys.artifacts.context.FLYSContext;
+import de.intevation.flys.artifacts.math.BackJumpCorrector;
+import de.intevation.flys.artifacts.model.MainValuesFactory;
+import de.intevation.flys.artifacts.model.WQCKms;
+import de.intevation.flys.artifacts.model.WQDay;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.artifacts.model.WstValueTable;
+
+
+/**
+ * The default WINFO artifact.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WINFOArtifact extends FLYSArtifact {
+
+    /** The logger for this class */
+    private static Logger logger = Logger.getLogger(WINFOArtifact.class);
+
+
+    /** The name of the artifact.*/
+    public static final String ARTIFACT_NAME = "winfo";
+
+    /** XPath */
+    public static final String XPATH_STATIC_UI ="/art:result/art:ui/art:static";
+
+
+    /**
+     * The default constructor.
+     */
+    public WINFOArtifact() {
+    }
+
+
+    /**
+     * This method returns a description of this artifact.
+     *
+     * @param data Some data.
+     * @param context The CallContext.
+     *
+     * @return the description of this artifact.
+     */
+    public Document describe(Document data, CallContext context) {
+        logger.debug("Describe: the current state is: " + getCurrentStateId());
+
+        FLYSContext flysContext = null;
+        if (context instanceof FLYSContext) {
+            flysContext = (FLYSContext) context;
+        }
+        else {
+            flysContext = (FLYSContext) context.globalContext();
+        }
+
+        StateEngine stateEngine = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        TransitionEngine transitionEngine = (TransitionEngine) flysContext.get(
+            FLYSContext.TRANSITION_ENGINE_KEY);
+
+        List<State> reachable = transitionEngine.getReachableStates(
+            this, getCurrentState(context), stateEngine);
+
+        Document description            = XMLUtils.newDocument();
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+            description,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element root = ProtocolUtils.createRootNode(creator);
+        description.appendChild(root);
+
+        State current = getCurrentState(context);
+
+        ProtocolUtils.appendDescribeHeader(creator, root, identifier(), hash());
+        ProtocolUtils.appendState(creator, root, current);
+        ProtocolUtils.appendReachableStates(creator, root, reachable);
+
+        Element name = ProtocolUtils.createArtNode(
+            creator, "name",
+            new String[] { "value" },
+            new String[] { getName() });
+
+        Element ui = ProtocolUtils.createArtNode(
+            creator, "ui", null, null);
+
+        Element staticUI  = ProtocolUtils.createArtNode(
+            creator, "static", null, null);
+
+        Element outs = ProtocolUtils.createArtNode(
+            creator, "outputmodes", null, null);
+        appendOutputModes(description, outs, context, identifier());
+
+        appendStaticUI(description, staticUI, context, identifier());
+
+        Element dynamic = current.describe(
+            this,
+            description,
+            root,
+            context,
+            identifier());
+
+        if (dynamic != null) {
+            ui.appendChild(dynamic);
+        }
+
+        ui.appendChild(staticUI);
+
+        root.appendChild(name);
+        root.appendChild(ui);
+        root.appendChild(outs);
+
+        return description;
+    }
+
+
+    /**
+     * Returns the name of the concrete artifact.
+     *
+     * @return the name of the concrete artifact.
+     */
+    public String getName() {
+        return ARTIFACT_NAME;
+    }
+
+
+    protected void appendOutputModes(
+        Document    doc,
+        Element     outs,
+        CallContext context,
+        String      uuid)
+    {
+        Vector<String> stateIds = getPreviousStateIds();
+
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        FLYSContext flysContext = getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        for (String stateId: stateIds) {
+            logger.debug("Append output modes for state: " + stateId);
+            State state = engine.getState(stateId);
+
+            List<Output> list = state.getOutputs();
+            if (list == null || list.size() == 0) {
+                continue;
+            }
+
+            ProtocolUtils.appendOutputModes(creator, outs, list);
+        }
+
+        try {
+            DefaultState cur = (DefaultState) getCurrentState(context);
+            if (cur.validate(this, context)) {
+                List<Output> list = cur.getOutputs();
+                if (list != null && list.size() > 0) {
+                    logger.debug(
+                        "Append output modes for state: " + cur.getID());
+
+                    ProtocolUtils.appendOutputModes(creator, outs, list);
+                }
+            }
+        }
+        catch (IllegalArgumentException iae) {
+            // state is not valid, so we do not append its outputs.
+        }
+    }
+
+
+    /**
+     * This method appends the static data - that has already been inserted by
+     * the user - to the static node of the DESCRIBE document.
+     *
+     * @param doc The document.
+     * @param ui The root node.
+     * @param context The CallContext.
+     * @param uuid The identifier of the artifact.
+     */
+    protected void appendStaticUI(
+        Document    doc,
+        Node        ui,
+        CallContext context,
+        String uuid)
+    {
+        Vector<String> stateIds = getPreviousStateIds();
+
+        FLYSContext flysContext = getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+
+        for (String stateId: stateIds) {
+            logger.debug("Append static data for state: " + stateId);
+            DefaultState state = (DefaultState) engine.getState(stateId);
+            state = (DefaultState) fillState(state);
+
+            ui.appendChild(state.describeStatic(doc, ui, context, uuid));
+        }
+    }
+
+
+    //
+    // METHODS FOR RETRIEVING COMPUTED DATA FOR DIFFERENT CHART TYPES
+    //
+
+    /**
+     * Returns the data that is computed by a waterlevel computation.
+     *
+     * @return an array of data triples that consist of W, Q and Kms.
+     */
+    public WQKms[] getWaterlevelData()
+    throws NullPointerException
+    {
+        logger.debug("WINFOArtifact.getWaterlevelData");
+
+        River river = getRiver();
+        if (river == null) {
+            throw new NullPointerException("No river selected.");
+        }
+
+        double[] kms = getKms();
+        if (kms == null) {
+            throw new NullPointerException("No Kms selected.");
+        }
+
+        double[] qs = getQs();
+        if (qs == null) {
+            logger.debug("Determine Q values based on a set of W values.");
+
+            double[] ws = getWs();
+            qs = getQsForWs(ws);
+        }
+
+        WstValueTable wst = WstValueTable.getTable(river);
+        if (wst == null) {
+            throw new NullPointerException("No Wst found for selected river.");
+        }
+
+        // TODO Introduce a caching mechanism here!
+
+        return computeWaterlevelData(kms, qs, wst);
+    }
+
+
+    /**
+     * Computes the data of a waterlevel computation based on the interpolation
+     * in WstValueTable.
+     *
+     * @param kms The kilometer values.
+     * @param qa The discharge values.
+     * @param wst The WstValueTable used for the interpolation.
+     *
+     * @return an array of data triples that consist of W, Q and Kms.
+     */
+    public static WQKms[] computeWaterlevelData(
+        double[]      kms,
+        double[]      qs,
+        WstValueTable wst)
+    {
+        logger.info("WINFOArtifact.computeWaterlevelData");
+
+        WQKms[] wqkms = new WQKms[qs.length];
+        for (int i = 0; i < wqkms.length; i++) {
+            wqkms[i] = new WQKms(kms.length);
+        }
+
+        double [] interpolatedW = new double[qs.length];
+
+        for (double km: kms) {
+            wst.interpolateW(km, qs, interpolatedW);
+
+            // TODO Modify the interpolation to return a better formed data
+            // structure.
+            for (int i = 0; i < interpolatedW.length; i++) {
+                wqkms[i].add(interpolatedW[i], qs[i], km);
+            }
+        }
+
+        return wqkms;
+    }
+
+
+    /**
+     * Returns the data that is computed by a duration curve computation.
+     *
+     * @return the data computed by a duration curve computation.
+     */
+    public WQDay getDurationCurveData()
+    throws NullPointerException
+    {
+        logger.debug("WINFOArtifact.getDurationCurveData");
+
+        River r = getRiver();
+
+        if (r == null) {
+            throw new NullPointerException("Cannot determine river.");
+        }
+
+        Gauge g = getGauge();
+
+        if (g == null) {
+            throw new NullPointerException("Cannot determine gauge.");
+        }
+
+        double[] locations = getLocations();
+
+        if (locations == null) {
+            throw new NullPointerException("Cannot determine location.");
+        }
+
+        WstValueTable wst = WstValueTable.getTable(r);
+        if (wst == null) {
+            throw new NullPointerException("No Wst found for selected river.");
+        }
+
+        // TODO Introduce a caching mechanism here!
+
+        return computeDurationCurveData(g, wst, locations[0]);
+    }
+
+
+    /**
+     * Computes the data used to create duration curves.
+     *
+     * @param gauge The selected gauge.
+     * @param location The selected location.
+     *
+     * @return the computed data.
+     */
+    public static WQDay computeDurationCurveData(
+        Gauge           gauge,
+        WstValueTable   wst,
+        double          location)
+    {
+        logger.info("WINFOArtifact.computeDurationCurveData");
+
+        Object[] obj = MainValuesFactory.getDurationCurveData(gauge);
+
+        int[]    days = (int[]) obj[0];
+        double[] qs   = (double[]) obj[1];
+
+        double[] interpolatedW = new double[qs.length];
+        interpolatedW          = wst.interpolateW(location, qs, interpolatedW);
+
+        WQDay wqday = new WQDay(qs.length);
+
+        for (int i = 0; i < days.length; i++) {
+            wqday.add(days[i], interpolatedW[i], qs[i]);
+        }
+
+        return wqday;
+    }
+
+
+    /**
+     * Returns the data that is computed by a discharge curve computation.
+     *
+     * @return the data computed by a discharge curve computation.
+     */
+    public WQKms getComputedDischargeCurveData()
+    throws NullPointerException
+    {
+        logger.debug("WINFOArtifact.getComputedDischargeCurveData");
+
+        River r = getRiver();
+
+        if (r == null) {
+            throw new NullPointerException("Cannot determine river.");
+        }
+
+        double[] locations = getLocations();
+
+        if (locations == null) {
+            throw new NullPointerException("Cannot determine location.");
+        }
+
+        WstValueTable wst = WstValueTable.getTable(r);
+        if (wst == null) {
+            throw new NullPointerException("No Wst found for selected river.");
+        }
+
+        // TODO Introduce a caching mechanism here!
+
+        return computeDischargeCurveData(wst, locations[0]);
+    }
+
+
+    /**
+     * Computes the data used to create computed discharge curves.
+     *
+     * @param wst The WstValueTable that is used for the interpolation.
+     * @param location The location where the computation should be based on.
+     *
+     * @return an object that contains tuples of W/Q values at the specified
+     * location.
+     */
+    public static WQKms computeDischargeCurveData(
+        WstValueTable wst,
+        double location)
+    {
+        logger.info("WINFOArtifact.computeDischargeCurveData");
+
+        double[][] wqs = wst.interpolateWQ(location);
+
+        if (wqs == null) {
+            logger.error("Cannot compute discharge curve data.");
+            return null;
+        }
+
+        double[] ws = wqs[0];
+        double[] qs = wqs[1];
+
+        WQKms wqkms = new WQKms(ws.length);
+
+        for (int i = 0; i < ws.length; i++) {
+            wqkms.add(ws[i], qs[i], location);
+        }
+
+        return wqkms;
+    }
+
+
+    /**
+     * Returns the data computed by the discharge longitudinal section
+     * computation.
+     *
+     * @return an array of WQKms object - one object for each given Q value.
+     */
+    public WQKms[] getDischargeLongitudinalSectionData() {
+        logger.debug("WINFOArtifact.getDischargeLongitudinalSectionData");
+
+        River river = getRiver();
+        if (river == null) {
+            logger.error("No river selected.");
+        }
+
+        WstValueTable wst = WstValueTable.getTable(river);
+        if (wst == null) {
+            logger.error("No Wst found for selected river.");
+        }
+
+        double[][] dist = getSplittedDistance();
+        int        num  = dist != null ? dist.length : 0;
+
+        WQKms[][] wqkms = new WQKms[num][];
+
+        for (int i = 0; i < num; i++) {
+            double[] kms = getKms(dist[i]);
+            if (kms == null) {
+                // XXX maybe we should cancel this operation here.
+                continue;
+            }
+
+            double[] qs = getQs(dist[i]);
+            if (qs == null) {
+                logger.debug("Determine Q values based on a set of W values.");
+
+                double[] ws = getWs(dist[i]);
+                qs = getQsForWs(ws);
+            }
+
+            wqkms[i] = computeWaterlevelData(kms, qs, wst);
+        }
+
+        WQKms[] merged    = WQKms.merge(wqkms);
+        int     numMerged = merged.length;
+        WQKms[] computed  = new WQKms[numMerged];
+
+        for (int i = 0; i < numMerged; i++) {
+            computed[i] = computeDischargeLongitudinalSectionData(merged[i]);
+        }
+
+        // TODO Introduce a caching mechanism here!
+
+        return computed;
+    }
+
+
+    /**
+     * Computes the data used for a discharge longitudinal section based on a
+     * given WQKms object. If there are backjumps while computing the data, a
+     * WQCKms object is returned, otherwise the incoming wqkms object.
+     *
+     * @param wqkms The WQKms object that contains W, Q and Kms.
+     *
+     * @return an instance of WQKms or WQCKms.
+     */
+    public static WQKms computeDischargeLongitudinalSectionData(WQKms wqkms) {
+        logger.info("WINFOArtifact.computeDischargeLongitudinalSectionData");
+
+        BackJumpCorrector bjc = new BackJumpCorrector();
+
+        bjc.doCorrection(wqkms.getKms(), wqkms.getWs());
+
+        if (bjc.hasBackJumps()) {
+            logger.info("Discharge longitudinal section has backjumps.");
+            return new WQCKms(
+                wqkms.getKms(),
+                wqkms.getQs(),
+                wqkms.getWs(),
+                bjc.getCorrected());
+        }
+        else {
+            logger.info("Discharge longitudinal section has no backjumps.");
+            return wqkms;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/WaterlevelState.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,29 @@
+package de.intevation.flys.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+
+public class WaterlevelState extends DefaultState {
+
+    /** The logger that is used in this state.*/
+    private static Logger logger = Logger.getLogger(WaterlevelState.class);
+
+
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("WaterlevelState.createItems");
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/cache/CacheFactory.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,61 @@
+package de.intevation.flys.artifacts.cache;
+
+import de.intevation.artifacts.common.utils.Config;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.CacheException;
+import net.sf.ehcache.CacheManager;
+
+import org.apache.log4j.Logger;
+
+public final class CacheFactory
+{
+    private static Logger log = Logger.getLogger(CacheFactory.class);
+
+    public static final String XPATH_CACHE_CONFIG_FILE =
+        "/artifact-database/cache/config-file/text()";
+
+    private CacheFactory() {
+    }
+
+    private static boolean      initialized;
+
+    private static CacheManager cacheManager;
+
+    public static final Cache getCache() {
+        return getCache(Cache.DEFAULT_CACHE_NAME);
+    }
+
+    public static final synchronized Cache getCache(String cacheName) {
+        if (!initialized) {
+            initialized = true; // try only once
+            String configFile = Config.getStringXPath(XPATH_CACHE_CONFIG_FILE);
+            if (configFile != null) {
+                configFile = Config.replaceConfigDir(configFile);
+                try {
+                    cacheManager = CacheManager.create(configFile);
+                    //System.setProperty(
+                    //  "net.sf.ehcache.enableShutdownHook", "true");
+                    Runtime.getRuntime().addShutdownHook(new Thread() {
+                        public void run() {
+                            log.info("shutting down caches");
+                            for (String name: cacheManager.getCacheNames()) {
+                                log.info("\tflushing '" + name + "'");
+                                cacheManager.getCache(name).flush();
+                            }
+                            cacheManager.shutdown();
+                        }
+                    });
+                }
+                catch (CacheException ce) {
+                    log.error("cannot configure cache", ce);
+                }
+            }
+        }
+
+        return cacheManager != null
+            ? cacheManager.getCache(cacheName)
+            : null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/FLYSContext.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,57 @@
+package de.intevation.flys.artifacts.context;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifactdatabase.DefaultArtifactContext;
+
+
+/**
+ * This class is used to store application wide information in a global context.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYSContext extends DefaultArtifactContext {
+
+    /** The logger used in this class */
+    private static Logger logger = Logger.getLogger(FLYSContext.class);
+
+    /** The key that is used to store the TransitionEngine in the context */
+    public static final String TRANSITION_ENGINE_KEY =
+        "artifact.transition.engine";
+
+    /** The key that is used to store the StateEngine in the context */
+    public static final String STATE_ENGINE_KEY =
+        "artifact.state.engine";
+
+    /** The key that is used to store the Map of OutGenerator classes in the
+     * context.*/
+    public static final String OUTGENERATORS_KEY =
+        "flys.export.outgenerators";
+
+    /** The key that is used to store the map of themes in the context.*/
+    public static final String THEMES =
+        "flys.themes.map";
+
+    /** The key that is used to store a map of theme mappings in the context.*/
+    public static final String THEME_MAPPING =
+        "flys.themes.mapping.map";
+
+
+    /**
+     * The default constructor.
+     */
+    public FLYSContext() {
+        super();
+    }
+
+
+    /**
+     * A constructor with a config document.
+     */
+    public FLYSContext(Document config) {
+        super(config);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/FLYSContextFactory.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,363 @@
+package de.intevation.flys.artifacts.context;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import de.intevation.artifacts.ArtifactContextFactory;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.Config;
+
+import de.intevation.artifactdatabase.state.State;
+import de.intevation.artifactdatabase.state.StateEngine;
+import de.intevation.artifactdatabase.transition.Transition;
+import de.intevation.artifactdatabase.transition.TransitionEngine;
+
+import de.intevation.flys.artifacts.states.StateFactory;
+import de.intevation.flys.artifacts.transitions.TransitionFactory;
+import de.intevation.flys.themes.Theme;
+import de.intevation.flys.themes.ThemeFactory;
+
+
+/**
+ * The ArtifactContextFactory is used to initialize basic components and put
+ * them into the global context of the application.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYSContextFactory implements ArtifactContextFactory {
+
+    /** The logger that is used in this class */
+    private static Logger logger = Logger.getLogger(FLYSContextFactory.class);
+
+    /** The XPath to the artifacts configured in the configuration. */
+    public static final String XPATH_ARTIFACTS =
+        "/artifact-database/artifacts/artifact";
+
+    /** The XPath to the name of the artifact. */
+    public static final String XPATH_ARTIFACT_NAME = "/artifact/@name";
+
+    /** The XPath to the xlink ref in an artifact configuration. */
+    public static final String XPATH_XLINK = "xlink:href";
+
+    /** The XPath to the transitions configured in the artifact config. */
+    public static final String XPATH_TRANSITIONS =
+        "/artifact/states/transition";
+
+    /** The XPath to the states configured in the artifact config. */
+    public static final String XPATH_STATES =
+        "/artifact/states/state";
+
+    public static final String XPATH_OUTPUT_GENERATORS =
+        "/artifact-database/output-generators/output-generator";
+
+    public static final String XPATH_THEME_CONFIG =
+        "/artifact-database/flys/themes/configuration/text()";
+
+    public static final String XPATH_THEMES =
+        "/themes/theme";
+
+    public static final String XPATH_THEME_MAPPINGS =
+        "/themes/mappings/mapping";
+
+    /**
+     * Creates a new FLYSArtifactContext object and initialize all
+     * components required by the application.
+     *
+     * @param config The artifact server configuration.
+     * @return a FLYSArtifactContext.
+     */
+    public Object createArtifactContext(Document config) {
+        FLYSContext context = new FLYSContext(config);
+
+        configureTransitions(config, context);
+        configureStates(config, context);
+        configureOutGenerators(config, context);
+        configureThemes(config, context);
+        configureThemesMappings(config, context);
+
+        return context;
+    }
+
+
+    /**
+     * This method initializes the transition configuration.
+     *
+     * @param config the config document.
+     * @param context the FLYSContext.
+     */
+    protected void configureTransitions(Document config, FLYSContext context) {
+        TransitionEngine engine = new TransitionEngine();
+
+        Document[] artifacts = getArtifactConfigurations(config);
+        logger.info("Found " + artifacts.length + " artifacts in the config.");
+
+        for (Document doc: artifacts) {
+            List<Transition> transitions = new ArrayList<Transition>();
+
+            String artName = (String) XMLUtils.xpath(
+                doc, XPATH_ARTIFACT_NAME, XPathConstants.STRING);
+
+            NodeList list = (NodeList) XMLUtils.xpath(
+                doc, XPATH_TRANSITIONS, XPathConstants.NODESET);
+
+            if (list == null) {
+                logger.warn("The artifact has no transitions configured.");
+                continue;
+            }
+
+            int trans = list.getLength();
+
+            logger.info(
+                "Artifact '" + artName + "' has " + trans + " transitions.");
+
+            for (int i = 0; i < trans; i++) {
+                Transition t = TransitionFactory.createTransition(list.item(i));
+                String     s = t.getFrom();
+                engine.addTransition(s, t);
+            }
+        }
+
+        context.put(FLYSContext.TRANSITION_ENGINE_KEY, engine);
+    }
+
+
+    /**
+     * This method returns all artifact documents defined in
+     * <code>config</code>. <br>NOTE: The artifact configurations need to be
+     * stored in own files referenced by an xlink.
+     *
+     * @param config The global configuration.
+     *
+     * @return an array of Artifact configurations.
+     */
+    protected Document[] getArtifactConfigurations(Document config) {
+        NodeList artifacts = (NodeList) XMLUtils.xpath(
+            config, XPATH_ARTIFACTS, XPathConstants.NODESET);
+
+        int count = artifacts.getLength();
+
+        Document[] artifactDocs = new Document[count];
+
+        for (int i = 0; i < count; i++) {
+            Element tmp = (Element) artifacts.item(i);
+
+            String xlink = tmp.getAttribute(XPATH_XLINK);
+            xlink        = Config.replaceConfigDir(xlink);
+
+            File artifactFile = new File(xlink);
+            artifactDocs[i]   = XMLUtils.parseDocument(artifactFile);
+        }
+
+        return artifactDocs;
+    }
+
+
+    /**
+     * This method initializes the transition configuration.
+     *
+     * @param config the config document.
+     * @param context the FLYSContext.
+     */
+    protected void configureStates(Document config, FLYSContext context) {
+        StateEngine engine = new StateEngine();
+
+        Document[] artifacts = getArtifactConfigurations(config);
+        logger.info("Found " + artifacts.length + " artifacts in the config.");
+
+        for (Document doc: artifacts) {
+            List<State> states = new ArrayList<State>();
+
+            String artName = (String) XMLUtils.xpath(
+                doc, XPATH_ARTIFACT_NAME, XPathConstants.STRING);
+
+            NodeList stateList = (NodeList) XMLUtils.xpath(
+                doc, XPATH_STATES, XPathConstants.NODESET);
+
+            if (stateList == null) {
+                logger.warn("The artifact has no states configured.");
+                continue;
+            }
+
+            int count = stateList.getLength();
+
+            logger.info(
+                "Artifact '" + artName + "' has " + count + " states.");
+
+            for (int i = 0; i < count; i++) {
+                states.add(StateFactory.createState(
+                    stateList.item(i)));
+            }
+
+            engine.addStates(artName, states);
+        }
+
+        context.put(FLYSContext.STATE_ENGINE_KEY, engine);
+    }
+
+
+    /**
+     * This method intializes the provided output generators.
+     *
+     * @param config the config document.
+     * @param context the FLYSContext.
+     */
+    protected void configureOutGenerators(Document config, FLYSContext context){
+        Map<String, Class> generators = new HashMap<String, Class>();
+
+        NodeList outGenerators = (NodeList) XMLUtils.xpath(
+            config,
+            XPATH_OUTPUT_GENERATORS,
+            XPathConstants.NODESET);
+
+        int num = outGenerators == null ? 0 : outGenerators.getLength();
+
+        if (num == 0) {
+            logger.warn("No output generators configured in this application.");
+            return;
+        }
+
+        logger.info("Found " + num + " configured output generators.");
+
+        int idx = 0;
+
+        for (int i = 0; i < num; i++) {
+            Node item = outGenerators.item(i);
+
+            String name = (String) XMLUtils.xpath(
+                item, "@name", XPathConstants.STRING);
+
+            String clazz = (String) XMLUtils.xpath(
+                item, "text()", XPathConstants.STRING);
+
+            if (name == null || clazz == null) {
+                continue;
+            }
+
+            try {
+                generators.put(name, Class.forName(clazz));
+
+                idx++;
+            }
+            catch (ClassNotFoundException cnfe) {
+                logger.warn(cnfe, cnfe);
+            }
+        }
+
+        logger.info("Successfully loaded " + idx + " output generators.");
+        context.put(FLYSContext.OUTGENERATORS_KEY, generators);
+    }
+
+
+    /**
+     * This methods reads the configured themes and puts them into the
+     * FLYSContext.
+     *
+     * @param config The global configuration.
+     * @param context The FLYSContext.
+     */
+    protected void configureThemes(Document config, FLYSContext context) {
+        logger.debug("FLYSContextFactory.configureThemes");
+
+        Document cfg = getThemeConfig(config);
+
+        NodeList themes = (NodeList) XMLUtils.xpath(
+            cfg, XPATH_THEMES, XPathConstants.NODESET);
+
+        int num = themes != null ? themes.getLength() : 0;
+
+        if (num == 0) {
+            logger.warn("There are no themes configured!");
+            return;
+        }
+
+        logger.debug("Found " + num + " configured themes.");
+
+        Map<String, Theme> theThemes = new HashMap<String, Theme>();
+
+        for (int i = 0; i < num; i++) {
+            Node theme = themes.item(i);
+
+            Theme theTheme = ThemeFactory.createTheme(cfg, theme);
+
+            if (theme != null) {
+                theThemes.put(theTheme.getName(), theTheme);
+            }
+        }
+
+        logger.debug("Initialized " + theThemes.size() + "/" + num + " themes");
+
+        context.put(FLYSContext.THEMES, theThemes);
+    }
+
+
+    /**
+     * This method is used to retrieve the theme configuration document.
+     *
+     * @param config The global configuration.
+     *
+     * @return the theme configuration.
+     */
+    protected Document getThemeConfig(Document config) {
+        String themeConfig = (String) XMLUtils.xpath(
+            config,
+            XPATH_THEME_CONFIG,
+            XPathConstants.STRING);
+
+        themeConfig = Config.replaceConfigDir(themeConfig);
+
+        logger.debug("Parse theme cfg: " + themeConfig);
+
+        return XMLUtils.parseDocument(new File(themeConfig));
+    }
+
+
+    protected void configureThemesMappings(Document cfg, FLYSContext context) {
+        logger.debug("FLYSContextFactory.configureThemesMappings");
+
+        Document config = getThemeConfig(cfg);
+
+        NodeList mappings = (NodeList) XMLUtils.xpath(
+            config, XPATH_THEME_MAPPINGS, XPathConstants.NODESET);
+
+        int num = mappings != null ? mappings.getLength() : 0;
+
+        if (num == 0) {
+            logger.warn("No theme <--> facet mappins found!");
+            return;
+        }
+
+        Map<String, String> mapping = new HashMap<String, String>();
+
+        for (int i = 0; i < num; i++) {
+            Node node = mappings.item(i);
+
+            String from = (String) XMLUtils.xpath(
+                node, "@from", XPathConstants.STRING);
+
+            String to = (String) XMLUtils.xpath(
+                node, "@to", XPathConstants.STRING);
+
+            if (from != null && to != null) {
+                mapping.put(from, to);
+            }
+        }
+
+        logger.debug("Found " + mapping.size() + " theme/facet mappings.");
+
+        context.put(FLYSContext.THEME_MAPPING, mapping);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/context/SessionCallContextListener.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,68 @@
+package de.intevation.flys.artifacts.context;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import org.hibernate.Session;
+
+import de.intevation.flys.backend.SessionHolder;
+
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallContext.Listener;
+
+
+/**
+ * This CallContextListener is used to initialize a ThreadLocal variable in
+ * each CallContext (for each request) that holds Sessions.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SessionCallContextListener implements Listener {
+
+    public static final String SESSION_KEY = "context.session";
+
+    /** The logger that is used in this class.*/
+    private static Logger logger =
+        Logger.getLogger(SessionCallContextListener.class);
+
+
+    public SessionCallContextListener() {
+    }
+
+
+    public void setup(Document config, Node listenerConfig) {
+        // nothing to do here
+    }
+
+
+    /**
+     * Initializes a ThreadLocal variable that is used to hold sessions.
+     *
+     * @param context The CallContext.
+     */
+    public void init(CallContext context) {
+        logger.debug("SessionCallContextListener.init");
+
+        Session session = SessionHolder.acquire();
+
+        context.putContextValue(SESSION_KEY, session);
+    }
+
+
+    /**
+     * Closes open sessions of the ThreadLocal variable opened in init().
+     *
+     * @param context The CallContext.
+     */
+    public void close(CallContext context) {
+        logger.debug("SessionCallContextListener.close");
+
+        Session session = (Session)context.getContextValue(SESSION_KEY);
+        session.close();
+
+        SessionHolder.release();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,228 @@
+package de.intevation.flys.artifacts.math;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import java.io.Serializable;
+
+import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
+
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+
+import org.apache.commons.math.ArgumentOutsideDomainException;
+
+import org.apache.log4j.Logger;
+
+public class BackJumpCorrector
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(BackJumpCorrector.class);
+
+    protected ArrayList<Double> backjumps;
+
+    protected double [] corrected;
+
+    public BackJumpCorrector() {
+        backjumps = new ArrayList<Double>();
+    }
+
+    public boolean hasBackJumps() {
+        return !backjumps.isEmpty();
+    }
+
+    public List<Double> getBackJumps() {
+        return backjumps;
+    }
+
+    public double [] getCorrected() {
+        return corrected;
+    }
+
+    private static final boolean isIncreasing(double [] ws) {
+        int inc = 0;
+        int dec = 0;
+        int sweet = (ws.length-1)/2;
+        for (int i = 1; i < ws.length; ++i) {
+            if (ws[i] > ws[i-1]) {
+                if (++inc > sweet) {
+                    return true;
+                }
+            }
+            else if (++dec > sweet) {
+                return false;
+            }
+        }
+        return inc > sweet;
+    }
+
+    public boolean doCorrection(double [] km, double [] ws) {
+        boolean isIncreasing = isIncreasing(ws);
+
+        if (isIncreasing) {
+            // mirror along x axis to simulate decreasing values
+            ws = (double [])ws.clone();
+            for (int i = 0; i < ws.length; ++i) {
+                ws[i] = -ws[i];
+            }
+        }
+
+        boolean hasBackJumps = doCorrectionClean(km, ws);
+
+        if (hasBackJumps && isIncreasing) {
+            // mirror back
+            for (int i = 0; i < corrected.length; ++i) {
+                corrected[i] = -corrected[i];
+            }
+        }
+
+        return hasBackJumps;
+    }
+
+    protected boolean doCorrectionClean(double [] km, double [] ws) {
+        int N = km.length;
+
+        if (N != ws.length) {
+            throw new IllegalArgumentException("km.length != ws.length");
+        }
+
+        if (N < 2) {
+            return false;
+        }
+
+        SplineInterpolator interpolator = null;
+
+        for (int i = 1; i < N; ++i) {
+            if (ws[i] <= ws[i-1]) {
+                // no back jump
+                continue;
+            }
+            backjumps.add(km[i]);
+
+            if (corrected == null) {
+                // lazy cloning
+                ws = corrected = (double [])ws.clone();
+            }
+
+            int    back = i-2;
+            double distance = Math.abs(km[i] - km[i-1]);
+            double rest = 0.0;
+            double ikm  = 0.0;
+
+            while (back > -1) {
+                if (ws[back] < ws[i]) { // under water
+                    // continue scanning backwards
+                    distance += Math.abs(km[back+1] - km[back]);
+                    --back;
+                    continue;
+                }
+                if (ws[back] > ws[i]) { // over water
+                    // need to calculate intersection
+                    log.debug("intersection case");
+                    double m = (km[back+1]-km[back])/(ws[back+1]-ws[back]);
+                    double b = km[back]-ws[back]*m;
+                    ikm = m*ws[i] + b;
+                    distance += Math.abs(ikm - km[back+1]);
+                    rest = Math.abs(km[back] - ikm);
+                }
+                else {
+                    // equals height at ws[back]
+                    log.debug("exact height match");
+                    distance += Math.abs(km[back+1] - km[back]);
+                    ikm = km[back];
+                }
+                break;
+            }
+
+            if (back < 0) {
+                log.debug("run over left border");
+                // fill with same as ws[i]
+                for (int j = 0; j < i; ++j) {
+                    ws[j] = ws[i];
+                }
+                continue;
+            }
+
+            double quarterDistance = 0.25*distance;
+
+            // Now further seek back for the max height
+
+            double restDistance = Math.max(0.0, quarterDistance - rest);
+            --back;
+
+            double mkmw = ws[i];
+            double mkm  = km[0];
+
+            while (back > -1) {
+                double d = Math.abs(km[back+1] - km[back]);
+                restDistance -= d;
+                if (restDistance > 0.0) {
+                    --back;
+                    continue;
+                }
+                if (restDistance < 0.0) {
+                    // need to calculate intersection
+                    if (km[back] == km[back+1]) { // should not happen
+                        mkm = km[back];
+                        mkmw = 0.5*(ws[back] + ws[back+1]);
+                    }
+                    else {
+                        double m = (ws[back+1]-ws[back])/(km[back+1]-km[back]);
+                        double b = ws[back] - km[back]*m;
+                        mkm = km[back] + restDistance;
+                        mkmw = m*mkm + b;
+                    }
+                }
+                else {
+                    // exact match
+                    mkm  = km[back];
+                    mkmw = ws[back];
+                }
+                break;
+            }
+
+            double factor = back >= 0 && Math.abs(restDistance) < 1e-4
+                ? 1.0
+                : 1.0 - Math.min(1, Math.max(0, restDistance/quarterDistance));
+
+            double ikmw = factor*0.25*(mkmw-ws[i]) + ws[i];
+
+            double end = ikm + quarterDistance*factor;
+
+            double [] x = { mkm,  ikm,  end   };
+            double [] y = { mkmw, ikmw, ws[i] };
+
+            if (interpolator == null) {
+                interpolator = new SplineInterpolator();
+            }
+
+            PolynomialSplineFunction spline = interpolator.interpolate(x, y);
+
+            try {
+                if (log.isDebugEnabled()) {
+                    log.debug("spline points:");
+                    for (int j = 0; j < x.length; ++j) {
+                        log.debug(x[j] + " " + y[j] + " " + spline.value(x[j]));
+                    }
+                }
+
+                for (back = Math.max(back, 0);
+                     back < i && km[back] < end; 
+                     ++back
+                ) {
+                    // to 3/4 point fill with spline values
+                    ws[back] = spline.value(km[back]);
+                }
+                while (back < i) {
+                    // fill the rest with ws[i]
+                    ws[back++] = ws[i];
+                }
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                log.error("spline interpolation failed", aode);
+            }
+        } // for all km
+
+        return !backjumps.isEmpty();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,78 @@
+package de.intevation.flys.artifacts.math;
+
+public class LinearRemap
+{
+    public static class Segment {
+
+        protected Segment next;
+
+        protected double from;
+        protected double to;
+
+        protected double m;
+        protected double b;
+
+        public Segment() {
+        }
+
+        public Segment(
+            double from, double to, 
+            double m,    double b,
+            Segment next
+        ) {
+            this.from = from;
+            this.to   = to;
+            this.m    = m;
+            this.b    = b;
+        }
+
+        public double eval(double x) {
+            return m*x + b;
+        }
+    } // class Segment
+
+    protected Segment head;
+
+    public LinearRemap() {
+    }
+
+    public void add(
+        double from, double to,
+        double x1,   double y1,
+        double x2,   double y2
+    ) {
+        // y1 = m*x1 + b <=> b = y1 - m*x1
+        // y2 = m*x2 + b
+        // y2 - y1 = m*(x2 - x1)
+        // m = (y2 - y1)/(x2 - x1)
+
+        double m, b;
+
+        if (x2 == x1) {
+            m = 0.0;
+            b = 0.5*(y2 + y1);
+        }
+        else {
+            m = (y2 - y1)/(x2 - x1);
+            b = y1 - m*x1;
+        }
+
+        head = new Segment(from, to, m, b, head);
+    }
+
+    public double eval(double pos, double x) {
+        Segment current = head;
+
+        while (current != null) {
+
+            if (pos >= current.from && pos <= current.to) {
+                return current.eval(x);
+            }
+
+            current = current.next;
+        }
+
+        return Double.NaN;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,37 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+
+import de.intevation.flys.backend.SessionHolder;
+import de.intevation.flys.model.Annotation;
+import de.intevation.flys.model.Range;
+import de.intevation.flys.model.River;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class AnnotationsFactory {
+
+    public static List<Annotation> getAnnotations(River river) {
+        return getAnnotations(river.getName());
+    }
+
+
+    public static List<Annotation> getAnnotations(String river) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query rangesQuery = session.createQuery(
+            "from Range where river.name=:name");
+        rangesQuery.setParameter("name", river);
+        List<Range> ranges = rangesQuery.list();
+
+        Query query = session.createQuery(
+            "from Annotation where range in (:ranges) order by range.a");
+        query.setParameterList("ranges", ranges);
+        return query.list();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,234 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import java.io.Serializable;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.backend.SessionHolder;
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.DischargeTable;
+import de.intevation.flys.model.DischargeTableValue;
+
+public class DischargeTables
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(DischargeTables.class);
+
+    public static final double DEFAULT_SCALE = 100.0;
+
+    public static final int MASTER = 0;
+
+    protected List<String> gaugeNames;
+
+    protected String riverName;
+
+    protected double scale;
+
+    protected int    kind;
+
+    protected Map<String, double [][]> values;
+
+    public DischargeTables() {
+    }
+
+    public DischargeTables(String riverName, String gaugeName) {
+        this(riverName, gaugeName, MASTER);
+    }
+
+    public DischargeTables(String riverName, String gaugeName, int kind) {
+        this(riverName, new String [] { gaugeName }, kind);
+    }
+
+    public DischargeTables(String riverName, String [] gaugeNames) {
+        this(riverName, gaugeNames, MASTER);
+    }
+
+    public DischargeTables(String riverName, String [] gaugeNames, int kind) {
+        this(riverName, Arrays.asList(gaugeNames), kind);
+    }
+
+    public DischargeTables(
+        String       riverName,
+        List<String> gaugeNames,
+        int          kind
+    ) {
+        scale           = Double.NaN;
+        this.kind       = kind;
+        this.riverName  = riverName;
+        this.gaugeNames = gaugeNames;
+    }
+
+    public double [][] getFirstTable() {
+        return getFirstTable(DEFAULT_SCALE);
+    }
+
+    public double [][] getFirstTable(double scale) {
+        Map<String, double [][]> values = getValues(scale);
+        for (double [][] table: values.values()) {
+            return table;
+        }
+        return null;
+    }
+
+    public Map<String, double [][]> getValues() {
+        return getValues(DEFAULT_SCALE);
+    }
+
+    public Map<String, double [][]> getValues(double scale) {
+        if (values == null || scale != this.scale) {
+            values = loadValues(scale);
+            this.scale = scale;
+        }
+        return values;
+    }
+
+    protected Map<String, double [][]> loadValues(double scale) {
+        Map<String, double [][]> values = new HashMap<String, double [][]>();
+
+        Session session = SessionHolder.HOLDER.get();
+
+        Query gaugeQuery = session.createQuery(
+            "from Gauge where name=:gauge and river.name=:river");
+        gaugeQuery.setParameter("river", riverName);
+
+        for (String gaugeName: gaugeNames) {
+            gaugeQuery.setParameter("gauge", gaugeName);
+            List<Gauge> gauges = gaugeQuery.list();
+            if (gauges.isEmpty()) {
+                log.warn(
+                    "no gauge '"+gaugeName+"' at river '"+riverName+"'");
+                continue;
+            }
+            Gauge gauge = gauges.get(0);
+
+            List<DischargeTable> tables = gauge.getDischargeTables();
+
+            if (tables.isEmpty()) {
+                log.warn(
+                    "no discharge table for gauge '" + gaugeName + "'");
+                continue;
+            }
+
+            // TODO: Filter by time interval
+            DischargeTable table = tables.get(0);
+
+            List<DischargeTableValue> dtvs =
+                table.getDischargeTableValues();
+
+            final double [][] vs = new double[2][dtvs.size()];
+
+            boolean qSorted = true;
+
+            double lastQ = -Double.MAX_VALUE;
+
+            int idx = 0;
+            for (DischargeTableValue dtv: dtvs) {
+                double q = dtv.getQ().doubleValue();
+                vs[0][idx] = q * scale;
+                vs[1][idx] = dtv.getW().doubleValue() * scale;
+                ++idx;
+
+                if (qSorted && lastQ > q) {
+                    qSorted = false;
+                }
+                lastQ = q;
+            }
+
+            if (!qSorted) {
+                log.debug("need to sort by q values");
+                // TODO: Do this db level.
+                // XXX: This is so ugly :-(
+                Integer [] indices = new Integer[vs[0].length];
+                for (int i = 0; i < indices.length; ++i) {
+                    indices[i] = i;
+                }
+
+                Arrays.sort(indices, new Comparator<Integer>() {
+                    public int compare(Integer a, Integer b) {
+                        double va = vs[1][a];
+                        double vb = vs[1][b];
+                        double d = va - vb;
+                        if (d < 0.0) return -1;
+                        if (d > 0.0) return +1;
+                        return 0;
+                    }
+                });
+
+                double [][] vs2 = new double[2][vs[0].length];
+                for (int i = 0; i < indices.length; ++i) {
+                    vs2[0][i] = vs[0][indices[i]];
+                    vs2[1][i] = vs[1][indices[i]];
+                }
+                values.put(gaugeName, vs2);
+            }
+            else {
+                values.put(gaugeName, vs);
+            }
+
+        }
+
+        return values;
+    }
+
+    public static double getQForW(double [][] values, double w) {
+
+        boolean debug = log.isDebugEnabled();
+
+        if (debug) {
+            log.debug("calculating getQForW(" + w + ")");
+        }
+
+        int index = Arrays.binarySearch(values[0], w);
+        if (index >= 0) {
+            return values[0][index];
+        }
+
+        index = -index - 1; // insert position
+
+        if (index < 1 || index >= values[0].length) {
+            // do not extraploate
+            if (debug) {
+                log.debug("we do not extrapolate: NaN");
+            }
+            return Double.NaN;
+        }
+
+        double w1 = values[0][index-1];
+        double w2 = values[0][index  ];
+        double q1 = values[1][index-1];
+        double q2 = values[1][index  ];
+
+        // q1 = m*w1 + b
+        // q2 = m*w2 + b
+        // q2 - q1 = m*(w2 - w1)
+        // m = (q2 - q1)/(w2 - w1) # w2 != w1
+        // b = q1 - m*w1
+
+        double q;
+        if (w1 == w2) {
+            q = 0.5*(q1 + q2);
+            if (debug) {
+                log.debug("same w1 and w1: " + w1);
+            }
+        }
+        else {
+            double m = (q2 - q1)/(w2 - w1);
+            double b = q1 - m*w1;
+            q = w*m + b;
+        }
+        if (debug) {
+            log.debug("Q(" + w + ") = " + q);
+        }
+        return q;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/GaugesFactory.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,57 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import de.intevation.flys.backend.SessionHolder;
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.Range;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+public class GaugesFactory
+{
+    public static List<Gauge> getGauges(River river) {
+        return getGauges(river.getName());
+    }
+
+    public static List<Gauge> getGauges(String river) {
+        Session session = SessionHolder.HOLDER.get();
+        Query query = session.createQuery(
+            "from Gauge where river.name=:name");
+        query.setParameter("name", river);
+        return query.list();
+    }
+
+    public static List<Gauge> filterRanges(
+        List<Gauge>     gauges,
+        List<double []> ranges
+    ) {
+        // XXX: Inefficent!
+        ArrayList<Range> rs = new ArrayList<Range>();
+        for (double [] range: ranges) {
+            double a = range[0];
+            double b = range[1];
+            rs.add(new Range(Math.min(a, b), Math.max(a, b), null));
+        }
+        return filter(gauges, rs);
+    }
+
+    public static List<Gauge> filter(List<Gauge> gauges, List<Range> ranges) {
+        // TODO: Make it an HQL filter!
+        ArrayList<Gauge> out = new ArrayList<Gauge>();
+        for (Gauge gauge: gauges) {
+            Range range = gauge.getRange();
+            for (Range cmp: ranges) {
+                if (range.intersects(cmp)) {
+                    out.add(gauge);
+                    break;
+                }
+            }
+        }
+        return out;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/MainValuesFactory.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,80 @@
+package de.intevation.flys.artifacts.model;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+import de.intevation.flys.backend.SessionHolder;
+
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.MainValue;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MainValuesFactory {
+
+    private static Logger logger = Logger.getLogger(MainValuesFactory.class);
+
+    public static List<MainValue> getMainValues(Gauge gauge) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from MainValue where gauge=:gauge");
+        query.setParameter("gauge", gauge);
+
+        return query.list();
+    }
+
+
+    /**
+     * Returns an array of [days, qs] necessary to create duration curves.
+     *
+     * @param gauge The selected gauge.
+     *
+     * @return a 2dim array of [days, qs] where days is an int[] and qs is
+     * an double[].
+     */
+    public static Object[] getDurationCurveData(Gauge gauge) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "select cast(nmv.name as integer) as days, mv.value as q " +
+            "from MainValue as mv " +
+            "join mv.mainValue as nmv " +
+            "join nmv.type mvt " +
+            "where mvt.name = 'D' and mv.gauge.id = :gauge_id " +
+            "order by days");
+
+        query.setParameter("gauge_id", gauge.getId());
+
+        List<Object> results = query.list();
+        int[]        days    = new int[results.size()];
+        double[]     qs      = new double[results.size()];
+
+        int idx = 0;
+
+        for (Object obj: results) {
+            Object[] arr = (Object[]) obj;
+
+            try {
+                int  day = ((Integer)    arr[0]).intValue();
+                double q = ((BigDecimal) arr[1]).doubleValue();
+
+                days[idx] = day;
+                qs[idx++] = q;
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        return new Object[] { days, qs };
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,57 @@
+package de.intevation.flys.artifacts.model;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+
+
+public class ManagedFacet extends DefaultFacet {
+
+    /** The uuid of the owner artifact.*/
+    protected String uuid;
+
+    /** A property that determines the position of this facet.*/
+    protected int position;
+
+    /** A property that determines if this facet is active or not.*/
+    protected int active;
+
+
+    public ManagedFacet(
+        String  name,
+        String  desc,
+        String  uuid,
+        int     pos,
+        int     active)
+    {
+        super(name, desc);
+
+        this.uuid     = uuid;
+        this.position = pos;
+        this.active   = active;
+    }
+
+
+    public void setPosition(int pos) {
+        this.position = pos;
+    }
+
+
+    public int getPosition() {
+        return position;
+    }
+
+
+    public void setActive(int active) {
+        this.active = active;
+    }
+
+
+    public int getActive() {
+        return active;
+    }
+
+
+    public String getArtifact() {
+        return uuid;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RangeWithValues.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,41 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class RangeWithValues implements Serializable {
+
+    protected double   lower;
+    protected double   upper;
+    protected double[] values;
+
+
+    public RangeWithValues() {
+    }
+
+
+    public RangeWithValues(double lower, double upper, double[] values) {
+        this.lower  = lower;
+        this.upper  = upper;
+        this.values = values;
+    }
+
+
+    public double getLower() {
+        return lower;
+    }
+
+
+    public double getUpper() {
+        return upper;
+    }
+
+
+    public double[] getValues() {
+        return values;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/RiverFactory.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,52 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+
+import de.intevation.flys.backend.SessionHolder;
+import de.intevation.flys.model.River;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class RiverFactory {
+
+    /** We don't need to instantiate concrete objects of this class. */
+    private RiverFactory() {
+    }
+
+
+    /**
+     * Returns all rivers that were found in the backend.
+     *
+     * @return all rivers.
+     */
+    public static List<River> getRivers() {
+        Session session = SessionHolder.HOLDER.get();
+        return (List<River>)session
+            .createQuery("from River order by name").list();
+    }
+
+
+    /**
+     * Returns a River object fetched from database based on its name.
+     *
+     * @param river The name of a river.
+     *
+     * @return the River object.
+     */
+    public static River getRiver(String river) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from River where name =:name");
+        query.setParameter("name", river);
+
+        List<River> rivers = query.list();
+
+        return (rivers != null && rivers.size() > 0) ? rivers.get(0) : null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQCKms.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,70 @@
+package de.intevation.flys.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+
+/**
+ * This class represents a pool of data triples that consists of 'W', 'Q' and
+ * 'KM' data with corrected 'W' values computed by a BackJumpCorrector.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQCKms extends WQKms {
+
+    protected TDoubleArrayList cw;
+
+
+    public WQCKms(double[] kms, double[] qs, double[] ws, double[] cws) {
+        super(kms, qs, ws);
+
+        this.cw = new TDoubleArrayList(cws);
+    }
+
+
+    /**
+     * Adds a new row to this data pool with corrected W.
+     *
+     * @param w a W.
+     * @param q a Q.
+     * @param kms a Kms.
+     * @param cw The corrected W.
+     */
+    public void add(double w, double q, double kms, double cw) {
+        add(w, q, kms);
+        this.cw.add(cw);
+    }
+
+
+    /**
+     * This method returns a 4dim array of W, Q,Kms and corrected W.
+     *
+     * @param idx The position of the triple.
+     * @param dst destination array
+     *
+     * @return a 4dim array of [W, Q, Kms, CW] in dst.
+     */
+    public double[] get(int idx, double[] dst) {
+        dst = super.get(idx, dst);
+
+        if (dst.length < 4) {
+            return dst;
+        }
+
+        if (cw != null && cw.size() > idx) {
+            dst[3] = cw.get(idx);
+        }
+
+        return dst;
+    }
+
+
+    /**
+     * Returns the double array of corrected W values.
+     *
+     * @return the double array of corrected W values.
+     */
+    public double[] getCWs() {
+        return cw.toNativeArray();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQDay.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,64 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+import gnu.trove.TDoubleArrayList;
+import gnu.trove.TIntArrayList;
+
+
+/**
+ * This class represents a pool of data triples that consists of 'W', 'Q' and
+ * 'Day' data.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQDay implements Serializable {
+
+    protected TIntArrayList days;
+
+    protected TDoubleArrayList ws;
+
+    protected TDoubleArrayList qs;
+
+
+    public WQDay() {
+        days = new TIntArrayList();
+        ws   = new TDoubleArrayList();
+        qs   = new TDoubleArrayList();
+    }
+
+
+    public WQDay(int capacity) {
+        days = new TIntArrayList(capacity);
+        ws   = new TDoubleArrayList(capacity);
+        qs   = new TDoubleArrayList(capacity);
+    }
+
+
+    public void add(int day, double w, double q) {
+        days.add(day);
+        ws.add(w);
+        qs.add(q);
+    }
+
+
+    public int size() {
+        return days.size();
+    }
+
+
+    public int getDay(int idx) {
+        return days.get(idx);
+    }
+
+
+    public double getW(int idx) {
+        return ws.get(idx);
+    }
+
+
+    public double getQ(int idx) {
+        return qs.get(idx);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WQKms.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,158 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * This class represents a pool of data triples that consists of 'W', 'Q' and
+ * 'KM' data.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQKms implements Serializable {
+
+    private static Logger logger = Logger.getLogger(WQKms.class);
+
+
+    /** The array that contains the 'W' values.*/
+    protected TDoubleArrayList w;
+
+    /** The array that contains the 'Q' values.*/
+    protected TDoubleArrayList q;
+
+    /** The array that contains the 'KMs' values.*/
+    protected TDoubleArrayList kms;
+
+
+    public WQKms() {
+        this.w   = new TDoubleArrayList();
+        this.q   = new TDoubleArrayList();
+        this.kms = new TDoubleArrayList();
+    }
+
+    public WQKms(int capacity) {
+        this.w   = new TDoubleArrayList(capacity);
+        this.q   = new TDoubleArrayList(capacity);
+        this.kms = new TDoubleArrayList(capacity);
+    }
+
+
+    public WQKms(double[] kms, double[] qs, double[] ws) {
+        this.w   = new TDoubleArrayList(ws);
+        this.q   = new TDoubleArrayList(qs);
+        this.kms = new TDoubleArrayList(kms);
+    }
+
+
+    /**
+     * Adds a new row to this data pool.
+     *
+     * @param w a W.
+     * @param q a Q.
+     * @param kms a Kms.
+     */
+    public void add(double w, double q, double kms) {
+        this.w.add(w);
+        this.q.add(q);
+        this.kms.add(kms);
+    }
+
+
+    public void add(double[] w, double[] q, double[] kms) {
+        this.w.add(w);
+        this.q.add(q);
+        this.kms.add(kms);
+    }
+
+
+    /**
+     * Returns the number of triples stored in this data pool.
+     *
+     * @return the number of triples stored in this data pool.
+     */
+    public int size() {
+        return kms.size();
+    }
+
+    /**
+     * This method returns a triple of W, Q and Kms in a single 3dim array.
+     *
+     * @param idx The position of the triple.
+     * @param dst destination array
+     *
+     * @return a triple of [W, Q, Kms] in dst.
+     */
+    public double[] get(int idx, double [] dst) {
+        dst[0] = w  .get(idx);
+        dst[1] = q  .get(idx);
+        dst[2] = kms.get(idx);
+        return dst;
+    }
+
+
+    public double getKms(int idx) {
+        return kms.get(idx);
+    }
+
+
+    public double[] getKms() {
+        return kms.toNativeArray();
+    }
+
+
+    public double[] getWs() {
+        return w.toNativeArray();
+    }
+
+
+    public double[] getQs() {
+        return q.toNativeArray();
+    }
+
+
+    /**
+     * Returns a string that consist of the first and last kilometer.
+     *
+     * @return a string that consist of the first and last kilometer.
+     */
+    public String toString() {
+        double from = getKms(0);
+        double to   = getKms(size()-1);
+        return Double.toString(from) + " - " + Double.toString(to);
+    }
+
+
+    /**
+     * Merges the WQKms objects of an incoming 2dim array (table) where the
+     * objects of a column belong together.
+     *
+     * @param toMerge The objects that need to be merged.
+     *
+     * @return an array of merged WQKms objects.
+     */
+    public static WQKms[] merge(WQKms[][] toMerge) {
+        int num = toMerge[0].length;
+
+        // TODO IS THE LENGTH CORRECT?
+        WQKms[] merged = new WQKms[num];
+        for (int i = 0; i < num; i++) {
+            merged[i] = new WQKms();
+        }
+
+        for (int i = 0; i < toMerge.length; i++) {
+            WQKms[] tmp = toMerge[i];
+
+            for (int j = 0; j < num; j++) {
+                WQKms toAdd = tmp[j];
+                merged[j].add(toAdd.getWs(), toAdd.getQs(), toAdd.getKms());
+            }
+        }
+
+        return merged;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstFactory.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,41 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+
+import de.intevation.flys.backend.SessionHolder;
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.Wst;
+
+import org.hibernate.Query;
+import org.hibernate.Session;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WstFactory {
+
+    /** We don't need to instantiate concrete objects of this class. */
+    private WstFactory() {
+    }
+
+
+    /**
+     * Returns the Wst object for a given river.
+     *
+     * @param river The river.
+     *
+     * @return the Wst of <i>river</i>.
+     */
+    public static Wst getWst(River river) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from Wst where river=:river");
+        query.setParameter("river", river);
+
+        List<Wst> wst = query.list();
+
+        return (wst != null && wst.size() > 0) ? wst.get(0) : null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,817 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.Wst;
+import de.intevation.flys.model.WstColumn;
+
+import de.intevation.flys.artifacts.math.LinearRemap;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import de.intevation.flys.backend.SessionHolder;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Collections;
+
+import org.apache.log4j.Logger;
+
+import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
+
+import org.apache.commons.math.analysis.polynomials.PolynomialSplineFunction;
+
+import org.apache.commons.math.ArgumentOutsideDomainException;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+import org.hibernate.SQLQuery;
+
+import org.hibernate.type.StandardBasicTypes;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+public class WstValueTable
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(WstValueTable.class);
+
+    public static final String CACHE_NAME = "wst-value-table";
+
+    // TODO: put this into a property file
+    public static final String SQL_POS_WQ = 
+        "SELECT position, w, q, column_pos" +
+        "    FROM wst_value_table"          +
+        "    WHERE wst_id = :wst_id";
+
+    public static final int DEFAULT_Q_STEPS = 500;
+
+    public static class Column
+    implements          Serializable
+    {
+        protected String name;
+
+        public Column() {
+        }
+
+        public Column(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+    }
+    // class Column
+
+    public static class QPosition {
+
+        protected double q;
+        protected int    index;
+        protected double weight;
+
+        public QPosition() {
+        }
+
+        public QPosition(double q, int index, double weight) {
+            this.index  = index;
+            this.weight = weight;
+        }
+
+    } // class Position
+
+    public static class Row
+    implements          Serializable, Comparable<Row>
+    {
+        double    km;
+        double [] ws;
+        double [] qs;
+        boolean   qSorted;
+
+        public Row() {
+        }
+
+        public Row(double km) {
+            this.km = km;
+        }
+
+        public Row(double km, double [] ws, double [] qs) {
+            this(km);
+            this.ws = ws;
+            this.qs = qs;
+        }
+
+        public static final double linear(
+            double x,
+            double x1, double x2,
+            double y1, double y2
+        ) {
+            // y1 = m*x1 + b
+            // y2 = m*x2 + b
+            // y2 - y1 = m*(x2 - x1)
+            // m = (y2 - y1)/(x2 - x1) # x2 != x1
+            // b = y1 - m*x1
+
+            if (x1 == x2) {
+                return 0.5*(y1 + y2);
+            }
+            double m = (y2 - y1)/(x2 - x1);
+            double b = y1 - m*x1;
+            return x*m + b;
+        }
+
+        public static final double factor(double x, double p1, double p2) {
+            // 0 = m*p1 + b <=> b = -m*p1
+            // 1 = m*p2 + b
+            // 1 = m*(p2 - p1)
+            // m = 1/(p2 - p1) # p1 != p2
+            // f(x) = x/(p2-p1) - p1/(p2-p1) <=> (x-p1)/(p2-p1)
+
+            return p1 == p2 ? 0.0 : (x-p1)/(p2-p1);
+        }
+
+        public static final double weight(double factor, double a, double b) {
+            return (1.0-factor)*a + factor*b;
+        }
+
+        public double getWForKM(Row other, int index, double km) {
+            double w1  = ws[index];
+            double w2  = other.ws[index];
+            double km1 = this.km;
+            double km2 = other.km;
+            return linear(km, km1, km2, w1, w2);
+        }
+
+        public void interpolateW(
+            Row       other,
+            double    km,
+            double [] input,
+            double [] output
+        ) {
+            double factor = factor(km, this.km, other.km);
+
+            for (int i = 0; i < input.length; ++i) {
+                double q = input[i];
+                int idx1 =       getQIndex(q);
+                int idx2 = other.getQIndex(q);
+
+                double w1 = idx1 >= 0
+                    ? ws[idx1]
+                    : interpolateW(-idx1-1, q);
+
+                double w2 = idx2 >= 0
+                    ? other.ws[idx2]
+                    : other.interpolateW(-idx2-1, q);
+
+                output[i] = weight(factor, w1, w2);
+            }
+        }
+
+        public double interpolateW(int idx, double q) {
+            return idx < 1 || idx >= qs.length
+                ? Double.NaN // do not extrapolate
+                : linear(q, qs[idx-1], qs[idx], ws[idx-1], ws[idx]);
+        }
+
+        public double interpolateW(double q) {
+            if (Double.isNaN(q)) return Double.NaN;
+            int index = getQIndex(q);
+            return index >= 0 ? ws[index] : interpolateW(-index -1, q);
+        }
+
+        public int getQIndex(double q) {
+            return qSorted ? binaryQIndex(q) : linearQIndex(q);
+        }
+
+        public int binaryQIndex(double q) {
+            return Arrays.binarySearch(qs, q);
+        }
+
+        public int linearQIndex(double q) {
+            switch (qs.length) {
+                case 0: break;
+                case 1:
+                    if (qs[0] == q) return 0;
+                    if (qs[0] <  q) return -(1+1);
+                    return -(0+1);
+                default:
+                    for (int i = 1; i < qs.length; ++i) {
+                        double qa = qs[i-1];
+                        double qb = qs[i];
+                        if (qa == q) return i-1;
+                        if (qb == q) return i;
+                        if (qa > qb) { double t = qa; qa = qb; qb = t; }
+                        if (q > qa && q < qb) return -(i+1);
+                    }
+                    return -qs.length;
+            }
+
+            return -1;
+        }
+
+        public int compareTo(Row other) {
+            double d = km - other.km;
+            if (d < 0.0) return -1;
+            if (d > 0.0) return +1;
+            return 0;
+        }
+
+        public double [][] cloneWQs() {
+            return new double [][] {
+                (double [])ws.clone(),
+                (double [])qs.clone() };
+        }
+
+        public double [][] interpolateWQ(Row other, double km, int steps) {
+
+            int W = Math.min(ws.length, other.ws.length);
+
+            if (W < 1) {
+                return new double[2][0];
+            }
+
+            double factor = factor(km, this.km, other.km);
+
+            double [] splineQ = new double[W];
+            double [] splineW = new double[W];
+
+            double minQ =  Double.MAX_VALUE;
+            double maxQ = -Double.MAX_VALUE;
+
+            for (int i = 0; i < W; ++i) {
+                double wws = weight(factor, ws[i], other.ws[i]);
+                double wqs = weight(factor, qs[i], other.qs[i]);
+                splineW[i] = wws;
+                splineQ[i] = wqs;
+                if (wqs < minQ) minQ = wqs;
+                if (wqs > maxQ) maxQ = wqs;
+            }
+
+            double stepWidth = (maxQ - minQ)/steps;
+
+            SplineInterpolator interpolator = new SplineInterpolator();
+            PolynomialSplineFunction spline =
+                interpolator.interpolate(splineQ, splineW);
+
+            double [] outWs = new double[steps];
+            double [] outQs = new double[steps];
+
+            try {
+                double q = minQ;
+                for (int i = 0; i < outWs.length; ++i, q += stepWidth) {
+                    outWs[i] = spline.value(outQs[i] = q);
+                }
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                log.error("Spline interpolation failed.", aode);
+            }
+
+            return new double [][] { outWs, outQs };
+        }
+
+        public double [][] interpolateWQ(int steps) {
+
+            int W = ws.length;
+
+            if (W < 1) {
+                return new double[2][0];
+            }
+
+            double [] splineW = new double[W];
+            double [] splineQ = new double[W];
+
+            double minQ =  Double.MAX_VALUE; 
+            double maxQ = -Double.MAX_VALUE; 
+
+            for (int i = 0; i < W; ++i) {
+                splineW[i] = ws[i];
+                splineQ[i] = qs[i];
+                if (qs[i] < minQ) minQ = qs[i];
+                if (qs[i] > maxQ) maxQ = qs[i];
+            }
+
+            double stepWidth = (maxQ - minQ)/steps;
+
+            SplineInterpolator interpolator = new SplineInterpolator();
+
+            PolynomialSplineFunction spline =
+                interpolator.interpolate(splineQ, splineW);
+
+            double [] outWs = new double[steps];
+            double [] outQs = new double[steps];
+
+            try {
+                double q = minQ;
+                for (int i = 0; i < outWs.length; ++i, q += stepWidth) {
+                    outWs[i] = spline.value(outQs[i] = q);
+                }
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                log.error("Spline interpolation failed.", aode);
+            }
+
+            return new double [][] { outWs, outQs };
+        }
+
+        public int ascendingWs() {
+            if (ws.length < 2) {
+                return ws.length;
+            }
+
+            int idx = 1;
+
+            for (; idx < ws.length; ++idx) {
+                if (Double.isNaN(ws[idx]) || ws[idx] < ws[idx-1]) {
+                    return idx;
+                }
+            }
+
+            return idx;
+        }
+
+        public double [][] weightWQs(Row other, double km) {
+            int W = Math.min(ws.length, other.ws.length);
+            double factor = factor(km, this.km, other.km);
+
+            double [] outWs = new double[W];
+            double [] outQs = new double[W];
+
+            for (int i = 0; i < W; ++i) {
+                outWs[i] = weight(factor, ws[i], other.ws[i]);
+                outQs[i] = weight(factor, qs[i], other.qs[i]);
+            }
+
+            return new double [][] { outWs, outQs };
+        }
+
+        public QPosition getQPosition(double q) {
+            int qIndex = getQIndex(q);
+
+            if (qIndex >= 0) {
+                // direct column match
+                return new QPosition(q, qIndex, 1.0);
+            }
+
+            qIndex = -qIndex -1;
+
+            if (qIndex < 0 || qIndex >= qs.length-1) {
+                // do not extrapolate
+                return null;
+            }
+
+            return new QPosition(
+                q, qIndex, factor(q, qs[qIndex-1], qs[qIndex]));
+        }
+
+        public QPosition getQPosition(Row other, double km, double q) {
+
+            QPosition qpt = getQPosition(q);
+            QPosition qpo = other.getQPosition(q);
+
+            if (qpt == null || qpo == null) {
+                return null;
+            }
+
+            double kmWeight = factor(km, this.km, other.km);
+
+            // XXX: Index interpolation is a bit sticky here!
+
+            int index = (int)Math.round(
+                weight(kmWeight, qpt.index, qpo.index));
+
+            double weight = weight(kmWeight, qpt.weight, qpo.weight);
+
+            return new QPosition(q, index, weight);
+        }
+
+        public void storeWQ(
+            QPosition qPosition,
+            double [] ows,
+            double [] oqs,
+            int       index
+        ) {
+            int    qIdx    = qPosition.index;
+            double qWeight = qPosition.weight;
+
+            if (qWeight == 1.0) {
+                oqs[index] = qs[qIdx];
+                ows[index] = ws[qIdx];
+            }
+            else {
+                oqs[index] = weight(qWeight, qs[qIdx-1], qs[qIdx]); 
+                ows[index] = weight(qWeight, ws[qIdx-1], ws[qIdx]); 
+            }
+        }
+
+        public void storeWQ(
+            Row       other,
+            double    km,
+            QPosition qPosition,
+            double [] ows,
+            double [] oqs,
+            int       index
+        ) {
+            double kmWeight = factor(km, this.km, other.km);
+
+            double tq, tw;
+            double oq, ow;
+
+            int    qIdx    = qPosition.index;
+            double qWeight = qPosition.weight;
+
+            if (qWeight == 1.0) {
+                tq = qs[qIdx];
+                tw = ws[qIdx];
+                oq = other.qs[qIdx];
+                ow = other.ws[qIdx];
+            }
+            else {
+                tq = weight(qWeight, qs[qIdx-1], qs[qIdx]); 
+                tw = weight(qWeight, ws[qIdx-1], ws[qIdx]); 
+                oq = weight(qWeight, other.qs[qIdx-1], other.qs[qIdx]); 
+                ow = weight(qWeight, other.ws[qIdx-1], other.ws[qIdx]); 
+            }
+
+            oqs[index] = weight(kmWeight, oq, tq);
+            ows[index] = weight(kmWeight, ow, tw);
+        }
+
+        public void storeWQ(
+            QPosition   qPosition,
+            LinearRemap remap,
+            double []   ows,
+            double []   oqs,
+            int         index
+        ) {
+            int    qIdx    = qPosition.index;
+            double qWeight = qPosition.weight;
+
+            oqs[index] = remap.eval(km, qWeight == 1.0
+                ? qs[qIdx]
+                : weight(qWeight, qs[qIdx-1], qs[qIdx])); 
+
+            ows[index] = interpolateW(oqs[index]);
+        }
+
+        public void storeWQ(
+            Row         other,
+            double      km,
+            QPosition   qPosition,
+            LinearRemap remap,
+            double []   ows,
+            double []   oqs,
+            int         index
+        ) {
+            double kmWeight = factor(km, this.km, other.km);
+
+            double tq, tw;
+            double oq, ow;
+
+            int    qIdx    = qPosition.index;
+            double qWeight = qPosition.weight;
+
+            if (qWeight == 1.0) {
+                tq = remap.eval(this.km,  qs[qIdx]);
+                oq = remap.eval(other.km, other.qs[qIdx]);
+            }
+            else {
+                tq = remap.eval(
+                    this.km,
+                    weight(qWeight, qs[qIdx-1], qs[qIdx]));
+                oq = remap.eval(
+                    other.km,
+                    weight(qWeight, other.qs[qIdx-1], other.qs[qIdx])); 
+            }
+
+            tw = interpolateW(tq);
+            ow = other.interpolateW(oq);
+
+            oqs[index] = weight(kmWeight, oq, tq);
+            ows[index] = weight(kmWeight, ow, tw);
+        }
+    }
+    // class Row
+
+    protected List<Row> rows;
+
+    protected Column [] columns;
+
+    public WstValueTable() {
+        rows = new ArrayList<Row>();
+    }
+
+    public WstValueTable(Column [] columns) {
+        this();
+        this.columns = columns;
+    }
+
+    public double [] interpolateW(double km, double [] qs) {
+        return interpolateW(km, qs, new double[qs.length]);
+    }
+
+    public double [] interpolateW(double km, double [] qs, double [] ws) {
+
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex >= 0) { // direct row match
+            Row row = rows.get(rowIndex);
+            for (int i = 0; i < qs.length; ++i) {
+                ws[i] = row.interpolateW(qs[i]);
+            }
+        }
+        else { // needs bilinear interpolation
+            rowIndex = -rowIndex -1;
+
+            if (rowIndex < 1 || rowIndex >= rows.size()) {
+                // do not extrapolate
+                Arrays.fill(ws, Double.NaN);
+            }
+            else {
+                rows.get(rowIndex-1).interpolateW(
+                    rows.get(rowIndex),
+                    km, qs, ws);
+            }
+        }
+
+        return ws;
+    }
+
+
+    public double [][] interpolateWQ(double km) {
+        return interpolateWQ(km, DEFAULT_Q_STEPS);
+    }
+
+    public double [][] interpolateWQ(double km, int steps) {
+
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex >= 0) { // direct row match
+            Row row = rows.get(rowIndex);
+            return row.interpolateWQ(steps);
+        }
+
+        rowIndex = -rowIndex -1;
+
+        if (rowIndex < 1 || rowIndex >= rows.size()) {
+            // do not extrapolate
+            return new double[2][0];
+        }
+
+        Row r1 = rows.get(rowIndex-1);
+        Row r2 = rows.get(rowIndex);
+
+        return r1.interpolateWQ(r2, km, steps);
+    }
+
+    public void interpolate(
+        double []   kms,
+        double []   ws,
+        double []   qs,
+        QPosition   qPosition,
+        LinearRemap remap
+    ) {
+        int R1 = rows.size()-1;
+
+        Row kmKey = new Row();
+        for (int i = 0; i < kms.length; ++i) {
+            kmKey.km = kms[i];
+            int rowIndex = Collections.binarySearch(rows, kmKey);
+
+            if (rowIndex >= 0) {
+                // direct row match
+                rows.get(rowIndex).storeWQ(qPosition, remap, ws, qs, i);
+                continue;
+            }
+
+            rowIndex = -rowIndex -1;
+
+            if (rowIndex < 1 || rowIndex >= R1) {
+                // do not extrapolate
+                ws[i] = qs[i] = Double.NaN;
+                continue;
+            }
+            Row r1 = rows.get(rowIndex-1);
+            Row r2 = rows.get(rowIndex);
+            r1.storeWQ(r2, kms[i], qPosition, remap, ws, qs, i);
+        }
+    }
+
+    public QPosition interpolate(
+        double    q,
+        int       referenceIndex,
+        double [] kms,
+        double [] ws,
+        double [] qs
+    ) {
+        Row kmKey = new Row(kms[referenceIndex]);
+
+        int rowIndex = Collections.binarySearch(rows, kmKey);
+
+        int R1 = rows.size()-1;
+
+        QPosition qPosition = null;
+
+        if (rowIndex >= 0) { // direct row match
+            qPosition = rows.get(rowIndex).getQPosition(q);
+        }
+        else {
+            rowIndex = -rowIndex -1;
+
+            if (rowIndex < 1 || rowIndex >= R1) {
+                // do not extrapolate
+                return null;
+            }
+
+            // interpolation case
+            Row r1 = rows.get(rowIndex-1);
+            Row r2 = rows.get(rowIndex);
+
+            qPosition = r1.getQPosition(r2, kms[referenceIndex], q);
+        }
+
+        if (qPosition == null) {
+            // we cannot locate q in reference row
+            return null;
+        }
+
+        for (int i = 0; i < kms.length; ++i) {
+            kmKey.km = kms[i];
+            rowIndex = Collections.binarySearch(rows, kmKey);
+
+            if (rowIndex >= 0) {
+                // direct row match
+                rows.get(rowIndex).storeWQ(qPosition, ws, qs, i);
+                continue;
+            }
+
+            rowIndex = -rowIndex -1;
+
+            if (rowIndex < 1 || rowIndex >= R1) {
+                // do not extrapolate
+                ws[i] = qs[i] = Double.NaN;
+                continue;
+            }
+            Row r1 = rows.get(rowIndex-1);
+            Row r2 = rows.get(rowIndex);
+            r1.storeWQ(r2, kms[i], qPosition, ws, qs, i);
+        }
+
+        return qPosition;
+    }
+
+    public static WstValueTable getTable(River river) {
+        return getTable(river, 0);
+    }
+
+    public static final class CacheKey
+    implements                Serializable
+    {
+        private int riverId;
+        private int kind;
+
+        public CacheKey(int riverId, int kind) {
+            this.riverId = riverId;
+            this.kind    = kind;
+        }
+
+        public int hashCode() {
+            return (riverId << 8) | kind;
+        }
+
+        public boolean equals(Object other) {
+            if (!(other instanceof CacheKey)) {
+                return false;
+            }
+            CacheKey o = (CacheKey)other;
+            return riverId == o.riverId && kind == o.kind;
+        }
+    } // class CacheKey
+
+    public static WstValueTable getTable(River river, int kind) {
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        CacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new CacheKey(river.getId(), kind);
+            Element element = cache.get(cacheKey);
+            if (element != null) {
+                log.debug("got wst value table from cache");
+                return (WstValueTable)element.getValue();
+            }
+        }
+        else {
+            cacheKey = null;
+        }
+
+        WstValueTable valueTable = getTableUncached(river, kind);
+
+        if (cacheKey != null) {
+            log.debug("store wst value in cache");
+            Element element = new Element(cacheKey, valueTable);
+            cache.put(element);
+        }
+
+        return valueTable;
+    }
+
+    public static WstValueTable getTableUncached(River river, int kind) {
+
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from Wst where river=:river and kind=:kind");
+        query.setParameter("river", river);
+        query.setInteger("kind",    kind);
+
+        List<Wst> wsts = query.list();
+
+        if (wsts.isEmpty()) {
+            return null;
+        }
+
+        Wst wst = wsts.get(0);
+
+        // TODO: Do this sorting at database level
+        List<WstColumn> wstColumns = new ArrayList(wst.getColumns());
+        Collections.sort(wstColumns, new Comparator<WstColumn>() {
+            public int compare(WstColumn a, WstColumn b) {
+                int pa = a.getPosition();
+                int pb = b.getPosition();
+                if (pa < pb) return -1;
+                if (pa > pb) return +1;
+                return 0;
+            }
+        });
+
+        Column [] columns = new Column[wstColumns.size()];
+        for (int i = 0; i < columns.length; ++i) {
+            columns[i] = new Column(wstColumns.get(i).getName());
+        }
+
+        // using native SQL here to avoid myriad of small objects.
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_POS_WQ)
+            .addScalar("position",   StandardBasicTypes.DOUBLE)
+            .addScalar("w",          StandardBasicTypes.DOUBLE)
+            .addScalar("q",          StandardBasicTypes.DOUBLE)
+            .addScalar("column_pos", StandardBasicTypes.INTEGER);
+
+        sqlQuery.setInteger("wst_id", wst.getId());
+
+        WstValueTable valueTable = new WstValueTable(columns);
+
+        int lastColumnNo = -1;
+        Row row          = null;
+
+        Double lastQ     = -Double.MAX_VALUE;
+        boolean qSorted  = true;
+
+        for (Object  r: sqlQuery.list()) {
+            Object[] result = (Object[]) r;
+
+            double km    = (Double) result[0];
+            Double w     = (Double) result[1];
+            Double q     = (Double) result[2];
+            int columnNo = (Integer)result[3];
+
+            if (columnNo > lastColumnNo) { // new row
+                if (row != null) {
+                    row.qSorted = qSorted;
+                    valueTable.rows.add(row);
+                }
+                row = new Row(
+                    km,
+                    new double[columnNo+1],
+                    new double[columnNo+1]);
+                lastQ = -Double.MAX_VALUE;
+                qSorted = true;
+            }
+
+            row.ws[columnNo] = w != null ? w : Double.NaN;
+            row.qs[columnNo] = q != null ? q : Double.NaN;
+
+            if (qSorted && (q == null || lastQ > q)) {
+                qSorted = false;
+            }
+            lastQ = q;
+
+            lastColumnNo = columnNo;
+        }
+
+        if (row != null) {
+            valueTable.rows.add(row);
+        }
+
+        return valueTable;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/resources/Resources.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,144 @@
+package de.intevation.flys.artifacts.resources;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallMeta;
+
+/**
+ * This class provides methods for i18n.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class Resources {
+
+    /** The logger that is used in this class.*/
+    private static Logger logger = Logger.getLogger(Resources.class);
+
+    /** The singleton instance.*/
+    private static Resources INSTANCE;
+
+    /** The locales supported by this server.*/
+    protected Locale[] locales;
+
+    /**
+     * No instance of this class is necessary.
+     */
+    private Resources() {
+    }
+
+
+    /**
+     * Returns the locales supported by this server.
+     *
+     * @return the supported locales.
+     */
+    public Locale[] getLocales() {
+        if (locales == null) {
+            readLocales();
+        }
+
+        return locales;
+    }
+
+
+    /**
+     * Read the locales configured for this server.
+     */
+    protected void readLocales() {
+        // TODO IMPLEMENT ME
+
+        locales = new Locale[2];
+        locales[0] = Locale.GERMANY;
+        locales[1] = Locale.ENGLISH;
+    }
+
+
+    public static Locale getLocale(CallMeta meta) {
+        if (INSTANCE == null) {
+            INSTANCE = new Resources();
+        }
+
+        Locale[] locales = INSTANCE.getLocales();
+        return meta.getPreferredLocale(locales);
+    }
+
+
+    /**
+     * This method returns the translated value for <i>key</i> or <i>def</i> if
+     * <i>key</i> is not existing in the resource bundle.
+     *
+     * @param meta The CallMeta object of the request that contains the
+     * preferred locale.
+     * @param key The key that should be translated.
+     * @param def A default value that is returned, if <i>key</i> was not found.
+     *
+     * @return the translated message.
+     */
+    public static String getMsg(CallMeta meta, String key, String def) {
+        if (INSTANCE == null) {
+            INSTANCE = new Resources();
+        }
+
+        Locale[] locales = INSTANCE.getLocales();
+        Locale   locale  = meta.getPreferredLocale(locales);
+
+        return getMsg(locale, key, def);
+    }
+
+
+    /**
+     * Returns a translated message based on a template specified by <i>key</i>
+     * that has necessary values to be filled in.
+     *
+     * @param meta The CallMeta object.
+     * @param key The key of the template in the resource bundle.
+     * @param def the default value if no template was found with <i>key</i>.
+     * @param args The arguments that are necessary for the template.
+     *
+     * @return a translated string.
+     */
+    public static String getMsg(
+        CallMeta meta,
+        String   key,
+        String   def,
+        Object[] args)
+    {
+        String template = getMsg(meta, key, null);
+
+        if (template == null) {
+            return def;
+        }
+
+        return MessageFormat.format(template, args);
+    }
+
+
+    /**
+     * This method returns the translated value for <i>key</i> or <i>def</i> if
+     * <i>key</i> is not existing in the resource bundle.
+     *
+     * @param locale The locale.
+     * @param key The key that should be translated.
+     * @param def A default value that is returned, if <i>key</i> was not found.
+     *
+     * @return the translated message.
+     */
+    public static String getMsg(Locale locale, String key, String def) {
+        ResourceBundle bundle = ResourceBundle.getBundle("messages", locale);
+
+        try {
+            return bundle.getString(key);
+        }
+        catch (MissingResourceException mre) {
+            logger.warn("No message found for key: " + key);
+
+            return def;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,126 @@
+package de.intevation.flys.artifacts.services;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.artifactdatabase.DefaultService;
+
+import de.intevation.flys.backend.SessionHolder;
+import de.intevation.flys.model.Annotation;
+import de.intevation.flys.model.Attribute;
+import de.intevation.flys.model.Position;
+import de.intevation.flys.model.Range;
+
+import de.intevation.flys.artifacts.model.AnnotationsFactory;
+
+import org.hibernate.Session;
+
+/**
+ * This service provides information about distances of a specified river.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DistanceInfoService extends DefaultService {
+
+    /** The logger used in this service.*/
+    private static Logger logger = Logger.getLogger(DistanceInfoService.class);
+
+
+    /**
+     * The default constructor.
+     */
+    public DistanceInfoService() {
+    }
+
+
+    public Document process(
+        Document data,
+        Object   globalContext,
+        CallMeta callMeta)
+    {
+        logger.debug("DistanceInfoService.process");
+
+        Document result = XMLUtils.newDocument();
+
+        String river = XMLUtils.xpathString(
+            data, "/art:river/text()", ArtifactNamespaceContext.INSTANCE);
+
+        if (river == null || river.trim().length() == 0) {
+            logger.warn("No river specified. Cannot return distance info!");
+            return result;
+        }
+
+        logger.debug("Search distances for river: " + river);
+
+        ElementCreator ec = new ElementCreator(
+            result,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Session session = SessionHolder.acquire();
+        try {
+            List<Annotation> annotations = AnnotationsFactory.getAnnotations(river);
+
+            if (annotations == null || annotations.size() == 0) {
+                logger.warn("No information found for the specified river!");
+                return result;
+            }
+
+            Element all = ec.create("distances");
+
+            for (Annotation a: annotations) {
+                Element distance = buildDistanceNode(ec, a);
+
+                if (distance != null) {
+                    all.appendChild(distance);
+                }
+            }
+
+            result.appendChild(all);
+        }
+        finally {
+            session.close();
+            SessionHolder.release();
+        }
+
+        return result;
+    }
+
+
+    /**
+     * This method build an Element for a distance info.
+     *
+     * @param ec The ElementCreator.
+     * @param anno The Annotation that provides information about the distance.
+     *
+     * @return an Element that contains information about a distance.
+     */
+    protected Element buildDistanceNode(ElementCreator ec, Annotation anno) {
+        Position  pos   = anno.getPosition();
+        Range     range = anno.getRange();
+        Attribute attr  = anno.getAttribute();
+
+        BigDecimal a = range.getA();
+        BigDecimal b = range.getB();
+
+        Element distance = ec.create("distance");
+        ec.addAttr(distance, "description", pos.getValue(), true);
+        ec.addAttr(distance, "from", a != null ? a.toString() : "", true);
+        ec.addAttr(distance, "to", b != null ? b.toString() : "", true);
+        ec.addAttr(distance, "riverside", attr.getValue(), true);
+
+        return distance;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,316 @@
+package de.intevation.flys.artifacts.services;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.hibernate.Session;
+
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifactdatabase.DefaultService;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.flys.backend.SessionHolder;
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.MainValue;
+import de.intevation.flys.model.MainValueType;
+import de.intevation.flys.model.NamedMainValue;
+import de.intevation.flys.model.Range;
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.model.MainValuesFactory;
+import de.intevation.flys.artifacts.model.RiverFactory;
+
+
+/**
+ * This service returns the main values of a river's gauge based on the start
+ * and end point of the river.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MainValuesService extends DefaultService {
+
+    /** The logger that is used by this service.*/
+    private static Logger logger = Logger.getLogger(MainValuesService.class);
+
+
+    /** The XPath that points to the river definition of the incoming request.*/
+    public static final String XPATH_RIVER = "/art:mainvalues/art:river/text()";
+
+    /** The XPath that points to the start definition of the incoming request.*/
+    public static final String XPATH_START = "/art:mainvalues/art:end/text()";
+
+    /** The XPath that points to the end definition of the incoming request.*/
+    public static final String XPATH_END = "/art:mainvalues/art:start/text()";
+
+    /**
+     * The default constructor.
+     */
+    public MainValuesService() {
+    }
+
+
+    public Document process(
+        Document data,
+        Object   context,
+        CallMeta callMeta)
+    {
+        logger.debug("MainValuesService.process");
+
+        try {
+            River river     = getRequestedRiver(data);
+            double[] minmax = getRequestedStartEnd(data, river);
+            Gauge gauge     = river.determineGauge(minmax[0], minmax[1]);
+
+            List<MainValue> mainValues = getMainValues(river, gauge);
+
+            return buildDocument(river, gauge, mainValues, context);
+        }
+        catch (NullPointerException npe) {
+            logger.error("Could not process the request.");
+            logger.error(npe, npe);
+
+            return XMLUtils.newDocument();
+        }
+    }
+
+
+    /**
+     * This method extracts the river from the incoming request. If no river
+     * string was found or no river is found in the database based on this
+     * string a NullPointerException is thrown.
+     *
+     * @param data The incoming request data.
+     *
+     * @return the River object.
+     */
+    protected River getRequestedRiver(Document data)
+    throws    NullPointerException
+    {
+        logger.debug("MainValuesService.getRiver");
+
+        String riverStr = XMLUtils.xpathString(
+            data, XPATH_RIVER, ArtifactNamespaceContext.INSTANCE);
+
+        if (riverStr == null || riverStr.trim().length() == 0) {
+            throw new NullPointerException("No river found in the request.");
+        }
+
+        River river = RiverFactory.getRiver(riverStr);
+
+        if (river == null) {
+            throw new NullPointerException("No such river found: " + riverStr);
+        }
+
+        return river;
+    }
+
+
+    /**
+     * This method extracts the start and end point from incoming request
+     * document and returns both values in an array. If no start and end strings
+     * are found in the document, the min/max values of the <i>river</i> are
+     * returned.
+     *
+     * @param data The incoming request data.
+     * @param river The river of the request.
+     *
+     * @return the start and end point.
+     */
+    protected double[] getRequestedStartEnd(Document data, River river) {
+        logger.debug("MainValuesService.getStartEnd");
+
+        String startStr = XMLUtils.xpathString(
+            data, XPATH_START, ArtifactNamespaceContext.INSTANCE);
+
+        String endStr = XMLUtils.xpathString(
+            data, XPATH_END, ArtifactNamespaceContext.INSTANCE);
+
+        try {
+            double start = Double.parseDouble(startStr);
+            double end   = Double.parseDouble(endStr);
+
+            logger.debug("Found start: " + start);
+            logger.debug("Found end: " + end);
+
+            return new double[] { start, end };
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn(nfe, nfe);
+
+            return river.determineMinMaxDistance();
+        }
+    }
+
+
+    /**
+     * This method creates the result document that includes the main values of
+     * the specified <i>gauge</i>.
+     *
+     * @param river The river.
+     * @param gauge The gauge.
+     *
+     * @return a document that includes the main values of the specified river
+     * at the specified gauge.
+     */
+    protected List<MainValue> getMainValues(River river, Gauge gauge)
+    throws    NullPointerException
+    {
+        if (logger.isDebugEnabled()) {
+            logger.debug("MainValuesService.buildMainValues");
+            logger.debug("River: " + river.getName());
+            logger.debug("Gauge: " + gauge.getName());
+        }
+
+        Session session = SessionHolder.acquire();
+        try {
+            List<MainValue> mainValues = MainValuesFactory.getMainValues(gauge);
+
+            if (mainValues == null || mainValues.isEmpty()) {
+                throw new NullPointerException("No main values found.");
+            }
+
+            logger.debug(mainValues.size() + " main values found.");
+
+            return mainValues;
+        }
+        finally {
+            session.close();
+            SessionHolder.release();
+        }
+    }
+
+
+    protected Document buildDocument(
+        River           river,
+        Gauge           gauge,
+        List<MainValue> mainValues,
+        Object          context)
+    {
+        logger.debug("MainValuesService.buildDocument");
+
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element rootEl = cr.create("service");
+        cr.addAttr(rootEl, "name", "mainvalues");
+
+        doc.appendChild(rootEl);
+
+        appendMetaInformation(doc, rootEl, river, gauge, context);
+        appendMainValues(doc, rootEl, mainValues, context);
+
+        return doc;
+    }
+
+
+    /**
+     * This method appends some meta information to the result document.
+     * Currently, the river's and gauge's names and the gauge's range are
+     * appended.
+     *
+     * @param root The root element of the result document.
+     * @param river The river.
+     * @param gauge The gauge.
+     * @param context The context object.
+     */
+    protected void appendMetaInformation(
+        Document doc,
+        Element  root,
+        River    river,
+        Gauge    gauge,
+        Object   context)
+    {
+        logger.debug("MainValuesService.appendMetaInformation");
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Range range = gauge.getRange();
+
+        Element riverEl = cr.create("river");
+        cr.addAttr(riverEl, "name", river.getName());
+
+        Element gaugeEl = cr.create("gauge");
+        cr.addAttr(gaugeEl, "name", gauge.getName());
+        cr.addAttr(gaugeEl, "from", range.getA().toString());
+        cr.addAttr(gaugeEl, "to", range.getB().toString());
+
+        root.appendChild(riverEl);
+        root.appendChild(gaugeEl);
+    }
+
+
+    protected void appendMainValues(
+        Document        doc,
+        Element         root,
+        List<MainValue> mainValues,
+        Object          context)
+    {
+        logger.debug("MainValuesService.appendMainValues");
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element list = cr.create("mainvalues");
+
+        for (MainValue mainValue: mainValues) {
+            Element newEl = buildMainValueElement(doc, mainValue, context);
+
+            if (newEl != null) {
+                list.appendChild(newEl);
+            }
+        }
+
+        root.appendChild(list);
+    }
+
+
+    /**
+     * This method builds a concrete mainvalue element. This element consists of
+     * three attributes: the value, its name and its type.
+     *
+     * @param doc The owner document.
+     * @param mainValue The mainvalue.
+     * @param context The context object.
+     *
+     * @return a mainvalue element.
+     */
+    protected Element buildMainValueElement(
+        Document  doc,
+        MainValue mainValue,
+        Object    context)
+    {
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        NamedMainValue namedMainValue = mainValue.getMainValue();
+        MainValueType  mainValueType  = namedMainValue.getType();
+
+        Element el = cr.create("mainvalue");
+
+        cr.addAttr(el, "value", mainValue.getValue().toString());
+        cr.addAttr(el, "name", namedMainValue.getName());
+        cr.addAttr(el, "type", mainValueType.getName());
+
+        return el;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/MetaDataService.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,129 @@
+package de.intevation.flys.artifacts.services;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import java.util.Map;
+import java.util.HashMap;
+
+import de.intevation.artifacts.CallMeta;
+import de.intevation.artifacts.ServiceFactory;
+
+import de.intevation.artifactdatabase.DefaultService;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+
+import de.intevation.flys.artifacts.services.meta.Builder;
+
+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);
+
+    public static final String XPATH_RIVER = "/art:river/text()";
+
+    public static final String META_DATA_TEMPLATE = "/metadata/template.xml";
+
+    protected Builder builder;
+
+    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);
+
+        if (river == null || (river = river.trim()).length() == 0) {
+            river = "%"; // matches all rivers
+        }
+
+        parameters.put("river", river);
+
+        return parameters;
+    }
+
+    @Override
+    public Document process(
+        Document data,
+        Object   globalContext,
+        CallMeta callMeta
+    ) {
+        log.debug("MetaDataService.process");
+
+        final Document result = XMLUtils.newDocument();
+
+        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;
+    }
+
+    @Override
+    public void setup(ServiceFactory factory, Object globalContext) {
+        log.debug("MetaDataService.setup");
+
+        InputStream in = getClass().getResourceAsStream(META_DATA_TEMPLATE);
+
+        if (in == null) {
+            log.error("cannot get template resource");
+            return;
+        }
+
+        try {
+            Document template = XMLUtils.parseDocument(in);
+            if (template == null) {
+                log.error("cannot parse meta data template");
+            }
+            else {
+                builder = new Builder(template);
+            }
+        }
+        finally {
+            try {
+                in.close();
+            }
+            catch (IOException ioe) {
+                log.error(ioe);
+            }
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/RiverService.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,80 @@
+package de.intevation.flys.artifacts.services;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.DefaultService;
+
+import de.intevation.flys.backend.SessionHolder;
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.model.RiverFactory;
+
+import org.hibernate.Session;
+
+/**
+ * This service provides information about the supported rivers by this
+ * application.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class RiverService extends DefaultService {
+
+    /** The logger used in this service.*/
+    private static Logger logger = Logger.getLogger(RiverService.class);
+
+
+    /**
+     * The default constructor.
+     */
+    public RiverService() {
+    }
+
+
+    public Document process(
+        Document data,
+        Object   globalContext,
+        CallMeta callMeta)
+    {
+        logger.debug("RiverService.process");
+
+        Document result = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            result,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Session session = SessionHolder.acquire();
+        try {
+            List<River> allRivers = RiverFactory.getRivers();
+
+            Element rivers = ec.create("rivers");
+
+            for (River river: allRivers) {
+                Element r = ec.create("river");
+                ec.addAttr(r, "name", river.getName(), true);
+
+                rivers.appendChild(r);
+            }
+
+            result.appendChild(rivers);
+        }
+        finally {
+            session.close();
+            SessionHolder.release();
+        }
+
+        return result;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/Builder.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,289 @@
+package de.intevation.flys.artifacts.services.meta;
+
+import java.util.regex.Matcher;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+import org.w3c.dom.Element;
+
+import java.sql.SQLException;
+import java.sql.Connection;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import org.apache.log4j.Logger;
+
+public class Builder
+{
+    private static Logger log = Logger.getLogger(Builder.class);
+
+    public static final String DC_NAMESPACE_URI =
+        "http://www.intevation.org/2011/Datacage";
+
+    protected Document template;
+
+    public class BuildHelper
+    {
+        protected Document                       output;
+        protected StackFrames                    frames;
+        protected Connection                     connection;
+        protected Map<String, CompiledStatement> statements;
+
+        public BuildHelper(
+            Document            output,
+            Connection          connection,
+            Map<String, Object> parameters
+        ) {
+            this.output     = output;
+            this.connection = connection;
+            frames          = new StackFrames(parameters);
+            statements      = new HashMap<String, CompiledStatement>();
+        }
+
+        public void build(List<Node> elements) throws SQLException {
+            try {
+                for (Node current: elements) {
+                    build(output, current);
+                }
+            }
+            finally {
+                for (CompiledStatement cs: statements.values()) {
+                    cs.close();
+                }
+                statements.clear();
+            }
+        }
+
+        protected void context(Node parent, Element current)
+        throws SQLException
+        {
+            NodeList elements = current.getElementsByTagNameNS(
+                DC_NAMESPACE_URI, "elements");
+
+            if (elements.getLength() < 1) {
+                log.warn("no elements found -> ignore");
+                return;
+            }
+
+            NodeList subs = elements.item(0).getChildNodes();
+            int S = subs.getLength();
+
+            if (S < 1) {
+                log.warn("elements is empty -> ignore");
+                return;
+            }
+
+            NodeList stmntNode = current.getElementsByTagNameNS(
+                DC_NAMESPACE_URI, "statement");
+
+            if (stmntNode.getLength() < 1) {
+                log.warn("dc:context: too less statements");
+                return;
+            }
+
+            String stmntText = stmntNode.item(0).getTextContent();
+
+            if (stmntText == null
+            || (stmntText = stmntText.trim()).length() == 0) {
+                log.warn("dc:context: no sql statement found -> ignored");
+                return;
+            }
+
+            CompiledStatement cs = statements.get(stmntText);
+
+            if (cs == null) {
+                cs = new CompiledStatement(stmntText);
+                statements.put(stmntText, cs);
+            }
+
+            ResultData rd = cs.execute(connection, frames);
+
+            String [] columns = rd.getColumnLabels();
+
+
+            for (Object [] row: rd.getRows()) {
+                frames.enter();
+                try {
+                    frames.put(columns, row);
+                    for (int i = 0; i < S; ++i) {
+                        build(parent, subs.item(i));
+                    }
+                }
+                finally {
+                    frames.leave();
+                }
+            }
+        }
+
+        protected void element(Node parent, Element current)
+        throws SQLException
+        {
+            String attr = expand(current.getAttribute("name"));
+
+            if (log.isDebugEnabled()) {
+                log.debug("dc:element -> '" + attr + "'");
+            }
+
+            if (attr.length() == 0) {
+                log.warn("no name attribute found");
+                return;
+            }
+
+            Element element = output.createElement(attr);
+
+            NodeList children = current.getChildNodes();
+            for (int i = 0, N = children.getLength(); i < N; ++i) {
+                build(element, children.item(i));
+            }
+
+            parent.appendChild(element);
+        }
+
+        protected void text(Node parent, Element current)
+        throws SQLException
+        {
+            log.debug("dc:text");
+            String value = expand(current.getTextContent());
+            parent.appendChild(output.createTextNode(value));
+        }
+
+
+        protected void attribute(Node parent, Element current) {
+
+            if (parent.getNodeType() != Node.ELEMENT_NODE) {
+                log.warn("need element here");
+                return;
+            }
+
+            String name  = expand(current.getAttribute("name"));
+            String value = expand(current.getAttribute("value"));
+
+            Element element = (Element)parent;
+
+            element.setAttribute(name, value);
+        }
+
+        protected void convert(Node parent, Element current) {
+
+            String variable = expand(current.getAttribute("var"));
+            String type     = expand(current.getAttribute("type"));
+
+            if (frames.containsKey(variable)) {
+                Object object = TypeConverter.convert(
+                    frames.get(variable),
+                    type);
+                frames.put(variable, object);
+            }
+        }
+
+        protected String expand(String s) {
+            Matcher m = CompiledStatement.VAR.matcher(s);
+
+            StringBuffer sb = new StringBuffer();
+            while (m.find()) {
+                String key = m.group(1);
+                Object value = frames.get(key);
+                m.appendReplacement(sb, value != null ? value.toString() : "");
+            }
+            m.appendTail(sb);
+            return sb.toString();
+        }
+
+        protected void build(Node parent, Node current) 
+        throws SQLException
+        {
+            String ns = current.getNamespaceURI();
+            if (ns != null && ns.equals(DC_NAMESPACE_URI)) {
+                if (current.getNodeType() != Node.ELEMENT_NODE) {
+                    log.warn("need elements here");
+                }
+                else {
+                    String localName = current.getLocalName();
+                    if ("context".equals(localName)) {
+                        context(parent, (Element)current);
+                    }
+                    else if ("attribute".equals(localName)) {
+                        attribute(parent, (Element)current);
+                    }
+                    else if ("element".equals(localName)) {
+                        element(parent, (Element)current);
+                    }
+                    else if ("text".equals(localName)) {
+                        text(parent, (Element)current);
+                    }
+                    else if ("convert".equals(localName)) {
+                        convert(parent, (Element)current);
+                    }
+                    else {
+                        log.warn("unknown '" + localName + "' -> ignore");
+                    }
+                }
+                return;
+            }
+
+            if (current.getNodeType() == Node.TEXT_NODE) {
+                String txt = current.getNodeValue();
+                if (txt != null && txt.trim().length() == 0) {
+                    return;
+                }
+            }
+
+            Node copy = output.importNode(current, false);
+
+            NodeList children = current.getChildNodes();
+            for (int i = 0, N = children.getLength(); i < N; ++i) {
+                build(copy, children.item(i));
+            }
+            parent.appendChild(copy);
+        }
+    } // class BuildHelper
+
+
+    public Builder() {
+    }
+
+    public Builder(Document template) {
+        this.template = template;
+    }
+
+    public Document build(Connection connection)
+    throws SQLException
+    {
+        return build(connection, XMLUtils.newDocument(), null);
+    }
+
+    public Document build(
+        Connection          connection,
+        Document            output,
+        Map<String, Object> parameters
+    )
+    throws SQLException
+    {
+        NodeList roots = template.getElementsByTagNameNS(
+            DC_NAMESPACE_URI, "template");
+
+        BuildHelper helper = new BuildHelper(output, connection, parameters);
+
+        List<Node> elements = new ArrayList<Node>();
+
+        for (int i = 0, N = roots.getLength(); i < N; ++i) {
+            NodeList rootChildren = roots.item(i).getChildNodes();
+            for (int j = 0, M = rootChildren.getLength(); j < M; ++j) {
+                Node child = rootChildren.item(j);
+                if (child.getNodeType() == Node.ELEMENT_NODE) {
+                    elements.add(child);
+                }
+            }
+        }
+        helper.build(elements);
+
+        return output;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/CompiledStatement.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,100 @@
+package de.intevation.flys.artifacts.services.meta;
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Connection;
+import java.sql.ResultSet;
+
+public class CompiledStatement
+{
+    public static final Pattern VAR = Pattern.compile("\\$\\{([a-zA-Z0-9_]+)\\}");
+
+    protected String original;
+    protected String statement;
+
+    protected Map<String, List<Integer>> positions;
+
+    protected PreparedStatement preparedStatement;
+
+    public CompiledStatement() {
+    }
+
+    public CompiledStatement(String original) {
+        this.original = original;
+        positions = new HashMap<String, List<Integer>>();
+        compile();
+    }
+
+    protected void compile() {
+
+        StringBuffer sb = new StringBuffer();
+
+        Matcher m = VAR.matcher(original);
+
+        int index = 1;
+
+        while (m.find()) {
+            String key = m.group(1);
+            List<Integer> indices = positions.get(key);
+            if (indices == null) {
+                indices = new ArrayList<Integer>();
+                positions.put(key, indices);
+            }
+            indices.add(index);
+            m.appendReplacement(sb, "?");
+            ++index;
+        }
+
+        m.appendTail(sb);
+
+        statement = sb.toString();
+    }
+
+    public String getStatement() {
+        return statement;
+    }
+
+    public ResultData execute(Connection connection, StackFrames frames) 
+    throws SQLException
+    {
+        if (preparedStatement == null) {
+            preparedStatement = connection.prepareStatement(statement);
+        }
+
+        for (Map.Entry<String, List<Integer>> entry: positions.entrySet()) {
+            Object value = frames.get(entry.getKey());
+            for (Integer index: entry.getValue()) {
+                preparedStatement.setObject(index, value);
+            }
+        }
+
+        ResultSet result = preparedStatement.executeQuery();
+        try {
+            return new ResultData(preparedStatement.getMetaData())
+                .addAll(result);
+        }
+        finally {
+            result.close();
+        }
+    }
+
+    public void close() {
+        if (preparedStatement != null) {
+            try {
+                preparedStatement.close();
+            }
+            catch (SQLException sqle) {
+            }
+            preparedStatement = null;
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/ResultData.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,57 @@
+package de.intevation.flys.artifacts.services.meta;
+
+import java.sql.ResultSetMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class ResultData
+{
+    protected String [] columns;
+
+    protected List<Object []> rows;
+
+    public ResultData() {
+        rows = new ArrayList<Object []>();
+    }
+
+    public ResultData(ResultSetMetaData meta) 
+    throws SQLException
+    {
+        this();
+
+        int N = meta.getColumnCount();
+
+        columns = new String[N];
+
+        for (int i = 1; i <= N; ++i) {
+            columns[i-1] = meta.getColumnLabel(i);
+        }
+    }
+
+    public String [] getColumnLabels() {
+        return columns;
+    }
+
+    public ResultData addAll(ResultSet result) throws SQLException {
+        while (result.next()) {
+            add(result);
+        }
+        return this;
+    }
+
+    public void add(ResultSet result) throws SQLException {
+        Object [] row = new Object[columns.length];
+        for (int i = 0; i < columns.length; ++i) {
+            row[i] = result.getObject(i+1);
+        }
+        rows.add(row);
+    }
+
+    public List<Object []> getRows() {
+        return rows;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/StackFrames.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,70 @@
+package de.intevation.flys.artifacts.services.meta;
+
+import java.util.Map;
+import java.util.List;
+import java.util.HashMap;
+import java.util.ArrayList;
+
+public class StackFrames
+{
+    protected List<Map<String, Object>> frames;
+
+    public StackFrames() {
+        frames = new ArrayList<Map<String, Object>>();
+    }
+
+    public StackFrames(Map<String, Object> initialFrame) {
+        this();
+        if (initialFrame != null) {
+            frames.add(new HashMap<String, Object>(initialFrame));
+        }
+    }
+
+    public void enter() {
+        frames.add(new HashMap<String, Object>());
+    }
+
+    public void leave() {
+        frames.remove(frames.size()-1);
+    }
+
+    public void put(String key, Object value) {
+        int N = frames.size();
+        if (N > 0) {
+            frames.get(N-1).put(key, value);
+        }
+    }
+
+    public void put(String [] keys, Object [] values) {
+        Map<String, Object> top = frames.get(frames.size()-1);
+        for (int i = 0; i < keys.length; ++i) {
+            top.put(keys[i], values[i]);
+        }
+    }
+
+    public boolean containsKey(String key) {
+        for (int i = frames.size()-1; i >= 0; --i) {
+            if (frames.get(i).containsKey(key)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public Object get(String key) {
+        return get(key, null);
+    }
+
+    public Object get(String key, Object def) {
+
+        for (int i = frames.size()-1; i >= 0; --i) {
+            Map<String, Object> frame = frames.get(i);
+            if (frame.containsKey(key)) {
+                return frame.get(key);
+            }
+        }
+
+        return def;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/services/meta/TypeConverter.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,31 @@
+package de.intevation.flys.artifacts.services.meta;
+
+public class TypeConverter
+{
+    private TypeConverter() {
+    }
+
+    public static Object convert(Object object, String type) {
+
+        if (type == null) {
+            return object;
+        }
+
+        if ("Integer".equals(type)) {
+            return Integer.valueOf(object.toString());
+        }
+
+        if ("Double".equals(type)) {
+            return Double.valueOf(object.toString());
+        }
+
+        if ("String".equals(type)) {
+            return object.toString();
+        }
+
+        // TODO: Add more types
+
+        return object;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,133 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.flys.artifacts.resources.Resources;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CalculationSelect extends DefaultState {
+
+    /** The logger that is used in this class.*/
+    private static Logger logger = Logger.getLogger(CalculationSelect.class);
+
+
+    /** Constant value for the reference line calculation.*/
+    public static final String CALCULATION_SURFACE_CURVE =
+        "calc.surface.curve";
+
+    /** Constant value for the differences calculation.*/
+    public static final String CALCULATION_DURATION_CURVE =
+        "calc.duration.curve";
+
+    /** Constant value for the flood map calculation.*/
+    public static final String CALCULATION_FLOOD_MAP =
+        "calc.flood.map";
+
+    /** Constant value for the profile calculation.*/
+    public static final String CALCULATION_DISCHARGE_LONGITUDINAL_CURVE =
+        "calc.discharge.longitudinal.section";
+
+    /** Constant value for the state discharge curve calculation.*/
+    public static final String CALCULATION_DISCHARGE_CURVE =
+        "calc.discharge.curve";
+
+    /** An array that holds all available calculation modes.*/
+    public static final String[] CALCULATIONS = {
+        CALCULATION_SURFACE_CURVE,
+        CALCULATION_FLOOD_MAP,
+        CALCULATION_DISCHARGE_CURVE,
+        CALCULATION_DURATION_CURVE,
+        CALCULATION_DISCHARGE_LONGITUDINAL_CURVE };
+
+
+    /** Error message that is thrown if no mode has been chosen.*/
+    public static final String ERROR_NO_CALCULATION_MODE =
+        "error_feed_no_calculation_mode";
+
+    /** Error message that is thrown if an invalid calculation mode has been
+     * chosen.*/
+    public static  final String ERROR_INVALID_CALCULATION_MODE =
+        "error_feed_invalid_calculation_mode";
+
+
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        CallMeta meta   = context.getMeta();
+        Element[] calcs = new Element[CALCULATIONS.length];
+
+        int i = 0;
+
+        for (String calc: CALCULATIONS) {
+            calcs[i++] = createItem(
+                cr, new String[] {
+                    Resources.getMsg(meta, calc, calc),
+                    calc
+                });
+        }
+
+        return calcs;
+    }
+
+
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        logger.debug("CalculationSelect.validate");
+
+        Map<String, StateData> data = getData();
+
+        String calc = (String) data.get("calculation_mode").getValue();
+
+        if (calc == null) {
+            throw new IllegalArgumentException(ERROR_NO_CALCULATION_MODE);
+        }
+
+        calc = calc.trim().toLowerCase();
+
+        for (String mode: CALCULATIONS) {
+            if (mode.equals(calc)) {
+                return true;
+            }
+        }
+
+        throw new IllegalArgumentException(ERROR_INVALID_CALCULATION_MODE);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,36 @@
+package de.intevation.flys.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+
+/**
+ * The final state that will be reached after the discharge curve calculation
+ * mode has been chosen.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ComputedDischargeCurveState extends DefaultState {
+
+    /** The logger that is used in this state.*/
+    private static Logger logger =
+        Logger.getLogger(ComputedDischargeCurveState.class);
+
+
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("ComputedDischargeCurveState.createItems");
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DefaultState.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,211 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+import de.intevation.artifactdatabase.data.StateData;
+import de.intevation.artifactdatabase.state.AbstractState;
+
+import de.intevation.flys.artifacts.resources.Resources;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class DefaultState extends AbstractState {
+
+    /** The logger that is used in this class.*/
+    private static Logger logger = Logger.getLogger(DefaultState.class);
+
+
+    public Element describeStatic(
+        Document    document,
+        Node        root,
+        CallContext context,
+        String      uuid)
+    {
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+            document,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        CallMeta meta = context.getMeta();
+
+        String label = Resources.getMsg(meta, getID(), getID());
+        Element ui   = ProtocolUtils.createArtNode(
+            creator, "state",
+            new String[] { "name", "uiprovider", "label" },
+            new String[] { getID(), getUIProvider(), label });
+
+        Map<String, StateData> theData = getData();
+        if (theData == null) {
+            return ui;
+        }
+
+        Collection<StateData> dataItems = theData.values();
+
+        for (StateData data: dataItems) {
+            String name  = data.getName();
+            String value = (String) data.getValue();
+
+            if (value == null) {
+                continue;
+            }
+
+            logger.debug("Append element '" + name + "' (" + value + ")");
+
+            Element dataElement = creator.create("data");
+            creator.addAttr(dataElement, "name", name, true);
+            creator.addAttr(dataElement, "type", data.getType(), true);
+
+            Element itemElement = creator.create("item");
+            creator.addAttr(itemElement, "value", value, true);
+            creator.addAttr(
+                itemElement,
+                "label",
+                Resources.getMsg(meta, value, value),
+                true);
+
+            dataElement.appendChild(itemElement);
+            ui.appendChild(dataElement);
+        }
+
+        return ui;
+    }
+
+
+    public Element describe(
+        Artifact    artifact,
+        Document    document,
+        Node        root,
+        CallContext context,
+        String      uuid)
+    {
+        XMLUtils.ElementCreator creator = new XMLUtils.ElementCreator(
+            document,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element ui        = null;
+        String uiprovider = getUIProvider();
+        if (uiprovider != null) {
+            ui  = ProtocolUtils.createArtNode(
+                creator, "dynamic",
+                new String[] { "uiprovider" },
+                new String[] { uiprovider });
+        }
+        else {
+            ui  = ProtocolUtils.createArtNode(creator, "dynamic", null, null);
+        }
+
+        Map<String, StateData> theData = getData();
+        if (theData == null) {
+            return ui;
+        }
+
+        Collection<StateData> dataItems = theData.values();
+
+        for (StateData data: dataItems) {
+            String name    = data.getName();
+            Element select = createData(creator, artifact, data, context);
+
+            Element choices = ProtocolUtils.createArtNode(
+            creator, "choices", null, null);
+
+            select.appendChild(choices);
+            ui.appendChild(select);
+
+            Element[] items = createItems(creator, artifact, name, context);
+            for (Element item: items) {
+                choices.appendChild(item);
+            }
+        }
+
+        return ui;
+    }
+
+
+    /**
+     * This method creates the root node that contains the list of selectable
+     * items.
+     *
+     * @param cr The ElementCreator.
+     * @param name The name of the amount of data.
+     *
+     * @return the root node of the item list.
+     */
+    protected Element createData(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        StateData   data,
+        CallContext context)
+    {
+        Element select = ProtocolUtils.createArtNode(
+            cr, "select", null, null);
+        cr.addAttr(select, "name", data.getName(), true);
+
+        Element label = ProtocolUtils.createArtNode(
+            cr, "label", null, null);
+
+        select.appendChild(label);
+
+        label.setTextContent(Resources.getMsg(
+            context.getMeta(),
+            getID(),
+            getID()));
+
+        return select;
+    }
+
+
+    /**
+     * This method creates a list of items. These items represent the amount of
+     * input data that is possible for this state.
+     *
+     * @param cr The ElementCreator.
+     * @param name The name of the amount of data.
+     *
+     * @return a list of items.
+     */
+    protected abstract Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context);
+
+
+    /**
+     * This method validates the inserted data and returns true, if everything
+     * was correct, otherwise an exception is thrown.
+     *
+     * @param artifact A reference to the owner artifact.
+     * @param context The CallContext object.
+     *
+     * @return true, if everything was fine.
+     */
+    public boolean validate(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        return true;
+    }
+
+
+    protected String getUIProvider() {
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,30 @@
+package de.intevation.flys.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+
+public class DischargeLongitudinalSection extends DefaultState {
+
+    /** The logger that is used in this state.*/
+    private static Logger logger =
+        Logger.getLogger(DischargeLongitudinalSection.class);
+
+
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("DischargeLongitudinalSection.createItems");
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,35 @@
+package de.intevation.flys.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+
+/**
+ * The final state that will be reached after the duration curve calculation
+ * mode has been chosen.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DurationCurveState extends DefaultState {
+
+    /** The logger that is used in this state.*/
+    private static Logger logger = Logger.getLogger(DurationCurveState.class);
+
+
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("DurationCurveState.createItems");
+        return null;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,185 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.RiverFactory;
+import de.intevation.flys.artifacts.resources.Resources;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LocationDistanceSelect extends RangeState {
+
+    /** The logger used in this class.*/
+    private static Logger logger = Logger.getLogger(LocationDistanceSelect.class);
+
+
+    /** The default step width.*/
+    public static final String DEFAULT_STEP = "100";
+
+    /** The name of the 'from' field. */
+    public static final String FROM = "ld_from";
+
+    /** The name of the 'to' field. */
+    public static final String TO = "ld_to";
+
+    /** The name of the 'step' field. */
+    public static final String STEP = "ld_step";
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public LocationDistanceSelect() {
+    }
+
+    protected Element createData(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        StateData   data,
+        CallContext context)
+    {
+        Element select = ProtocolUtils.createArtNode(
+            cr, "select", null, null);
+
+        cr.addAttr(select, "name", data.getName(), true);
+
+        Element label = ProtocolUtils.createArtNode(
+            cr, "label", null, null);
+
+        Element choices = ProtocolUtils.createArtNode(
+            cr, "choices", null, null);
+
+        label.setTextContent(Resources.getMsg(
+            context.getMeta(),
+            data.getName(),
+            data.getName()));
+
+        select.appendChild(label);
+
+        return select;
+    }
+
+
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        double[] minmax = getMinMaxDistance(artifact);
+
+        double minVal = Double.MIN_VALUE;
+        double maxVal = Double.MAX_VALUE;
+
+        if (minmax != null) {
+            minVal = minmax[0];
+            maxVal = minmax[1];
+        }
+        else {
+            logger.warn("Could not read min/max distance values!");
+        }
+
+        if (name.equals("ld_from")) {
+            Element min = createItem(
+                cr,
+                new String[] {"min", new Double(minVal).toString()});
+
+            return new Element[] { min };
+        }
+        else if (name.equals("ld_to")) {
+            Element max = createItem(
+                cr,
+                new String[] {"max", new Double(maxVal).toString()});
+
+            return new Element[] { max };
+        }
+        else {
+            Element step = createItem(cr, new String[] {"step", DEFAULT_STEP});
+            return new Element[] { step };
+        }
+
+    }
+
+
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+
+    protected String getUIProvider() {
+        return "location_distance_panel";
+    }
+
+
+    protected double[] getMinMaxDistance(Artifact artifact) {
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+        StateData    data         = flysArtifact.getData("river");
+
+        String name = (String) data.getValue();
+
+        logger.debug("Search for the min/max distances of '" + name + "'");
+
+        River river = RiverFactory.getRiver(name);
+
+        return river != null ? river.determineMinMaxDistance() : null;
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        logger.debug("LocationDistanceSelect.validate");
+
+        Map<String, StateData> data = getData();
+
+        String fromStr = (String) data.get(FROM).getValue();
+        String toStr   = (String) data.get(TO).getValue();
+        String stepStr = (String) data.get(STEP).getValue();
+
+        if (fromStr == null || toStr == null || stepStr == null) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        try {
+            double from = Double.parseDouble(fromStr);
+            double to   = Double.parseDouble(toStr);
+            double step = Double.parseDouble(stepStr);
+
+            double[] minmaxDist = getMinMaxDistance(artifact);
+
+            return validateBounds(minmaxDist[0], minmaxDist[1], from, to, step);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException("error_feed_number_format");
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,93 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.Map;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.data.StateData;
+
+
+/**
+ * This state is used to realize the input of multiple locations as string. The
+ * string should be a whitespace separated list of double values where each
+ * double value represents a location.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LocationSelect extends LocationDistanceSelect {
+
+    /** The logger used in this class.*/
+    private static Logger logger = Logger.getLogger(LocationSelect.class);
+
+
+    /** The name of the StateData object that stores the location string.*/
+    public static final String FIELD_LOCATIONS = "ld_locations";
+
+
+    public LocationSelect() {
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "location_panel";
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        logger.debug("LocationSelect.validate");
+
+        Map<String, StateData> data = getData();
+
+        String locationStr = (String) data.get(FIELD_LOCATIONS).getValue();
+
+        if (locationStr == null || locationStr.length() == 0) {
+            logger.error("No locations given.");
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        double[] minmax = getMinMaxDistance(artifact);
+        double[] mm     = extractLocations(locationStr);
+
+        logger.debug("Inserted min location: " + mm[0]);
+        logger.debug("Inserted max location: " + mm[mm.length-1]);
+
+        return validateBounds(minmax[0], minmax[1], mm[0], mm[mm.length-1], 0d);
+    }
+
+
+    /**
+     * This method takes a string that consist of whitespace separated double
+     * values and returns the double values as array.
+     *
+     * @param locationStr The locations inserted in this state.
+     *
+     * @return the locations as array.
+     */
+    protected double[] extractLocations(String locationStr) {
+        String[] tmp               = locationStr.split(" ");
+        TDoubleArrayList locations = new TDoubleArrayList();
+
+        for (String l: tmp) {
+            try {
+                locations.add(Double.parseDouble(l));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        locations.sort();
+
+        return locations.toNativeArray();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RangeState.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,54 @@
+package de.intevation.flys.artifacts.states;
+
+import org.apache.log4j.Logger;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class RangeState extends DefaultState {
+
+    /** The logger that is used in this class.*/
+    private static Logger logger = Logger.getLogger(RangeState.class);
+
+
+    public RangeState() {
+    }
+
+
+    /**
+     * Validates a given range with a given valid range.
+     *
+     * @param fromValid Valid lower value of the range.
+     * @param toValid Valid upper value of the range.
+     * @param from The lower value.
+     * @param to The upper value.
+     * @param step The step width.
+     *
+     * @return true, if everything was fine, otherwise an exception is thrown.
+     */
+    protected boolean validateBounds(
+        double fromValid, double toValid,
+        double from,      double to,      double step)
+    throws IllegalArgumentException
+    {
+        logger.debug("RangeState.validateRange");
+
+        if (from < fromValid) {
+            logger.error(
+                "Invalid 'from'. " + from + " is smaller than " + fromValid);
+            throw new IllegalArgumentException("error_feed_from_out_of_range");
+        }
+        else if (to > toValid) {
+            logger.error(
+                "Invalid 'to'. " + to + " is bigger than " + toValid);
+            throw new IllegalArgumentException("error_feed_to_out_of_range");
+        }
+        else if (from > to) {
+            throw new IllegalArgumentException("error_feed_from_bigger_to");
+        }
+
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/RiverSelect.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,162 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.model.RiverFactory;
+import de.intevation.flys.artifacts.resources.Resources;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class RiverSelect extends DefaultState {
+
+    /** The logger used in this class. */
+    private static Logger logger = Logger.getLogger(RiverSelect.class);
+
+
+    /** Error message that is thrown if no river was found based on a given
+     * name.*/
+    public static final String ERROR_NO_SUCH_RIVER =
+        "error_feed_no_such_river";
+
+    /** Error message that is thrown if no river was found based on a given
+     * name.*/
+    public static final String ERROR_NO_RIVER_SELECTED =
+        "error_feed_no_river_selected";
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public RiverSelect() {
+    }
+
+
+    /**
+     * Initialize the state based on the state node in the configuration.
+     *
+     * @param config The state configuration node.
+     */
+    public void setup(Node config) {
+        super.setup(config);
+    }
+
+
+    protected Element createData(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        StateData   data,
+        CallContext context)
+    {
+        Element select = ProtocolUtils.createArtNode(
+            cr, "select",
+            new String[] { "uiprovider" },
+            new String[] { "select_with_map" });
+        cr.addAttr(select, "name", data.getName(), true);
+
+        Element label = ProtocolUtils.createArtNode(
+            cr, "label", null, null);
+
+        Element choices = ProtocolUtils.createArtNode(
+            cr, "choices", null, null);
+
+        select.appendChild(label);
+
+        label.setTextContent(Resources.getMsg(
+            context.getMeta(),
+            getID(),
+            getID()));
+
+        return select;
+    }
+
+
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        List<River> rivers = RiverFactory.getRivers();
+        Element[] items    = new Element[rivers.size()];
+
+        int idx = 0;
+        for (River river: rivers) {
+            items[idx++] = createRiverItem(cr, river);
+        }
+
+        return items;
+    }
+
+
+    /**
+     * This method creates a node that represents a river item. This node
+     * contains the label and the value that describe the river.
+     *
+     * @param cr The ElementCreator.
+     * @param river The river.
+     *
+     * @return the element that contains the information about the river.
+     */
+    protected Element createRiverItem(XMLUtils.ElementCreator cr, River river) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        label.setTextContent(river.getName());
+        value.setTextContent(river.getName());
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        logger.debug("RiverSelect.validate");
+
+        Map<String, StateData> data = getData();
+
+        StateData dRiver = data.get("river");
+
+        if (dRiver == null || dRiver.getValue() == null) {
+            throw new IllegalArgumentException(ERROR_NO_RIVER_SELECTED);
+        }
+
+        River river = RiverFactory.getRiver((String) dRiver.getValue());
+
+        if (river == null) {
+            throw new IllegalArgumentException(ERROR_NO_SUCH_RIVER);
+        }
+
+        return true;
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "river_panel";
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/StateFactory.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,121 @@
+package de.intevation.flys.artifacts.states;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import de.intevation.artifactdatabase.data.DefaultStateData;
+import de.intevation.artifactdatabase.state.State;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class StateFactory {
+
+    /** The logger used in this class */
+    private static Logger logger = Logger.getLogger(StateFactory.class);
+
+    /** The XPath to the classname of the state */
+    public static final String XPATH_STATE = "@state";
+
+    /** The XPath to the data items of the state relative to the state node. */
+    public static final String XPATH_DATA = "data";
+
+    /** The XPath to the data name relative to the data node.*/
+    public static final String XPATH_DATA_NAME = "@name";
+
+    /** The XPath to the data type relative to the data node.*/
+    public static final String XPATH_DATA_TYPE = "@type";
+
+    /** The XPath to the data description relative to the data node.*/
+    public static final String XPATH_DATA_DESCRIPTION = "@description";
+
+
+    /**
+     * Creates a new State based on the configured class provided by
+     * <code>stateConf</code>.
+     *
+     * @param stateConf The configuration of the state.
+     *
+     * @return a State.
+     */
+    public static State createState(Node stateConf) {
+        String clazz = (String) XMLUtils.xpath(
+            stateConf, XPATH_STATE, XPathConstants.STRING);
+
+        State state = null;
+
+        try {
+            logger.debug("Create a new State for class: " + clazz);
+            state = (State) Class.forName(clazz).newInstance();
+            state.setup(stateConf);
+
+            initializeStateData(state, stateConf);
+        }
+        catch (InstantiationException ie) {
+            logger.error(ie, ie);
+        }
+        catch (IllegalAccessException iae) {
+            logger.error(iae, iae);
+        }
+        catch (ClassNotFoundException cnfe) {
+            logger.error(cnfe, cnfe);
+        }
+
+        return state;
+    }
+
+
+    /**
+     * This method extracts the configured input data of a state and adds new
+     * StateData objects to the State.
+     *
+     * @param state The state.
+     * @param stateConf The state configuration node.
+     */
+    protected static void initializeStateData(State state, Node stateConf) {
+        NodeList dataList = (NodeList) XMLUtils.xpath(
+            stateConf, XPATH_DATA, XPathConstants.NODESET);
+
+        if (dataList == null || dataList.getLength() == 0) {
+            logger.debug("The state has no input data configured.");
+
+            return;
+        }
+
+        int items = dataList.getLength();
+
+        logger.debug("The state has " + items + " data items configured.");
+
+        for (int i = 0; i < items; i++) {
+            Node data = dataList.item(i);
+
+            String name = (String) XMLUtils.xpath(
+                data, XPATH_DATA_NAME, XPathConstants.STRING);
+            String type = (String) XMLUtils.xpath(
+                data, XPATH_DATA_TYPE, XPathConstants.STRING);
+            String desc = (String) XMLUtils.xpath(
+                data, XPATH_DATA_DESCRIPTION, XPathConstants.STRING);
+
+            if (name == null || name.equals("")) {
+                logger.warn("No name for data item at pos " + i + " found.");
+                continue;
+            }
+
+            if (type == null || type.equals("")) {
+                logger.warn("No type for data item at pos " + i + " found.");
+                logger.warn("Default type 'string' used.");
+                type = "string";
+            }
+
+            state.addData(name, new DefaultStateData(name, type, desc));
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,321 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.Range;
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.Wst;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.RangeWithValues;
+import de.intevation.flys.artifacts.model.WstFactory;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQAdapted extends DefaultState {
+
+    /** The logger used in this state.*/
+    private static Logger logger = Logger.getLogger(WQAdapted.class);
+
+
+    public static final String FIELD_WQ_MODE = "wq_mode";
+
+    public static final String FIELD_WQ_VALUES = "wq_values";
+
+
+    /**
+     * This method creates one element for each gauge of the selected river that
+     * is intersected by the given kilometer range. Each element is a tuple of
+     * (from;to) where <i>from</i> is the lower bounds of the gauge or the lower
+     * kilometer range. <i>to</i> is the upper bounds of the gauge or the upper
+     * kilometer range.
+     *
+     * @param cr The ElementCreator.
+     * @param artifact The FLYS artifact.
+     * @param name The name of the data item.
+     * @param context The CallContext.
+     *
+     * @return a list of elements that consist of tuples of the intersected
+     * gauges of the selected river.
+     */
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("WQAdapted.createItems");
+
+        if (name != null && name.equals(FIELD_WQ_MODE)) {
+            return createModeItems(cr, artifact, name, context);
+        }
+        else if (name != null && name.equals(FIELD_WQ_VALUES)) {
+            return createValueItems(cr, artifact, name, context);
+        }
+        else {
+            logger.warn("Unknown data object: " + name);
+            return null;
+        }
+    }
+
+
+    protected Element[] createModeItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("WQAdapted.createModeItems");
+
+        Element w = createItem(cr, new String[] { "w", "W" });
+        Element q = createItem(cr, new String[] { "q", "Q" });
+
+        return new Element[] { w, q };
+    }
+
+
+    protected Element[] createValueItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        logger.debug("WQAdapted.createValueItems");
+
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+
+        double[]    dist   = flysArtifact.getDistance();
+        List<Gauge> gauges = flysArtifact.getGauges();
+
+        int num = gauges != null ? gauges.size() : 0;
+
+        if (num == 0) {
+            logger.warn("Selected distance matches no gauges.");
+            return null;
+        }
+
+        Element[] elements = new Element[num];
+
+        int idx = 0;
+
+        for (Gauge gauge: gauges) {
+            Range range  = gauge.getRange();
+            double lower = range.getA().doubleValue();
+            double upper = range.getB().doubleValue();
+
+            double from = dist[0] < lower ? lower : dist[0];
+            double to   = dist[1] > upper ? upper : dist[1];
+
+            String key = Double.toString(from) + ";" + Double.toString(to);
+
+            elements[idx++] = createItem(cr, new String[] {key, ""});
+        }
+
+        return elements;
+    }
+
+
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "wq_panel_adapted";
+    }
+
+
+    public boolean validate(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQAdapted.validate");
+
+        Map<String, StateData> data = getData();
+
+        String mode = (String) data.get(FIELD_WQ_MODE).getValue();
+
+        if (mode != null && mode.equals("W")) {
+            return validateW(artifact, context);
+        }
+        else if (mode != null && mode.equals("Q")) {
+            return validateQ(artifact, context);
+        }
+        else {
+            throw new IllegalArgumentException("error_feed_no_wq_mode_selected");
+        }
+    }
+
+
+    protected boolean validateW(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQAdapted.validateW");
+
+        RangeWithValues[] rwvs = extractInput(getData("wq_values"));
+        List<Gauge>     gauges = ((FLYSArtifact) artifact).getGauges();
+
+        for (Gauge gauge: gauges) {
+            Range range  = gauge.getRange();
+            double lower = range.getA().doubleValue();
+            double upper = range.getB().doubleValue();
+
+            for (RangeWithValues rwv: rwvs) {
+                if (lower <= rwv.getLower() && upper >= rwv.getUpper()) {
+                    compareWsWithGauge(gauge, rwv.getValues());
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    protected boolean validateQ(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQAdapted.validateQ");
+
+        RangeWithValues[] rwvs = extractInput(getData("wq_values"));
+        List<Gauge>     gauges = ((FLYSArtifact) artifact).getGauges();
+        River            river = ((FLYSArtifact) artifact).getRiver();
+        Wst                wst = WstFactory.getWst(river);
+
+        for (Gauge gauge: gauges) {
+            Range range  = gauge.getRange();
+            double lower = range.getA().doubleValue();
+            double upper = range.getB().doubleValue();
+
+            for (RangeWithValues rwv: rwvs) {
+                if (lower <= rwv.getLower() && upper >= rwv.getUpper()) {
+                    compareQsWithGauge(wst, gauge, rwv.getValues());
+                }
+            }
+        }
+
+        return true;
+    }
+
+
+    protected boolean compareQsWithGauge(Wst wst, Gauge gauge, double[] qs)
+    throws IllegalArgumentException
+    {
+        double[] minmax = gauge != null
+            ? wst.determineMinMaxQ(gauge.getRange())
+            : null;
+
+        if (minmax == null) {
+            logger.warn("Could not determine min/max Q of gauge.");
+            return true;
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Validate Qs with:");
+            logger.debug("-- Gauge: " + gauge.getName());
+            logger.debug("-- Gauge min: " + minmax[0]);
+            logger.debug("-- Gauge max: " + minmax[1]);
+        }
+
+        for (double q: qs) {
+            if (q < minmax[0] || q > minmax[1]) {
+                throw new IllegalArgumentException(
+                    "error_feed_q_values_invalid");
+            }
+        }
+
+        return true;
+    }
+
+
+    protected boolean compareWsWithGauge(Gauge gauge, double[] ws)
+    throws IllegalArgumentException
+    {
+        double[] minmax = gauge != null
+            ? gauge.determineMinMaxW()
+            : null;
+
+        if (minmax == null) {
+            logger.warn("Could not determine min/max W of gauge.");
+            return true;
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Validate Ws with:");
+            logger.debug("-- Gauge: " + gauge.getName());
+            logger.debug("-- Gauge min: " + minmax[0]);
+            logger.debug("-- Gauge max: " + minmax[1]);
+        }
+
+        for (double w: ws) {
+            if (w < minmax[0] || w > minmax[1]) {
+                throw new IllegalArgumentException(
+                    "error_feed_w_values_invalid");
+            }
+        }
+
+        return true;
+    }
+
+
+    protected RangeWithValues[] extractInput(StateData data) {
+        String dataString = (String) data.getValue();
+        String[]   ranges = dataString.split(":");
+
+        List<RangeWithValues> rwv = new ArrayList<RangeWithValues>();
+
+        for (String range: ranges) {
+            String[] parts = range.split(";");
+
+            double lower = Double.parseDouble(parts[0]);
+            double upper = Double.parseDouble(parts[1]);
+
+            String[] values = parts[2].split(",");
+
+            int      num = values.length;
+            double[] res = new double[num];
+
+            for (int i = 0; i < num; i++) {
+                try {
+                    res[i] = Double.parseDouble(values[i]);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.warn(nfe, nfe);
+                }
+            }
+
+            rwv.add(new RangeWithValues(lower, upper, res));
+        }
+
+        return (RangeWithValues[]) rwv.toArray(new RangeWithValues[rwv.size()]);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/states/WQSelect.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,356 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.Map;
+
+import gnu.trove.TDoubleArrayList;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.Wst;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.WstFactory;
+import de.intevation.flys.artifacts.resources.Resources;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQSelect extends RangeState {
+
+    /** The logger used in this class.*/
+    private static Logger logger = Logger.getLogger(WQSelect.class);
+
+
+    /** The default step width for Qs.*/
+    public static final String DEFAULT_STEP_Q = "50";
+
+    /** The default step width for Qs.*/
+    public static final String DEFAULT_STEP_W = "30";
+
+    /** The name of the 'mode' field. */
+    public static final String WQ_MODE = "wq_mode";
+
+    /** The name of the 'selection' field.*/
+    public static final String WQ_SELECTION = "wq_selection";
+
+    /** The name of the 'from' field. */
+    public static final String WQ_FROM = "wq_from";
+
+    /** The name of the 'to' field. */
+    public static final String WQ_TO = "wq_to";
+
+    /** The name of the 'step' field. */
+    public static final String WQ_STEP = "wq_step";
+
+    /** The name of the 'single' field. */
+    public static final String WQ_SINGLE = "wq_single";
+
+
+    /**
+     * The default constructor that initializes an empty State object.
+     */
+    public WQSelect() {
+    }
+
+    protected Element createData(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        StateData   data,
+        CallContext context)
+    {
+        Element select = ProtocolUtils.createArtNode(
+            cr, "select", null, null);
+
+        cr.addAttr(select, "name", data.getName(), true);
+
+        Element label = ProtocolUtils.createArtNode(
+            cr, "label", null, null);
+
+        Element choices = ProtocolUtils.createArtNode(
+            cr, "choices", null, null);
+
+        label.setTextContent(Resources.getMsg(
+            context.getMeta(),
+            data.getName(),
+            data.getName()));
+
+        select.appendChild(label);
+
+        return select;
+    }
+
+
+    protected Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context)
+    {
+        // TODO Insert correct min/max values!
+        double[] minmaxW = determineMinMaxW(artifact);
+        double[] minmaxQ = determineMinMaxQ(artifact);
+
+        if (name.equals("wq_from")) {
+            Element minW = createItem(
+                cr, new String[] {"minW", new Double(minmaxW[0]).toString()});
+            Element minQ = createItem(
+                cr, new String[] {"minQ", new Double(minmaxQ[0]).toString()});
+            return new Element[] { minW, minQ };
+        }
+        else if (name.equals("wq_to")) {
+            Element maxW = createItem(
+                cr, new String[] {"maxW", new Double(minmaxW[1]).toString()});
+            Element maxQ = createItem(
+                cr, new String[] {"maxQ", new Double(minmaxQ[1]).toString()});
+            return new Element[] { maxW, maxQ };
+        }
+        else {
+            Element stepW = createItem(cr, new String[] {"stepW", DEFAULT_STEP_W});
+            Element stepQ = createItem(cr, new String[] {"stepQ", DEFAULT_STEP_Q});
+            return new Element[] { stepW, stepQ };
+        }
+    }
+
+
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        Element item  = ProtocolUtils.createArtNode(cr, "item", null, null);
+        Element label = ProtocolUtils.createArtNode(cr, "label", null, null);
+        Element value = ProtocolUtils.createArtNode(cr, "value", null, null);
+
+        String[] arr = (String[]) obj;
+
+        label.setTextContent(arr[0]);
+        value.setTextContent(arr[1]);
+
+        item.appendChild(label);
+        item.appendChild(value);
+
+        return item;
+    }
+
+
+    protected String getUIProvider() {
+        return "wq_panel";
+    }
+
+
+    /**
+     * Determines the min and max W value for the current gauge. If no min and
+     * max values could be determined, this method will return
+     * [Double.MIN_VALUE, Double.MAX_VALUE].
+     *
+     * @param artifact The FLYSArtifact.
+     *
+     * @return the min and max W values for the current gauge.
+     */
+    protected double[] determineMinMaxW(Artifact artifact) {
+        logger.debug("WQSelect.determineCurrentGauge");
+
+        Gauge    gauge   = ((FLYSArtifact) artifact).getGauge();
+        double[] minmaxW = gauge != null ? gauge.determineMinMaxW() : null;
+
+        double minW = minmaxW != null ? minmaxW[0] : Double.MIN_VALUE;
+        double maxW = minmaxW != null ? minmaxW[1] : Double.MAX_VALUE;
+
+        return new double[] { minW, maxW };
+    }
+
+
+    /**
+     * Determines the min and max Q value for the current gauge. If no min and
+     * max values could be determined, this method will return
+     * [Double.MIN_VALUE, Double.MAX_VALUE].
+     *
+     * @param artifact The FLYSArtifact.
+     *
+     * @return the min and max Q values for the current gauge.
+     */
+    protected double[] determineMinMaxQ(Artifact artifact) {
+        logger.debug("WQSelect.determineMinMaxQ");
+
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+
+        River    river = flysArtifact.getRiver();
+        Gauge    gauge = flysArtifact.getGauge();
+        Wst      wst   = WstFactory.getWst(river);
+
+        double[] minmaxQ = gauge != null
+            ? wst.determineMinMaxQ(gauge.getRange())
+            : null;
+
+        double minQ = minmaxQ != null ? minmaxQ[0] : Double.MIN_VALUE;
+        double maxQ = minmaxQ != null ? minmaxQ[1] : Double.MAX_VALUE;
+
+        return new double[] { minQ, maxQ };
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQSelect.validate");
+
+        Map<String, StateData> data = getData();
+
+        String selectionMode = (String) data.get(WQ_SELECTION).getValue();
+
+        if (selectionMode == null || selectionMode.equals("single")) {
+            return validateSingle(artifact, context);
+        }
+        else {
+            return validateRange(artifact, context);
+        }
+    }
+
+
+    protected boolean validateSingle(Artifact artifact, CallContext context)
+    throws    IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateSingle");
+
+        String tmp = (String) data.get(WQ_SINGLE).getValue();
+
+        if (tmp == null || tmp.length() == 0) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        String[] strValues = tmp.split(" ");
+        TDoubleArrayList all = new TDoubleArrayList();
+
+        for (String strValue: strValues) {
+            try {
+                all.add(Double.parseDouble(strValue));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        all.sort();
+
+        String mode = (String) data.get(WQ_MODE).getValue();
+        logger.debug("WQ Mode: " + mode);
+
+        double[] minmax = null;
+
+        if (mode != null && mode.trim().toLowerCase().equals("w")) {
+            minmax = determineMinMaxW(artifact);
+        }
+        else {
+            minmax = determineMinMaxQ(artifact);
+        }
+
+        double min = all.get(0);
+        double max = all.get(all.size()-1);
+
+        logger.debug("Inserted min value = " + min);
+        logger.debug("Inserted max value = " + max);
+
+        return validateBounds(minmax[0], minmax[1], min, max, 0d);
+    }
+
+
+    protected boolean validateRange(Artifact artifact, CallContext context)
+    throws    IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateRange");
+
+        String mode = (String) data.get(WQ_MODE).getValue();
+        logger.debug("WQ Mode: " + mode);
+
+        String fromStr = (String) data.get(WQ_FROM).getValue();
+        String toStr   = (String) data.get(WQ_TO).getValue();
+        String stepStr = (String) data.get(WQ_STEP).getValue();
+
+        if (fromStr == null || toStr == null || stepStr == null) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        double from = Double.parseDouble(fromStr);
+        double to   = Double.parseDouble(toStr);
+        double step = Double.parseDouble(stepStr);
+
+        try {
+            if (mode != null && mode.trim().toLowerCase().equals("w")) {
+                return validateW(artifact, context, from, to, step);
+            }
+            else if (mode != null && mode.trim().toLowerCase().equals("q")) {
+                return validateQ(artifact, context, from, to, step);
+            }
+            else {
+                throw new IllegalArgumentException("error_feed_invalid_wq_mode");
+            }
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException("error_feed_number_format");
+        }
+    }
+
+
+    /**
+     * Validates the inserted W values.
+     *
+     * @param artifact The owner artifact.
+     * @param context The CallContext
+     * @param from The lower value of the W range.
+     * @param to The upper value of the W range.
+     * @param step The step width.
+     *
+     * @return true, if everything was fine, otherwise an exception is thrown.
+     */
+    protected boolean validateW(
+        Artifact    artifact,
+        CallContext context,
+        double from,
+        double to,
+        double step)
+    throws    IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateW");
+
+        double[] minmaxW = determineMinMaxW(artifact);
+
+        return validateBounds(minmaxW[0], minmaxW[1], from, to, step);
+    }
+
+
+    /**
+     * Validates the inserted Q values.
+     *
+     * @param artifact The owner artifact.
+     * @param context The CallContext
+     * @param from The lower value of the Q range.
+     * @param to The upper value of the Q range.
+     * @param step The step width.
+     *
+     * @return true, if everything was fine, otherwise an exception is thrown.
+     */
+    protected boolean validateQ(
+        Artifact    artifact,
+        CallContext context,
+        double from,
+        double to,
+        double step)
+    throws    IllegalArgumentException
+    {
+        logger.debug("WQSelect.validateQ");
+
+        double[] minmaxQ = determineMinMaxQ(artifact);
+
+        return validateBounds(minmaxQ[0], minmaxQ[1], from, to, step);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/DefaultTransition.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,104 @@
+package de.intevation.flys.artifacts.transitions;
+
+import org.w3c.dom.Node;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.artifactdatabase.state.State;
+import de.intevation.artifactdatabase.transition.Transition;
+
+
+/**
+ * The default implementation of a <code>Transition</code>.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultTransition implements Transition {
+
+    /** The ID of the current state */
+    protected String from;
+
+    /** The ID of the target state */
+    protected String to;
+
+
+    /**
+     * The default constructor.
+     */
+    public DefaultTransition() {
+    }
+
+
+    /**
+     * The default constructor.
+     *
+     * @param from The current state.
+     * @param to The target state.
+     */
+    public DefaultTransition(String from, String to) {
+        this.from = from;
+        this.to   = to;
+    }
+
+
+    public void init(Node config) {
+        // nothing to do in the default transition
+    }
+
+
+    /**
+     * Returns the current state ID.
+     *
+     * @return the current state ID.
+     */
+    public String getFrom() {
+        return from;
+    }
+
+
+    /**
+     * Returns the target state ID.
+     *
+     * @return the target state ID.
+     */
+    public String getTo() {
+        return to;
+    }
+
+
+    /**
+     * Set the current state ID.
+     *
+     * @param to the current state ID.
+     */
+    public void setFrom(String from) {
+        this.from = from;
+    }
+
+
+    /**
+     * Set the target state ID.
+     *
+     * @param to the target state ID.
+     */
+    public void setTo(String to) {
+        this.to = to;
+    }
+
+
+    /**
+     * Determines if its valid to step from state <i>a</i> of an artifact
+     * <i>artifact</i> to state <i>b</i>. This method always returns true - no
+     * validation takes place.
+     *
+     * @param artifact The owner artifact of state a and b.
+     * @param a The current state.
+     * @param b The target state.
+     *
+     * @return true, if it is valid to step from a to b, otherwise false.
+     */
+    public boolean isValid(Artifact artifact, State a, State b) {
+        return true;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/TransitionFactory.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,71 @@
+package de.intevation.flys.artifacts.transitions;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Node;
+
+import de.intevation.artifactdatabase.transition.Transition;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class TransitionFactory {
+
+    /** The logger used in this class */
+    private static Logger logger = Logger.getLogger(TransitionFactory.class);
+
+    /** The XPath to the classname of the transition */
+    public static final String XPATH_TRANSITION = "@transition";
+
+    /** The XPath to the current state ID */
+    public static final String XPATH_CURRENT_STATE = "from/@state";
+
+    /** The XPath to the target state ID */
+    public static final String XPATH_TARGET_STATE = "to/@state";
+
+
+    /**
+     * Creates a new Transition based on the configured class provided by
+     * <code>transitionConf</code>.
+     *
+     * @param transitionConf The configuration of the transition.
+     *
+     * @return a Transition.
+     */
+    public static Transition createTransition(Node transitionConf) {
+        String clazz = (String) XMLUtils.xpath(
+            transitionConf, XPATH_TRANSITION, XPathConstants.STRING);
+
+        Transition transition = null;
+
+        try {
+            transition = (Transition) Class.forName(clazz).newInstance();
+
+            String from = (String) XMLUtils.xpath(
+                transitionConf, XPATH_CURRENT_STATE, XPathConstants.STRING);
+            String to = (String) XMLUtils.xpath(
+                transitionConf, XPATH_TARGET_STATE, XPathConstants.STRING);
+
+            transition.init(transitionConf);
+            transition.setFrom(from);
+            transition.setTo(to);
+        }
+        catch (InstantiationException ie) {
+            logger.error(ie, ie);
+        }
+        catch (IllegalAccessException iae) {
+            logger.error(iae, iae);
+        }
+        catch (ClassNotFoundException cnfe) {
+            logger.error(cnfe, cnfe);
+        }
+
+        return transition;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/artifacts/transitions/ValueCompareTransition.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,118 @@
+package de.intevation.flys.artifacts.transitions;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Node;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.artifactdatabase.data.StateData;
+import de.intevation.artifactdatabase.state.State;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+
+/**
+ * This transition compares data objects with a <i>equal</i> or <i>notequal</i>
+ * operator.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ValueCompareTransition extends DefaultTransition {
+
+    /** The logger that is used in this transition.*/
+    private static Logger log = Logger.getLogger(ValueCompareTransition.class);
+
+
+    /** XPath that points to the condition's operator.*/
+    public static final String XPATH_OPERATOR = "condition/@operator";
+
+    /** XPath that points to the condition's value.*/
+    public static final String XPATH_VALUE    = "condition/@value";
+
+    /** XPath that points  to the condition's dataname.*/
+    public static final String XPATH_DATANAME = "condition/@data";
+
+    /** The value of the 'equal' operator.*/
+    public static final String OPERATOR_EQUAL = "equal";
+
+    /** The value of the 'not equal' operator.*/
+    public static final String OPERATOR_NOTEQUAL = "notequal";
+
+
+    /** The operator.*/
+    protected String operator;
+
+    /** The value used for comparison.*/
+    protected String value;
+
+    /** The name of the data used for the comparison.*/
+    protected String dataname;
+
+
+
+    public ValueCompareTransition() {
+    }
+
+
+    public ValueCompareTransition(String from, String to) {
+        super(from, to);
+    }
+
+
+    @Override
+    public void init(Node config) {
+        log.debug("ValueCompareTransition.init");
+
+        String tmp = (String) XMLUtils.xpath(
+            config,
+            XPATH_OPERATOR,
+            XPathConstants.STRING);
+        operator = tmp.trim().toLowerCase();
+
+        value = (String) XMLUtils.xpath(
+            config,
+            XPATH_VALUE,
+            XPathConstants.STRING);
+
+        dataname = (String) XMLUtils.xpath(
+            config,
+            XPATH_DATANAME,
+            XPathConstants.STRING);
+    }
+
+
+    @Override
+    public boolean isValid(Artifact artifact, State a, State b) {
+        log.debug("ValueCompareTransition.isValid");
+
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+
+        StateData dataObj = flysArtifact.getData(dataname);
+        String    dataVal = dataObj != null ? (String) dataObj.getValue() : "";
+
+        if (log.isDebugEnabled()) {
+            log.debug("Compare settings:");
+            log.debug("-- dataname: " + dataname);
+            log.debug("-- operator: " + operator);
+            log.debug("-- compare value: " + value);
+            log.debug("-- state value: " + dataVal);
+        }
+
+        if (operator.equals(OPERATOR_EQUAL)) {
+            return value.equals(dataVal);
+        }
+        else if (operator.equals(OPERATOR_NOTEQUAL)) {
+            return !(value.equals(dataVal));
+        }
+
+        log.error("Wrong operator set! No comparison takes place.");
+
+        return false;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/AttributeParser.java	Fri Sep 28 12:14:10 2012 +0200
@@ -0,0 +1,126 @@
+package de.intevation.flys.collections;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import de.intevation.artifacts.ArtifactNamespaceContext;
+
+import de.intevation.artifactdatabase.state.DefaultOutput;
+import de.intevation.artifactdatabase.state.Output;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.model.ManagedFacet;
+
+
+public class AttributeParser {
+
+    /** Constant XPath that points to the outputmodes of an artifact.*/
+    public static final String XPATH_ARTIFACT_OUTPUTMODES =
+        "/art:output";
+
+
+    private static Logger logger = Logger.getLogger(AttributeParser.class);
+
+
+    protected Map<String, Output> outs;
+
+
+    public AttributeParser() {
+        this.outs = new HashMap<String, Output>();
+    }
+
+
+    public void parse(Document doc) {
+        logger.debug("AttributeParser.parse");
+
+        NodeList outs = (NodeList) XMLUtils.xpath(
+            doc,
+            XPATH_ARTIFACT_OUTPUTMODES,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = outs != null ? outs.getLength() : 0;
+
+        logger.debug("Attribute has " + num + " outputs.");
+
+        for (int i = 0; i < num; i++) {
+            Node out = outs.item(i);
+
+            parseOutput(out);
+        }
+    }
+
+
+    public Map<String, Output> getOuts() {
+        return outs;
+    }
+
+
+    protected void addItem(String out, ManagedFacet item) {
+        Output o = outs.get(out);
+
+        if (o != null) {
+            o.addFacet(item);
+        }
+    }
+
+
+    protected void parseOutput(Node out) {
+        String name = XMLUtils.xpathString(
+            out, "@name", ArtifactNamespaceContext.INSTANCE);
+
+        if (outs.get(name) == null) {
+            logger.debug("Create new output: " + name);
+
+            Output o = new DefaultOutput(name, null, null);
+            outs.put(name, o);
+        }
+
+        parseItems(out, name);
+    }
+
+
+    protected void parseItems(Node out, String outname) {
+        NodeList themes = (NodeList) XMLUtils.xpath(
+            out, "art:theme",
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = themes != null ? themes.getLength() : 0;
+
+        logger.debug("Output has " + num + " themes.");
+
+        for (int i = 0; i < num; i++) {
+            Node theme = themes.item(i);
+
+            String name = XMLUtils.xpathString(
+                theme, "@facet", ArtifactNamespaceContext.INSTANCE);
+
+            String uuid = XMLUtils.xpathString(
+                theme, "@artifact", ArtifactNamespaceContext.INSTANCE);
+
+            String pos = XMLUtils.xpathString(
+                theme, "@pos", ArtifactNamespaceContext.INSTANCE);
+
+            String active = XMLUtils.xpathString(
+                theme, "@active", ArtifactNamespaceContext.INSTANCE);
+
+            ManagedFacet item = new ManagedFacet(
+                name, "", uuid,
+                Integer.parseInt(pos),
+                Integer.parseInt(active));
+
+            addItem(outname, item);
+        }
+    }