changeset 508:a9c7f6ec3a5a 2.3.1

merged flys-backend/2.3.1
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:12 +0200
parents ebf049a1eb53 (diff) 730f8500a73a (current diff)
children 7391c79669f3
files
diffstat 250 files changed, 31442 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:12 2012 +0200
@@ -0,0 +1,1928 @@
+2011-05-20  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 2.3.1
+
+2011-05-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* Changes: Prepared changes for the upcoming release.
+
+2011-05-20  Hans Plum <hans@intevation.de>
+
+	* NEWS:
+	Hint to Release 2.3.1. For further information look into module
+	flys-client/NEWS
+
+2011-05-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java (getExplodedValues):
+	  Increment kms array size by one to take the end of range, too.
+
+2011-05-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java:
+	  Sort by rows (should not be necessary).
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Use unsharp km lookup (epsilon = 0.0001). This fixes the problem
+	  that some kms were not found.
+
+2011-05-19  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue66
+
+	* src/main/resources/messages_en.properties: Fixed broken template.
+
+2011-05-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Set the name of the computed discharge curve objects.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  The curves of this chart will now have names that consist of the word
+	  'Discharge Curve', the river name and the kilometer that has been used
+	  for the computation.
+
+	* 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
+	  computed discharge curves.
+
+2011-05-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Added convenience method isQ() to determine if we are
+	  doing Q calculations.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Reintroduced titles for the "W for unausgeglichene Abfluesse".
+
+2011-05-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  The curves will now have names that consist of the gauge name and its
+	  valid time range.
+
+	* 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
+	  discharge curves.
+
+2011-05-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Removed dead code.
+
+2011-05-19	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Use the correct method to generate kms.
+
+	* src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java:
+	  Added some logging to test the map in debug mode.
+
+2011-05-18	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Made getExplodedValues static.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Use new logic to calculate "W für ungleichwertige Abfluesse".
+	  Not working, yet.
+
+	* ChangeLog: Fixed former entry.
+
+2011-05-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Registered the WST export for discharge
+	  longitudinal sections.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Set the
+	  names of the discharge longitudinal section computation results.
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java:
+	  The W/Q curves in the chart will now have names.
+
+	* src/main/java/de/intevation/flys/exports/WaterlevelExporter.java:
+	  The WstWriter is filled with column names in an own method. So, we are
+	  able to override this process in subclasses.
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java:
+	  Adapted the column names for the WST export.
+
+2011-05-18	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Work on flys/issue69
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Use new logic to calculate "Wasserstand/Wasserspiegellage".
+	  Compared to desktop FLYS are the results are structurally right 
+	  but a bit off in the positions after the decimal points.
+	  Maybe a result of the interpolation? Need to debug this.
+
+2011-05-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  W and Q curves will now have names based on the defined W or Q values
+	  for the waterlevel computation.
+
+2011-05-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/NamedObject.java:
+	  New. This object is used to give objects a name.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java: Inherit
+	  from NamedObject now. Because we need to display names for those objects
+	  in different places.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: The WQKms
+	  objects returned by a waterlevel computation will now have names.
+
+	* src/main/java/de/intevation/flys/exports/WaterlevelExporter.java:
+	  Insert the column names for the WSTs into the WstWriter.
+
+	* src/main/java/de/intevation/flys/exports/WstWriter.java: The column
+	  names are written into the head of the WSTs now.
+
+2011-05-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstLine.java: New. This
+	  class is used to store the W/Q values of a specific kilometer of a WST.
+
+	* src/main/java/de/intevation/flys/exports/WstWriter.java: New. A writer
+	  that creates WSTs.
+
+	  TODO: The header of the WSTs is not finished. The Q descriptions are
+	  missing.
+
+	* src/main/java/de/intevation/flys/exports/WaterlevelExporter.java:
+	  Enabled WST exports.
+
+	* doc/conf/artifacts/winfo.xml: Registered the WST export for waterlevels.
+
+2011-05-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/utils/Formatter.java: New. This class
+	  supports functions to retrieve formatters for specific types of data
+	  used in FLYS.
+
+	* 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,
+	  src/main/java/de/intevation/flys/exports/AbstractExporter.java:
+	  Removed the formatter declaration - the whole formatter stuff is done in
+	  Formatter now.
+
+2011-05-17  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-72
+
+	* src/main/java/de/intevation/flys/artifacts/services/MainValuesService.java:
+	  Repaired broken XPath expressions to extract start and end kilometer.
+
+2011-05-17	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Removed the Hibernate loading stuff.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java:
+	  New. The Hibernate loading.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Use the WstValueTableFactory for loading now.
+
+2011-05-17  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 0.1 aka Version 2.3.0
+
+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:12 2012 +0200
@@ -0,0 +1,89 @@
+2011-05-19  RELEASE 2.3.1
+
+    NEW:
+
+        * New export format for waterlevels: WST.
+
+        * Added descriptions for the curves of the following charts:
+          - discharge curves (dt. 'Abflusskurven am Pegel')
+          - computed discharge curves (dt. 'berechnete Abflusskurven')
+          - longitudinal section curves (dt. 'Längsschnitt')
+          - discharge longitudinal section curves (dt. 'Abflusslängsschnitt')
+
+        * Number formatting is done in a central place/class.
+
+    FIXES:
+
+        * flys/issue47 (Diagramm: Farbliche Unterscheidung von Abfluß und Wasserstand)
+
+        * flys/issue52 (WINFO: W-Längsschnitt - Wasser jeweils von links nach rechts laufen lassen)
+
+        * flys/issue53 (WINFO/Berechnungsausgabe: Kilometerierung und Wasserstände werden zum Teil mit vielen Nachkommastellen angezeigt)
+
+        * flys/issue66: (i18n: Untertitel bei Längsschnitten - Bereich der Strecke enthält "double")
+
+        * flys/issue67 (WINFO: Längsschnitt - Wasser fließt bergauf)
+
+        * flys/issue72: (WINFO: Q/W/D-Info liefert selten eine Antwort)
+
+
+
+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:12 2012 +0200
@@ -0,0 +1,13 @@
+NEWS 
+
+for enduser-specific information, c.f. the NEWS -file in the module flys-client
+
+2011-05-19  Release V 2.3.1
+    * Bugfixing Release for WINFO
+
+2011-05-16  Release V 2.3.0
+    * Enhanced functionality for WINFO, diagram and Data-Manager (Datenkorb)
+
+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:12 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:12 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:12 2012 +0200
@@ -0,0 +1,189 @@
+<?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" />
+                        <facet name="wst" description="facet.waterlevel_export.wst" />
+                    </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" />
+                        <facet name="wst" 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:12 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:12 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:12 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:12 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:12 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:12 2012 +0200
@@ -0,0 +1,931 @@
+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;
+        }
+    }
+
+    public boolean isQ() {
+        StateData mode = getData("wq_mode");
+        return mode != null && mode.getValue().equals("Q");
+    }
+
+
+    /**
+     * 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 static double[] getExplodedValues(double from, double to, double step) {
+        double lower = from;
+
+        double diff = to - from;
+        double tmp  = diff / step;
+        int    num = (int)Math.abs(Math.ceil(tmp)) + 1;
+
+        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:12 2012 +0200
@@ -0,0 +1,707 @@
+package de.intevation.flys.artifacts;
+
+import java.util.List;
+import java.util.Vector;
+import java.util.ArrayList;
+
+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;
+import de.intevation.flys.artifacts.model.WstValueTable.QPosition;
+import de.intevation.flys.artifacts.model.WstValueTableFactory;
+
+import de.intevation.flys.artifacts.math.LinearRemap;
+
+import gnu.trove.TDoubleArrayList;
+
+/**
+ * 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();
+        double[] ws   = null;
+        boolean  qSel = true;
+
+        if (qs == null) {
+            logger.debug("Determine Q values based on a set of W values.");
+            qSel = false;
+            ws   = getWs();
+            qs   = getQsForWs(ws);
+        }
+
+        WstValueTable wst = WstValueTableFactory.getTable(river);
+        if (wst == null) {
+            throw new NullPointerException("No Wst found for selected river.");
+        }
+
+        WQKms[] results = computeWaterlevelData(kms, qs, wst);
+
+        // TODO Introduce a caching mechanism here!
+
+        setWaterlevelNames(results, qSel ? qs : ws, qSel ? "Q" : "W");
+
+        return results;
+    }
+
+
+    /**
+     * Sets the name for waterlevels where each WQKms in <i>r</i> represents a
+     * column.
+     *
+     * @param r The waterlevel columns.
+     * @param v The input values of the computations.
+     * @param wq The WQ mode - can be one of "W" or "Q".
+     */
+    public static void setWaterlevelNames(WQKms[] r, double[] v, String wq) {
+        for (int i = 0; i < v.length; i++) {
+            r[i].setName(wq + "=" + Double.toString(v[i]));
+        }
+    }
+
+
+    /**
+     * 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];
+
+        ArrayList<WQKms> results = new ArrayList<WQKms>();
+
+        for (int i = 0; i < qs.length; i++) {
+            double [] oqs = new double[kms.length];
+            double [] ows = new double[kms.length];
+            int referenceIndex = 0; // TODO: Make depend on the flow direction
+            WstValueTable.QPosition qPosition =
+                wst.interpolate(qs[i], referenceIndex, kms, ows, oqs);
+            if (qPosition != null) {
+                results.add(new WQKms(kms, oqs, ows));
+            }
+            else {
+                logger.warn("interpolation failed for q = " + qs[i]);
+            }
+        }
+
+        return results.toArray(new WQKms[results.size()]);
+    }
+
+
+    /**
+     * 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 = WstValueTableFactory.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 = WstValueTableFactory.getTable(r);
+        if (wst == null) {
+            throw new NullPointerException("No Wst found for selected river.");
+        }
+
+        WQKms wqkms = computeDischargeCurveData(wst, locations[0]);
+
+        // TODO Introduce a caching mechanism here!
+
+        setComputedDischargeCurveNames(wqkms, locations[0]);
+
+        return wqkms;
+    }
+
+
+    /**
+     * Sets the name of the computed discharge curve data.
+     *
+     * @param wqkms The computed WQKms object.
+     * @param l The location used for the computation.
+     */
+    public static void setComputedDischargeCurveNames(WQKms wqkms, double l) {
+        wqkms.setName(Double.toString(l));
+    }
+
+
+    /**
+     * 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.");
+            return new WQKms[0];
+        }
+
+        WstValueTable wst = WstValueTableFactory.getTable(river);
+        if (wst == null) {
+            logger.error("No wst found for selected river.");
+            return new WQKms[0];
+        }
+
+        double [][] segments = getSplittedDistance();
+
+        if (segments.length < 1) {
+            logger.warn("no segments given");
+            return new WQKms[0];
+        }
+
+        if (segments.length == 1) {
+            // fall back to normal "Wasserstand/Wasserspiegellage" calculation
+            double [] qs = toQs(segments[0]);
+            if (qs == null) {
+                logger.warn("no qs given");
+                return new WQKms[0];
+            }
+            if (qs.length == 1) {
+                double [] kms = getKms(segments[0]);
+                return computeWaterlevelData(kms, qs, wst);
+            }
+        }
+
+        // more than one segment
+
+        double [] boundKms;
+
+        if (segments.length == 2) {
+            boundKms = new double [] { segments[0][0], segments[1][1] };
+        }
+        else {
+            TDoubleArrayList bounds = new TDoubleArrayList();
+
+            bounds.add(segments[0][0]);
+
+            for (int i = 1; i < segments.length-1; ++i) {
+                double [] segment = segments[i];
+
+                Gauge gauge = river.determineGauge(segment[0], segment[1]);
+
+                if (gauge == null) {
+                    logger.warn("no gauge found between " + 
+                        segment[0] + " and " + segment[1]);
+                    bounds.add(0.5*(segment[0] + segment[1]));
+                }
+                else {
+                    bounds.add(gauge.getStation().doubleValue());
+                }
+            }
+
+            bounds.add(segments[segments.length-1][1]);
+            boundKms = bounds.toNativeArray();
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("bound kms: " + joinDoubles(boundKms));
+        }
+
+        double [][] iqs = null;
+
+        for (int i = 0; i < segments.length; ++i) {
+            double [] iqsi = toQs(segments[i]);
+            if (iqsi == null) {
+                logger.warn("iqsi == null");
+                return new WQKms[0];
+            }
+
+            if (iqs == null) {
+                iqs = new double[iqsi.length][boundKms.length];
+            }
+            else if (iqs.length != iqsi.length) {
+                logger.warn("iqsi.logger != iqs.length: "
+                    + iqsi.length + " " + iqsi.length);
+                return new WQKms[0];
+            }
+
+            if (logger.isDebugEnabled()) {
+                logger.debug("segments qs[ " + i + "]: " + joinDoubles(iqsi));
+            }
+
+            for (int j = 0; j < iqs.length; ++j) {
+                iqs[j][i] = iqsi[j];
+            }
+        }
+
+        if (logger.isDebugEnabled()) {
+            for (int i = 0; i < iqs.length; ++i) {
+                logger.debug("iqs[" + i + "]: " + joinDoubles(iqs[i]));
+            }
+        }
+
+        double [] boundWs = new double[boundKms.length];
+        double [] boundQs = new double[boundKms.length];
+
+        double [] okms = getKms(new double [] {
+            boundKms[0], boundKms[boundKms.length-1] });
+
+        ArrayList<WQKms> results = new ArrayList<WQKms>();
+
+        for (int i = 0; i < iqs.length; ++i) {
+            double [] iqsi = iqs[i];
+
+            QPosition qPosition = wst.interpolate(
+                iqsi[0], 0, boundKms, boundWs, boundQs);
+
+            if (qPosition == null) {
+                logger.warn("interpolation failed for " + iqsi[i]);
+                continue;
+            }
+
+            LinearRemap remap = new LinearRemap();
+
+            for (int j = 1; j < boundKms.length; ++j) {
+                remap.add(
+                    boundKms[j-1], boundKms[j],
+                    boundQs[j-1],  iqsi[j-1],
+                    boundQs[j],    iqsi[j]);
+            }
+
+            double [] oqs = new double[okms.length];
+            double [] ows = new double[okms.length];
+
+            wst.interpolate(okms, ows, oqs, qPosition, remap);
+
+            BackJumpCorrector bjc = new BackJumpCorrector();
+            if (bjc.doCorrection(okms, ows)) {
+                logger.debug("Discharge longitudinal section has backjumps.");
+                results.add(new WQCKms(okms, oqs, ows, bjc.getCorrected()));
+            }
+            else {
+                results.add(new WQKms(okms, oqs, ows));
+            }
+        }
+
+        WQKms [] wqkms = results.toArray(new WQKms[results.size()]);
+
+        setDischargeLongitudinalSectionNames(wqkms, iqs, isQ() ? "Q" : "W");
+
+        return wqkms;
+    }
+
+    protected static String joinDoubles(double [] x) {
+        if (x == null) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < x.length; ++i) {
+            if (i > 0) sb.append(", ");
+            sb.append(x[i]);
+        }
+        return sb.toString();
+    }
+
+    protected double [] toQs(double [] range) {
+        double [] qs = getQs(range);
+        if (qs == null) {
+            logger.debug("Determine Q values based on a set of W values.");
+            double [] ws = getWs(range);
+            qs = getQsForWs(ws);
+        }
+        return qs;
+    }
+
+
+    /**
+     * Sets the name for discharge longitudinal section curves where each WQKms
+     * in <i>r</i> represents a column.
+     */
+    public static void setDischargeLongitudinalSectionNames(
+        WQKms  []   wqkms,
+        double [][] iqs,
+        String      wq
+    ) {
+        logger.debug("WINFOArtifact.setDischargeLongitudinalSectionNames");
+
+        // TODO: I18N
+
+        for (int j = 0; j < wqkms.length; ++j) {
+            StringBuilder sb = new StringBuilder(wq)
+                .append(" benutzerdefiniert (");
+
+            double [] iqsi = iqs[j];
+            for (int i = 0; i < iqsi.length; i++) {
+                if (i > 0) {
+                    sb.append("; ");
+                }
+                sb.append(iqsi[i]);
+            }
+            sb.append(")");
+
+            wqkms[j].setName(sb.toString());
+        }
+    }
+}
+// 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:12 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:12 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:12 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:12 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:12 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:12 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:12 2012 +0200
@@ -0,0 +1,90 @@
+package de.intevation.flys.artifacts.math;
+
+import org.apache.log4j.Logger;
+
+public class LinearRemap
+{
+    private static Logger logger = Logger.getLogger(LinearRemap.class);
+
+    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);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("LinearRemap.add --------- enter");
+            logger.debug("  range: [" + from + ", " + to + "]");
+            logger.debug("  " + x1 + " -> " + y1 + " (" + head.eval(x1) + ")");
+            logger.debug("  " + x2 + " -> " + y2 + " (" + head.eval(x2) + ")");
+            logger.debug("LinearRemap.add --------- leave");
+        }
+    }
+
+    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:12 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:12 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:12 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:12 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:12 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/NamedObject.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,32 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+
+/**
+ * This class represents an object that has a name. The default case would be to
+ * inherit from this class.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class NamedObject implements Serializable {
+
+    /** The name of this object.*/
+    protected String name;
+
+
+    public NamedObject(String name) {
+        this.name = name;
+    }
+
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+}
+// 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:12 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:12 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:12 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:12 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:12 2012 +0200
@@ -0,0 +1,178 @@
+package de.intevation.flys.artifacts.model;
+
+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 extends NamedObject {
+
+    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("");
+    }
+
+
+    public WQKms(String name) {
+        super(name);
+
+        this.w   = new TDoubleArrayList();
+        this.q   = new TDoubleArrayList();
+        this.kms = new TDoubleArrayList();
+    }
+
+
+    public WQKms(int capacity) {
+        this(capacity, "");
+    }
+
+
+    public WQKms(int capacity, String name) {
+        super(name);
+
+        this.w   = new TDoubleArrayList(capacity);
+        this.q   = new TDoubleArrayList(capacity);
+        this.kms = new TDoubleArrayList(capacity);
+    }
+
+
+    public WQKms(double[] kms, double[] qs, double[] ws) {
+        this(kms, qs, ws, "");
+    }
+
+
+    public WQKms(double[] kms, double[] qs, double[] ws, String name) {
+        super(name);
+
+        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:12 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/WstLine.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,88 @@
+package de.intevation.flys.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+
+/**
+ * A model class that is used to store a line of a WST.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WstLine {
+
+    /** The kilometer value of the line.*/
+    protected double km;
+
+    /** The W values.*/
+    protected TDoubleArrayList ws;
+
+    /** The Q values.*/
+    protected TDoubleArrayList qs;
+
+
+    /**
+     * A constructor that builds a new WstLine for a specific kilometer.
+     *
+     * @param km The kilometer.
+     */
+    public WstLine(double km) {
+        this.km = km;
+        this.ws = new TDoubleArrayList();
+        this.qs = new TDoubleArrayList();
+    }
+
+
+    /**
+     * Adds a pair of W/Q to this line.
+     *
+     * @param w The W value.
+     * @param q The Q value.
+     */
+    public void add(double w, double q) {
+        ws.add(w);
+        qs.add(q);
+    }
+
+
+    /**
+     * Returns the kilometer of this line.
+     *
+     * @return the kilomter of this line.
+     */
+    public double getKm() {
+        return km;
+    }
+
+
+    /**
+     * Returns the W value at index <i>idx</i> of this line.
+     *
+     * @param idx The position of the desired W value.
+     *
+     * @return the W at position <i>idx</i>.
+     */
+    public double getW(int idx) {
+        return ws.size() > idx ? ws.get(idx) : -1d;
+    }
+
+
+    /**
+     * Returns the Q values of this line.
+     *
+     * @return the Q values of this line.
+     */
+    public double[] getQs() {
+        return qs.toNativeArray();
+    }
+
+
+    /**
+     * Returns the number of columns this line consists of.
+     *
+     * @return the columns this line consists of.
+     */
+    public int getSize() {
+        return qs.size();
+    }
+}
+// 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/WstValueTable.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,606 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+
+import de.intevation.flys.artifacts.math.LinearRemap;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+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;
+
+public class WstValueTable
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(WstValueTable.class);
+
+    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 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.0001) return -1;
+            if (d >  0.0001) return +1;
+            return 0;
+        }
+
+        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 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 void sortRows() {
+        Collections.sort(rows);
+    }
+
+
+    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;
+    }
+}
+// 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/WstValueTableFactory.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,196 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Collections;
+
+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;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.Wst;
+import de.intevation.flys.model.WstColumn;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import de.intevation.flys.backend.SessionHolder;
+
+public class WstValueTableFactory
+{
+    private static Logger log = Logger.getLogger(WstValueTableFactory.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 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
+
+    private WstValueTableFactory() {
+    }
+
+    public static WstValueTable getTable(River river) {
+        return getTable(river, 0);
+    }
+
+    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 table 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;
+            }
+        });
+
+        WstValueTable.Column [] columns =
+            new WstValueTable.Column[wstColumns.size()];
+
+        for (int i = 0; i < columns.length; ++i) {
+            columns[i] = new WstValueTable.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;
+        WstValueTable.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 WstValueTable.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);
+        }
+
+        // XXX: should not be necessary
+        valueTable.sortRows();
+
+        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:12 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:12 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:12 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:start/text()";
+
+    /** The XPath that points to the end definition of the incoming request.*/
+    public static final String XPATH_END = "/art:mainvalues/art:end/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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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:12 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);
+        }
+    }
+}
+// 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/AttributeWriter.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,148 @@
+package de.intevation.flys.collections;
+
+import java.util.Iterator;
+import java.util.List;
+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.ArtifactNamespaceContext;
+
+import de.intevation.artifactdatabase.state.Facet;
+import de.intevation.artifactdatabase.state.Output;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.flys.artifacts.model.ManagedFacet;
+
+
+public class AttributeWriter {
+
+    protected Map<String, Output> oldAttr;
+    protected Map<String, Output> newAttr;
+
+    private static Logger logger = Logger.getLogger(AttributeWriter.class);
+
+
+    public AttributeWriter(
+        Map<String, Output> oldAttr,
+        Map<String, Output> newAttr)
+    {
+        this.oldAttr = oldAttr;
+        this.newAttr = newAttr;
+    }
+
+
+    protected Document write() {
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element outs = cr.create("outputs");
+        doc.appendChild(outs);
+
+        Iterator<String> iter = newAttr.keySet().iterator();
+
+        while (iter.hasNext()) {
+            String outName = iter.next();
+
+            Output a = newAttr.get(outName);
+            Output b = oldAttr.get(outName);
+
+            writeOutput(doc, outs, cr, a, b);
+        }
+
+        return doc;
+    }
+
+
+    protected void writeOutput(
+        Document       doc,
+        Node           outs,
+        ElementCreator cr,
+        Output         a,
+        Output         b)
+    {
+        Element output = cr.create("output");
+        cr.addAttr(output, "name", a.getName());
+
+        outs.appendChild(output);
+
+        List<Facet> facetsA = a.getFacets();
+        List<Facet> facetsB = null;
+
+        if (b != null) {
+            facetsB = b.getFacets();
+        }
+
+        writeFacets(doc, cr, output, facetsA, facetsB);
+    }
+
+
+    protected void writeFacets(
+        Document       doc,
+        ElementCreator cr,
+        Element        output,
+        List<Facet>    a,
+        List<Facet>    b)
+    {
+        int num = a.size();
+
+        for (int i = 0; i < num; i++) {
+            ManagedFacet fA = (ManagedFacet) a.get(i);
+
+            if (!mergeFacets(doc, cr, output, fA, b)) {
+                writeFacet(doc, cr, output, fA);
+            }
+        }
+    }
+
+
+    protected boolean mergeFacets(
+        Document       doc,
+        ElementCreator cr,
+        Element        output,
+        ManagedFacet   a,
+        List<Facet>    list)
+    {
+        String name = a.getName();
+
+        if (list == null) {
+            return false;
+        }
+
+        for (Facet facet: list) {
+            if (name.equals(facet.getName())) {
+                writeFacet(doc, cr, output, (ManagedFacet) facet);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    protected void writeFacet(
+        Document       doc,
+        ElementCreator cr,
+        Node           output,
+        ManagedFacet   f)
+    {
+        Element theme = cr.create("theme");
+        cr.addAttr(theme, "artifact", f.getArtifact(), true);
+        cr.addAttr(theme, "facet", f.getName(), true);
+        cr.addAttr(theme, "pos", Integer.toString(f.getPosition()), true);
+        cr.addAttr(theme, "active", Integer.toString(f.getActive()), true);
+
+        output.appendChild(theme);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,754 @@
+package de.intevation.flys.collections;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+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.Artifact;
+import de.intevation.artifacts.ArtifactDatabase;
+import de.intevation.artifacts.ArtifactDatabaseException;
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallContext;
+import de.intevation.artifacts.CallMeta;
+
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.Backend;
+import de.intevation.artifactdatabase.Backend.PersistentArtifact;
+import de.intevation.artifactdatabase.DefaultArtifactCollection;
+
+import de.intevation.flys.artifacts.context.FLYSContext;
+import de.intevation.flys.artifacts.model.ManagedFacet;
+import de.intevation.flys.exports.OutGenerator;
+import de.intevation.flys.themes.Theme;
+import de.intevation.flys.themes.ThemeFactory;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYSArtifactCollection extends DefaultArtifactCollection {
+
+    /** The logger used in this class.*/
+    private static Logger log = Logger.getLogger(FLYSArtifactCollection.class);
+
+
+    /** Constant XPath that points to the outputmodes of an artifact.*/
+    public static final String XPATH_ARTIFACT_OUTPUTMODES =
+        "/art:result/art:outputmodes";
+
+    public static final String XPATH_COLLECTION_ITEMS =
+        "/art:result/art:artifact-collection/art:collection-item";
+
+    public static final String XPATH_OUT_NAME = "/art:action/@art:name";
+
+    public static final String XPATH_OUT_TYPE = "/art:action/@art:type";
+
+
+
+    @Override
+    public Document describe(CallContext context) {
+        log.debug("FLYSArtifactCollection.describe: " + identifier);
+
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Date creationTime = getCreationTime();
+        String creation   = creationTime != null
+            ? Long.toString(creationTime.getTime())
+            : "";
+
+        Element collection = ec.create("artifact-collection");
+        Element artifacts  = ec.create("artifacts");
+        Element attributes = ec.create("attribute");
+
+        ec.addAttr(collection, "name", getName(), true);
+        ec.addAttr(collection, "uuid", identifier(), true);
+        ec.addAttr(collection, "creation", creation,  true);
+
+        collection.appendChild(artifacts);
+        collection.appendChild(attributes);
+        doc.appendChild(collection);
+
+        ArtifactDatabase db = context.getDatabase();
+
+        try {
+            String[] artifactUUIDs = getArtifactUUIDs(context);
+
+            Document oldAttrs = getAttribute();
+            Document attrs    = buildAttributes(
+                db, context,
+                oldAttrs,
+                artifactUUIDs);
+
+            db.setCollectionAttribute(identifier(), context.getMeta(), attrs);
+
+            Node child = attrs.getFirstChild();
+            attributes.appendChild(doc.importNode(child, true));
+
+            for (String uuid: artifactUUIDs) {
+                try {
+                    artifacts.appendChild(
+                        buildArtifactNode(db, uuid, context, ec));
+                }
+                catch (ArtifactDatabaseException dbe) {
+                    log.warn(dbe, dbe);
+                }
+            }
+        }
+        catch (ArtifactDatabaseException ade) {
+            log.error(ade, ade);
+        }
+
+        return doc;
+    }
+
+
+    @Override
+    public void out(Document format, OutputStream out, CallContext context)
+    throws IOException
+    {
+        log.info("FLYSArtifactCollection.out");
+
+        String name = XMLUtils.xpathString(
+            format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE);
+
+        String type = XMLUtils.xpathString(
+            format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE);
+
+        log.debug("Output name = " + name);
+        log.debug("Output type = " + type);
+
+        OutGenerator generator = getOutGenerator(context, name, type);
+        if (generator == null) {
+            log.error("There is no generator specified for output: " + name);
+            // TODO throw an exception.
+
+            return;
+        }
+
+        generator.init(format, out, context);
+
+        // TODO Determine the correct master artifact here!
+
+        try {
+            doOut(generator, name, type, getAttribute(context, name), context);
+        }
+        catch (ArtifactDatabaseException adbe) {
+            log.error(adbe, adbe);
+        }
+    }
+
+
+    /**
+     * This method creates the concrete output.
+     *
+     * @param generator The OutGenerator that creates the output.
+     * @param outputName The name of the requested output.
+     * @param attributes The collection's attributes for this concrete output
+     * type.
+     * @param context The context object.
+     */
+    protected void doOut(
+        OutGenerator generator,
+        String       outName,
+        String       facet,
+        Document     attributes,
+        CallContext  context)
+    throws IOException
+    {
+        log.debug("FLYSArtifactCollection.doOut: " + outName);
+
+        ThemeList themeList = new ThemeList(attributes);
+
+        int size = themeList.size();
+        log.debug("Output will contain " + size + " elements.");
+
+        try {
+            for (int i = 0; i < size; i++) {
+                ManagedFacet theme = themeList.get(i);
+
+                if (theme == null) {
+                    log.debug("Theme is empty - no output is generated.");
+                    continue;
+                }
+
+                String art = theme.getArtifact();
+
+                if (log.isDebugEnabled()) {
+                    log.debug("Do output for...");
+                    log.debug("... artifact: " + art);
+                    log.debug("... facet: " + theme.getName());
+                }
+
+                String facetName = theme.getName();
+
+                if (outName.equals("export") && !facetName.equals(facet)) {
+                    continue;
+                }
+
+                // TODO Remove the following two lines of code! The master
+                // artifact has to be determined correctly after
+                // OutGenerator.init is called!
+                Artifact artifact = getArtifact(art, context);
+                generator.setMasterArtifact(artifact);
+
+                generator.doOut(
+                    artifact,
+                    facetName,
+                    getFacetThemeFromAttribute(
+                        art,
+                        outName,
+                        facetName,
+                        context));
+            }
+        }
+        catch (ArtifactDatabaseException ade) {
+            log.error(ade, ade);
+        }
+
+        generator.generate();
+    }
+
+
+    protected Document buildAttributes(
+        ArtifactDatabase db,
+        CallContext      context,
+        Document         oldAttr,
+        String[]         items)
+    {
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        AttributeParser aParser = new AttributeParser();
+        OutputParser    oParser = new OutputParser(db, context.getMeta());
+
+        for (String uuid: items) {
+            try {
+                oParser.parse(uuid);
+            }
+            catch (ArtifactDatabaseException ade) {
+                log.warn(ade, ade);
+            }
+        }
+
+        aParser.parse(oldAttr);
+
+        return new AttributeWriter(aParser.getOuts(), oParser.getOuts()).write();
+    }
+
+
+    /**
+     * Returns the attribute for a specific output type.
+     *
+     * @param output The name of the desired output type.
+     *
+     * @return the attribute for the desired output type.
+     */
+    protected Document getAttribute(CallContext context, String output)
+    throws    ArtifactDatabaseException
+    {
+        Document attr = buildAttributes(
+            context.getDatabase(),
+            context,
+            getAttribute(),
+            getArtifactUUIDs(context));
+
+        Node out = (Node) XMLUtils.xpath(
+            attr,
+            "art:outputs/art:output[@name='" + output + "']",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+
+        if (out != null) {
+            Document o = XMLUtils.newDocument();
+
+            o.appendChild(o.importNode(out, true));
+
+            return o;
+        }
+
+        return null;
+    }
+
+
+    /**
+     * This method returns the list of artifact UUIDs that this collections
+     * contains.
+     *
+     * @param context The CallContext that is necessary to get information about
+     * the ArtifactDatabase.
+     *
+     * @return a list of uuids.
+     */
+    protected String[] getArtifactUUIDs(CallContext context)
+    throws    ArtifactDatabaseException
+    {
+        log.debug("FLYSArtifactCollection.getArtifactUUIDs");
+
+        ArtifactDatabase db = context.getDatabase();
+        CallMeta meta       = context.getMeta();
+
+        Document itemList = db.listCollectionArtifacts(identifier(), meta);
+        NodeList items    = (NodeList) XMLUtils.xpath(
+            itemList,
+            XPATH_COLLECTION_ITEMS,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (items == null || items.getLength() == 0) {
+            log.debug("No artifacts found in this collection.");
+            return null;
+        }
+
+        int num = items.getLength();
+
+        List<String> uuids = new ArrayList<String>(num);
+
+        for (int i = 0; i < num; i++) {
+            String uuid = XMLUtils.xpathString(
+                items.item(i),
+                "@art:uuid",
+                ArtifactNamespaceContext.INSTANCE);
+
+            if (uuid != null && uuid.trim().length() != 0) {
+                uuids.add(uuid);
+            }
+        }
+
+        return (String[]) uuids.toArray(new String[uuids.size()]);
+    }
+
+
+    /**
+     * Returns a concrete Artifact of this collection specified by its uuid.
+     *
+     * @param uuid The Artifact's uuid.
+     * @param context The CallContext.
+     *
+     * @return an Artifact.
+     */
+    protected Artifact getArtifact(String uuid, CallContext context)
+    throws    ArtifactDatabaseException
+    {
+        log.debug("FLYSArtifactCollection.getArtifact");
+
+        Backend backend               = Backend.getInstance();
+        PersistentArtifact persistent = backend.getArtifact(uuid);
+
+        return persistent != null ? persistent.getArtifact() : null;
+    }
+
+
+    /**
+     * Returns the attribute that belongs to an artifact stored in this
+     * collection.
+     *
+     * @param uuid The Artifact's uuid.
+     * @param outname The name of the requested output.
+     * @param facet The name of the requested facet.
+     * @param context The CallContext.
+     *
+     * @return an attribute in form of a document.
+     */
+    protected Document getFacetThemeFromAttribute(
+        String      uuid,
+        String      outName,
+        String      facet,
+        CallContext context)
+    throws    ArtifactDatabaseException
+    {
+        log.debug("FLYSArtifactCollection.getFacetThemeFromAttribute");
+
+        ArtifactDatabase db = context.getDatabase();
+        CallMeta       meta = context.getMeta();
+
+        FLYSContext flysContext = context instanceof FLYSContext
+            ? (FLYSContext) context
+            : (FLYSContext) context.globalContext();
+
+        Map<String, String> mappings = (Map<String, String>)
+            flysContext.get(FLYSContext.THEME_MAPPING);
+
+        String themeName = mappings.get(facet);
+
+        Document attr = db.getCollectionItemAttribute(identifier(), uuid, meta);
+
+        if (attr == null) {
+            attr = initItemAttribute(uuid, facet, context);
+
+            if (attr == null) {
+                return null;
+            }
+        }
+
+        log.debug("Search attribute of collection item: " + uuid);
+
+        Node tmp = (Node) XMLUtils.xpath(
+            attr,
+            "/art:attribute",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (tmp == null) {
+            log.warn("No attribute found. Operation failed.");
+            return null;
+        }
+
+        log.debug("Search theme '" + themeName + "' in attribute.");
+
+        Node theme = (Node) XMLUtils.xpath(
+            tmp,
+            "art:themes/theme[@name='" + themeName + "']",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (theme == null) {
+            log.warn("Could not find the theme in attribute of: " + uuid);
+
+            Theme t = getThemeForFacet(uuid, facet, context);
+
+            if (t == null) {
+                log.warn("No theme found for facet: " + facet);
+                return null;
+            }
+
+            addThemeToAttribute(uuid, attr, t, context);
+            theme   = t.toXML().getFirstChild();
+        }
+
+        Document doc = XMLUtils.newDocument();
+        doc.appendChild(doc.importNode(theme, true));
+
+        return doc;
+    }
+
+
+    /**
+     * Adds the theme of a facet to a CollectionItem's attribute.
+     *
+     * @param uuid The uuid of the artifact.
+     * @param attr The current attribute of an artifact.
+     * @param t The theme to add.
+     * @param context The CallContext.
+     */
+    protected void addThemeToAttribute(
+        String      uuid,
+        Document    attr,
+        Theme       t,
+        CallContext context)
+    {
+        log.debug("FLYSArtifactCollection.addThemeToAttribute: " + uuid);
+
+        if (t == null) {
+            log.warn("Theme is empty - cancel adding it to attribute!");
+            return;
+        }
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            attr,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Node tmp = (Node) XMLUtils.xpath(
+            attr,
+            "/art:attribute",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (tmp == null) {
+            tmp = ec.create("attribute");
+            attr.appendChild(tmp);
+        }
+
+        Node themes = (Node) XMLUtils.xpath(
+            tmp,
+            "art:themes",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (themes == null) {
+            themes = ec.create("themes");
+            tmp.appendChild(themes);
+        }
+
+        themes.appendChild(attr.importNode(t.toXML().getFirstChild(), true));
+
+        try {
+            setCollectionItemAttribute(uuid, attr, context);
+
+            log.debug("Successfully added theme to item attribute.");
+        }
+        catch (ArtifactDatabaseException e) {
+            // do nothing
+            log.warn("Cannot set attribute of item: " + uuid);
+        }
+    }
+
+
+    /**
+     * Initializes the attribute of an collection item with the theme of a
+     * specific facet.
+     *
+     * @param uuid The uuid of an artifact.
+     * @param facet The name of a facet.
+     * @param context The CallContext.
+     *
+     * @param the new attribute.
+     */
+    protected Document initItemAttribute(
+        String      uuid,
+        String      facet,
+        CallContext context)
+    {
+        log.info("FLYSArtifactCollection.initItemAttribute");
+
+        Theme t = getThemeForFacet(uuid, facet, context);
+
+        if (t == null) {
+            log.info("Could not find theme for facet. Cancel initialization.");
+            return null;
+        }
+
+        Document attr = XMLUtils.newDocument();
+
+        addThemeToAttribute(uuid, attr, t, context);
+
+        return attr;
+    }
+
+
+    /**
+     * Sets the attribute of a CollectionItem specified by <i>uuid</i> to a new
+     * value <i>attr</i>.
+     *
+     * @param uuid The uuid of the CollectionItem.
+     * @param attr The new attribute for the CollectionItem.
+     * @param context The CallContext.
+     */
+    public void setCollectionItemAttribute(
+        String      uuid,
+        Document    attr,
+        CallContext context)
+    throws ArtifactDatabaseException
+    {
+        Document doc = ClientProtocolUtils.newSetItemAttributeDocument(
+            uuid,
+            attr);
+
+        if (doc == null) {
+            log.warn("Cannot set item attribute: No attribute found.");
+            return;
+        }
+
+        ArtifactDatabase db = context.getDatabase();
+        CallMeta       meta = context.getMeta();
+
+        db.setCollectionItemAttribute(identifier(), uuid, doc, meta);
+    }
+
+
+    /**
+     * Returns the theme of a specific facet.
+     *
+     * @param uuid The uuid of an artifact.
+     * @param facet The name of the facet.
+     * @param context The CallContext object.
+     *
+     * @return the desired theme.
+     */
+    protected Theme getThemeForFacet(
+        String uuid,
+        String facet,
+        CallContext context)
+    {
+        log.info("FLYSArtifactCollection.getThemeForFacet: " + facet);
+
+        FLYSContext flysContext = context instanceof FLYSContext
+            ? (FLYSContext) context
+            : (FLYSContext) context.globalContext();
+
+        return ThemeFactory.getTheme(flysContext, facet);
+    }
+
+
+    /**
+     * Returns the OutGenerator for a specified <i>type</i>.
+     *
+     * @param name The name of the output type.
+     * @param type Defines the type of the desired OutGenerator.
+     *
+     * @return The OutGenerator specified by <i>type</i>.
+     */
+    protected OutGenerator getOutGenerator(
+        CallContext context,
+        String      name,
+        String      type)
+    {
+        FLYSContext flysContext = context instanceof FLYSContext
+            ? (FLYSContext) context
+            : (FLYSContext) context.globalContext();
+
+        Map<String, Class> generators = (Map<String, Class>)
+            flysContext.get(FLYSContext.OUTGENERATORS_KEY);
+
+        if (generators == null) {
+            log.error("No output generators found in the running application!");
+            return null;
+        }
+
+        Class clazz = generators.get(name);
+
+        try {
+            return clazz != null ? (OutGenerator) clazz.newInstance() : null;
+        }
+        catch (InstantiationException ie) {
+            log.error(ie, ie);
+        }
+        catch (IllegalAccessException iae) {
+            log.error(iae, iae);
+        }
+
+        return null;
+    }
+
+
+    protected Element buildArtifactNode(
+        ArtifactDatabase        database,
+        String                  uuid,
+        CallContext             context,
+        XMLUtils.ElementCreator ec)
+    throws ArtifactDatabaseException
+    {
+        log.debug("Append artifact '" + uuid + "' to collection description");
+
+        // TODO
+        String hash = "MYHASH";
+
+        Element ci   = ec.create("artifact");
+        ec.addAttr(ci, "uuid", uuid, true);
+        ec.addAttr(ci, "hash", hash, true);
+
+        // XXX I am not sure if it works well every time with an empty document
+        // in the describe operation of an artifact.
+        Document description = database.describe(uuid, null, context.getMeta());
+
+        Node outputModes = (Node) XMLUtils.xpath(
+            description,
+            XPATH_ARTIFACT_OUTPUTMODES,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (outputModes != null) {
+            Document doc = ci.getOwnerDocument();
+            ci.appendChild(doc.importNode(outputModes, true));
+        }
+
+        return ci;
+    }
+
+
+    /**
+     * Inner class to structure/order the themes of a chart.
+     */
+    private class ThemeList {
+        private Logger logger = Logger.getLogger(ThemeList.class);
+        protected Vector<ManagedFacet> themes;
+
+        public ThemeList(Document output) {
+            themes = new Vector<ManagedFacet>();
+            parse(output);
+        }
+
+        protected void parse(Document output) {
+            NodeList themes = (NodeList) XMLUtils.xpath(
+                output,
+                "art:output/art:theme",
+                XPathConstants.NODESET,
+                ArtifactNamespaceContext.INSTANCE);
+
+            int num = themes != null ? themes.getLength() : 0;
+
+            logger.debug("Output has " +  num + " elements.");
+
+            if (num == 0) {
+                return;
+            }
+
+            for (int i = 0; i < num; i++) {
+                Node theme = themes.item(i);
+
+                String name = XMLUtils.xpathString(
+                    theme, "@art:facet", ArtifactNamespaceContext.INSTANCE);
+
+                String uuid = XMLUtils.xpathString(
+                    theme, "@art:artifact", ArtifactNamespaceContext.INSTANCE);
+
+                String pos = XMLUtils.xpathString(
+                    theme, "@art:pos", ArtifactNamespaceContext.INSTANCE);
+
+                String active = XMLUtils.xpathString(
+                    theme, "@art:active", ArtifactNamespaceContext.INSTANCE);
+
+                addTheme(uuid, name, pos, active);
+            }
+        }
+
+        protected void addTheme(
+            String uuid,
+            String name,
+            String position,
+            String active)
+        {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Add theme: ");
+                logger.debug(".. name: " + name);
+                logger.debug(".. uuid: " + uuid);
+                logger.debug(".. position: " + position);
+                logger.debug(".. active: " + active);
+            }
+
+            try {
+                int pos = Integer.parseInt(position);
+                int act = Integer.parseInt(active);
+
+                themes.add(pos-1, new ManagedFacet(name, null, uuid, pos, act));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        public ManagedFacet get(int idx) {
+            return themes.get(idx);
+        }
+
+        public int size() {
+            return themes.size();
+        }
+    }
+}
+// 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/OutputParser.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,141 @@
+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.ArtifactDatabase;
+import de.intevation.artifacts.ArtifactDatabaseException;
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.CallMeta;
+
+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 OutputParser {
+
+    /** Constant XPath that points to the outputmodes of an artifact.*/
+    public static final String XPATH_ARTIFACT_OUTPUTMODES =
+        "/art:result/art:outputmodes/art:output";
+
+
+    private static Logger logger = Logger.getLogger(OutputParser.class);
+
+    protected ArtifactDatabase db;
+    protected CallMeta         meta;
+
+    protected Map<String, Output> outs;
+
+
+    public OutputParser(ArtifactDatabase db, CallMeta meta) {
+        this.db   = db;
+        this.meta = meta;
+        this.outs = new HashMap<String, Output>();
+    }
+
+
+    public void parse(String uuid)
+    throws ArtifactDatabaseException
+    {
+        logger.debug("OutputParser.parse: " + uuid);
+
+        // XXX I am not sure if it works well every time with an empty
+        // document in the describe operation of an artifact.
+        Document description = db.describe(uuid, null, meta);
+
+        NodeList outs = (NodeList) XMLUtils.xpath(
+            description,
+            XPATH_ARTIFACT_OUTPUTMODES,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = outs != null ? outs.getLength() : 0;
+
+        logger.debug("Artifact has " + num + " outputs.");
+
+        for (int i = 0; i < num; i++) {
+            Node out = outs.item(i);
+
+            parseOutput(uuid, out);
+        }
+    }
+
+
+    public Map<String, Output> getOuts() {
+        return outs;
+    }
+
+
+    protected void addItem(String out, ManagedFacet item) {
+        Output o = outs.get(out);
+
+        if (o != null) {
+            int num = o.getFacets().size();
+            item.setPosition(num+1);
+
+            o.addFacet(item);
+        }
+    }
+
+
+    protected void parseOutput(String uuid, Node out) {
+        String name = XMLUtils.xpathString(
+            out, "@art:name", ArtifactNamespaceContext.INSTANCE);
+
+        if (outs.get(name) == null) {
+            logger.debug("Create new output: " + name);
+            newOutput(out, name);
+        }
+
+        parseItems(uuid, out, name);
+    }
+
+
+    protected void newOutput(Node out, String name) {
+        String desc = XMLUtils.xpathString(
+            out, "@art:description", ArtifactNamespaceContext.INSTANCE);
+
+        String mimetype = XMLUtils.xpathString(
+            out, "@art:mime-type", ArtifactNamespaceContext.INSTANCE);
+
+        Output o = new DefaultOutput(name, desc, mimetype);
+
+        outs.put(name, o);
+    }
+
+
+    protected void parseItems(String uuid, Node out, String outname) {
+        NodeList facets = (NodeList) XMLUtils.xpath(
+            out, "art:facets/art:facet",
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = facets != null ? facets.getLength() : 0;
+
+        logger.debug("Output has " + num + " facets.");
+
+        for (int i = 0; i < num; i++) {
+            Node facet = facets.item(i);
+
+            String name = XMLUtils.xpathString(
+                facet, "@art:name", ArtifactNamespaceContext.INSTANCE);
+
+            ManagedFacet item = new ManagedFacet(name, null, uuid, 1, 1);
+
+            addItem(outname, item);
+        }
+    }
+}
+// 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/exports/AbstractExporter.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,212 @@
+package de.intevation.flys.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.resources.Resources;
+
+
+/**
+ * An abstract exporter that implements some basic methods for exporting data of
+ * artifacts.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class AbstractExporter implements OutGenerator {
+
+    /** The logger used in this exporter.*/
+    private static Logger logger = Logger.getLogger(AbstractExporter.class);
+
+
+    /** The name of the CSV facet which triggers the CSV creation.*/
+    public static final String FACET_CSV = "csv";
+
+    /** The default charset for the CSV export.*/
+    public static final String DEFAULT_CSV_CHARSET = "UTF-8";
+
+    /** The default separator for the CSV export.*/
+    public static final char DEFAULT_CSV_SEPARATOR = ',';
+
+    /** XPath that points to the desired export facet.*/
+    public static final String XPATH_FACET = "/art:action/@art:type";
+
+
+    /** The document of the incoming out() request.*/
+    protected Document request;
+
+    /** The output stream where the data should be written to.*/
+    protected OutputStream out;
+
+    /** The CallContext object.*/
+    protected CallContext context;
+
+    /** The selected facet.*/
+    protected String facet;
+
+    /** The master artifact.*/
+    protected Artifact master;
+
+
+    /**
+     * Concrete subclasses need to use this method to write their special data
+     * objects into the CSV document.
+     *
+     * @param writer The CSVWriter.
+     */
+    protected abstract void writeCSVData(CSVWriter writer);
+
+
+    /**
+     * This method enables concrete subclasses to collected its own special
+     * data.
+     *
+     * @param artifacts The artifact that stores the data that has to be
+     * exported.
+     */
+    protected abstract void addData(Artifact artifact);
+
+
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("AbstractExporter.init");
+
+        this.request = request;
+        this.out     = out;
+        this.context = context;
+    }
+
+
+    public void setMasterArtifact(Artifact master) {
+        this.master = master;
+    }
+
+
+    /**
+     * This doOut() just collects the data of multiple artifacts. Therefore, it
+     * makes use of the addData() method which enables concrete subclasses to
+     * store its data on its own. The real output creation takes place in the
+     * concrete generate() methods.
+     *
+     * @param artifact The artifact.
+     * @param facet The facet to add - NOTE: the facet needs to fit to the first
+     * facet inserted into this exporter. Otherwise this artifact/facet is
+     * skipped.
+     * @param attr The attr document.
+     */
+    public void doOut(Artifact artifact, String facet, Document attr) {
+        logger.debug("AbstractExporter.doOut: " + facet);
+
+        if (!isFacetValid(facet)) {
+            logger.warn("Facet '" + facet + "' not valid. No output created!");
+            return;
+        }
+
+        addData(artifact);
+    }
+
+
+    /**
+     * Generates an export based on a specified facet.
+     */
+    public void generate()
+    throws IOException
+    {
+        logger.debug("AbstractExporter.generate");
+
+        if (facet != null && facet.equals(FACET_CSV)) {
+            generateCSV();
+        }
+        else {
+            throw new IOException("invalid facet for exporter.");
+        }
+    }
+
+
+    /**
+     * Determines if the desired facet is valid for this exporter. If no facet
+     * is currently set, <i>facet</i> is set.
+     *
+     * @param facet The desired facet.
+     *
+     * @return true, if <i>facet</i> is valid, otherwise false.
+     */
+    protected boolean isFacetValid(String facet) {
+        logger.debug("AbstractExporter.isFacetValid");
+
+        String thisFacet = getFacet();
+
+        if (thisFacet == null || thisFacet.length() == 0) {
+            return false;
+        }
+        else if (facet == null || facet.length() == 0) {
+            return false;
+        }
+        else {
+            return thisFacet.equals(facet);
+        }
+    }
+
+
+    /**
+     * Returns the name of the desired facet.
+     *
+     * @return the name of the desired facet.
+     */
+    protected String getFacet() {
+        if (facet == null) {
+            facet = getFacetFromRequest();
+        }
+
+        return facet;
+    }
+
+
+    /**
+     * Extracts the name of the requested facet from request document.
+     *
+     * @return the name of the requested facet.
+     */
+    protected String getFacetFromRequest() {
+        return XMLUtils.xpathString(
+            request, XPATH_FACET, ArtifactNamespaceContext.INSTANCE);
+    }
+
+
+    protected String msg(String key, String def) {
+        return Resources.getMsg(context.getMeta(), key, def);
+    }
+
+
+    /**
+     * This method starts CSV creation. It makes use of writeCSVData() which has
+     * to be implemented by concrete subclasses.
+     */
+    protected void generateCSV()
+    throws    IOException
+    {
+        logger.info("AbstractExporter.generateCSV");
+
+        CSVWriter writer = new CSVWriter(
+            new OutputStreamWriter(
+                out,
+                DEFAULT_CSV_CHARSET),
+            DEFAULT_CSV_SEPARATOR);
+
+        writeCSVData(writer);
+
+        writer.close();
+    }
+}
+// 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/exports/ChartExportHelper.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,305 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+package de.intevation.flys.exports;
+
+import com.lowagie.text.Document;
+import com.lowagie.text.DocumentException;
+import com.lowagie.text.PageSize;
+import com.lowagie.text.Rectangle;
+
+import com.lowagie.text.pdf.PdfContentByte;
+import com.lowagie.text.pdf.PdfTemplate;
+import com.lowagie.text.pdf.PdfWriter;
+
+import java.awt.Graphics2D;
+import java.awt.Transparency;
+
+import java.awt.geom.Rectangle2D.Double;
+
+import java.awt.geom.Rectangle2D;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+
+import javax.imageio.ImageIO;
+
+import org.apache.batik.svggen.SVGGraphics2D;
+import org.apache.batik.svggen.SVGGraphics2DIOException;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.JFreeChart;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+
+/**
+ * This class is a helper class which supports some methods to export charts
+ * into specific formats.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartExportHelper {
+
+    /**
+     * Constant field to define A4 as default page size.
+     */
+    private static final String  DEFAULT_PAGE_SIZE = "A4";
+
+    /**
+     * Constant field to define UTF-8 as default encoding.
+     */
+    private static final String  DEFAULT_ENCODING  = "UTF-8";
+
+    /**
+     * Logger used for logging with log4j.
+     */
+    private static Logger log = Logger.getLogger(ChartExportHelper.class);
+
+
+    /**
+     * A method to export a <code>JFreeChart</code> as image to an
+     * <code>OutputStream</code> with a given format, width and height.
+     *
+     * @param out OutputStream
+     * @param chart JFreeChart object to be exported.
+     * @param format Format (e.g. png, gif, jpg)
+     * @param width Width, the image used to be
+     * @param height Height, the image used to be
+     *
+     * @throws IOException if writing image to OutputStream failed.
+     */
+    public static void exportImage(
+        OutputStream out,
+        JFreeChart chart,
+        String format,
+        int width,
+        int height
+    )
+    throws IOException
+    {
+        log.info("export chart as png");
+
+        ImageIO.write(
+            chart.createBufferedImage(
+                width, height, Transparency.BITMASK, null
+            ),
+            format,
+            out
+        );
+    }
+
+
+    /**
+     * A method to export a <code>JFreeChart</code> as SVG to an
+     * <code>OutputStream</code>.
+     *
+     * @param out OutputStream
+     * @param chart JFreeChart to be exported
+     * @param encoding Encoding, defaults to {@link #DEFAULT_ENCODING} if null
+     * @param width Width the svg used to be
+     * @param height Height the svg used to be
+     */
+    public static void exportSVG(
+        OutputStream out,
+        JFreeChart   chart,
+        String       encoding,
+        int          width,
+        int          height
+    ) {
+        log.info("export chart as svg");
+
+        if (encoding == null)
+            encoding = DEFAULT_ENCODING;
+
+        org.w3c.dom.Document document = XMLUtils.newDocument();
+        SVGGraphics2D        graphics = new SVGGraphics2D(document);
+
+        chart.draw(graphics, new Rectangle2D.Double(0.0D, 0.0D,width,height));
+
+        try {
+            graphics.stream(new OutputStreamWriter(out, encoding));
+        }
+        catch (SVGGraphics2DIOException svge) {
+            log.error("Error while writing svg export to output stream.", svge);
+        }
+        catch (UnsupportedEncodingException uee) {
+            log.error("Unsupported encoding: " + encoding, uee);
+        }
+    }
+
+
+    /**
+     * A method to export a <code>JFreeChart</code> as PDF to an
+     * <code>OutputStream</code>.
+     *
+     * @param out OutputStream
+     * @param chart JFreeChart
+     * @param pageFormat String to specify a page format, {@link
+     * #DEFAULT_PAGE_SIZE} is used if no pageFormat is given
+     * @param landscape If this is true, the pdf is delivered in landscape
+     * format
+     * @param marginLeft Space to left border
+     * @param marginRight Space to right border
+     * @param marginTop Space to upper border
+     * @param marginBottom Space to lower border
+     */
+    public static void exportPDF(
+        OutputStream out,
+        JFreeChart   chart,
+        String       pageFormat,
+        float        marginLeft,
+        float        marginRight,
+        float        marginTop,
+        float        marginBottom,
+        CallContext  context
+    ) {
+        log.info("export chart as pdf.");
+
+        if (pageFormat == null)
+            pageFormat = DEFAULT_PAGE_SIZE;
+
+        // max size of the chart
+        Rectangle page = PageSize.getRectangle(pageFormat);
+        float pageWidth  = page.getWidth();
+        float pageHeight = page.getHeight();
+
+        // the chart width
+        int chartWidth  = (Integer) context.getContextValue("chart.width");
+        int chartHeight = (Integer) context.getContextValue("chart.height");
+
+        boolean landscape = chartWidth > chartHeight ? true : false;
+
+        float width  = 0;
+        float height = 0;
+        if (landscape) {
+            width  = pageHeight;
+            height = pageWidth;
+        }
+        else {
+            width  = pageWidth;
+            height = pageHeight;
+        }
+
+        float spaceX = width  - marginLeft - marginRight;
+        if (chartWidth > spaceX) {
+            log.warn("Width of the chart is too big for pdf -> resize it now.");
+            double ratio = ((double)spaceX) / chartWidth;
+            chartWidth  *= ratio;
+            chartHeight *= ratio;
+            log.debug("Resized chart to " + chartWidth + "x" + chartHeight);
+        }
+
+        float spaceY = height - marginTop  - marginBottom;
+        if (chartHeight > spaceY) {
+            log.warn("Height of the chart is too big for pdf -> resize it now.");
+            double ratio = ((double)spaceY) / chartHeight;
+            chartWidth  *= ratio;
+            chartHeight *= ratio;
+            log.debug("Resized chart to " + chartWidth + "x" + chartHeight);
+        }
+
+        Document document = null;
+        if (landscape) {
+            document = new Document(page.rotate());
+            log.debug("Create landscape pdf.");
+        }
+        else
+            document = new Document(page);
+
+        try {
+            PdfWriter writer = PdfWriter.getInstance(document, out);
+
+            document.addSubject(chart.getTitle().getText());
+            document.addCreationDate();
+            document.open();
+
+            PdfContentByte content  = writer.getDirectContent();
+
+            PdfTemplate template = content.createTemplate(width, height);
+            Graphics2D  graphics = template.createGraphics(width, height);
+
+            double[] origin = getCenteredAnchor(
+                marginLeft, marginRight, marginBottom, marginTop,
+                width, height,
+                chartWidth, chartHeight);
+
+            Rectangle2D area = new Rectangle2D.Double(
+                origin[0], origin[1], chartWidth, chartHeight);
+
+            chart.draw(graphics, area);
+            graphics.dispose();
+            content.addTemplate(template, 0f, 0f);
+        }
+        catch (DocumentException de) {
+            log.error("Error while exporting chart to pdf.", de);
+        }
+        finally {
+            document.close();
+        }
+    }
+
+
+    /**
+     * This method returns the anchor of the chart so that the chart is centered
+     * according to the given parameters.
+     *
+     * @param mLeft Left margin
+     * @param mRight Right margin
+     * @param mBottom Bottom margin
+     * @param mTop Top margin
+     * @param width The complete width of the drawing area.
+     * @param height The complete height of the drawing area.
+     * @param chartWidth The width of the chart.
+     * @param chartHeight The height of the chart.
+     *
+     * @return an array that contains the anchor for a chart with the given
+     * parameters. The first value is the x point, the second value is the y
+     * point.
+     */
+    public static double[] getCenteredAnchor(
+        double mLeft,      double mRight,      double mBottom, double mTop,
+        double width,      double height,
+        double chartWidth, double chartHeight
+    ) {
+        if (log.isDebugEnabled()) {
+            log.debug("Calculate centered origin...");
+            log.debug("-> PDF width    : " + width);
+            log.debug("-> PDF height   : " + height);
+            log.debug("-> Chart width  : " + chartWidth);
+            log.debug("-> Chart height : " + chartHeight);
+            log.debug("-> margin left  : " + mLeft);
+            log.debug("-> margin right : " + mRight);
+            log.debug("-> margin bottom: " + mBottom);
+            log.debug("-> margin top   : " + mTop);
+        }
+
+        double[] origin = new double[2];
+
+        double centerX = width  / 2;
+        double centerY = height / 2;
+
+        origin[0] = centerX - chartWidth / 2;
+        origin[1] = centerY - chartHeight / 2;
+
+        origin[0] = origin[0] >= mLeft ? origin[0] : mLeft;
+        origin[1] = origin[1] >= mTop ? origin[1] : mTop;
+
+        if (log.isDebugEnabled()) {
+            log.debug("==> centered left origin: " + origin[0]);
+            log.debug("==> centered top  origin: " + origin[1]);
+        }
+
+        return origin;
+    }
+}
+// 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/exports/ChartGenerator.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,152 @@
+package de.intevation.flys.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.resources.Resources;
+
+
+/**
+ * The base class for chart creation. It should provide some basic things that
+ * equal in all chart types.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class ChartGenerator implements OutGenerator {
+
+    private static Logger logger = Logger.getLogger(ChartGenerator.class);
+
+
+    /** The default chart width, if no other width is set.*/
+    public static final int DEFAULT_CHART_WIDTH  = 600;
+
+    /** The default chart height, if no other height is set.*/
+    public static final int DEFAULT_CHART_HEIGHT = 400;
+
+    /** The XPath that points to the chart size of the incoming request
+     * document.*/
+    public static final String XPATH_CHART_SIZE =
+        "/art:action/art:attributes/art:size";
+
+
+    /** The document of the incoming out() request.*/
+    protected Document request;
+
+    /** The output stream where the data should be written to.*/
+    protected OutputStream out;
+
+    /** The CallContext object.*/
+    protected CallContext context;
+
+    /** The artifact that is used to decorate the chart with meta information.*/
+    protected Artifact master;
+
+
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("ChartGenerator.init");
+
+        this.request = request;
+        this.out     = out;
+        this.context = context;
+    }
+
+
+    public void setMasterArtifact(Artifact master) {
+        this.master = master;
+    }
+
+
+    protected String msg(String key, String def) {
+        return Resources.getMsg(context.getMeta(), key, def);
+    }
+
+
+    protected String msg(String key, String def, Object[] args) {
+        return Resources.getMsg(context.getMeta(), key, def, args);
+    }
+
+
+    protected String getRiverName() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        River river = flys.getRiver();
+        return river != null ? river.getName() : "";
+    }
+
+
+    protected double[] getRange() {
+        FLYSArtifact flys = (FLYSArtifact) master;
+
+        return flys.getDistance();
+    }
+
+
+    /**
+     * Returns the size of a chart export as array which has been specified by
+     * the incoming request document.
+     *
+     * @return the size of a chart as [width, height] or the result of
+     * getDefaultSize() if no width or height are given in the request document.
+     */
+    protected int[] getSize() {
+        int[] size = new int[2];
+
+        Node sizeEl = (Node) XMLUtils.xpath(
+            request,
+            XPATH_CHART_SIZE,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (sizeEl != null) {
+            String w = XMLUtils.xpathString(
+                sizeEl, "@art:width", ArtifactNamespaceContext.INSTANCE);
+
+            String h = XMLUtils.xpathString(
+                sizeEl, "@art:height", ArtifactNamespaceContext.INSTANCE);
+
+            if (w != null && w.length() > 0 && h != null && h.length() > 0) {
+                try {
+                    size[0] = Integer.parseInt(w);
+                    size[1] = Integer.parseInt(h);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.warn("Wrong values for chart width/height.");
+                }
+            }
+        }
+
+        return size[0] > 0 && size[1] > 0 ? size : getDefaultSize();
+    }
+
+
+    /**
+     * Returns the default size of a chart export as array.
+     *
+     * @return the default size of a chart as [width, height].
+     */
+    protected int[] getDefaultSize() {
+        return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT };
+    }
+
+
+    public abstract void doOut(Artifact artifact, String facet, Document attr);
+
+    public abstract void generate() throws IOException;
+}
+// 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/exports/ComputedDischargeCurveExporter.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,114 @@
+package de.intevation.flys.exports;
+
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.utils.Formatter;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ComputedDischargeCurveExporter extends AbstractExporter {
+
+    /** The logger used in this exporter.*/
+    private static Logger logger =
+        Logger.getLogger(ComputedDischargeCurveExporter.class);
+
+
+    public static final String CSV_W_HEADER =
+        "export.computed.discharge.curve.csv.header.w";
+
+    public static final String CSV_Q_HEADER =
+        "export.computed.discharge.curve.csv.header.q";
+
+    public static final String DEFAULT_CSV_W_HEADER  = "W [NN + m]";
+    public static final String DEFAULT_CSV_Q_HEADER  = "Q [m³/s]";
+
+
+    protected List<WQKms> data;
+
+
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("ComputedDischargeCurveExporter.init");
+
+        super.init(request, out, context);
+
+        this.data = new ArrayList<WQKms>();
+    }
+
+
+    protected void addData(Artifact artifact) {
+        WINFOArtifact winfoArtifact = (WINFOArtifact) artifact;
+        data.add(winfoArtifact.getComputedDischargeCurveData());
+    }
+
+
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("ComputedDischargeCurveExporter.writeData");
+
+        writeCSVHeader(writer);
+
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        double[] res = new double[3];
+
+        for (WQKms wqkms: data) {
+            int size = wqkms.size();
+
+            for (int i = 0; i < size; i++) {
+                res = wqkms.get(i, res);
+
+                writer.writeNext(new String[] {
+                    wf.format(res[0]),
+                    qf.format(res[1])
+                });
+            }
+        }
+    }
+
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        logger.debug("ComputedDischargeCurveExporter.writeCSVHeader");
+
+        writer.writeNext(new String[] {
+            msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER),
+            msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER)
+        });
+    }
+
+
+    /**
+     * Returns the number formatter for W values.
+     *
+     * @return the number formatter for W values.
+     */
+    protected NumberFormat getWFormatter() {
+        return Formatter.getComputedDischargeW(context);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values.
+     *
+     * @return the number formatter for Q values.
+     */
+    protected NumberFormat getQFormatter() {
+        return Formatter.getComputedDischargeQ(context);
+    }
+}
+// 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/exports/ComputedDischargeCurveGenerator.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,110 @@
+package de.intevation.flys.exports;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.title.TextTitle;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+import de.intevation.flys.artifacts.model.WQKms;
+
+
+/**
+ * An OutGenerator that generates discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ComputedDischargeCurveGenerator extends DischargeCurveGenerator {
+
+    /** The logger used in this generator.*/
+    private static Logger logger =
+        Logger.getLogger(ComputedDischargeCurveGenerator.class);
+
+
+    public static final String I18N_CHART_TITLE =
+        "chart.computed.discharge.curve.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.computed.discharge.curve.subtitle";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.computed.discharge.curve.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT = "Abflusskurve";
+    public static final String I18N_YAXIS_LABEL_DEFAULT = "W [NN + m]";
+
+
+    @Override
+    protected String getChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+
+    @Override
+    protected void addSubtitles(JFreeChart chart) {
+        double[] dist  = getRange();
+
+        Object[] args = new Object[] {
+            getRiverName(),
+            dist[0]
+        };
+
+        String subtitle = msg(I18N_CHART_SUBTITLE, "", args);
+        chart.addSubtitle(new TextTitle(subtitle));
+    }
+
+
+    @Override
+    protected String getYAxisLabel() {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+    }
+
+
+    @Override
+    public void doOut(Artifact artifact, String facet, Document attr) {
+        logger.debug("ComputedDischargeCurveGenerator.doOut");
+
+        WQKms wqkms = getData(artifact);
+
+        int size = wqkms.size();
+
+        double[][] data = new double[2][size];
+        double[]   res  = new double[3];
+
+        for (int i = 0; i < size; i++) {
+            res = wqkms.get(i, res);
+
+            data[0][i] = res[1];
+            data[1][i] = res[0];
+        }
+
+        dataset.addSeries(getSeriesName(wqkms), data);
+    }
+
+
+    protected WQKms getData(Artifact artifact) {
+        logger.debug("ComputedDischargeCurveGenerator.getData");
+
+        WINFOArtifact winfoArtifact = (WINFOArtifact) artifact;
+
+        return winfoArtifact.getComputedDischargeCurveData();
+    }
+
+
+    protected String getSeriesName(WQKms wqkms) {
+        Object[] args = new Object[] {
+            getRiverName(),
+            wqkms.getName()
+        };
+
+        return msg(
+            "chart.computed.discharge.curve.curve.label",
+            "",
+            args);
+    }
+}
+// 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/exports/DischargeCurveGenerator.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,177 @@
+package de.intevation.flys.exports;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.xy.DefaultXYDataset;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.flys.model.DischargeTable;
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.TimeInterval;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.DischargeTables;
+import de.intevation.flys.artifacts.model.GaugesFactory;
+
+
+/**
+ * An OutGenerator that generates discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DischargeCurveGenerator extends XYChartGenerator {
+
+    /** The logger used in this generator.*/
+    private static Logger logger =
+        Logger.getLogger(DischargeCurveGenerator.class);
+
+
+    public static final String I18N_CHART_TITLE =
+        "chart.discharge.curve.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.discharge.curve.subtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+        "chart.discharge.curve.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.discharge.curve.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT  = "Abflusskurven";
+    public static final String I18N_XAXIS_LABEL_DEFAULT  = "Q [m³/s]";
+    public static final String I18N_YAXIS_LABEL_DEFAULT  = "W [cm]";
+
+
+    /** The Dataset that contains the data for the chart.*/
+    protected DefaultXYDataset dataset;
+
+
+    public DischargeCurveGenerator() {
+        super();
+
+        this.dataset = new DefaultXYDataset();
+    }
+
+
+    protected String getChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+
+    @Override
+    protected void addSubtitles(JFreeChart chart) {
+
+    }
+
+
+    protected String getXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
+    }
+
+
+    protected String getYAxisLabel() {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+    }
+
+
+    protected void addDatasets(JFreeChart chart) {
+        XYPlot plot = (XYPlot) chart.getPlot();
+        plot.setDataset(0, dataset);
+    }
+
+
+    public void doOut(Artifact artifact, String facet, Document attr) {
+        logger.debug("DischargeCurveGenerator.doOut: " + facet);
+
+        if (!(artifact instanceof FLYSArtifact)) {
+            logger.error("Artifact is no instance of FLYSArtifact.");
+            return;
+        }
+
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+
+        River    river    = flysArtifact.getRiver();
+        double[] distance = flysArtifact.getDistance();
+
+        List<double[]> ranges = new ArrayList<double[]>();
+        ranges.add(distance);
+
+        List<Gauge> gauges   = GaugesFactory.getGauges(river);
+        List<Gauge> filtered = GaugesFactory.filterRanges(gauges, ranges);
+
+        if (logger.isDebugEnabled()) {
+            int numGauges   = gauges != null ? gauges.size() : 0;
+            int numFiltered = filtered != null ? filtered.size() : 0;
+
+            logger.debug("++++++++++++++++++++");
+            logger.debug("Search gauges for river: " + river.getName());
+            logger.debug("-> ... from range: " + distance[0]);
+            logger.debug("-> ... to range: " + distance[1]);
+            logger.debug("-> Found " + numGauges + " gauges in total.");
+            logger.debug("-> Found " + numFiltered + " gauges after filtering.");
+            logger.debug("++++++++++++++++++++");
+        }
+
+        String[] gaugeNames = new String[filtered.size()];
+        int idx = 0;
+        for (Gauge gauge: filtered) {
+            gaugeNames[idx++] = gauge.getName();
+        }
+
+        DischargeTables dt = new DischargeTables(river.getName(), gaugeNames);
+        Map<String, double[][]> gaugeValues = dt.getValues(100d);
+
+        for (Gauge gauge: filtered) {
+            String gaugeName = gauge.getName();
+
+            double[][] values = gaugeValues.get(gaugeName);
+            dataset.addSeries(getSeriesName(gauge), values);
+        }
+    }
+
+
+    protected String getSeriesName(Gauge gauge) {
+        List<DischargeTable> dts = gauge.getDischargeTables();
+
+        for (DischargeTable dt: dts) {
+            if (dt.getKind() == 0) {
+                TimeInterval ti = dt.getTimeInterval();
+
+                Date start = ti.getStartTime();
+                Date end   = ti.getStopTime();
+
+                String name  = gauge.getName();
+
+                if (end == null) {
+                    Object[] args = new Object[] { name, start };
+                    return msg(
+                        "chart.discharge.curve.curve.valid.from",
+                        "",
+                        args);
+                }
+                else {
+                    Object[] args = new Object[] { name, start, end };
+                    return msg(
+                        "chart.discharge.curve.curve.valid.range",
+                        "",
+                        args);
+                }
+            }
+        }
+
+        return gauge.getName();
+    }
+}
+// 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/exports/DischargeLongitudinalSectionExporter.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,123 @@
+package de.intevation.flys.exports;
+
+import java.text.NumberFormat;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+import de.intevation.flys.artifacts.model.WQCKms;
+import de.intevation.flys.artifacts.model.WQKms;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DischargeLongitudinalSectionExporter extends WaterlevelExporter {
+
+    /** The logger used in this exporter.*/
+    private static Logger logger =
+        Logger.getLogger(DischargeLongitudinalSectionExporter.class);
+
+
+    public static final String CSV_KM_HEADER =
+        "export.discharge.longitudinal.section.csv.header.km";
+
+    public static final String CSV_W_HEADER =
+        "export.discharge.longitudinal.section.csv.header.w";
+
+    public static final String CSV_CW_HEADER =
+        "export.discharge.longitudinal.section.csv.header.cw";
+
+    public static final String CSV_Q_HEADER =
+        "export.discharge.longitudinal.section.csv.header.q";
+
+    public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km";
+    public static final String DEFAULT_CSV_W_HEADER  = "W [NN + m]";
+    public static final String DEFAULT_CSV_CW_HEADER = "W korr.";
+    public static final String DEFAULT_CSV_Q_HEADER  = "Q [m³/s]";
+
+
+    @Override
+    protected void addData(Artifact artifact) {
+        WINFOArtifact winfo = (WINFOArtifact) artifact;
+        WQKms[]       wqkms = winfo.getDischargeLongitudinalSectionData();
+
+        logger.debug("Got " + wqkms != null ? wqkms.length : 0 + " WQKms obj.");
+
+        data.add(wqkms);
+    }
+
+
+    @Override
+    protected void writeCSVHeader(CSVWriter writer) {
+        logger.info("WaterlevelExporter.writeCSVHeader");
+
+        writer.writeNext(new String[] {
+            msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
+            msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER),
+            msg(CSV_CW_HEADER, DEFAULT_CSV_CW_HEADER),
+            msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER)
+        });
+    }
+
+    @Override
+    protected void wQKms2CSV(CSVWriter writer, WQKms wqkms) {
+        logger.debug("WaterlevelExporter.wQKms2CSV");
+
+        int      size   = wqkms.size();
+        double[] result = new double[4];
+
+        NumberFormat kmf = getKmFormatter();
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        for (int i = 0; i < size; i ++) {
+            result = wqkms.get(i, result);
+
+            String wc = "";
+            if (wqkms instanceof WQCKms) {
+                wc = wf.format(result[3]);
+            }
+
+            writer.writeNext(new String[] {
+                kmf.format(result[2]),
+                wf.format(result[0]),
+                wc,
+                qf.format(result[1])
+            });
+        }
+    }
+
+
+    @Override
+    protected void addWSTColumn(WstWriter writer, WQKms wqkms) {
+        String name = wqkms.getName();
+
+        // is it a W or a Q mode?
+        int wIdx = name.indexOf("W");
+        int qIdx = name.indexOf("Q");
+
+        String wq = null;
+        if (wIdx >= 0) {
+            wq = "W";
+        }
+        else if (qIdx >= 0) {
+            wq = "Q";
+        }
+
+        // we just want to display the first W or Q value in the WST
+        int start = name.indexOf("(");
+        int end   = name.indexOf(")");
+
+        String   tmp    = name.substring(start+1, end);
+        String[] values = tmp.split(";");
+
+        String column = wq + "=" + values[0];
+
+        writer.addColumn(column);
+    }
+}
+// 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/exports/DischargeLongitudinalSectionGenerator.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,193 @@
+package de.intevation.flys.exports;
+
+import java.awt.Color;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+import de.intevation.flys.artifacts.model.WQCKms;
+import de.intevation.flys.artifacts.model.WQKms;
+
+
+/**
+ * An OutGenerator that generates discharge longitudinal section curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DischargeLongitudinalSectionGenerator
+extends      LongitudinalSectionGenerator
+{
+    private static Logger logger =
+        Logger.getLogger(DischargeLongitudinalSectionGenerator.class);
+
+
+    /** The facet for Ws.*/
+    public static final String FACET_W = "discharge_longitudinal_section.w";
+
+    /** The facet for Qs.*/
+    public static final String FACET_Q = "discharge_longitudinal_section.q";
+
+
+    /** The storage for the corrected W series to be drawn in this chart.*/
+    protected XYSeriesCollection cw;
+
+
+    public DischargeLongitudinalSectionGenerator() {
+        super();
+
+        this.cw = new XYSeriesCollection();
+    }
+
+
+    public void addDatasets(JFreeChart chart) {
+        super.addDatasets(chart);
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+
+        plot.setDataset(2, cw);
+    }
+
+
+    protected void adjustPlot(XYPlot plot) {
+        super.adjustPlot(plot);
+
+        // TODO REMOVE THIS CODE, IF WE HAVE INTRODUCED THEMES!
+        XYLineAndShapeRenderer rw = (XYLineAndShapeRenderer)
+            plot.getRendererForDataset(w);
+
+        XYLineAndShapeRenderer rcw = null;
+        try {
+            rcw = (XYLineAndShapeRenderer) rw.clone();
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+
+        int cwNum = cw.getSeriesCount();
+
+        for (int i = 0; i < cwNum; i++) {
+            rcw.setSeriesPaint(i, Color.RED);
+        }
+
+        plot.setRenderer(2, rcw);
+    }
+
+
+
+    @Override
+    public void doOut(Artifact artifact, String facet, Document attr) {
+        logger.debug("DischargeLongitudinalSectionGenerator.doOut");
+
+        if (facet != null && facet.equals(FACET_W)) {
+            doWOut(getDischargeLongitudinalSectionData(artifact));
+        }
+        else if (facet != null && facet.equals(FACET_Q)) {
+            doQOut(getDischargeLongitudinalSectionData(artifact));
+        }
+        else {
+            logger.warn("Unknown facet name: " + facet);
+            return;
+        }
+    }
+
+
+    protected WQKms[] getDischargeLongitudinalSectionData(Artifact artifact) {
+        WINFOArtifact winfoArtifact = (WINFOArtifact) artifact;
+        return winfoArtifact.getDischargeLongitudinalSectionData();
+    }
+
+
+    /**
+     * This method adds new Series for the wqkms objects. If there are instances
+     * of WQCKms in this array, there is a further curve that displays the
+     * corrected W values.
+     *
+     * @param wqkms An array of WQKms objects to be displayed.
+     */
+    protected void doWOut(WQKms[] wqkms) {
+        logger.debug("LongitudinalSectionGenerator.doWOut");
+
+        int idx = 0;
+        for (WQKms tmp: wqkms) {
+            if (tmp instanceof WQCKms) {
+                doCorrectedWOut((WQCKms) tmp);
+            }
+
+            doWOut(tmp);
+        }
+    }
+
+
+    /**
+     * Adds a new series for the W curve.
+     *
+     * @param wqkms The object that contains the W values.
+     */
+    protected void doWOut(WQKms wqkms) {
+        logger.debug("DischargeLongitudinalSectionGenerator.doWOut");
+
+        XYSeries series = new XYSeries(getSeriesName(wqkms, "W"));
+
+        double[] target = new double[4];
+        int      size   = wqkms.size();
+
+        for (int i = 0; i < size; i++) {
+            target = wqkms.get(i, target);
+
+            series.add(target[2], target[0]);
+        }
+
+        w.addSeries(series);
+    }
+
+
+    /**
+     * Adds a new series for the corrected W curve.
+     *
+     * @param wqckms The object that contains the corrected W values.
+     */
+    protected void doCorrectedWOut(WQCKms wqckms) {
+        logger.debug("DischargeLongitudinalSectionGenerator.doCorrectedWOut");
+
+        XYSeries series = new XYSeries(getSeriesNameForCorrected(wqckms, "W"));
+
+        double[] target = new double[4];
+        int      size   = wqckms.size();
+
+        for (int i = 0; i < size; i++) {
+            target = wqckms.get(i, target);
+
+            series.add(target[2], target[3]);
+        }
+
+        if (series.getItemCount() > 0) {
+            cw.addSeries(series);
+        }
+    }
+
+
+    protected String getSeriesNameForCorrected(WQKms wqkms, String mode) {
+        String name = wqkms.getName();
+
+        name = name.replace(
+            "Benutzerdefiniert",
+            "Benutzerdefiniert [korrigiert]");
+
+        String prefix = name != null && name.indexOf(mode) >= 0 ? null : mode;
+
+        return prefix != null && prefix.length() > 0
+            ? prefix + "(" + name +")"
+            : name;
+    }
+}
+// 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/exports/DurationCurveExporter.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,133 @@
+package de.intevation.flys.exports;
+
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+import de.intevation.flys.artifacts.model.WQDay;
+import de.intevation.flys.utils.Formatter;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DurationCurveExporter extends AbstractExporter {
+
+    /** The logger used in this exporter.*/
+    private static Logger logger = Logger.getLogger(WaterlevelExporter.class);
+
+
+    public static final String CSV_DURATION_HEADER =
+        "export.duration.curve.csv.header.duration";
+
+    public static final String CSV_W_HEADER =
+        "export.duration.curve.csv.header.w";
+
+    public static final String CSV_Q_HEADER =
+        "export.duration.curve.csv.header.q";
+
+    public static final String DEFAULT_CSV_DURATION_HEADER = "D [Tagen]";
+    public static final String DEFAULT_CSV_W_HEADER  = "W [NN + m]";
+    public static final String DEFAULT_CSV_Q_HEADER  = "Q [m³/s]";
+
+
+    /** The storage that contains all WQKms objects for the different facets.*/
+    protected List<WQDay> data;
+
+
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("DurationCurveExporter.init");
+
+        super.init(request, out, context);
+
+        this.data = new ArrayList<WQDay>();
+    }
+
+
+    protected void addData(Artifact artifact) {
+        WINFOArtifact winfoArtifact = (WINFOArtifact) artifact;
+        data.add(winfoArtifact.getDurationCurveData());
+    }
+
+
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("DurationCurveExporter.writeData");
+
+        writeCSVHeader(writer);
+
+        for (WQDay wqday: data) {
+            wQDay2CSV(writer, wqday);
+        }
+    }
+
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        logger.info("DurationCurveExporter.writeCSVHeader");
+
+        writer.writeNext(new String[] {
+            msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER),
+            msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER),
+            msg(CSV_DURATION_HEADER, DEFAULT_CSV_DURATION_HEADER)
+        });
+    }
+
+
+    protected void wQDay2CSV(CSVWriter writer, WQDay wqday) {
+        logger.debug("DurationCurveExporter.wQDay2CSV");
+
+        int size = wqday.size();
+
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+        NumberFormat df  = getDFormatter();
+
+        for (int i = 0; i < size; i ++) {
+            writer.writeNext(new String[] {
+                wf.format(wqday.getW(i)),
+                qf.format(wqday.getQ(i)),
+                df.format(wqday.getDay(i))
+            });
+        }
+    }
+
+
+    /**
+     * Returns the number formatter for W values.
+     *
+     * @return the number formatter for W values.
+     */
+    protected NumberFormat getWFormatter() {
+        return Formatter.getDurationW(context);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values.
+     *
+     * @return the number formatter for Q values.
+     */
+    protected NumberFormat getQFormatter() {
+        return Formatter.getDurationQ(context);
+    }
+
+
+    /**
+     * Returns the number formatter for duration values.
+     *
+     * @return the number formatter for duration values.
+     */
+    protected NumberFormat getDFormatter() {
+        return Formatter.getDurationD(context);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,234 @@
+package de.intevation.flys.exports;
+
+import java.awt.Color;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.chart.title.TextTitle;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+import de.intevation.flys.artifacts.model.WQDay;
+
+
+/**
+ * An OutGenerator that generates duration curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DurationCurveGenerator extends XYChartGenerator {
+
+    private static Logger logger =
+        Logger.getLogger(DurationCurveGenerator.class);
+
+    /** The storage for the W series to be drawn in this chart.*/
+    protected XYSeriesCollection w;
+
+    /** The storage for the Q series to be drawn in this chart.*/
+    protected XYSeriesCollection q;
+
+
+    public static final String DURATION_CURVE_W =
+        "duration_curve.w";
+
+    public static final String DURATION_CURVE_Q =
+        "duration_curve.q";
+
+    public static final String I18N_CHART_TITLE =
+        "chart.duration.curve.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.duration.curve.subtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+        "chart.duration.curve.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.duration.curve.yaxis.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT  =
+        "Dauerlinie";
+
+    public static final String I18N_XAXIS_LABEL_DEFAULT  =
+        "Unterschreitungsdauer [Tage]";
+
+    public static final String I18N_YAXIS_LABEL_DEFAULT  =
+        "W [NN + m]";
+
+
+    public DurationCurveGenerator() {
+        super();
+
+        this.w = new XYSeriesCollection();
+        this.q = new XYSeriesCollection();
+    }
+
+
+    protected String getChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+
+    @Override
+    protected void addSubtitles(JFreeChart chart) {
+        double[] dist  = getRange();
+
+        Object[] args = new Object[] {
+            getRiverName(),
+            dist[0]
+        };
+
+        String subtitle = msg(I18N_CHART_SUBTITLE, "", args);
+        chart.addSubtitle(new TextTitle(subtitle));
+    }
+
+
+    protected String getXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
+    }
+
+
+    protected String getYAxisLabel() {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+    }
+
+
+    public void addDatasets(JFreeChart chart) {
+        XYPlot plot = (XYPlot) chart.getPlot();
+
+        plot.setDataset(0, w);
+        plot.setDataset(1, q);
+    }
+
+
+    protected void adjustPlot(XYPlot plot) {
+        super.adjustPlot(plot);
+
+        // TODO REMOVE THIS CODE, IF WE HAVE INTRODUCED THEMES!
+        XYLineAndShapeRenderer rw = (XYLineAndShapeRenderer)
+            plot.getRendererForDataset(w);
+
+        XYLineAndShapeRenderer rq = null;
+        try {
+            rq = (XYLineAndShapeRenderer) rw.clone();
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+
+        int wNum = w.getSeriesCount();
+        int qNum = q.getSeriesCount();
+
+        for (int i = 0; i < wNum; i++) {
+            rw.setSeriesPaint(i, Color.BLUE);
+        }
+
+        for (int i = 0; i < qNum; i++) {
+            rq.setSeriesPaint(i, Color.GREEN);
+        }
+
+        plot.setRenderer(0, rw);
+        plot.setRenderer(1, rq);
+    }
+
+
+    protected void adjustAxes(XYPlot plot) {
+        super.adjustAxes(plot);
+
+        NumberAxis qAxis = new NumberAxis("Q [m³/s]");
+
+        plot.setRangeAxis(2, qAxis);
+        plot.mapDatasetToRangeAxis(1, 2);
+    }
+
+
+    public void doOut(Artifact artifact, String facet, Document attr) {
+        logger.debug("DurationCurveGenerator.doOut: " + facet);
+
+        if (facet == null || facet.length() == 0) {
+            logger.error("No facet given. Cannot create dataset.");
+            return;
+        }
+
+        if (facet.equals(DURATION_CURVE_W)) {
+            doWOut(getDurationCurveData(artifact));
+        }
+        else if (facet.equals(DURATION_CURVE_Q)) {
+            doQOut(getDurationCurveData(artifact));
+        }
+        else {
+            logger.warn("Unknown facet name: " + facet);
+            return;
+        }
+    }
+
+
+    /**
+     * Creates the series for a duration curve's W facet.
+     *
+     * @param wqdays The WQDay store that contains the Ws.
+     */
+    protected void doWOut(WQDay wqdays) {
+        logger.debug("DurationCurveGenerator.doWOut");
+
+        // TODO find the correct series name
+        XYSeries series = new XYSeries("W-1");
+
+        int size = wqdays.size();
+        for (int i = 0; i < size; i++) {
+            int  day = wqdays.getDay(i);
+            double w = wqdays.getW(i);
+
+            series.add((double) day, w);
+        }
+
+        this.w.addSeries(series);
+    }
+
+
+    /**
+     * Creates the series for a duration curve's Q facet.
+     *
+     * @param wqdays The WQDay store that contains the Qs.
+     */
+    protected void doQOut(WQDay wqdays) {
+        logger.debug("DurationCurveGenerator.doQOut");
+
+        // TODO find the correct series name
+        XYSeries series = new XYSeries("Q-1");
+
+        int size = wqdays.size();
+        for (int i = 0; i < size; i++) {
+            int  day = wqdays.getDay(i);
+            double q = wqdays.getQ(i);
+
+            series.add((double) day, q);
+        }
+
+        this.q.addSeries(series);
+    }
+
+
+    /**
+     * Returns the computed data for a duration curve based on the artifact's
+     * computation method.
+     *
+     * @param artifact The WINFO artifact.
+     *
+     * @return the computed data for a duration curve's W and Q facet.
+     */
+    protected WQDay getDurationCurveData(Artifact artifact) {
+        WINFOArtifact winfoArtifact = (WINFOArtifact) artifact;
+        return winfoArtifact.getDurationCurveData();
+    }
+}
+// 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/exports/LongitudinalSectionGenerator.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,300 @@
+package de.intevation.flys.exports;
+
+import java.awt.Color;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+import org.jfree.chart.title.TextTitle;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.WINFOArtifact;
+import de.intevation.flys.artifacts.model.WQKms;
+
+
+/**
+ * An OutGenerator that generates discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LongitudinalSectionGenerator extends XYChartGenerator {
+
+    /** The logger that is used in this generator.*/
+    private static Logger logger =
+        Logger.getLogger(LongitudinalSectionGenerator.class);
+
+
+    public static final String LONGITUDINAL_SECTION_W =
+        "longitudinal_section.w";
+
+    public static final String LONGITUDINAL_SECTION_Q =
+        "longitudinal_section.q";
+
+    public static final String I18N_CHART_TITLE =
+        "chart.longitudinal.section.title";
+
+    public static final String I18N_CHART_SUBTITLE =
+        "chart.longitudinal.section.subtitle";
+
+    public static final String I18N_XAXIS_LABEL =
+        "chart.longitudinal.section.xaxis.label";
+
+    public static final String I18N_YAXIS_LABEL =
+        "chart.longitudinal.section.yaxis.label";
+
+    public static final String I18N_2YAXIS_LABEL =
+        "chart.longitudinal.section.yaxis.second.label";
+
+    public static final String I18N_CHART_TITLE_DEFAULT  = "W-Längsschnitt";
+    public static final String I18N_XAXIS_LABEL_DEFAULT  = "km";
+    public static final String I18N_YAXIS_LABEL_DEFAULT  = "W [NN + m]";
+    public static final String I18N_2YAXIS_LABEL_DEFAULT = "Q [m³/s]";
+
+    /** The storage for the W series to be drawn in this chart.*/
+    protected XYSeriesCollection w;
+
+    /** The storage for the Q series to be drawn in this chart.*/
+    protected XYSeriesCollection q;
+
+
+    public LongitudinalSectionGenerator() {
+        super();
+
+        this.w = new XYSeriesCollection();
+        this.q = new XYSeriesCollection();
+    }
+
+
+    protected String getChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+
+    @Override
+    protected void addSubtitles(JFreeChart chart) {
+        double[] dist  = getRange();
+
+        Object[] args = new Object[] {
+            getRiverName(),
+            dist[0],
+            dist[1]
+        };
+
+        String subtitle = msg(I18N_CHART_SUBTITLE, "", args);
+        chart.addSubtitle(new TextTitle(subtitle));
+    }
+
+
+    protected String getXAxisLabel() {
+        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
+    }
+
+
+    protected String getYAxisLabel() {
+        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
+    }
+
+
+    protected void addDatasets(JFreeChart chart) {
+        XYPlot plot = (XYPlot) chart.getPlot();
+
+        plot.setDataset(0, w);
+        plot.setDataset(1, q);
+    }
+
+
+    protected void adjustPlot(XYPlot plot) {
+        super.adjustPlot(plot);
+
+        // TODO REMOVE THIS CODE, IF WE HAVE INTRODUCED THEMES!
+        XYLineAndShapeRenderer rw = (XYLineAndShapeRenderer)
+            plot.getRendererForDataset(w);
+
+        XYLineAndShapeRenderer rq = null;
+        try {
+            rq = (XYLineAndShapeRenderer) rw.clone();
+        }
+        catch (Exception e) {
+            logger.error(e, e);
+        }
+
+        int wNum = w.getSeriesCount();
+        int qNum = q.getSeriesCount();
+
+        for (int i = 0; i < wNum; i++) {
+            rw.setSeriesPaint(i, Color.BLUE);
+        }
+
+        for (int i = 0; i < qNum; i++) {
+            rq.setSeriesPaint(i, Color.GREEN);
+        }
+
+        plot.setRenderer(0, rw);
+        plot.setRenderer(1, rq);
+    }
+
+
+    protected void adjustAxes(XYPlot plot) {
+        super.adjustAxes(plot);
+
+        NumberAxis qAxis = new NumberAxis(
+            msg(I18N_2YAXIS_LABEL, I18N_2YAXIS_LABEL_DEFAULT));
+
+        plot.setRangeAxis(2, qAxis);
+        plot.mapDatasetToRangeAxis(1, 2);
+
+        invertXAxis(plot.getDomainAxis());
+    }
+
+
+    /**
+     * This method inverts the x-axis based on the kilometer information of the
+     * selected river. If the head of the river is at kilometer 0, the axis is
+     * not inverted, otherwise it is.
+     *
+     * @param xaxis The domain axis.
+     */
+    protected void invertXAxis(ValueAxis xaxis) {
+        FLYSArtifact artifact = (FLYSArtifact) master;
+        River        river    = artifact.getRiver();
+
+        boolean up = river.getKmUp();
+
+        if (up) {
+            logger.info("Invert X-Axis.");
+            xaxis.setInverted(true);
+        }
+    }
+
+
+    public void doOut(Artifact artifact, String facet, Document attr) {
+        logger.debug("LongitudinalSectionGenerator.doOut: " + facet);
+
+        if (facet == null) {
+            logger.error("No facet name for doOut(). No output generated!");
+            return;
+        }
+
+        if (facet.equals(LONGITUDINAL_SECTION_W)) {
+            doWOut(getWaterlevelData(artifact));
+        }
+        else if (facet.equals(LONGITUDINAL_SECTION_Q)) {
+            doQOut(getWaterlevelData(artifact));
+        }
+        else {
+            logger.warn("Unknown facet name: " + facet);
+            return;
+        }
+    }
+
+
+    /**
+     * Returns the waterlevel data computed by the WINFOArtifact.
+     *
+     * @param artifact The WINFOArtifact.
+     *
+     * @return the computed waterlevel data.
+     */
+    protected WQKms[] getWaterlevelData(Artifact artifact) {
+        WINFOArtifact winfoArtifact = (WINFOArtifact) artifact;
+        WQKms[]       wqkms         = winfoArtifact.getWaterlevelData();
+
+        logger.debug("Got " + wqkms.length + " WQKms objects.");
+
+        return wqkms;
+    }
+
+
+    /**
+     * Process the output for W facets in a longitudinal section curve.
+     *
+     * @param wqkms An array of WQKms values.
+     */
+    protected void doWOut(WQKms[] wqkms) {
+        logger.debug("LongitudinalSectionGenerator.doWOut");
+
+        int idx = 0;
+        for (WQKms tmp: wqkms) {
+            XYSeries series = new XYSeries(getSeriesName(tmp, "W"));
+
+            double[] target = new double[3];
+            int      size   = tmp.size();
+
+            if (logger.isDebugEnabled()) {
+                if (tmp.size() > 0) {
+                    logger.debug("Generate series: " + series.getKey());
+                    logger.debug("Start km: " + tmp.getKms(0));
+                    logger.debug("End   km: " + tmp.getKms(size-1));
+                    logger.debug("Values  : " + size);
+                }
+            }
+
+            for (int i = 0; i < size; i++) {
+                target = tmp.get(i, target);
+
+                series.add(target[2], target[0]);
+            }
+
+            w.addSeries(series);
+        }
+    }
+
+
+    /**
+     * Process the output for Q facets in a longitudinal section curve.
+     *
+     * @param wqkms An array of WQKms values.
+     */
+    protected void doQOut(WQKms[] wqkms) {
+        logger.debug("LongitudinalSectionGenerator.doQOut");
+
+        int idx = 0;
+        for (WQKms tmp: wqkms) {
+            XYSeries series = new XYSeries(getSeriesName(tmp, "Q"));
+
+            double[] target = new double[3];
+            int      size   = tmp.size();
+
+            if (logger.isDebugEnabled()) {
+                if (tmp.size() > 0) {
+                    logger.debug("Generate series: " + series.getKey());
+                    logger.debug("Start km: " + tmp.getKms(0));
+                    logger.debug("End   km: " + tmp.getKms(size-1));
+                    logger.debug("Values  : " + size);
+                }
+            }
+
+            for (int i = 0; i < size; i++) {
+                target = tmp.get(i, target);
+
+                //logger.debug("++ Q Tuple: " + target[2] + " -> " + target[1]);
+                series.add(target[2], target[1]);
+            }
+
+            q.addSeries(series);
+        }
+    }
+
+
+    protected String getSeriesName(WQKms wqkms, String mode) {
+        String name   = wqkms.getName();
+        String prefix = name != null && name.indexOf(mode) >= 0 ? null : mode;
+
+        return prefix != null && prefix.length() > 0
+            ? prefix + "(" + name +")"
+            : name;
+    }
+}
+// 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/exports/OutGenerator.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,56 @@
+package de.intevation.flys.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+
+/**
+ * An OutGenerator is used to create a collected outputs of a list of Artifacts.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface OutGenerator {
+
+    /**
+     * Initializes the OutGenerator with meta information which are necessary
+     * for the output generation.
+     *
+     * @param request The incomding request document.
+     * @param out     The output stream.
+     * @param context The CallContext that provides further information and
+     * objects used for the output generation.
+     */
+    void init(Document request, OutputStream out, CallContext context);
+
+    /**
+     * This method is used to tell the OutGenerator which artifact is the master
+     * artifact which is used for special operations.
+     *
+     * @param master The master artifact.
+     */
+    void setMasterArtifact(Artifact master);
+
+    /**
+     * Creates the output of an Artifact and appends that single output to the
+     * total output.
+     *
+     * @param artifact The artifact that provides information and data for the
+     * single output.
+     * @param attr A document that might contain some attributes used while
+     * producing the output.
+     */
+    void doOut(Artifact artifact, String facet, Document attr);
+
+    /**
+     * Writes the collected output of all artifacts specified in the
+     * <i>request</i> (see init()) document to the OutputStream <i>out</i> (see
+     * init()).
+     */
+    void generate() throws IOException;
+}
+// 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/exports/WaterlevelExporter.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,220 @@
+package de.intevation.flys.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.utils.Formatter;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WaterlevelExporter extends AbstractExporter {
+
+    /** The logger used in this exporter.*/
+    private static Logger logger = Logger.getLogger(WaterlevelExporter.class);
+
+
+    public static final String FACET_WST = "wst";
+
+
+    public static final String CSV_KM_HEADER =
+        "export.waterlevel.csv.header.km";
+
+    public static final String CSV_W_HEADER =
+        "export.waterlevel.csv.header.w";
+
+    public static final String CSV_Q_HEADER =
+        "export.waterlevel.csv.header.q";
+
+    public static final String DEFAULT_CSV_KM_HEADER = "Fluss-Km";
+    public static final String DEFAULT_CSV_W_HEADER  = "W [NN + m]";
+    public static final String DEFAULT_CSV_Q_HEADER  = "Q [m³/s]";
+
+
+    /** The storage that contains all WQKms objects for the different facets.*/
+    protected List<WQKms[]> data;
+
+
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("WaterlevelExporter.init");
+
+        super.init(request, out, context);
+
+        this.data = new ArrayList<WQKms[]>();
+    }
+
+
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("WaterlevelExporter.generate");
+
+        if (facet != null && facet.equals(AbstractExporter.FACET_CSV)) {
+            generateCSV();
+        }
+        else if (facet != null && facet.equals(FACET_WST)) {
+            generateWST();
+        }
+        else {
+            throw new IOException("invalid facet for exporter");
+        }
+    }
+
+
+    protected void addData(Artifact artifact) {
+        data.add(getWaterlevelData(artifact));
+    }
+
+
+    /**
+     * Returns the waterlevel data computed by the WINFOArtifact.
+     *
+     * @param artifact The WINFOArtifact.
+     *
+     * @return the computed waterlevel data.
+     */
+    protected WQKms[] getWaterlevelData(Artifact artifact) {
+        WINFOArtifact winfoArtifact = (WINFOArtifact) artifact;
+        WQKms[]       wqkms         = winfoArtifact.getWaterlevelData();
+
+        logger.debug("Got " + wqkms.length + " WQKms objects.");
+
+        return wqkms;
+    }
+
+
+    protected void writeCSVData(CSVWriter writer) {
+        logger.info("WaterlevelExporter.writeData");
+
+        writeCSVHeader(writer);
+
+        for (WQKms[] tmp: data) {
+            for (WQKms wqkms: tmp) {
+                wQKms2CSV(writer, wqkms);
+            }
+        }
+    }
+
+
+    protected void writeCSVHeader(CSVWriter writer) {
+        logger.info("WaterlevelExporter.writeCSVHeader");
+
+        writer.writeNext(new String[] {
+            msg(CSV_KM_HEADER, DEFAULT_CSV_KM_HEADER),
+            msg(CSV_W_HEADER, DEFAULT_CSV_W_HEADER),
+            msg(CSV_Q_HEADER, DEFAULT_CSV_Q_HEADER)
+        });
+    }
+
+
+    protected void wQKms2CSV(CSVWriter writer, WQKms wqkms) {
+        logger.debug("WaterlevelExporter.wQKms2CSV");
+
+        NumberFormat kmf = getKmFormatter();
+        NumberFormat wf  = getWFormatter();
+        NumberFormat qf  = getQFormatter();
+
+        int      size   = wqkms.size();
+        double[] result = new double[3];
+
+        for (int i = 0; i < size; i ++) {
+            result = wqkms.get(i, result);
+
+            writer.writeNext(new String[] {
+                kmf.format(result[2]),
+                wf.format(result[0]),
+                qf.format(result[1])
+            });
+        }
+    }
+
+
+    /**
+     * Generates the output in WST format.
+     */
+    protected void generateWST()
+    throws    IOException
+    {
+        logger.info("WaterlevelExporter.generateWST");
+
+        int cols = data.get(0).length;
+        WstWriter writer = new WstWriter(cols);
+
+        writeWSTData(writer);
+
+        writer.write(out);
+    }
+
+
+    protected void writeWSTData(WstWriter writer) {
+        logger.debug("WaterlevelExporter.writeWSTData");
+
+        double[] result = new double[3];
+
+        for (WQKms[] tmp: data) {
+            for (WQKms wqkms: tmp) {
+                int size = wqkms != null ? wqkms.size() : 0;
+
+                addWSTColumn(writer, wqkms);
+
+                for (int i = 0; i < size; i++) {
+                    result = wqkms.get(i, result);
+
+                    writer.add(result);
+                }
+            }
+        }
+    }
+
+
+    protected void addWSTColumn(WstWriter writer, WQKms wqkms) {
+        writer.addColumn(wqkms.getName());
+    }
+
+
+    /**
+     * Returns the number formatter for kilometer values.
+     *
+     * @return the number formatter for kilometer values.
+     */
+    protected NumberFormat getKmFormatter() {
+        return Formatter.getWaterlevelKM(context);
+    }
+
+
+    /**
+     * Returns the number formatter for W values.
+     *
+     * @return the number formatter for W values.
+     */
+    protected NumberFormat getWFormatter() {
+        return Formatter.getWaterlevelW(context);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values.
+     *
+     * @return the number formatter for Q values.
+     */
+    protected NumberFormat getQFormatter() {
+        return Formatter.getWaterlevelQ(context);
+    }
+}
+// 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/exports/WstWriter.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,210 @@
+package de.intevation.flys.exports;
+
+import java.io.BufferedWriter;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.artifacts.model.WstLine;
+
+
+/**
+ * A writer that creates WSTs.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WstWriter {
+
+    /** The logger used in this class.*/
+    private static Logger logger = Logger.getLogger(WstWriter.class);
+
+
+    /** The default unit that is written into the header of the WST.*/
+    public static final String DEFAULT_UNIT = "Wassserstand [NN + m]";
+
+
+    /** The lines that need to be included for the export.*/
+    protected Map<Double, WstLine> lines;
+
+    /** The column names.*/
+    protected List<String> columnNames;
+
+    /** The locale used to format the values.*/
+    protected Locale locale;
+
+    /** The number of discharge columns.*/
+    protected int cols;
+
+    /** The last Q values.*/
+    protected double[] qs;
+
+
+
+    /**
+     * This constructor creates a new WstWriter with an OutputStream and a
+     * number of Q columns.
+     *
+     * @param out The output stream where the WST is written to.
+     * @param cols The number of columns of the resulting WST.
+     */
+    public WstWriter(int cols) {
+        this.columnNames = new ArrayList<String>(cols);
+        this.lines       = new HashMap<Double, WstLine>();
+        this.qs          = new double[cols];
+        this.locale      = Locale.US;
+        this.cols        = cols;
+
+    }
+
+
+    /**
+     * This method is used to create the WST from the data that has been
+     * inserted using add(double[]) before.
+     */
+    public void write(OutputStream out) {
+        logger.info("WstWriter.write");
+
+        PrintWriter writer = new PrintWriter(
+            new BufferedWriter(
+                new OutputStreamWriter(out)));
+
+        writeHeader(writer);
+
+        Collection<WstLine> collection = new TreeMap(lines).values();
+
+        for (WstLine line: collection) {
+            writeWLine(writer, line);
+        }
+
+        writer.flush();
+        writer.close();
+    }
+
+
+    /**
+     * This method is used to add a new line to the WST.
+     *
+     * @param wqkms A 3dim double array with [W,Q, KM].
+     */
+    public void add(double[] wqkms) {
+        Double km = wqkms[2];
+
+        WstLine line = lines.get(km);
+
+        if (line == null) {
+            line = new WstLine(km.doubleValue());
+            lines.put(km, line);
+        }
+
+        line.add(wqkms[0], wqkms[1]);
+    }
+
+
+    /**
+     * Adds a further column name.
+     *
+     * @param name The name of the new column.
+     */
+    public void addColumn(String name) {
+        if (name != null) {
+            columnNames.add(name);
+        }
+    }
+
+
+    /**
+     * This method writes the header of the WST.
+     *
+     * @param writer The PrintWriter that creates the output.
+     */
+    protected void writeHeader(PrintWriter writer) {
+        logger.debug("WstWriter.writeHeader");
+
+        writer.println(cols);
+        writer.print("        ");
+
+        for (String columnName: columnNames) {
+            writer.printf(locale, "%9s", columnName);
+        }
+
+        writer.println();
+
+        writer.write("*   KM     ");
+        writer.write(DEFAULT_UNIT);
+        writer.println();
+    }
+
+
+    /**
+     * This method writes a line with W values and a certain kilometer.
+     *
+     * @param writer The PrintWriter that is used to create the output.
+     * @param line The WstLine that should be written to the output.
+     */
+    protected void writeWLine(PrintWriter writer, WstLine line) {
+        double   km  = line.getKm();
+        double[] qs  = line.getQs();
+        int      num = line.getSize();
+
+        if (dischargesChanged(qs)) {
+            writeQLine(writer, qs);
+        }
+
+        writer.printf(locale, "%8.3f", km);
+
+        for (int i = 0; i < num; i++) {
+            writer.printf(locale, "%9.2f", line.getW(i));
+        }
+
+        writer.println();
+    }
+
+
+    /**
+     * Writes a discharge line (Q values) into a WST.
+     *
+     * @param qs the Q values for the next range.
+     */
+    protected void writeQLine(PrintWriter writer, double[] qs) {
+        writer.write("*\u001f      ");
+
+        for (int i = 0; i < cols; i++) {
+            this.qs[i] = qs[i];
+
+            writer.printf(locale, "%9.2f", qs[i]);
+        }
+
+        writer.println();
+    }
+
+
+    /**
+     * This method determines if a Q has changed from the last line to the
+     * current one.
+     *
+     * @param newQs The Q values of the next line.
+     *
+     * @return true, if a Q value have changed, otherwise false.
+     */
+    protected boolean dischargesChanged(double[] newQs) {
+        // XXX maybe there is a way to do this faster
+        for (int i = 0; i < cols; i++) {
+            if (Math.abs(newQs[i] - qs[i]) >= 0.001) {
+                return true;
+            }
+        }
+
+        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/exports/XYChartGenerator.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,135 @@
+package de.intevation.flys.exports;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Stroke;
+
+import java.io.IOException;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+
+import de.intevation.flys.exports.ChartExportHelper;
+
+
+/**
+ * An abstract base class for creating XY charts.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class XYChartGenerator extends ChartGenerator {
+
+    /** The logger that is used in this generator.*/
+    private static Logger logger = Logger.getLogger(ChartGenerator.class);
+
+
+    public static final Color DEFAULT_GRID_COLOR      = Color.GRAY;
+    public static final float DEFAULT_GRID_LINE_WIDTH = 0.3f;
+
+
+    /**
+     * Returns the title of a chart.
+     *
+     * @return the title of a chart.
+     */
+    protected abstract String getChartTitle();
+
+    /**
+     * Returns the X-Axis label of a chart.
+     *
+     * @return the X-Axis label of a chart.
+     */
+    protected abstract String getXAxisLabel();
+
+    /**
+     * Returns the Y-Axis label of a chart.
+     *
+     * @return the Y-Axis label of a chart.
+     */
+    protected abstract String getYAxisLabel();
+
+    /**
+     * This method is called to add all datasets of a concrete XYChartGenerator
+     * to the JFreeChart.
+     *
+     * @param chart The JFreeChart object.
+     */
+    protected abstract void addDatasets(JFreeChart chart);
+
+
+    public void generate()
+    throws IOException
+    {
+        logger.debug("XYChartGenerator.generate");
+
+        JFreeChart chart = ChartFactory.createXYLineChart(
+            getChartTitle(),
+            getXAxisLabel(),
+            getYAxisLabel(),
+            null,
+            PlotOrientation.VERTICAL,
+            true,
+            false,
+            false);
+
+        chart.setBackgroundPaint(Color.WHITE);
+        chart.getPlot().setBackgroundPaint(Color.WHITE);
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+
+        addDatasets(chart);
+        addSubtitles(chart);
+        adjustPlot(plot);
+        adjustAxes(plot);
+
+        int[] size = getSize();
+
+        ChartExportHelper.exportImage(
+            out,
+            chart,
+            "png",
+            size[0], size[1]);
+    }
+
+
+    /**
+     * Adjusts the axes of a plot.
+     *
+     * @param plot The XYPlot of the chart.
+     */
+    protected void adjustAxes(XYPlot plot) {
+        NumberAxis yAxis = (NumberAxis) plot.getRangeAxis();
+
+        yAxis.setAutoRangeIncludesZero(false);
+    }
+
+
+    protected void adjustPlot(XYPlot plot) {
+        Stroke gridStroke = new BasicStroke(
+            DEFAULT_GRID_LINE_WIDTH,
+            BasicStroke.CAP_BUTT,
+            BasicStroke.JOIN_MITER,
+            3.0f,
+            new float[] { 3.0f },
+            0.0f);
+
+        plot.setDomainGridlineStroke(gridStroke);
+        plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR);
+        plot.setDomainGridlinesVisible(true);
+
+        plot.setRangeGridlineStroke(gridStroke);
+        plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR);
+        plot.setRangeGridlinesVisible(true);
+    }
+
+
+    protected void addSubtitles(JFreeChart chart) {
+        // override this method in subclasses that need subtitles
+    }
+}
+// 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/themes/DefaultTheme.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,167 @@
+package de.intevation.flys.themes;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultTheme implements Theme {
+
+    /** The name of the theme.*/
+    protected String name;
+
+    /** The description of the theme.*/
+    protected String description;
+
+
+    /** The map storing the fields of this theme.*/
+    protected Map<String, ThemeField> fields;
+
+    /** The map storing the attributes of this theme.*/
+    protected Map<String, String> attr;
+
+
+    /**
+     * Initializes the components of this Theme.
+     */
+    public DefaultTheme(String name, String description) {
+        this.name        = name;
+        this.description = description;
+        this.fields      = new HashMap<String, ThemeField>();
+        this.attr        = new HashMap<String, String>();
+    }
+
+
+    public void init(Node config) {
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public void addAttribute(String name, String value) {
+        if (name != null && value != null) {
+            attr.put(name, value);
+        }
+    }
+
+
+    public String getAttribute(String name) {
+        return attr.get(name);
+    }
+
+
+    public void addField(String name, ThemeField field) {
+        if (name != null && field != null) {
+            fields.put(name, field);
+        }
+    }
+
+
+    public void setFieldValue(String name, Object value) {
+        if (name != null && value != null) {
+            ThemeField field = fields.get(name);
+
+            if (field != null) {
+                field.setValue(value);
+            }
+        }
+    }
+
+
+    public ThemeField getField(String name) {
+        return fields.get(name);
+    }
+
+
+    public String getFieldType(String name) {
+        ThemeField field = fields.get(name);
+
+        return field != null ? field.getType() : null;
+    }
+
+
+    public Object getFieldValue(String name) {
+        ThemeField field = fields.get(name);
+
+        return field != null ? field.getValue() : null;
+    }
+
+
+    public Document toXML() {
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(doc, null, null);
+
+        Element theme = cr.create("theme");
+
+        appendAttributes(cr, theme);
+        appendFields(cr, theme);
+
+        doc.appendChild(theme);
+
+        return doc;
+    }
+
+
+    /**
+     * Appends the attributes configured for this theme.
+     *
+     * @param cr The ElementCreator.
+     * @param theme The document root element.
+     */
+    protected void appendAttributes(ElementCreator cr, Element theme) {
+        Iterator<String> iter = attr.keySet().iterator();
+
+        while (iter.hasNext()) {
+            String key = iter.next();
+            String val = getAttribute(key);
+
+            if (key == null || val == null) {
+                continue;
+            }
+
+            cr.addAttr(theme, key, val);
+        }
+    }
+
+
+    /**
+     * Appends the fields configured for this theme.
+     *
+     * @param cr The ElementCreator.
+     * @param theme The document root element.
+     */
+    protected void appendFields(ElementCreator cr, Element theme) {
+        Iterator<String> iter = fields.keySet().iterator();
+
+        while (iter.hasNext()) {
+            String name = iter.next();
+
+            ThemeField field = getField(name);
+
+            Document doc = field.toXML();
+            Node    root = doc.getFirstChild();
+
+            theme.appendChild(theme.getOwnerDocument().importNode(root, 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/themes/DefaultThemeField.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,85 @@
+package de.intevation.flys.themes;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultThemeField implements ThemeField {
+
+    protected String name;
+
+    protected Map<String, Object> attr;
+
+
+    public DefaultThemeField(String name) {
+        this.name = name;
+        this.attr = new HashMap<String, Object>();
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public String getType() {
+        return (String) getAttribute("type");
+    }
+
+
+    public Object getValue() {
+        return getAttribute("value");
+    }
+
+
+    public void setValue(Object value) {
+        setAttribute("value", value);
+    }
+
+
+    public Object getAttribute(String name) {
+        return attr.get(name);
+    }
+
+
+    public void setAttribute(String name, Object value) {
+        if (name == null || value == null) {
+            return;
+        }
+
+        attr.put(name, value);
+    }
+
+
+    public Document toXML() {
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(doc, null, null);
+
+        Element field = cr.create("field");
+
+        Iterator<String> iter = attr.keySet().iterator();
+
+        while (iter.hasNext()) {
+            String name  = iter.next();
+            String value = (String) getAttribute(name);
+
+            cr.addAttr(field, name, value);
+        }
+
+        doc.appendChild(field);
+
+        return doc;
+    }
+}
+// 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/themes/Theme.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,110 @@
+package de.intevation.flys.themes;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface Theme {
+
+    /**
+     * Method to initialize the theme.
+     *
+     * @param config The configuration node.
+     */
+    void init(Node config);
+
+
+    /**
+     * Returns the name of the theme.
+     *
+     * @return the name of the theme.
+     */
+    String getName();
+
+
+    /**
+     * Returns the description of the theme.
+     *
+     * @return the description of the theme.
+     */
+    String getDescription();
+
+
+    /**
+     * Adds a new attribute.
+     *
+     * @param name The name of the attribute.
+     * @param value The value of the attribute.
+     */
+    void addAttribute(String name, String value);
+
+
+    /**
+     * Returns the value of a specific attribute.
+     *
+     * @param name the name of the attribute.
+     *
+     * @return the value of the attribute <i>name</i>.
+     */
+    String getAttribute(String name);
+
+
+    /**
+     * Adds a new field to the theme.
+     *
+     * @param name The name of the field.
+     * @param field The field.
+     */
+    void addField(String name, ThemeField field);
+
+
+    /**
+     * Sets the value of an field.
+     *
+     * @param name The name of the field.
+     * @param value The new value of the field.
+     */
+    void setFieldValue(String name, Object value);
+
+
+    /**
+     * Returns the field specified by name.
+     *
+     * @param name The name of the desired field.
+     *
+     * @return an field.
+     */
+    ThemeField getField(String name);
+
+
+    /**
+     * Returns the typename of a field.
+     *
+     * @param name the name of the field.
+     *
+     * @return the typename of a field.
+     */
+    String getFieldType(String name);
+
+
+    /**
+     * Returns the value of a field.
+     *
+     * @param name The name of the field.
+     *
+     * @return the value of a field.
+     */
+    Object getFieldValue(String name);
+
+
+    /**
+     * Dumps the theme to XML.
+     *
+     * @return a document.
+     */
+    Document toXML();
+}
+// 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/themes/ThemeFactory.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,218 @@
+package de.intevation.flys.themes;
+
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.context.FLYSContext;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ThemeFactory {
+
+    private static Logger logger = Logger.getLogger(ThemeFactory.class);
+
+    private ThemeFactory() {
+    }
+
+
+    /**
+     * Creates a new theme from <i>config</i>.
+     *
+     * @param themeCfg The theme config document that is used to fetch parent
+     * themes.
+     * @param config The theme config node.
+     *
+     * @return a new theme.
+     */
+    public static Theme createTheme(Document themeCfg, Node config) {
+        String name = getName(config);
+        String desc = getDescription(config);
+
+        logger.debug("Create new theme: " + name);
+
+        Theme theme = new DefaultTheme(name, desc);
+
+        parseInherits(themeCfg, config, theme);
+        parseFields(config, theme);
+        parseAttrs(config, theme);
+
+        return theme;
+    }
+
+
+    /**
+     * Returns the theme for a specified output type and facet.
+     *
+     * @param c The FLYSContext that stores the mappings and themes.
+     * @param name The name of the mapping.
+     *
+     * @return a theme.
+     */
+    public static Theme getTheme(FLYSContext c, String name) {
+        if (c == null || name == null) {
+            logger.warn("Cannot search for theme.");
+            return null;
+        }
+
+        Map<String, String> map = (Map<String, String>)
+            c.get(FLYSContext.THEME_MAPPING);
+
+        Map<String, Theme> t = (Map<String, Theme>)
+            c.get(FLYSContext.THEMES);
+
+        if (map == null || map.size() == 0 || t == null || t.size() == 0) {
+            logger.warn("No mappings or themes found. Cannot retrieve theme!");
+            return null;
+        }
+
+        String themeName = map.get(name);
+
+        if (themeName == null) {
+            logger.warn("No theme found for mapping: " + name);
+        }
+
+        return t.get(themeName);
+    }
+
+
+    protected static String getName(Node config) {
+        return (String) XMLUtils.xpath(config, "@name", XPathConstants.STRING);
+    }
+
+
+    protected static String getDescription(Node config) {
+        return (String) XMLUtils.xpath(config, "@desc", XPathConstants.STRING);
+    }
+
+
+    protected static void parseInherits(Document themeCfg, Node cfg, Theme t) {
+        logger.debug("ThemeFactory.parseInherits");
+
+        NodeList inherits = (NodeList) XMLUtils.xpath(
+            cfg, "inherits/inherit", XPathConstants.NODESET);
+
+        int num = inherits != null ? inherits.getLength() : 0;
+
+        if (num == 0) {
+            logger.debug("Theme does not inherit from other themes.");
+            return;
+        }
+
+        logger.debug("Theme inherits from " + num + " other themes.");
+
+        for (int i = 0; i < num; i++) {
+            Node inherit = inherits.item(i);
+            String from  = (String) XMLUtils.xpath(
+                inherit, "@from", XPathConstants.STRING);
+
+            Node tmp = getThemeNode(themeCfg, from);
+
+            parseInherits(themeCfg, tmp, t);
+            parseFields(tmp, t);
+        }
+    }
+
+
+    protected static Node getThemeNode(Document themeCfg, String name) {
+        if (name == null) {
+            logger.warn("Cannot search theme config without name!");
+            return null;
+        }
+
+        logger.debug("Search for theme: " + name);
+
+        String xpath = "/themes/theme[@name='" + name + "']";
+
+        return (Node) XMLUtils.xpath(themeCfg, xpath, XPathConstants.NODE);
+    }
+
+
+    protected static void parseFields(Node config, Theme theme) {
+        if (config == null || theme == null) {
+            logger.warn("Parsing fields without node or theme is senseless!");
+            return;
+        }
+
+        NodeList fields = (NodeList) XMLUtils.xpath(
+            config, "fields/field", XPathConstants.NODESET);
+
+        int num = fields != null ? fields.getLength() : 0;
+
+        logger.debug("Found " + num + " own fields in this theme.");
+
+        if (num == 0) {
+            logger.debug("Theme has no own fields.");
+            return;
+        }
+
+        for (int i = 0; i < num; i++) {
+            Node field = fields.item(i);
+
+            addField(theme, field);
+        }
+    }
+
+
+    protected static void addField(Theme theme, Node field) {
+        String name = (String) XMLUtils.xpath(
+            field, "@name", XPathConstants.STRING);
+
+        logger.debug("Add field: " + name);
+
+        NamedNodeMap attrs = field.getAttributes();
+
+        int num = attrs != null ? attrs.getLength() : 0;
+
+        if (num == 0) {
+            logger.warn("This field has no attributes.");
+            return;
+        }
+
+        ThemeField theField = new DefaultThemeField(name);
+
+        for (int i = 0; i < num; i++) {
+            Node attr    = attrs.item(i);
+
+            String key   = attr.getNodeName();
+            String value = attr.getNodeValue();
+
+            theField.setAttribute(key, value);
+        }
+
+        theme.addField(name, theField);
+    }
+
+
+    protected static void parseAttrs(Node config, Theme theme) {
+        NamedNodeMap attrs = config.getAttributes();
+
+        int num = attrs != null ? attrs.getLength() : 0;
+
+        if (num == 0) {
+            logger.debug("Theme has no attributes set.");
+            return;
+        }
+
+        for (int i = 0; i < num; i++) {
+            Node attr = attrs.item(i);
+
+            String name  = attr.getNodeName();
+            String value = attr.getNodeValue();
+
+            theme.addAttribute(name, value);
+        }
+    }
+}
+// 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/themes/ThemeField.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,58 @@
+package de.intevation.flys.themes;
+
+import org.w3c.dom.Document;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface ThemeField {
+
+    /**
+     * Returns the name of this field.
+     *
+     * @return the name of this field.
+     */
+    String getName();
+
+    /**
+     * Returns the type of this field.
+     *
+     * @return the type of this field.
+     */
+    String getType();
+
+
+    /**
+     * Returns the value of this field.
+     *
+     * @return the value of this field.
+     */
+    Object getValue();
+
+
+    /**
+     * Changes the value of this field.
+     *
+     * @param value The new value.
+     */
+    void setValue(Object value);
+
+
+    /**
+     * Sets the value of an attribute.
+     *
+     * @param name The name of an attribute.
+     * @param value The value of an attribute.
+     */
+    void setAttribute(String name, Object value);
+
+
+    /**
+     * Dumps the field to XML.
+     *
+     * @return a document.
+     */
+    Document toXML();
+}
+// 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/utils/Formatter.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,154 @@
+package de.intevation.flys.utils;
+
+import java.text.NumberFormat;
+import java.util.Locale;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.flys.artifacts.resources.Resources;
+
+
+public final class Formatter {
+
+    // WATERLEVEL FORMATTER CONSTANTS
+    public static final int WATERLEVEL_KM_MIN_DIGITS = 3;
+    public static final int WATERLEVEL_KM_MAX_DIGITS = 3;
+    public static final int WATERLEVEL_W_MIN_DIGITS  = 0;
+    public static final int WATERLEVEL_W_MAX_DIGITS  = 2;
+    public static final int WATERLEVEL_Q_MIN_DIGITS  = 0;
+    public static final int WATERLEVEL_Q_MAX_DIGITS  = 2;
+
+
+    // COMPUTED DISCHARGE CURVE FORMATTER CONSTANTS
+    public static final int COMPUTED_DISCHARGE_W_MIN_DIGITS  = 2;
+    public static final int COMPUTED_DISCHARGE_W_MAX_DIGITS  = 2;
+    public static final int COMPUTED_DISCHARGE_Q_MIN_DIGITS  = 0;
+    public static final int COMPUTED_DISCHARGE_Q_MAX_DIGITS  = 0;
+
+
+    // DURATION CURVE FORMATTER CONSTANTS
+    public static final int DURATION_W_MIN_DIGITS = 0;
+    public static final int DURATION_W_MAX_DIGITS = 2;
+    public static final int DURATION_Q_MIN_DIGITS = 0;
+    public static final int DURATION_Q_MAX_DIGITS = 1;
+    public static final int DURATION_D_MIN_DIGITS = 0;
+    public static final int DURATION_D_MAX_DIGITS = 0;
+
+
+    public static NumberFormat getFormatter(CallContext c, int min, int max){
+        Locale       locale = Resources.getLocale(c.getMeta());
+        NumberFormat nf     = NumberFormat.getInstance(locale);
+
+        nf.setMaximumFractionDigits(max);
+        nf.setMinimumFractionDigits(min);
+
+        return nf;
+    }
+
+
+    /**
+     * Returns the number formatter for kilometer values in waterlevel exports.
+     *
+     * @return the number formatter for kilometer values.
+     */
+    public static NumberFormat getWaterlevelKM(CallContext context) {
+        return getFormatter(
+            context,
+            WATERLEVEL_KM_MIN_DIGITS,
+            WATERLEVEL_KM_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for W values in waterlevel exports.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getWaterlevelW(CallContext context) {
+        return getFormatter(
+            context,
+            WATERLEVEL_W_MIN_DIGITS,
+            WATERLEVEL_W_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values in waterlevel exports.
+     *
+     * @return the number formatter for Q values.
+     */
+    public static NumberFormat getWaterlevelQ(CallContext context) {
+        return getFormatter(
+            context,
+            WATERLEVEL_Q_MIN_DIGITS,
+            WATERLEVEL_Q_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for W values in exports of computed
+     * discharge curves.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getComputedDischargeW(CallContext context) {
+        return getFormatter(
+            context,
+            COMPUTED_DISCHARGE_W_MIN_DIGITS,
+            COMPUTED_DISCHARGE_W_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values in exports of computed
+     * discharge curves.
+     *
+     * @return the number formatter for Q values.
+     */
+    public static NumberFormat getComputedDischargeQ(CallContext context) {
+        return getFormatter(
+            context,
+            COMPUTED_DISCHARGE_Q_MIN_DIGITS,
+            COMPUTED_DISCHARGE_Q_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for W values in duration curve exports.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getDurationW(CallContext context) {
+        return getFormatter(
+            context,
+            DURATION_W_MIN_DIGITS,
+            DURATION_W_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for Q values in duration curve exports.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getDurationQ(CallContext context) {
+        return getFormatter(
+            context,
+            DURATION_Q_MIN_DIGITS,
+            DURATION_Q_MAX_DIGITS);
+    }
+
+
+    /**
+     * Returns the number formatter for D values in duration curve exports.
+     *
+     * @return the number formatter for W values.
+     */
+    public static NumberFormat getDurationD(CallContext context) {
+        return getFormatter(
+            context,
+            DURATION_D_MIN_DIGITS,
+            DURATION_D_MAX_DIGITS);
+    }
+}
+// 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/resources/messages.properties	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,48 @@
+state.winfo.river = River
+state.winfo.calculation_mode = Calculation Mode
+state.winfo.location_distance = Location or distance selection
+state.winfo.wq = Input for W/Q data
+state.winfo.wq_adapted = Input for W/Q data
+state.winfo.location = Choose the location
+
+calc.surface.curve = Water Level/Surface Curve
+calc.flood.map = Flood Plain
+calc.discharge.curve = State Discharge Curve/Stage Discharge Relation
+calc.duration.curve = Duration Curve
+calc.discharge.longitudinal.section = TODO (W bei...)
+
+river = River
+calculation_mode = Calculation Mode
+ld_locations = Location(s)
+
+chart.longitudinal.section.title = W-Longitudinal Section
+chart.longitudinal.section.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.longitudinal.section.xaxis.label = km
+chart.longitudinal.section.yaxis.label = W [NN + m]
+chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
+chart.discharge.curve.title = Discharge Curve
+chart.discharge.curve.xaxis.label = Q [m\u00b3/s]
+chart.discharge.curve.yaxis.label = W [cm]
+chart.discharge.curve.curve.valid.from = {0} (valid from {1,date,short})
+chart.discharge.curve.curve.valid.range = {0} (valid from {1,date,short} - {2,date,short})
+chart.computed.discharge.curve.title = Discharge Curve
+chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###}
+chart.computed.discharge.curve.yaxis.label = W [NN + m]
+chart.computed.discharge.curve.curve.label = Discharge Curve {0} km {1}
+chart.duration.curve.title = Duration Curve
+chart.duration.curve.subtitle = {0}-km: {1,number,#.###}
+chart.duration.curve.xaxis.label = Duration of Non-Exceedence [Days]
+chart.duration.curve.yaxis.label = W [NN + m]
+
+export.waterlevel.csv.header.km = River-Km
+export.waterlevel.csv.header.w = W [NN + m]
+export.waterlevel.csv.header.q = Q [m\u00b3/s]
+export.computed.discharge.curve.csv.header.w = W [NN + m]
+export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s]
+export.duration.curve.csv.header.duration = D [Days]
+export.duration.curve.csv.header.w = W [NN + m]
+export.duration.curve.csv.header.q = Q [m\u00b3/s]
+export.discharge.longitudinal.section.csv.header.km = River-Km
+export.discharge.longitudinal.section.csv.header.w = W [NN + m]
+export.discharge.longitudinal.section.csv.header.cw = W corr.
+export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/resources/messages_de.properties	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,48 @@
+state.winfo.river = Gew\u00e4sser
+state.winfo.calculation_mode = Berechnungsart
+state.winfo.location_distance = Wahl des Berechnungsortes/strecke
+state.winfo.wq = Eingabe f\u00fcr W/Q Daten
+state.winfo.wq_adapted = Eingabe f\u00fcr W/Q Daten
+state.winfo.location = Wahl des Berechnungsortes
+
+calc.surface.curve = Wasserstand/Wasserspiegellage
+calc.flood.map = \u00dcberschwemmungsfl\u00e4che
+calc.discharge.curve = Abflusskurve/Abflusstafel
+calc.duration.curve = Dauerlinie
+calc.discharge.longitudinal.section = W bei ungleichm\u00e4\u00dfigem Abflussl\u00e4ngsschnitt
+
+river = Fluss
+calculation_mode = Berechnungsart
+ld_locations = Ort(e)
+
+chart.longitudinal.section.title = W-L\u00e4ngsschnitt
+chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.longitudinal.section.xaxis.label = km
+chart.longitudinal.section.yaxis.label = W [NN + m]
+chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
+chart.discharge.curve.title = Abflusskurve
+chart.discharge.curve.xaxis.label = Q [m\u00b3/s]
+chart.discharge.curve.yaxis.label = W [cm]
+chart.discharge.curve.curve.valid.from = {0} (g\u00fcltig ab {1,date,medium})
+chart.discharge.curve.curve.valid.range = {0} (g\u00fcltig ab {1,date,medium} - {2,date,medium})
+chart.computed.discharge.curve.title = Abflusskurve
+chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###}
+chart.computed.discharge.curve.yaxis.label = W [NN + m]
+chart.computed.discharge.curve.curve.label = Abflusskurve {0} km {1}
+chart.duration.curve.title = Dauerlinie
+chart.duration.curve.subtitle = {0}-km: {1,number,#.###}
+chart.duration.curve.xaxis.label = Unterschreitungsdauer [Tage]
+chart.duration.curve.yaxis.label = W [NN + m]
+
+export.waterlevel.csv.header.km = Fluss-Km
+export.waterlevel.csv.header.w = W [NN + m]
+export.waterlevel.csv.header.q = Q [m\u00b3/s]
+export.computed.discharge.curve.csv.header.w = W [NN + m]
+export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s]
+export.duration.curve.csv.header.duration = D [Tagen]
+export.duration.curve.csv.header.w = W [NN + m]
+export.duration.curve.csv.header.q = Q [m\u00b3/s]
+export.discharge.longitudinal.section.csv.header.km = Fluss-Km
+export.discharge.longitudinal.section.csv.header.w = W [NN + m]
+export.discharge.longitudinal.section.csv.header.cw = W korr.
+export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/resources/messages_de_DE.properties	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,48 @@
+state.winfo.river = Gew\u00e4sser
+state.winfo.calculation_mode = Berechnungsart
+state.winfo.location_distance = Wahl des Berechnungsortes/strecke
+state.winfo.wq = Eingabe f\u00fcr W/Q Daten
+state.winfo.wq_adapted = Eingabe f\u00fcr W/Q Daten
+state.winfo.location = Wahl des Berechnungsortes
+
+calc.surface.curve = Wasserstand/Wasserspiegellage
+calc.flood.map = \u00dcberschwemmungsfl\u00e4che
+calc.discharge.curve = Abflusskurve/Abflusstafel
+calc.duration.curve = Dauerlinie
+calc.discharge.longitudinal.section = W bei ungleichm\u00e4\u00dfigem Abflussl\u00e4ngsschnitt
+
+river = Fluss
+calculation_mode = Berechnungsart
+ld_locations = Ort(e)
+
+chart.longitudinal.section.title = W-L\u00e4ngsschnitt
+chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.longitudinal.section.xaxis.label = km
+chart.longitudinal.section.yaxis.label = W [NN + m]
+chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
+chart.discharge.curve.title = Abflusskurve
+chart.discharge.curve.xaxis.label = Q [m\u00b3/s]
+chart.discharge.curve.yaxis.label = W [cm]
+chart.discharge.curve.curve.valid.from = {0} (g\u00fcltig ab {1,date,medium})
+chart.discharge.curve.curve.valid.range = {0} (g\u00fcltig ab {1,date,medium} - {2,date,medium})
+chart.computed.discharge.curve.title = Abflusskurve
+chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###}
+chart.computed.discharge.curve.yaxis.label = W [NN + m]
+chart.computed.discharge.curve.curve.label = Abflusskurve {0} km {1}
+chart.duration.curve.title = Dauerlinie
+chart.duration.curve.subtitle = {0}-km: {1,number,#.###}
+chart.duration.curve.xaxis.label = Unterschreitungsdauer [Tage]
+chart.duration.curve.yaxis.label = W [NN + m]
+
+export.waterlevel.csv.header.km = Fluss-Km
+export.waterlevel.csv.header.w = W [NN + m]
+export.waterlevel.csv.header.q = Q [m\u00b3/s]
+export.computed.discharge.curve.csv.header.w = W [NN + m]
+export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s]
+export.duration.curve.csv.header.duration = D [Tagen]
+export.duration.curve.csv.header.w = W [NN + m]
+export.duration.curve.csv.header.q = Q [m\u00b3/s]
+export.discharge.longitudinal.section.csv.header.km = Fluss-Km
+export.discharge.longitudinal.section.csv.header.w = W [NN + m]
+export.discharge.longitudinal.section.csv.header.cw = W korr.
+export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/resources/messages_en.properties	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,48 @@
+state.winfo.river = River
+state.winfo.calculation_mode = Calculation Mode
+state.winfo.location_distance = Location or distance selection
+state.winfo.wq = Input for W/Q data
+state.winfo.wq_adapted = Input for W/Q data
+state.winfo.location = Choose the location
+
+calc.surface.curve = Water Level/Surface Curve
+calc.flood.map = Flood Plain
+calc.discharge.curve = State Discharge Curve/Stage Discharge Relation
+calc.duration.curve = Duration Curve
+calc.discharge.longitudinal.section = TODO (W bei...)
+
+river = River
+calculation_mode = Calculation Mode
+ld_locations = Location(s)
+
+chart.longitudinal.section.title = W-Longitudinal Section
+chart.longitudinal.section.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.longitudinal.section.xaxis.label = km
+chart.longitudinal.section.yaxis.label = W [NN + m]
+chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
+chart.discharge.curve.title = Discharge Curve
+chart.discharge.curve.xaxis.label = Q [m\u00b3/s]
+chart.discharge.curve.yaxis.label = W [cm]
+chart.discharge.curve.curve.valid.from = {0} (valid from {1,date,short})
+chart.discharge.curve.curve.valid.range = {0} (valid from {1,date,short} - {2,date,short})
+chart.computed.discharge.curve.title = Discharge Curve
+chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###}
+chart.computed.discharge.curve.yaxis.label = W [NN + m]
+chart.computed.discharge.curve.curve.label = Discharge Curve {0} km {1}
+chart.duration.curve.title = Duration Curve
+chart.duration.curve.subtitle = {0}-km: {1,number,#.###}
+chart.duration.curve.xaxis.label = Duration of Non-Exceedence [Days]
+chart.duration.curve.yaxis.label = W [NN + m]
+
+export.waterlevel.csv.header.km = River-Km
+export.waterlevel.csv.header.w = W [NN + m]
+export.waterlevel.csv.header.q = Q [m\u00b3/s]
+export.computed.discharge.curve.csv.header.w = W [NN + m]
+export.computed.discharge.curve.csv.header.q = Q [m\u00b3/s]
+export.duration.curve.csv.header.duration = D [Days]
+export.duration.curve.csv.header.w = W [NN + m]
+export.duration.curve.csv.header.q = Q [m\u00b3/s]
+export.discharge.longitudinal.section.csv.header.km = River-Km
+export.discharge.longitudinal.section.csv.header.w = W [NN + m]
+export.discharge.longitudinal.section.csv.header.cw = W corr.
+export.discharge.longitudinal.section.csv.header.q = Q [m\u00b3/s]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/main/resources/metadata/template.xml	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<dc:template xmlns:dc="http://www.intevation.org/2011/Datacage">
+<datacage>
+  <dc:context>
+    <dc:statement>
+      SELECT id AS river_id, name as river_name FROM rivers
+      WHERE name ILIKE ${river}
+    </dc:statement>
+    <dc:elements>
+      <river>
+        <dc:attribute name="name" value="${river_name}"/>
+        <dc:attribute name="river-id" value="${river_id}"/>
+        <discharge-table-nn>
+          <discharge-table-gauge>
+            <dc:context>
+              <dc:statement>
+                SELECT id   AS gauge_id,
+                       name AS gauge_name
+                FROM gauges WHERE river_id = ${river_id}
+              </dc:statement>
+              <dc:elements>
+                <gauge>
+                  <dc:attribute name="name" value="${gauge_name}"/>
+                  <dc:attribute name="gauge-id" value="${gauge_id}"/>
+                  <dc:context>
+                    <dc:statement>
+                      SELECT description   AS gauge_desc, 
+                             d.id          AS discharge_id,
+                             ti.start_time AS g_start, 
+                             ti.stop_time  AS g_stop
+                      FROM discharge_tables d JOIN time_intervals ti 
+                      ON d.time_interval_id = ti.id
+                      WHERE d.gauge_id = ${gauge_id} AND d.kind = 1
+                    </dc:statement>
+                    <dc:elements>
+                      <historical>
+                        <dc:attribute name="name" value="${gauge_desc}"/>
+                        <dc:attribute name="from" value="${g_start}"/>
+                        <dc:attribute name="to" value="${g_stop}"/>
+                        <dc:attribute name="discharge-id" value="${discharge_id}"/></historical>
+                    </dc:elements>
+                  </dc:context>
+                </gauge>
+              </dc:elements>
+            </dc:context>
+          </discharge-table-gauge>
+          <fixations>
+            <dc:attribute name="id" value="fixations-${river_id}"/>
+            <dc:context>
+              <dc:statement>
+                SELECT id          AS fix_id,
+                       description AS fix_description
+                FROM wsts WHERE kind = 2 AND river_id = ${river_id}
+              </dc:statement>
+              <dc:elements>
+                <fixation>
+                  <dc:attribute name="name" value="${fix_description}"/>
+                  <dc:attribute name="fix-id" value="${fix_id}"/>
+                  <columns>
+                    <dc:context>
+                      <dc:statement>
+                        SELECT id   AS fix_column_id,
+                               name AS fix_column_name
+                        FROM wst_columns WHERE wst_id = ${fix_id}
+                        ORDER by position
+                      </dc:statement>
+                      <dc:elements>
+                        <column>
+                          <dc:attribute name="name" value="${fix_column_name}"/>
+                          <dc:attribute name="column-id" value="${fix_column_id}"/></column>
+                      </dc:elements>
+                    </dc:context>
+                  </columns>
+                </fixation>
+              </dc:elements>
+            </dc:context>
+          </fixations>
+          <flood-protections>
+            <dc:attribute name="id" value="flood-protections-${river_id}"/>
+            <dc:context>
+              <dc:statement>
+                SELECT id          AS prot_id,
+                       description AS prot_description
+                FROM wsts WHERE kind = 5 AND river_id = ${river_id}
+              </dc:statement>
+              <dc:elements>
+                <flood-protection>
+                  <dc:attribute name="name" value="${prot_description}"/>
+                  <dc:attribute name="prot-id" value="${prot_id}"/>
+                  <columns>
+                    <dc:context>
+                      <dc:statement>
+                        SELECT id   AS prot_column_id,
+                               name AS prot_column_name
+                        FROM wst_columns WHERE wst_id = ${prot_id}
+                        ORDER by position
+                      </dc:statement>
+                      <dc:elements>
+                        <column>
+                          <dc:attribute name="name" value="${prot_column_name}"/>
+                          <dc:attribute name="column-id" value="${prot_column_id}"/></column>
+                      </dc:elements>
+                    </dc:context>
+                  </columns>
+                </flood-protection>
+              </dc:elements>
+            </dc:context>
+          </flood-protections>
+          <flood-water-marks>
+            <dc:attribute name="id" value="flood-water-marks-${river_id}"/>
+            <dc:context>
+              <dc:statement>
+                SELECT id          AS fw_id,
+                       description AS fw_description
+                FROM wsts WHERE kind = 4 AND river_id = ${river_id}
+              </dc:statement>
+              <dc:elements>
+                <flood-water-mark>
+                  <dc:attribute name="name" value="${fw_description}"/>
+                  <dc:attribute name="fw-id" value="${fw_id}"/>
+                  <columns>
+                    <dc:context>
+                      <dc:statement>
+                        SELECT id   AS fw_column_id,
+                               name AS fw_column_name
+                        FROM wst_columns WHERE wst_id = ${fw_id}
+                        ORDER by position
+                      </dc:statement>
+                      <dc:elements>
+                        <column>
+                          <dc:attribute name="name" value="${fw_column_name}"/>
+                          <dc:attribute name="column-id" value="${fw_column_id}"/></column>
+                      </dc:elements>
+                    </dc:context>
+                  </columns>
+                </flood-water-mark>
+              </dc:elements>
+            </dc:context>
+          </flood-water-marks>
+          <water-levels>
+            <dc:attribute name="id" value="water-levels-${river_id}"/>
+            <dc:context>
+              <dc:statement>
+                SELECT id          AS wl_id,
+                       description AS wl_description
+                FROM wsts WHERE kind = 0 AND river_id = ${river_id}
+              </dc:statement>
+              <dc:elements>
+                <water-level>
+                  <dc:attribute name="name" value="${wl_description}"/>
+                  <dc:attribute name="wl-id" value="${wl_id}"/>
+                  <columns>
+                    <dc:context>
+                      <dc:statement>
+                        SELECT id   AS wl_column_id,
+                               name AS wl_column_name
+                        FROM wst_columns WHERE wst_id = ${wl_id}
+                        ORDER by position
+                      </dc:statement>
+                      <dc:elements>
+                        <column>
+                          <dc:attribute name="name" value="${wl_column_name}"/>
+                          <dc:attribute name="column-id" value="${wl_column_id}"/></column>
+                      </dc:elements>
+                    </dc:context>
+                  </columns>
+                </water-level>
+              </dc:elements>
+            </dc:context>
+          </water-levels>
+          <extra-longitudinal-sections>
+            <dc:attribute name="id" value="extra-longitudinal-sections-${river_id}"/>
+            <dc:context>
+              <dc:statement>
+                SELECT id          AS els_id,
+                       description AS els_description
+                FROM wsts WHERE kind = 1 AND river_id = ${river_id}
+              </dc:statement>
+              <dc:elements>
+                <extra-longitudinal-section>
+                  <dc:attribute name="name" value="${els_description}"/>
+                  <dc:attribute name="els-id" value="${els_id}"/>
+                  <columns>
+                    <dc:context>
+                      <dc:statement>
+                        SELECT id   AS els_column_id,
+                               name AS els_column_name
+                        FROM wst_columns WHERE wst_id = ${els_id}
+                        ORDER by position
+                      </dc:statement>
+                      <dc:elements>
+                        <column>
+                            <dc:attribute name="name" value="${els_column_name}"/>
+                            <dc:attribute name="column-id" value="${els_column_id}"/></column>
+                      </dc:elements>
+                    </dc:context>
+                  </columns>
+                </extra-longitudinal-section>
+              </dc:elements>
+            </dc:context>
+          </extra-longitudinal-sections>
+        </discharge-table-nn>
+        <longitudinal-section>
+          <fixations><dc:attribute name="ref" value="fixations-${river_id}"/></fixations>
+          <flood-protections><dc:attribute name="ref" value="flood-protections-${river_id}"/></flood-protections>
+          <flood-water-marks><dc:attribute name="ref" value="flood-water-marks-${river_id}"/></flood-water-marks>
+          <water-levels><dc:attribute name="ref" value="water-levels-${river_id}"/></water-levels>
+          <extra-longitudinal-sections><dc:attribute name="ref" value="extra-longitudinal-sections-${river_id}"/></extra-longitudinal-sections>
+        </longitudinal-section>
+      </river>
+    </dc:elements>
+  </dc:context>
+</datacage>
+</dc:template>
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-artifacts/src/test/java/de/intevation/flys/artifacts/AppTest.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,38 @@
+package de.intevation.flys.artifacts;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest
+    extends TestCase
+{
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public AppTest( String testName )
+    {
+        super( testName );
+    }
+
+    /**
+     * @return the suite of tests being tested
+     */
+    public static Test suite()
+    {
+        return new TestSuite( AppTest.class );
+    }
+
+    /**
+     * Rigourous Test :-)
+     */
+    public void testApp()
+    {
+        assertTrue( true );
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/.classpath	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<classpath>
+   <classpathentry kind="src" path="src/main/java"/>
+   <classpathentry kind="src" path="src/test/java"/>
+   <classpathentry kind="lib" path="/vol1/download/ingo/gwt-2.1.1/gwt-user.jar"/>
+   <classpathentry kind="lib" path="/vol1/download/ingo/gwt-2.1.1/gwt-dev.jar"/>
+   <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+   <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
+   <classpathentry kind="output" path="src/main/webapp/WEB-INF/classes"/>
+
+</classpath>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/.project	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<projectDescription>
+   <name>FLYS</name>
+   <comment>FLYS project</comment>
+   <projects/>
+   <buildSpec>
+       <buildCommand>
+           <name>org.eclipse.jdt.core.javabuilder</name>
+           <arguments/>
+       </buildCommand>
+   </buildSpec>
+   <natures>
+       <nature>org.eclipse.jdt.core.javanature</nature>
+   </natures>
+</projectDescription>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/ChangeLog	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,2206 @@
+2011-05-20  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 2.3.1
+
+2011-05-20  Hans Plum <hans@intevation.de>
+
+	* Changes: Prepared Changes for the upcoming release.
+
+2011-05-20  Hans Plum <hans@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/config.xml:
+	Reverted to default config; unintentional change in previous commit
+
+2011-05-20  Hans Plum <hans@intevation.de>
+
+	* NEWS:
+	Added information for Release V 2.3.1 for endusers
+
+2011-05-19  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 2.3.1
+
+	* Changes: Prepared for release.
+
+2011-05-19  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-35
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Added a maximize button to this view.
+
+2011-05-19  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-43
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties:
+	  Adapted the english waring that is displayed before a language switch
+	  takes place.
+
+2011-05-19  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added i18n strings for exports of computed discharge curves.
+
+2011-05-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added i18n strings for exports.
+
+2011-05-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added i18n string for wst export.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ExportPanel.java:
+	  Set a maximum size of the export button and added a tooltip to
+	  distinguish exports.
+
+	  NOTE: We should consider using specific images for each export type.
+
+2011-05-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ExportServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/ChartOutputServiceImpl.java:
+	  Adapted a method signature of ClientProtocolUtils that has been changed
+	  in the last revision.
+
+2011-05-17  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 2.3
+
+2011-05-16  Hans Plum <hans@intevation.de>
+
+	* NEWS:
+	Added version number and release date for V 2.3
+
+2011-05-16  Hans Plum <hans@intevation.de>
+
+	* NEWS:
+	New. Describing new functionality and some backgrounds in
+	MoinMoin-Wiki Syntax (in german	only) from the user perspective. It
+	does not differentiate between client and server-specific 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 change of version number.
+
+2011-05-12	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+    * src/main/java/de/intevation/flys/client/server/meta/Converter.java:
+      New. Converts the incoming XML to a more informative way.
+
+    * src/main/java/de/intevation/flys/client/server/MetaDataServiceImpl.java:
+      Use the new converter.
+
+2011-05-12  Raimund Renkert <rrenkert@intevation.de>
+
+	  ISSUE-61
+
+	* src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  Changed datacage button style.
+
+2011-05-12  Raimund Renkert <rrenkert@intevation.de>
+
+	  ISSUE-46
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Removed the table header context menu.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LinkSelection.java:
+	  Removed log message.
+
+2011-05-12  Raimund Renkert <rrenkert@intevation.de>
+
+	  ISSUE-43
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Changed i18n strings.
+
+2011-05-12  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LinkSelection.java:
+	  New. Shows a list of rivers displayed as links.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java,
+	  src/main/java/de/intevation/flys/client/client/ui/MapSelection.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java:
+	  Use the new Panel to show rivers as links.
+
+2011-05-12  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-15
+
+	* src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  After resizing the chart panel, the chart/image is replaced by a new
+	  chart with adjusted width and height (requested from server).
+
+2011-05-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  Defined the chart size (600x500).
+
+	* src/main/java/de/intevation/flys/client/server/ChartOutputServiceImpl.java:
+	  Query the chart in the requested size defined in the GET-parameters of
+	  the request.
+
+2011-05-12  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-34
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSWorkspace.java:
+	  The number of windows which are moved the right lower browser window
+	  corner is limited to 10 windows now. The 11th window is placed at the
+	  left upper corner again. Window 12-20 are moved with an offset into to
+	  the right lower corner again.
+
+2011-05-11	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DataCageNode.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DataCageTree.java:
+	  New. Server -> Client transport model. Ugly because it re-invents XML
+	  documents. XML is received by the RPC service from the artifact server,
+	  transformed into this transport representation and this is transformed
+	  in ui elements on the client side.
+
+	* src/main/java/de/intevation/flys/client/server/MetaDataServiceImpl.java:
+	  Transform the incoming XML to transport representation. TODO:
+	  Do not simply copy the XML.
+
+	* src/main/java/de/intevation/flys/client/client/services/MetaDataServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/MetaDataService.java:
+	  Changed signature tio transport the tree.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Transform the transport tree into ui elements.
+
+2011-05-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ExportPanel.java:
+	  Changed the height of this panel to be visible in all browsers.
+
+2011-05-11	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/MetaDataServiceImpl.java:
+	  Call the service. TODO: transport artifact server result to client.
+
+	* src/main/java/de/intevation/flys/client/client/services/MetaDataServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/MetaDataService.java:
+	  Extended interfaces to cope with i18n.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Error messages if the artifact meta data service is not reachable.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Forward collection view to chart output tab.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  Forward artifact to fetch river name later.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  New. UI code for the data cage panel. TODO: build tree from
+	  service data.
+
+	* src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Added simple break in a loop if river was found.
+
+2011-05-11  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-56
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultOutputMode.java,
+	  src/main/java/de/intevation/flys/client/shared/model/OutputMode.java:
+	  Added a further method that returns all available facets.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ExportPanel.java: New.
+	  This panel is used to serve download links for each export mode / type
+	  of a collection.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Implements the CollectionChangeHandler interface now. If a Collection
+	  changes, the available output modes are analyzed. If there are export
+	  modes available, those modes are displayed in a so called 'ExportPanel'.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Registered the ParameterList as CollectionChange handler.
+
+	* src/main/webapp/FLYS.css: Added a stylesheet for the export panels
+	  title.
+
+	* src/main/webapp/images/save.gif: New. This image is displayed in the
+	  export panel.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java: New
+	  i18n strings for the export panel.
+
+2011-05-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ExportServiceImpl.java:
+	  New. This service is a "proxy servlet" that enables the GUI to add
+	  download buttons for exporting the computed data of the artifact server.
+
+	* src/main/java/de/intevation/flys/client/client/services/CSVExportService.java:
+	  Changed the URL part to query the CSV export ('export' -> 'csv').
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the new ExportService.
+
+2011-05-11  Raimund Renkert <rrenkert@intevation.de>
+
+	  ISSUE 34
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSWorkspace.java:
+	  Move every new project window 20 px multiplicated with the window count
+	  down and right.
+
+2011-05-11  Raimund Renkert <rrenkert@intevation.de>
+
+	  ISSUE 44
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Moved the location/range and WQ input tables to the helper panel on the
+	  right. The tables have a dynamic size now.
+
+2011-05-11  Raimund Renkert <rrenkert@intevation.de>
+
+	  ISSUE 41 (part2)
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Set the computation result panel width and height to 100%. The panel
+	  resizes with its parent component now.
+
+2011-05-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/ExportMode.java:
+	  New. A derived OutputMode that is used to mark an OutputMode as an
+	  export.
+
+	* src/main/java/de/intevation/flys/client/shared/model/OutputMode.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultOutputMode.java:
+	  Added methods to provide facets in output modes.
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java:
+	  Extract facets from DESCRIBE and distinguish between an ordinary
+	  OutputMode or an ExportMode.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Replaced the determination of an export mode for creating new
+	  OutputTabs. Now, we just create new OutputTabs for OutputModes that are
+	  no instance of ExportMode. Furthermore, the computation output panel
+	  is now displayed if there is an output mode which:
+	   - is an ExportMode
+	   - has a facet which name is "csv"
+
+2011-05-11  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-41 (part1)
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Moved the computation result panel right beside the parameterization.
+
+	  TODO: This panel has now hard coded width/height. The result of this is,
+	  that it does not change its size when the parent component is resized.
+	  So, the width and height should be set to 100%.
+
+2011-05-11  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-57
+
+	* src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Replaced wrong i18n function name.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-48
+
+	* src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  Added labels placeholder for the theme control panel and the toolbar.
+	  Both panels now have a black border.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-38
+
+	* src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  Set the first value of a select box as default value.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java: Added
+	  i18n error messages required if the data input failed in the adapted WQ
+	  panel.
+
+2011-05-10  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Changed the empty table message for the WQ input table.
+
+2011-05-10  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Changed the empty table text from default to "Loading data".
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added i18n strings for empty table text.
+
+	* src/main/java/de/intevation/flys/client/server/CSVExportServiceImpl.java:
+	  Changed author.
+
+2011-05-10	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/MetaDataServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/MetaDataServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/MetaDataService.java:
+	  New. Stub for new meta data service.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered meta data service.
+
+2011-05-10  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Fixed the location input. A range selection sets the correct location values
+	  now.
+
+2011-05-10  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Remove the data table on step back.
+
+2011-05-10  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Use the name instead of the description to determine the output mode.
+
+	* src/main/java/de/intevation/flys/client/server/CSVExportServiceImpl.java:
+	  Removed obsolete code.
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java:
+	  Build up a dynamic table for different header count. The first line of CSV
+	  data is used to set the header title.
+
+2011-05-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/CSVExportServiceImpl.java:
+	  Bugfix: Use the ElementCreator from XMLUtils to add attribute to nodes
+	  in the xml document. Then, those nodes will have the ArtifactNamespace
+	  automatically. If we do something like
+	    node.setAttribute("art:attr", "value")
+	  Restlet will throw an exception, because it does not know the "art:"
+	  namespace.
+
+2011-05-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* pom.xml: Bugfix: replaced the goupId of the OpenCSV dependency with the
+	  correct id.
+
+2011-05-09  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/CSVExportServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/CSVExportServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/CSVExportService.java:
+	  New service. Requests CSV data and returns a list of lines in a string
+	  array.
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java:
+	  New. Displays the data requested by the new service.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Added the table to UI.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered new service.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added strings for the new table.
+
+	* pom.xml: Added dependency: au.com.bytecode.opencsv, version 2.0;
+
+2011-05-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQAdaptedInputPanel.java:
+	  Added radio buttons to choose between W/Q mode.
+
+2011-05-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ChartOutputServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  Bugfix: the current locale string is used to fetch the chart image now
+	  which enables the client to present localized charts.
+
+2011-05-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Added a doubleclick listener to the rows of this list. A doubleclick
+	  will now open the selected project.
+
+2011-05-09	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DistanceInfoRecord.java,
+	  src/main/java/de/intevation/flys/client/client/Config.java,
+	  src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Removed superfluous imports.
+
+2011-05-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties:
+	  Changed wording: 'distance' -> 'range'.
+
+2011-05-05  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Enable or disable table rows depending on the WQ selection.
+
+2011-05-05  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Implemented handler for the value selection.
+
+2011-05-05  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Added table for WQ inputs. TODO: Implement the selection and set the
+	  values.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.java,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  New i18n strings for the WQ input table.
+
+2011-05-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Moved functions getData() and getDataItem() from WQInputPanel to
+	  AbstractUIProvider to make it accessible in all UIProvider that inherit
+	  from AbstractUIProvider.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQAdaptedInputPanel.java:
+	  Implemented the methods to create the old widget of thi panel.
+
+2011-05-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java: Added
+	  new i18n displayed in the WQAdaptedInputPanel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQAdaptedInputPanel.java:
+	  The list of input fields for each intersected gauge is created now. The
+	  correct w/q values will be returned after the submit button has been
+	  clicked.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java:
+	  The orientation of the field label might be adjusted using a new
+	  constructor parameter. Furthermore, there is a new method (which has no
+	  parameters) that returns the double values inserted in this panel.
+
+2011-05-04  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/WQInfoServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/WQInfoServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/WQInfoService.java:
+	  New. This service fetches WQ information from the server.
+
+	* src/main/java/de/intevation/flys/client/shared/model/WQInfoRecord.java,
+	  src/main/java/de/intevation/flys/client/shared/model/WQInfoObjectImpl.java,
+	  src/main/java/de/intevation/flys/client/shared/model/WQInfoObject.java:
+	  New. Data structures for the WQ information.
+
+	* src/main/webapp/WEB-INF/web.xml: Added new service.
+
+2011-05-04  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Do not update the locations table if no data is cached.
+
+2011-05-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQAdaptedInputPanel.java:
+	  New. This UIProvider is used to enter W/Q values associated with a set
+	  of gauges.
+
+	  NOTE: This is currently just a stub implementation used for further
+	  development.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  Initialize the WQAdaptedInputPanel if the uiprovider flag is set to
+	  "wq_panel_adapted".
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java: New
+	  i18n strings for the "discharge longitudinal section.
+
+2011-05-04  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Fixed the input panel. It takes manual input now and the table data is
+	  filtered correctly.
+
+2011-05-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Renamed the non-computed discharge curves into 'Discharge Curves at
+	  Gauges' (german 'Abflusskurven an Pegeln') to distinuish the two output
+	  types for discharge curves.
+
+2011-05-04  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  New i18n strings for the computed discharge curve.
+
+2011-05-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  New i18n strings for new output modes.
+
+2011-05-03  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  Added new UI provider for single location selection.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java:
+	  Check if the values are not null.
+
+	* src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  New. Takes a single location from the location input table.
+
+2011-05-02  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  The table data is cached in a new variable. The location table for
+	  distance input is rebuild on distance selection.
+
+2011-05-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Introduced two further parameters to determine the selection mode of
+	  this panel.
+
+2011-05-02  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Added location and distance tables. A selection fills the textfield with
+	  the selected values.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DistanceInfoRecord.java:
+	  New.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added i18n strings for the new tables.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSImages.java: Added new
+	  images.
+
+	* src/main/java/de/intevation/flys/client/client/images/marker_green.png,
+	  src/main/java/de/intevation/flys/client/client/images/marker_red.png: New.
+
+2011-05-02  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Bugfix: The WQ panel now returns the correct step width value if the
+	  range mode is selected.
+
+2011-04-29  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DoubleRangePanel.java:
+	  Added setter for the location and distance values.
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/UIProvider.java:
+	  Added setter for an artifact.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Set the current artifact to the provider to make the old data available
+	  for the other widgets.
+
+2011-04-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java: New
+	  i18n strings for a 'longitudinal section curve' output tab.
+
+2011-04-28  Raimund Renkert <rrenkert@intevation.de>
+
+	Added class diagrams.
+
+	* doc/Flys-UI-Classes.png, doc/Flys-UI-Services-Classes.png: New.
+
+2011-04-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/StepForwardServiceImpl.java:
+	  The error key that is provided by the FLYS artifact server is read from
+	  feed() result document and used to bring up an error popup with an
+	  translated error message.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Added error messages for the feed operation.
+
+2011-04-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/Config.java:
+	  A new method returns the name of the current locale now.
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/UserCollectionsServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/GetArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/UserServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/CreateCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/StepForwardServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/AdvanceServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/AddArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/DistanceInfoServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/RiverServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/UserService.java,
+	  src/main/java/de/intevation/flys/client/client/services/CreateCollectionService.java,
+	  src/main/java/de/intevation/flys/client/client/services/RiverServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/RiverService.java,
+	  src/main/java/de/intevation/flys/client/client/services/DescribeCollectionServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/GetArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/ArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/DescribeCollectionService.java,
+	  src/main/java/de/intevation/flys/client/client/services/ArtifactService.java,
+	  src/main/java/de/intevation/flys/client/client/services/GetArtifactService.java,
+	  src/main/java/de/intevation/flys/client/client/services/StepForwardServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/AdvanceServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/StepForwardService.java,
+	  src/main/java/de/intevation/flys/client/client/services/AdvanceService.java,
+	  src/main/java/de/intevation/flys/client/client/services/AddArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/DistanceInfoServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/AddArtifactService.java,
+	  src/main/java/de/intevation/flys/client/client/services/DistanceInfoService.java,
+	  src/main/java/de/intevation/flys/client/client/services/UserCollectionsServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/UserServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/CreateCollectionServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/UserCollectionsService.java:
+	  Introduced a "locale" parameter that stores the name of the current
+	  locale. This is used to set the locale of the request object manually.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ProjectList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Modified the RPC service calls (introduction of a locale).
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  The title of the ParameterList depends on the Artifact's name if we
+	  gonna create a new CollectionView with an existing Artifact.
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/Artifact.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultArtifact.java:
+	  An artifact knows its name now. It is retrievable via getName().
+
+	* src/main/java/de/intevation/flys/client/shared/model/WINFOArtifact.java:
+	  New. A concrete class that represents an Artifact using the WINFO
+	  parameterization.
+
+	* src/main/java/de/intevation/flys/client/server/FLYSArtifactCreator.java:
+	  The ArtifactCreator builds up new Artifacts based on the name in the
+	  DESCRIBE document. If no name is included, a DefaultArtifact is created.
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/CollectionRecord.java:
+	  This record class uses the default mechanism to store/load values -
+	  it uses the setAttribute() and getAttribute() methods now.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java: The
+	  list is sorted based on the project's creation time now.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionGrid.java:
+	  Removed. The collection grid is no longer used, because the fields are
+	  created by using default mechanisms (setting the field type, using cell
+	  formatter).
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java:
+	  Implemented a method to open an existing project. The method takes the
+	  UUID of a Collection and creates a new CollectionView with
+	  ParameterList based on the first Artifact that is stored in that
+	  Collection.
+
+	  NOTE: Maybe we need to introduce a mechanism to mark the artifact that
+	  defines the parameterization. I am not sure, if the first artifact is
+	  always the artifact that the Collection's parameterization is based on!
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Triggers the method of FLYS to open an existing project.
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Added new constructors that enables us to create instances with an
+	  existing parameterization (with a given artifact).
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added further error messages.
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/CollectionRecord.java:
+	  There is a new method that returns the Collection object which is
+	  represented by this record.
+
+2011-04-12  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/GetArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/GetArtifactService.java,
+	  src/main/java/de/intevation/flys/client/server/GetArtifactServiceImpl.java:
+	  New. A service that queries the artifact description based on the
+	  identifier of an artifact.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the new GetArtifactService.
+
+2011-04-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added strings that are used in the context menu of the project list.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  A right mouse click on a project opens a context menu with the options
+	  to open / delete a project.
+
+2011-04-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DistanceInfoServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/DistanceInfoServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/DistanceInfoService.java:
+	  New. This service fetches river specific distance information from
+	  server.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the DistanceInfoService.
+
+2011-04-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DistanceInfoObjectImpl.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DistanceInfoObject.java:
+	  New. This model will be used by the LocationDistancePanel to bring up a
+	  table with distances which supports the user while entering the start
+	  and end point of the WINFO paramterization. A DistanceInfoObject
+	  contains information about a specific distance (description, from, to
+	  and riverside).
+
+2011-04-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/UserServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/UserService.java:
+	  This service now throws an AuthenticationException if an error occured
+	  while user authentication.
+
+	* src/main/java/de/intevation/flys/client/server/RiverServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/RiverService.java:
+	  This service now throws a ServerException if an error occured while
+	  reading the supported rivers from artifact server.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Show warnings
+	  if errors occur while fetching supported rivers or 
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added further strings for error messages.
+
+2011-04-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/exceptions/AuthenticationException.java:
+	  New. An exception that should be used if the user authentication fails.
+
+2011-04-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/CreateCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/StepForwardServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/AdvanceServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/AddArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/CreateCollectionService.java,
+	  src/main/java/de/intevation/flys/client/client/services/DescribeCollectionService.java,
+	  src/main/java/de/intevation/flys/client/client/services/ArtifactService.java,
+	  src/main/java/de/intevation/flys/client/client/services/StepForwardService.java,
+	  src/main/java/de/intevation/flys/client/client/services/AdvanceService.java,
+	  src/main/java/de/intevation/flys/client/client/services/AddArtifactService.java:
+	  Improved the exception handling. If an exception occurs specific to an
+	  artifact / collection specific operation, a ServerException is thrown.
+	  The message of this exception is a key that needs to be translated using
+	  the lookup mechanism of FLYSConstants.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  The dialog helper class SC of the SmartGWT framework is used to bring up
+	  warning dialogs that inform the user about errors that occured after the
+	  artifact / collection specific operations.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added strings for the exceptions thrown in the services.
+
+2011-04-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/exceptions/ServerException.java:
+	  New. This exception is used to be thrown after an error occured while
+	  one of the artifact / collection operations.
+
+2011-04-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ChartOutputServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  Adjusted the code to trigger the out() operation of the Collection
+	  instead of the out() operation of the first Artifact in that Collection.
+
+2011-04-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added new strings for a warning dialog that is opened when the user
+	  start changing the current language.
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java: Fixed
+	  the function to toggle the language and added a warning dialog that
+	  informs the user about the creation of a new session when the language
+	  is changed.
+
+2011-04-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  New. A constants interface with the ability to lookup an i18n string
+	  with a given key.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionGrid.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ProjectList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DoubleRangePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/MainMenu.java,
+	  src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java,
+	  src/main/java/de/intevation/flys/client/client/ui/FLYSHeader.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java,
+	  src/main/java/de/intevation/flys/client/client/ui/FLYSFooter.java:
+	  Replaced the FLYSMessages interface with the FLYSConstants interface.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java:
+	  Removed.
+
+2011-04-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java:
+	  Implemented the toggle button to switch the current locale.
+
+2011-04-05  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties:
+	  Added missing strings used in the footer.
+
+2011-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 0.1
+
+2011-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Removed some comments that have been commited by mistaken.
+
+2011-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages.java:
+	  Derives the ConstantsWithLookup interface now. This makes it possible to
+	  fetch i18n string using a getString(String key) lookup method.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties:
+	  Removed '@DefaultMessage' annotations - they are not available for
+	  ConstantsWithLookup, added a further i18n string and fixed another one.
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Some modifications related to the changes above.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Removed the hard coded title determination based on the selected module.
+	  Now, the getString() of FLYSMessages is used to determine the human
+	  readable string.
+
+2011-03-30  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/AddArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/DescribeCollectionService.java,
+	  src/main/java/de/intevation/flys/client/client/FLYS.java,
+	  src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/FLYSFooter.java:
+	  Removed useless imports.
+
+2011-03-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: The
+	  CollectionView no longer gets an empty collection when it is created.
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSWorkspace.java:
+	  The CollectionViews are no longer stored in a Map but in a List.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Bugfix: We just create a new collection a single time - if no one is
+	  existing. If the output modes of the artifact that is used for the
+	  parameterization changes, we fetch the new DESCRIBE document of the
+	  collection using the new DescribeCollectionService.
+
+2011-03-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/DescribeCollectionServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/DescribeCollectionService.java:
+	  New. This service is used to fetch the DESCRIBE document of a specific
+	  collection and returns a Collection object that contains the information of
+	  that document. The code to parse the DESCRIBE has been moved here from
+	  the AddArtifactServiceImpl which now derives the
+	  DescribeCollectionServiceImpl.
+
+	* src/main/java/de/intevation/flys/client/server/AddArtifactServiceImpl.java:
+	  This service is derived from DescribeCollectionServiceImpl now, because
+	  the methods to parse the DESCRIBE document are moved to that service.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the
+	  DescribeCollectionService.
+
+2011-03-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties:
+	  Replaced the german title of the project list 'Berechnungen' with
+	  'Projekte'.
+
+2011-03-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Registered the
+	  ProjectList as CollectionChangeHandler of each created CollectionView.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Implements the CollectionChangeHandler to update the list of user
+	  collections after a collection changed (or has been created).
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionGrid.java,
+	  src/main/java/de/intevation/flys/client/shared/model/CollectionRecord.java:
+	  Modified the output of the 'name' and 'date' fields.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java:
+	  Added a format for datetime strings.
+
+2011-03-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/UserCollectionsServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/UserCollectionsServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/UserCollectionsService.java:
+	  New. This service returns a list of Collections owned by a specified
+	  user.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the UserCollectionsService.
+
+2011-03-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultCollection.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Collection.java:
+	  Added methods to retrieve the creation time.
+
+2011-03-29  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java: Fixed code
+	  in ProjectList toggle method.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java: Fixed
+	  the WQInputPanel label to have the correct size.
+
+2011-03-29  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/webapp/FLYS.html: Added style information to avoid the smartgwt
+	  theme margin.
+
+2011-03-29  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties:
+	  Changed some german strings.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Changed tab title.
+
+2011-03-29  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java: Set
+	  label width to have a tabled style for WINFO selections.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Resized window to fit the content size.
+
+2011-03-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  The ParameterList is a Tab now and can set its title itself. So, the
+	  title will change after the module is chosen.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Removed code to insert the ParameterList into a new Tab. This is no longer
+	  needed, because the ParameterList is a Tab itself.
+
+2011-03-29  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java:
+	  Modified the string that points to the 'back button' image.
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  Modified the URL that points to the 'back button' image. Now, the
+	  base URL is taken into account as well. The button will be shown
+	  everywhere now (in every installation)!
+
+2011-03-29  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java: Set
+	  label width to have a tabled style for WINFO selections.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	 Resized window to fit the content size.
+
+2011-03-29  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java: Fixed
+	 toggle method.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/CollectionItem.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultCollectionItem.java:
+	  A CollectionItem has a hash() method now. The value comes from the
+	  Collection's DESCRIBE document.
+
+	* src/main/java/de/intevation/flys/client/server/AddArtifactServiceImpl.java:
+	  The hash value of a collection item is parsed from DESCRIBE document.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  Appended the hash value of the artifact to the URL of the chart image.
+	  This bypasses the browser cache of a previous chart image.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  A parameterization change will always remove all output tabs before
+	  updating the output tab panel.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java:
+	  Modified existing string and added a new one.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java:
+	  Added a further radio button for a plugin and changed the alignment of
+	  the radio button to vertical.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  Removed the "TODO: ThemeEditor" label.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Removed the date string from title bar.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  A UIProvider might now have a panel that can be used to render helper
+	  widgets. E.g. the river selection will render its river map into this
+	  panel. It is injected and can be placed somewhere around the
+	  application.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Injected the right panel of the parameter panel into the UIProvider.
+	  Helper widgets are rendered into this panel now!
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/MapSelection.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java:
+	  Some layout improvements (heights, position of elements and so far).
+
+2011-03-28  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java: The project
+	list button toggles the project list now.
+
+2011-03-28  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  Replaced next button image with smartgwt button.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Removed
+	 button path, added text for new button.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties:
+	  Removed image path, added english text for button.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties:
+	  Removed image path, added german text for button.
+
+2011-03-28  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Added text
+	  for FLYSFooter.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties:
+	  Added german text for footer label.
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSFooter.java: New. A
+	  new ui element at the bottom of the page.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Added footer
+	  element to FLYS webpage.
+
+2011-03-28  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Customized ProjectList style.
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSView.java: Set
+	  background color and size constraints.
+
+	* src/main/webapp/FLYS.css: Added background style.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  The right side of this panel displays an image now. This image
+	  represents a chart that is generated by an artifact. The request is made
+	  up in the ChartOutputServiceImpl.
+
+	* src/main/java/de/intevation/flys/client/server/ChartOutputServiceImpl.java:
+	  New. This service creates the request to retrieve chart images and
+	  writes the response to the output stream.
+
+	  NOTE: This service is not asynchron! It is derived directly from
+	  HttpServlet.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the ChartOutputService.
+
+2011-03-25  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Limited the area for CollectionView to the size of FLYSWorkspace.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/OutputTab.java: Fixed
+	  a typo.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java:
+	  An OutputTab with two panels. The left one is not implemented yet
+	  (ThemeEditor). The right one will display an image.
+
+	* src/main/java/de/intevation/flys/client/client/ui/OutputTab.java: The
+	  content pane is set in the constructor.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  New output tabs will use the ChartOutputTab.
+
+	  NOTE: We should move the code that constructs the concrete output tabs
+	  into a factory class that decides which OutputTab has to be used.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  The tab bar is extended with output tabs if the artifact used for the
+	  parameterization reaches a state with output modes.
+
+	* src/main/java/de/intevation/flys/client/client/ui/OutputTab.java: A
+	  base class that might be used to derive concrete output tabs for charts,
+	  maps and so on.
+
+2011-03-28  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Moved parameterization specific code from CollectionView to
+	  ParameterList.
+
+2011-03-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Moved ParameterList specific code into the ParameterList.
+
+2011-03-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/CreateCollectionService.java,
+	  src/main/java/de/intevation/flys/client/client/services/CreateCollectionServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/server/CreateCollectionServiceImpl.java:
+	  Changed the return type to 'Collection' - returned a UUID (String)
+	  before.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  The artifact is put into the collection (or new collection) if we reach
+	  a step in the parameterization in that an artifact has reachable
+	  outputs. Furthermore, some adjustments related to the changes of the
+	  return type in the CreateCollectionService.
+
+2011-03-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/webapp/WEB-INF/web.xml: Made the AddArtifactService accessible.
+
+2011-03-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/AddArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/AddArtifactService.java,
+	  src/main/java/de/intevation/flys/client/server/AddArtifactServiceImpl.java:
+	  New. This service adds an existing artifact to an existing collection.
+	  The return value of this service is a Collection that is filled with
+	  CollectionItems and OutputModes.
+
+2011-03-25  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/FLYS.gwt.xml: Changed theme to
+	  smartgwt Enterprise Blue.
+
+2011-03-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/Facet.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultFacet.java:
+	  New. An interface and its default implementation that provide methods
+	  to get information about facets. Currently, the only information that is
+	  supported is the name of the facet.
+
+	* src/main/java/de/intevation/flys/client/shared/model/CollectionItem.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultCollectionItem.java:
+	  New. An interface and its default implementation that provide methods to
+	  get information about the available output modes and its facets of
+	  artifacts -> a CollectionItem is related to an artifact - both have the
+	  same identifier.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultCollection.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Collection.java:
+	  The Collection no longer stores references to artifacts, but to
+	  CollectionItems. I have changed this, because the artifact contains a
+	  lot more information we need for the Collections. So, I decided to
+	  create CollectionItems that are related to artiacts but just know about
+	  the possible outputmodes and facets.
+
+2011-03-24  Raimund Renkert <rrenkert@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java: Set styles
+	  for label.
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSHeader.java: Fixed the
+	  method to calculate the image width and resized the BfG logo.
+
+	* src/main/webapp/FLYS.css: Introduced styles for fonts and BfG colorscheme.
+
+2011-03-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Extract the output modes from DESCRIBE document and fill the
+	  ArtifactDescription with those objects.
+
+2011-03-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/ArtifactDescription.java:
+	  Added a method to retrieve the available output modes of the artifact.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultArtifactDescription.java:
+	  Implemented the method to retrieve the available output modes and
+	  enhanced the default constructor with a parameter for output modes.
+
+2011-03-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/OutputMode.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultOutputMode.java:
+	  New. An interface and its default implementation that describes
+	  available output modes of artifacts.
+
+2011-03-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/webapp/images/next.xcf: Moved to images/next.xcf to avoid that
+	  this file is included in the WAR archive of the web application.
+
+	* images/next.xcf: Moved from src/main/webapp/images/next.xcf.
+
+2011-03-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Parse the selected values as human readable strings from the static UI
+	  part.
+
+	* src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  Display human readable values in the static UI part.
+
+2011-03-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/event/HasStepBackHandlers.java,
+	  src/main/java/de/intevation/flys/client/client/event/StepBackHandler.java,
+	  src/main/java/de/intevation/flys/client/client/event/StepBackEvent.java:
+	  New. These classes are used to realize a notification mechanism to
+	  listen to the step-back part of the advance() operation. UI elements
+	  should fire a StepBackEvent if the user tries to step back to a previous
+	  state.
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  The UIProvider implements the HasStepBackHandlers interface and fires
+	  StepBackEvents if the 'back' button has been clicked.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Listens to StepBackEvents. If such an event is received, we gonna remove
+	  all old state items from the list and revert the view. Afterwards, the
+	  artifact description is used to redraw the whole view.
+
+	  NOTE: I think we should just remove those items that belong to states
+	  between the current state and the target state. This would avoid a
+	  complete refresh of the ParameterPanel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Listens to StepBackEvents. If such an event is received, the advance()
+	  operation is triggerd with the current artifact and the new target
+	  state identifier to step back to that state.
+
+2011-03-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleRangePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java:
+	  The methods that validates a FormItem will skip StaticTextItem
+	  validation.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Added a mechanism to validate and save user input before returning the
+	  data entered in the fields.
+	  Reason: the BlurEvent - after that we gonne validate and save the input
+	  stuff - is not fired when the focus is still in one of the text fields
+	  and we click the 'next' button immediately. A nullpointer exception
+	  would be the result (which is avoided with the mechanism).
+
+2011-03-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java:
+	  Added localized strings that point to the source of the 'back' button.
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  Added a new method that creates the 'back' button and returns it.
+
+	  TODO: Fire an event that triggers the ADVANCE action.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  Append the 'back' button in the panel with the 'old' parameters.
+
+	* src/main/webapp/images/back.png,
+	  src/main/webapp/images/back_en.png,
+	  src/main/webapp/images/back_de.png: New. Images für the 'back' button in
+	  the parameterization.
+
+2011-03-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Added
+	  localized strings that point to the images of the 'next' button.
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  There is a method that creates the 'next' button, adds the UIProvider as
+	  ClickHandler and returns the button.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java:
+	  The 'next' button is created in the AbstractUIProvider. So, we have a
+	  central place to adjust the look of this button.
+
+	* src/main/webapp/images/next.xcf,
+	  src/main/webapp/images/next.png,
+	  src/main/webapp/images/next_en.png,
+	  src/main/webapp/images/next_de.png: Images for the 'next' button in the
+	  parameterization.
+
+2011-03-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Read min/max values and define those values as default values for the
+	  distance modes.
+
+2011-03-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Read min/max values and define those values as default values for the
+	  distance mode.
+
+2011-03-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  When a state is reached with no further user input, the 'current'
+	  property of the ParameterList is set to 'null' and no dynamic panel is
+	  shown.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties:
+	  Fixed a typo.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  The widgets displayed in the static panel are created using the
+	  UIProvider defined in the DataList.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProvider.java: There
+	  is a new method createOld() that is used to create the static
+	  representation of a DataList.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/MapSelection.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  Implemented the createOld() method.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DataList.java:
+	  Added a constructor to create a DataList with a label.
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Read the label of the static state data objects and create the DataList
+	  objects with this attribute.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DataList.java: This
+	  data structure manages a list of Data objects. A DataList is the list of
+	  Data objects that a single State can have. So, this class has methods to
+	  retrieve the name of the state it belongs to, the list of Data objects,
+	  the recommended UIProvider and a label.
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java,
+	  src/main/java/de/intevation/flys/client/shared/model/ArtifactDescription.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultArtifactDescription.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Data.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultData.java,
+	  src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ParameterList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/MapSelection.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/UIProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java:
+	  Necessary refactoring to introduce the DataList data structure to manage
+	  the list of Data objects a single State provides.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Added a bit more space between the values that have been entered in
+	  former states and the current input panel.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/UserServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Data.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultData.java,
+	  src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/MapSelection.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Removed needless imported.
+
+	* src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  The SelectProvider extends the AbstractUIProvider now. Now, each
+	  concrete UIProvider is an instance of the AbstractUIProvider that
+	  handles some basic events.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  The Single-W panel is displayed initially when the WQInputPanel is
+	  rendered.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Implemented some methods to retrieve the data that have been entered by
+	  the user. The getData() method retrieves data now!
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Implemented some methods to retrieve the data that have been entered by
+	  the user. The getData() method retrieves data now!
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java:
+	  Defined the 'WINFO' plugin as default plugin. The initial view will
+	  display a preselcted winfo radio button.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Added the missing 'next' button.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Added the ClickHandler to the 'next' button.
+
+2011-03-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  The 'uiprovider' attribute 'wq_panel' will return a WQInputPanel
+	  provider.
+
+2011-03-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Some refactoring. This class now uses the DoubleRangePanel and
+	  DoubleArrayPanel for the two different input modes as well.
+
+2011-03-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Added
+	  i18n strings used in the WQInputPanel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleRangePanel.java:
+	  New. This panel contains three input fields that enables the user to
+	  enter a start and end value and a step width. Furthermore, there are
+	  methods to validate the fields and to retrieve its values.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java:
+	  New. This panel contains a single input field that enables the user to
+	  enter a list of double values. There is a method to validate the input
+	  and a method to retrieve the list of entered double values.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  New. This panel allows the user to enter W or Q values for single or
+	  range input in one single state.
+
+2011-03-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Added
+	  i18n strings used in the LocationDistancePanel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  New. This abstract class implements the UIProvider and the
+	  HasStepForwardHandlers interface and its necessary methods. These two
+	  things are required by each concrete UIProvider, so this class should be
+	  the base class for further concrete UIProviders.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  New. A UIProvider that enables the user to enter locations or a
+	  distance.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  States with a uiprovider attribute set to 'location_distance_panel' use
+	  the LocationDistancePanel for user input.
+
+2011-03-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Clear the panel that displays the current input widget before adding a
+	  new one to this panel.
+
+2011-03-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Added
+	  a i18n string for a new calculation.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSImages.java,
+	  src/main/java/de/intevation/flys/client/client/images/gewkarte.png: New
+	  image: the map that shows the relevant rivers.
+
+	* src/main/java/de/intevation/flys/client/client/ui/MapSelection.java:
+	  Added the river map to the right panel of that widget.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Implemented the step forward mechanism.
+
+2011-03-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java:
+	  Save the name of the data item. We need the name for feed().
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Before adding further old data items, we gonna check if the item is
+	  already existing in the list. If it exists, the item is not added.
+
+2011-03-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  Read i18n strings from DESCRIBE document and display them in the UI.
+
+2011-03-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Integrated the mechnism to listen on paramer changes. The CollectionView
+	  is both: a HasParameterChangeHandler and a ParameterChangeHandler.
+
+2011-03-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  Bugfix: Removed static string from gui.
+
+2011-03-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Parse the static data from DESCRIBE document and put it into the
+	  ArtifactDescription.
+
+2011-03-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/event/HasParameterChangeHandler.java,
+	  src/main/java/de/intevation/flys/client/client/event/ParameterChangeHandler.java,
+	  src/main/java/de/intevation/flys/client/client/event/ParameterChangeEvent.java:
+	  New. Interfaces and classes used for a listener mechanism to notify
+	  listeners when the parameterization of a Collection/Artifact has
+	  changed.
+
+2011-03-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/StepForwardServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/StepForwardService.java,
+	  src/main/java/de/intevation/flys/client/server/StepForwardServiceImpl.java:
+	  New. This service bundles the artifact operations feed() and advance()
+	  into a single service.
+
+	* src/main/java/de/intevation/flys/client/client/services/AdvanceServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/AdvanceService.java,
+	  src/main/java/de/intevation/flys/client/server/AdvanceServiceImpl.java:
+	  New. This service provides a method that triggers the advance() operation
+	  of the artifact server.
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Added code to parse the reachable states from DESCRIBE.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Make use of the 'forward' service after choosing the module and the river.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the new services 'forward' and
+	  'advance'.
+
+2011-03-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/test/java/de/intevation/flys/client/FLYSJUnit.gwt.xml,
+	  src/main/java/de/intevation/flys/client/client/GreetingServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/GreetingService.java:
+	  Removed the GreetingService from out application - this service has been
+	  added initially by creating this repository.
+
+2011-03-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Added
+	  i18n strings for the module and river selection.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java:
+	  New. A wrapper for the module and river selection. Both information can
+	  be chosen in just one step.
+
+	* src/main/java/de/intevation/flys/client/client/ui/MapSelection.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  Some layout improvements.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Makes use of the ModuleSelection if no artifact is existing in a new
+	  CollectionView. Users may now choose the desired plugin and the river in
+	  one single step.
+
+2011-03-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/RiverServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/RiverService.java,
+	  src/main/java/de/intevation/flys/client/server/RiverServiceImpl.java:
+	  New. A service that retrieves a list of supported rivers by the artifact
+	  server.
+
+	* src/main/java/de/intevation/flys/client/shared/model/River.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultRiver.java:
+	  New. A model class and its default implementation to store rivers.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: At application
+	  start, the provided rivers by the artifact server are fetched using the
+	  RiverService. The rivers are queriable via a getRivers() method.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the RiverService.
+
+2011-03-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Added
+	  i18n strings used in the menu panel.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Added a method
+	  to retrieve the project list.
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java: Added
+	  further buttons (labels with ClickHandler) to switch the language,
+	  logout the current user or open an info panel.
+
+2011-03-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/images/flys_logo.gif,
+	  src/main/java/de/intevation/flys/client/client/images/bfg_logo.gif: New.
+	  A logo of the BfG and a logo of the application FLYS. Both copied from
+	  desktop FLYS.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSImages.java: New.
+	  This interface grants access to the images used in this application.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java:
+	  Added the fullname of FLYS.
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSHeader.java: New.
+	  This class displays the header containing a FLYS and a BfG logo.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Integrated the
+	  FLYSHeader into the layout at the bottom of the application.
+
+	* src/main/webapp/FLYS.html: Set the title of the application to
+	  "FLYS-3.0".
+
+2011-03-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/CreateCollectionService.java,
+	  src/main/java/de/intevation/flys/client/client/services/CreateCollectionServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/server/CreateCollectionServiceImpl.java:
+	  New. A service that might be used to create new collections in the
+	  artifact server.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the service to create new
+	  collections.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Added the service to create new collections. Currently, this service is
+	  not used.
+
+2011-03-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultUser.java,
+	  src/main/java/de/intevation/flys/client/shared/model/User.java: A user
+	  interface and its default implementation.
+
+	* src/main/java/de/intevation/flys/client/client/services/UserService.java,
+	  src/main/java/de/intevation/flys/client/client/services/UserServiceAsync.java,
+	* src/main/java/de/intevation/flys/client/server/UserServiceImpl.java: This
+	  service will currently return the first user that is returned by the
+	  artifact server.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: The FLYS
+	  instance stores the user that is logged in, because we need to make use of
+	  this user in many services.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java,
+	  src/main/java/de/intevation/flys/client/client/ui/MainMenu.java: Make use
+	  of the User of this package - the User of the common package is not used
+	  any longer.
+
+2011-03-02	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* pom.xml: Added missing SmartGWT repository.
+
+2011-02-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java: A UI
+	  widget that stores and displays the data of former states and the current
+	  data. The widget displaying of the current data is created by the
+	  UIProvider that is stored in the Data object.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java: We
+	  are able to create new WINFO artifacts. After the artifact has been
+	  created, the first 'state' is rendered in a 'WINFO tab' using the
+	  ParameterList.
+
+2011-02-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  The 'uiprovider' attribute is parsed from DESCRIBE document and stored at
+	  the Data object.
+
+	* src/main/java/de/intevation/flys/client/shared/model/Data.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultData.java:
+	  Implemented code to store the UIProvider (as string!) for the Data object.
+
+2011-02-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/event/StepForwardEvent.java:
+	  An event that is thrown after the user has selected or entered data in the
+	  UI and triggers the feed & advance operations via UI button.
+
+	* src/main/java/de/intevation/flys/client/client/event/HasStepForwardHandlers.java:
+	  This interface is used by classes that provide that step forward
+	  mechanism.
+
+	* src/main/java/de/intevation/flys/client/client/event/StepForwardHandler.java:
+	  This interface is used by classes that want to listen to
+	  StepForwardEvents.
+
+2011-02-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProvider.java,
+	  src/main/java/de/intevation/flys/client/client/ui/MapSelection.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java: The
+	  interface description and two implementations of a UIProvider. A
+	  UIProvider is used to create widgets for the user input. The UIProvider
+	  that is used in the current state depends on the data type in the describe
+	  document and a 'uiprovider' flag that might be configured there.
+
+	* src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java:
+	  The factory that is used to create new instances of UIProvider.
+
+2011-02-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  This view implements the HasCollectionChangeHandlers interface -
+	  CollectionChangeHandler can register to this class and retrieve
+	  notifications when the collection of this view changes.
+
+2011-02-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/event/CollectionChangeHandler.java,
+	  src/main/java/de/intevation/flys/client/client/event/CollectionChangeEvent.java,
+	  src/main/java/de/intevation/flys/client/client/event/HasCollectionChangeHandlers.java:
+	  New. These interfaces and classes should be used to listen to changes in
+	  Collections.
+
+2011-02-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultCollection.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Collection.java:
+	  Enhanced the Collection and its default implementation with new methods
+	  to add new Artifacts, retrieve Artifacts and get the number of artifacts
+	  managed by the Collection.
+
+2011-02-18  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  New. This factory is used to create an ArtifactDescription based on an
+	  DESCRIBE document returned by the artifact server.
+
+	* src/main/java/de/intevation/flys/client/server/FLYSArtifactCreator.java: A
+	  new DefaultArtifact with an ArtifactDescription that contains the dynamic
+	  UI part is returned by the create() method now.
+
+2011-02-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* pom.xml: This client uses the artifacts http-client for the communication
+	  between client and server now.
+
+	* src/main/java/de/intevation/flys/client/server/FLYSArtifactCreator.java:
+	  New. An instance of the ArtitactCreator interface of the http-client. It
+	  uses the document returned by the artifact server to create an artifact
+	  instance.
+
+	  NOTE: The artifact creation needs to be implemented! This is currently
+	  just a stub to make the other things work.
+
+	* src/main/java/de/intevation/flys/client/client/services/ArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/ArtifactService.java:
+	  The method signature of create() changed: the server url is required now.
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java:
+	  Make use of the HttpClient and the FLYSArtifactCreator to create
+	  artifacts.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java,
+	  src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Added the server url to the create() call of the ArtifactService.
+
+2011-02-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  This view will now display radio buttons to choose between the module
+	  'WINFO', 'MINFO', 'Map' and 'Fix-Analyse'. A button is shown to create an
+	  artifact of the selected module.
+
+	  NOTE: The next step here is to use the returning artifact with its
+	  description and display widgets based on these information.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Added
+	  i18n strings for the CollectionView's 'next' button.
+
+2011-02-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/ArtifactDescription.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DataItem.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Data.java: The
+	  interfaces implement the Serializable interface now.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultArtifactDescription.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultDataItem.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultData.java:
+	  New. Default implementions of the interfaces above. These classes
+	  implements constructors and the necessary methods of the interface
+	  descriptions only!
+
+2011-02-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/ArtifactServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/ArtifactService.java:
+	  New. Interface descriptions and the server implementation of an artifact
+	  service that provides basic methods for working with artifacts.
+
+	  NOTE: The implementation is currently just a stub. The artifact creation
+	  needs to be implemented.
+
+	* src/main/webapp/WEB-INF/web.xml: Added a servlet for the artifact
+	  interface.
+
+2011-02-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/Artifact.java:
+	  Implements the serializable interface which is necessary to be able to
+	  use this object in the GWT client code.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultArtifact.java:
+	  New. A simple default implementation of an artifact.
+
+2011-02-16  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/FLYS.gwt.xml: Added the
+	  Resources and XML modules of GWT.
+
+	* src/main/java/de/intevation/flys/client/client/config.xml: An xml file
+	  that will contain the client configuration.
+
+	* src/main/java/de/intevation/flys/client/client/Config.java: New. This
+	  class should be used to handle the client configuration and provides
+	  methods for retrieving information about the configuration.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSResources.java: The
+	  configuration (Config) is initialized at the startup.
+
+2011-02-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/Artifact.java: New.
+	  The interface description of an artifact used in this client. There are
+	  several methods that provide information about the artifact itself and its
+	  representation.
+
+	* src/main/java/de/intevation/flys/client/shared/model/ArtifactDescription.java:
+	  New. The ArtifactDescription provides information about the current
+	  representation of an artifact.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DataItem.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Data.java: New. The
+	  interfaces are used to handle user input.
+
+2011-02-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Added
+	  strings for the plugins.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Display radio buttons if the collection is new and no plugin (winfo,
+	  minfo, map, fix analyse) has been chosen.
+
+2011-02-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java: New
+	  strings for the CollectionViews.
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSWorkspace.java: New.
+	  This is the workspace for FLYS. It contains the windows for each
+	  collection of the user.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  New. This window will display a collection. Currently, it has just a
+	  title, but no content.
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSView.java: Displays
+	  the FLYSWorkspace.
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java: Added a
+	  button to open new collections.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: The
+	  FLYSWorkspace is created here. FLYS stores a reference to it and puts the
+	  workspace into the FLYSView. Furthermore, there is a new method that
+	  creates new Collections.
+
+	  NOTE: Currently, there is no communication with the artifact server,
+	  because Collections aren't implemented yet!
+
+2011-02-11  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultCollection.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Collection.java:
+	  Added a new method that returns the UUID of the collection.
+
+2011-02-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java: The
+	  ProjectList stores a reference to the current user and provides a list
+	  with his existing projects.
+
+	  NOTE: There are created two Collections and two CollectionRecords for the
+	  mockup. This code needs to be removed after a service to fetch the user
+	  collections has been implemented.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionGrid.java:
+	  This grid will show a list of collections with its last modification date,
+	  their uuid and two buttons to publish and delete the collection.
+
+	  NOTE: The buttons have no effect yet.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: The FLYS class
+	  has a reference to the ProjectList. If a logged in user was found, the
+	  ProjectList is initialized and added to the FLYSView.
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSView.java: There is
+	  a new method to set the ProjectList. Furthermore, all UI components are
+	  put into a HLayout now. On this way, we are able to open/close the
+	  ProjectList.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages.java,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties:
+	  Added strings for the project list.
+
+2011-02-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultCollection.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Collection.java: The
+	  interface and its default implementation of a Collection.
+	  NOTE: I think both classes will change pretty much, but they have been
+	  necessary for the ProjectList mockup.
+
+2011-02-10  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/FLYS.gwt.xml: The artifact-common
+	  package is available in the GWT code now.
+
+	* pom.xml: Made the artifact-common package available.
+
+	* src/main/java/de/intevation/flys/client/server/UserServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/UserService.java,
+	  src/main/java/de/intevation/flys/client/client/services/UserServiceAsync.java:
+	  A service definition that retrieves user information. Currently, this
+	  service defines a single method that returns the user that is currently
+	  logged in.
+
+	* src/main/webapp/WEB-INF/web.xml: Added a servlet definition that provides
+	  the UserService.
+
+	* src/main/java/de/intevation/flys/client/client/FLYSMessages_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSMessages.java: Added
+	  I18N string for a guest user.
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java: There are
+	  new methods to set the current user and to update the menu with its name.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: After creating
+	  the necessary components, the current user is queried by the UserService
+	  and displayed in the menu bar.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/Changes	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,76 @@
+2011-05-19  RELEASE 2.3.1
+
+    NEW:
+
+        * Enabled the UI to display more export modes that a single one.
+
+        * Added tooltips for export buttons.
+
+
+    FIXES:
+
+        * flys/issue23 (Doppel-Click auf Eintrag in der Projektliste sollte ein Projekt öffnen)
+
+        * flys/issue35 The project window is maximizable with a 'maximize' button.
+
+        * flys/issue38 (WINFO: Vorbelegung der Berechnungsart auf "Wasserstand/Wasserspiegellagen")
+
+        * flys/issue39 (WINFO: Auswahl einer Strecke in bei Eingabeunterstützung liefert nicht die richtigen Werte)
+
+        * flys/issue43 (i18n: Einige Verbesserungen)
+
+        * flys/issue44 (WINFO: Unterstützungstabellen werden in fester Fensterbreite angezeigt)
+
+        * flys/issue45 (WINFO: Ladedialog bei Laden der Daten in Unterstützungstabellen anzeigen)
+
+        * flys/issue48 (Diagramm: Bereiche für Themensteuerung und Werkzeugleiste vorgeben.)
+
+        * flys/issue56: (Knopf zum Exportieren der Berechnungsergebnissen nach CSV in GUI einbauen)
+
+
+
+2011-05-13  RELEASE 2.3
+
+    NEW:
+
+        * Initial GWT/Ajax based Web-Client for the REST based artifact system:
+          - Renderer for states specific for WINFO artifact
+          - Panels for W/Q selection
+          - Panels for Range selection
+          - Chart panels for chart output creation
+          - Export panels that provides download links for exporting computation
+            data
+          - Project list displays former projects that might be opened by
+            double click
+          - Datacage (dt. 'Datenkorb') UI that displays loadable data for charts
+
+        * RPC-services enable data exchange between client and server
+
+        * Connection settings are configured in config.xml
+
+        * I18N is splitted into two parts and support English and German:
+          - GUI elements are translated using the GWT i18n mechanism
+          - Information/data served by the artifact system is translated by the
+            artifact server
+
+
+    LIMITATIONS:
+
+        * Language switch drops the current session
+
+        * Charts served by the artifact system are rendered as PNG only
+
+        * Theme control for charts is not implemented yet.
+
+        * Data served by the datacage can't be added to projects/charts
+
+
+    !!!
+
+    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-client/FLYS.launch	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;FLYS&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/FLYS/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento project=&quot;FLYS&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx256M"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war src/main/webapp -startupUrl FLYS.html de.intevation.flys.client.FLYS"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="FLYS"/>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+</launchConfiguration>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/FLYSTest-dev.launch	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="de.intevation.flys.client.client.FLYSTest"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;FLYS&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/FLYS/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/FLYS/src/test/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento project=&quot;FLYS&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;/vol1/download/ingo/gwt-2.1.1/gwt-dev.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="FLYS"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dgwt.args=&quot;-standardsMode -logLevel WARN&quot; -Xmx256M"/>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+</launchConfiguration>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/FLYSTest-prod.launch	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
+<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="de.intevation.flys.client.client.FLYSTest"/>
+<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;FLYS&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/FLYS/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/FLYS/src/test/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento project=&quot;FLYS&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry externalArchive=&quot;/vol1/download/ingo/gwt-2.1.1/gwt-dev.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="FLYS"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Dgwt.args=&quot;-prod -standardsMode -logLevel WARN -out www-test&quot; -Xmx256M"/>
+<booleanAttribute key="org.eclipse.debug.core.appendEnvironmentVariables" value="true"/>
+</launchConfiguration>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/NEWS	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,160 @@
+NEWS
+
+2011-05-19  Release V 2.3.1
+
+=== Framework ===
+
+  * Projektverwaltung
+   * Projektfenster maximieren (vgl. https://roundup-intern.intevation.de/flys/issue35)
+
+  * Internationalisierung/Mehrsprachigkeit
+   * i18n: Untertitel bei Längsschnitten - Bereich der Strecke enthält "double" (https://roundup-intern.intevation.de/flys/issue66)
+
+
+=== Fachdatenbestand ===
+ * Import der hydrologischen Daten der Elbe
+  * Importer: Lücken in den Q-Bereichen (https://roundup-intern.intevation.de/flys/issue76)
+   * Seiteneffekt: Durch diese Lücken werden z.T. Berechungsausgaben verändert. Durch eine Fehlerbehebung und einen Reimport werden diese Lücken geschlossen.
+ 
+
+=== WINFO ===
+
+  * Wahl von Abflüssen, Wasserständen und Dauerzahlen (Pflichtenheft: Bestehende Funktionen aus Desktop-FLYS; keine Entsprechung in Leistungsbeschreibung)
+   * WINFO: Q/W/D-Info liefert selten eine Antwort (https://roundup-intern.intevation.de/flys/issue72)
+
+ * Berechnungsausgabe (Pflichtenheft: Teile in Kap. 3.4.1.1; bestehende Funktionen aus Desktop-FLYS + Verbesserungen)
+  * Ergebnisse werden jeweils im WST-Format ausgegeben (https://roundup-intern.intevation.de/flys/issue56)
+   * Auf Berechnungsergebnisse zu korrigierten Wasserstands-Abflussbeziehungen können über CSV-Dateien zugegriffen werden
+
+ * Zu berücksichtigende Punkte bei der Durchführung der Berechnungsarten
+  * Allgemein:
+   * Besondere Beachtung der Berücksichtigung des Rücksprungverhaltens
+  * Wasserspiegellage/Wasserstand
+   * Berechnungsart 1 "Wasserstand/Wasserspiegellage" fehlerhaft (https://roundup-intern.intevation.de/flys/issue69)
+  * Abflusskurve/-tafel
+   * Weitere Infos zu Fällen für Rücksprünge werden benötigt.
+  * Dauerlinie
+   * WINFO: CSV-Export für Dauerlinien fehlt (https://roundup-intern.intevation.de/flys/issue79)
+    * Wird zur fachlichen Abnahme noch nachgereicht.
+  * W bei ungleichmäßigem Abflusslängsschnitt
+   * WINFO: Reihenfolge der Kilometer Eingabe liefert unterschiedliche Ergebnisse (https://roundup-intern.intevation.de/flys/issue71)
+   * WINFO: Gewichtungsfaktor für Abflusstabellen in der Berechnung "W für angepaßten Abfluss-Längsschnitt" (https://roundup-intern.intevation.de/flys/issue55); hier ist das Vorgehen zur Intervallbildung noch zu klären.
+
+
+
+=== Diagramme ===
+  * Allgemein:
+   * Beschriftung der Legendeneinträge zu Kurven zu Eingangswerten in die Berechnung (https://roundup-intern.intevation.de/flys/issue77)
+    
+ 
+=== Offene Punkte ===
+ * Version 2.3.1 behebt Fehler und ergänzt eine Funktion. Punkte mit noch nicht bereitgestellten Funktionen finden sich unter [[Flys-3.0/Version2.3Todo]].
+ 
+
+2011-05-16  Release V 2.3.0
+
+=== Framework ===
+
+ * Projektverwaltung (Pflichtenheft Kap. 3.3, Lokale Daten > Arbeitssitzungen)
+  * Mit dem Anlegen eines neuen Projektes wird die Projektliste automatisch gefüllt.
+   * Zu beachten: Zur Zeit operiert das System noch mit '''einem''' Benutzer, so dass jeder alle auf dem Server liegenden Projekte sieht. Mit der Einführung einer Nutzeranmeldung sieht dann jeder Nutzer nur noch seine eigenen Projekte.
+  * Mit einem Doppelklick oder das Kontextmenü können die jeweiligen Projekte wieder geöffnet werden. Sie stellen den Arbeitszustand des letzten "Übernehmen" wieder her. Die Einstellung ist sprachunabhängig, d.h. auch wenn zuvor Diagramme in deutsch erstellt worden sind, können diese in englischer Sprache geöffnet und dargestellt werden.
+   * Projekte können Projekte noch in mehreren Fenster geöffnet werden. Um Konsistenz herzustellen, kann man ein Projekt nur einmal öffnen. (vgl. https://roundup-intern.intevation.de/flys/issue21)
+  * Neue Fenster überlappen sich nicht mit der Position des vorherigen Fensters, um beim Anlegen eines neuen Projektes eine vollständige Überdeckung zu vermeiden
+   * Projektfenster maximieren (vgl. https://roundup-intern.intevation.de/flys/issue35)
+
+ * Projekteinstieg
+  * Klick-Optimierung der Gewässerauswahl über eine Liste: von 3 Klicks zu 1 Klick.
+
+ * Internationalisierung (Pflichtenheft, Kap.4.3.4)
+  * Beim Start wird die Spracheinstellung des Browser automatisch ausgewertet und die dazugehörige Oberfläche in deutsch oder englisch geladen. Wenn eine andere Sprache als deutsch oder englisch angefragt wird, fällt die Anwendung auf englisch (TOCHECK) zurück.
+  * Die Sprache kann jeweils manuell gewechselt werden. Dies hat Auswirkungen auf die Bedienoberfläche, Diagramme und Nutzung von Dezimaltrenner und Punkt
+
+=== Fachdatenbestand ===
+ * Import der hydrologischen Daten der Elbe
+##  * Was wird nicht berücksichtigt?
+##   * Spalten in wst-Dateien mit einer - Notation; andere, nicht einheitliche werde jeweils mit einer Ziffer erweitert
+ * Einige Zahlen zum Bestand:
+  * 3 Gewässern mit Saar, Mosel und Elbe
+  * 15 Pegel
+  * 108 wst/zus-Dateien
+   * 711 Spalten aus diesen
+   * rund 500.000 Einzelwerte
+  * rund 2.500 Stationierungen aus km-Dateien
+ 
+
+=== WINFO ===
+
+ * Wahl des Gewässers (Pflichtenheft, Teile aus 3.4.1, 3.4.1.1)
+  * Automatische Vorbelegung des ersten Eintrags
+
+ * Wahl der Berechnungsart (Pflichtenheft 3.4.1.1 - bestehende Funktionen)
+  * Automatische Vorbelegung des ersten Eintrags
+  * Umsetzung der folgenden Berechnungsarten
+   * Wasserstand/Wasserspiegellage
+   * Abflusskurve/Abflusstafel
+   * Dauerlinie
+   * W bei ungleichmäßigem Abflusslängsschnitt
+
+ * Wahl von Strecke oder Ort (Pflichtenheft: Bestehende Funktionen aus Desktop-FLYS; keine Entsprechung in Leistungsbeschreibung)
+
+   * Automatisches Umschalten der Reiter bei Wahl von Orten oder Strecken
+
+  * Eingabe eines Ortes (Dauerlinie, Abflusstafel/-kurve)
+   * Freie Texteingabe
+   * Unterstützung der Eingabe durch Liste an Ortsfavoriten. Per Klick wird jeweils der Wert in das Textfeld eingetragen.
+
+  * Eingabe mehrere Orte (Wasserstand/Wasserspiegellage)
+   * Freie Texteingabe
+   * Unterstützung der Eingabe durch Auswahltabellen an Ortsfavoriten. Per Klick wird jeweils der Wert in das Textfeld eingetragen.
+   * Übernehmen von Beginn einer Strecke und Ende einer Strecke (abweichend von Desktop-FLYS, um die Bedienführung gradliniger zu gestalten)
+   
+  * Eingabe von Strecken (Wasserstand/Wasserspiegellage)
+   * Freie Texteingabe
+   * Unterstützung der Eingabe durch Auswahltabellen an Streckenfavoriten. Per Klick wird jeweils der Wert in das Textfeld eingetragen.
+   * Übernehmen von Beginn einer Strecke und Ende einer Strecke aus den Ortsfavoriten mit explizitem Setzen von Beginn (grüne Fahne) und Ende (Rote Fahne) (Erweiterung zu Desktop-FLYS)
+   * Bemerkungen:
+    * Es gibt Streckendaten, die außerhalb der Berechnungsstrecke des Modells liegen. Können die Ortsfavoritenbestände hierauf reduziert werden, da hier keine Ermittlung von Wasserständen erfolgen kann bzw. Favoriten außerhalb der Bezugspegelstrecken erlauben keine Ermittlung eines Bezugspegels.
+
+ * Automatische Ermittlung des Bezugspegels (Keine Entsprechung in Leistungsbeschreibung. Neue Anforderung als Ergebnis des Bedienkonzeptes.)
+  * Gewässer und Streckenwahl ermitteln den Bezugspegel automatisch
+  * Anzeige der Abflusskurven aller Pegel auf der vorgegebenen Strecke
+
+  * Wahl von Abflüssen, Wasserständen und Dauerzahlen (Pflichtenheft: Bestehende Funktionen aus Desktop-FLYS; keine Entsprechung in Leistungsbeschreibung)
+   * Berechnungsart: Wasserspiegellagen/Wasserstand
+    * Eingabefelder wie in Desktop-FLYS für W oder Q; Einzelwerte oder Wertebereich
+    * Eingaben werden über eine tabellarische Auswahl unterstützt
+     * Bei der Eingabe von W: Wasserstände sind auswählbar
+     * Bei der Eingabe von Q: Hauptwerte und Dauerzahlen sind auswählbar
+   * Berechnungsart: W bei ungleichmäßigem Abflusslängsschnitt
+    * Eingabefelder für W und Q analog zu bisherigen genutzten Mechanismen
+
+ * Berechnungsausgabe (Pflichtenheft: Teile in Kap. 3.4.1.1; bestehende Funktionen aus Desktop-FLYS + Verbesserungen)
+  * Ausgabe jeweils in einer strukturierten Tabelle.
+  * Die Werte können in Abhängigkeit von der Spalte auf- und absteigend sortiert werden.
+  * Ergebnisse werden jeweils im CSV-Format ausgegeben
+  * Es werden weitere Ausgaben in zusätzlichen Reitern angezeigt, die auch abgefragt werden können.
+
+=== Diagramme ===
+  * Allgemein:
+   * Integrierte Ausgabe der Diagramme in WINFO (Pflichtenheft Kap. 3.1)
+   * Zugriff auf die Projektdefinition/Parameterisierung (WINFO-Reiter)
+   * Freie Größenskalierung der Diagramme in Abhängigkeit vom Projektfenster
+   * Internationalisierung der Diagramme in deutsch und englisch (Beschriftung, Dezimaltrennzeichen und Tausendertrennzeichen)
+   * Beschriftung der Diagrammtitel gemäß Anhang (Leistungsbeschreibung, Anhang 1)
+    
+  * Anpassungen in Abhängigkeit von Berechnungsart/Diagrammtyp (Bestehendes Verhalten aus Desktop-FLYS):
+   * Abflusskurve am Pegel
+   * Längsschnitt
+    * Wasser fließt immer von links nach rechts (unabhängig von der Orientierung der Stationierung; die Orientierung kann pro Gewässer explizit gesetzt werden.)
+   * Abflusskurve/-tafel
+   * Dauerlinie
+    * Kombiniertes Diagramm von W- und Q-Dauerlinie (Pflichtenheft 3.4.1.8.6)
+   * W bei ungleichmäßigem Abflusslängsschnitt
+    * Kombiniertes Diagramm von W-Diagramm und Q-Vorschau (Pflichtenheft 3.4.1.8.6)
+
+
+=== Datenkorb (Pflichtenheft 3.4.1, ausgehend von Desktop-FLYS) ===
+ * Anzeige aller in der Datenbank vorhandenen Datensätze für das gewählte Gewässer (FLYS-Daten aus der Datenbank)
+ * Mehrfachauswahl durch Bereiche mit der Taste "Shift"; kombinierte Einzelauswahl mit der Taste "Strg"
+ * XML-basierte Konfiguration des Datenkorbs
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/README.txt	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,84 @@
+--- Generated by GWT WebAppCreator ---
+
+Congratulations, you've successfully generated a starter project!  What next?
+
+-- Option A: Import your project into Eclipse (recommended) --
+
+If you use Eclipse, you can simply import the generated project into Eclipse.
+We've tested against Eclipse 3.4 and 3.5.  Later versions will likely also
+work, earlier versions may not.
+
+If the directory containing this file does not have a .classpath or .project
+file, generate them by running 'ant eclipse.generate'
+
+In Eclipse, go to the File menu and choose:
+
+  File -> Import... -> Existing Projects into Workspace
+
+  Browse to the directory containing this file,
+  select "FLYS".
+  
+  Be sure to uncheck "Copy projects into workspace" if it is checked.
+  
+  Click Finish.
+  
+You can now browse the project in Eclipse.
+
+To launch your web app in GWT development mode, go to the Run menu and choose:
+
+  Run -> Open Debug Dialog...
+
+  Under Java Application, you should find a launch configuration
+  named "FLYS".  Select and click "Debug".
+
+  You can now use the built-in debugger to debug your web app in development mode.
+
+If you supplied the junit path when invoking webAppCreator, you should see
+launch configurations for running your tests in development and production
+mode.
+
+-- Option B: Build from the command line with Ant --
+
+If you prefer to work from the command line, you can use Ant to build your
+project. (http://ant.apache.org/)  Ant uses the generated 'build.xml' file
+which describes exactly how to build your project.  This file has been tested
+to work against Ant 1.7.1.  The following assumes 'ant' is on your command
+line path.
+
+To run development mode, just type 'ant devmode'.
+
+To compile your project for deployment, just type 'ant'.
+
+To compile and also bundle into a .war file, type 'ant war'.
+
+If you supplied the junit path when invoking webAppCreator, you can type 'ant
+test' to run tests in development and production mode.
+ 
+For a full listing of other targets, type 'ant -p'.
+
+-- Option C: Using another IDE --
+
+GWT projects can be run in other IDEs as well, but will require some manual
+setup.  If you go this route, be sure to:
+
+* Have your IDE build .class files into 'war/WEB-INF/classes'.
+* Add gwt-user.jar and gwt-dev.jar to your project build path.
+* When creating a launch configuration, add a classpath entry for your 'src'
+  folder (this is somewhat unusual but GWT needs access to your source files).
+
+If you get stuck, try to mimic what the Ant 'build.xml' would do.
+
+-- Option D: Using Maven --
+
+If you have generated your project with the option '-maven', you have a 'pom.xml'
+file ready to use. Assuming you have 'maven2' installed in your system, 'mvn' is 
+in your path, and you have access to maven repositories, you should be able to run:
+
+mvn clean         # delete temporary stuff
+mvn test          # run all the tests (gwt and junit)
+mvn gwt:run       # run development mode
+mvn gwt:compile   # compile to javascript
+mvn package       # generate a .war package ready to deploy
+
+For more information about other available goals, read maven and gwt-maven-plugin 
+documentation (http://maven.apache.org, http://mojo.codehaus.org/gwt-maven-plugin)  
Binary file flys-client/doc/Flys-UI-Classes.png has changed
Binary file flys-client/doc/Flys-UI-Services-Classes.png has changed
Binary file flys-client/images/next.xcf has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/pom.xml	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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/maven-v4_0_0.xsd">
+
+  <!-- POM file generated with GWT webAppCreator -->
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>de.intevation.flys.client</groupId>
+  <artifactId>FLYS</artifactId>
+  <packaging>war</packaging>
+  <version>1.0-SNAPSHOT</version>
+  <name>de.intevation.flys.client.FLYS</name>
+  
+  <properties>
+      <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+      <!-- Convenience property to set the GWT version -->
+      <gwtVersion>2.1.1</gwtVersion>
+      <!-- GWT needs at least java 1.5 -->
+      <maven.compiler.source>1.5</maven.compiler.source>
+      <maven.compiler.target>1.5</maven.compiler.target>
+  </properties>
+
+  <dependencies>
+    <dependency>
+      <groupId>com.google.gwt</groupId>
+      <artifactId>gwt-servlet</artifactId>
+      <version>${gwtVersion}</version>
+      <scope>runtime</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.gwt</groupId>
+      <artifactId>gwt-user</artifactId>
+      <version>${gwtVersion}</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.gwt</groupId>
+      <artifactId>gwt-dev</artifactId>
+      <version>${gwtVersion}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.smartgwt</groupId>
+      <artifactId>smartgwt</artifactId>
+      <version>2.4</version>
+    </dependency>
+    <dependency>
+      <groupId>de.intevation.artifacts.common</groupId>
+      <artifactId>artifacts-common</artifactId>
+      <version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+        <groupId>de.intevation.artifacts.httpclient</groupId>
+        <artifactId>http-client</artifactId>
+        <version>1.0-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+        <groupId>net.sf.opencsv</groupId>
+        <artifactId>opencsv</artifactId>
+        <version>2.0</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <version>4.4</version>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+  
+  <build>
+    <!-- Generate compiled stuff in the folder used for developing mode --> 
+    <outputDirectory>target/www/WEB-INF/classes</outputDirectory>
+    
+    <plugins>
+      
+      <!-- GWT Maven Plugin-->
+      <plugin>
+        <groupId>org.codehaus.mojo</groupId>
+        <artifactId>gwt-maven-plugin</artifactId>
+        <!-- JS is only needed in the package phase, this speeds up testing --> 
+        <executions>
+          <execution>
+            <phase>prepare-package</phase>
+            <goals>
+              <goal>compile</goal>
+            </goals>
+          </execution>
+        </executions>
+        <!-- Plugin configuration. There are many available options, 
+             see gwt-maven-plugin documentation at codehaus.org --> 
+        <configuration>
+          <!-- URL that should be automatically opened in the GWT shell (gwt:run). -->
+          <runTarget>FLYS.html</runTarget>
+          <!-- Location of the develop-mode web application structure (gwt:run). -->
+          <hostedWebapp>target/www</hostedWebapp>
+          <!-- Ask GWT to create the Story of Your Compile (SOYC) (gwt:compile) -->
+          <soyc>true</soyc>
+        </configuration>
+      </plugin>
+
+      <!-- Add source folders to test classpath in order to run gwt-tests as normal junit-tests -->
+      <plugin>
+        <artifactId>maven-surefire-plugin</artifactId>
+        <configuration>
+          <additionalClasspathElements>
+            <additionalClasspathElement>${project.build.sourceDirectory}</additionalClasspathElement>
+            <additionalClasspathElement>${project.build.testSourceDirectory}</additionalClasspathElement>
+          </additionalClasspathElements>
+          <useManifestOnlyJar>false</useManifestOnlyJar>
+          <forkMode>always</forkMode>
+
+          <!-- Folder for generated testing stuff --> 
+          <systemProperties>
+            <property>
+              <name>gwt.args</name>
+              <value>-out target/www</value>
+            </property>
+          </systemProperties>
+        </configuration>
+      </plugin>
+      
+      <!-- Copy static web files before executing gwt:run -->
+      <plugin>
+        <artifactId>maven-resources-plugin</artifactId>
+        <executions>
+          <execution>
+            <phase>compile</phase>
+            <goals>
+              <goal>copy-resources</goal>
+            </goals>
+            <configuration>
+              <outputDirectory>target/www</outputDirectory>
+              <resources>
+                <resource>
+                  <directory>src/main/webapp</directory>
+                </resource>
+              </resources>
+            </configuration>
+          </execution>
+        </executions>
+      </plugin>
+
+      <!-- Delete gwt generated stuff -->
+      <plugin>
+        <artifactId>maven-clean-plugin</artifactId>
+        <configuration>
+          <filesets>
+            <fileset><directory>src/main/webapp/flys</directory></fileset>
+            <fileset><directory>src/main/webapp/WEB-INF/classes</directory></fileset>
+            <fileset><directory>tomcat</directory></fileset>
+            <fileset><directory>www-test</directory></fileset>
+            <fileset><directory>.gwt-tmp</directory></fileset>
+          </filesets>
+        </configuration>
+      </plugin>
+      
+    </plugins>
+    </build>
+      <repositories>
+        <repository>
+           <id>com.smartgwt</id>
+           <name>SmartGWT</name>
+           <url>http://www.smartclient.com/maven2</url>
+      </repository>
+    </repositories>
+</project>
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/FLYS.gwt.xml	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module rename-to='flys'>
+  <!-- Inherit the core Web Toolkit stuff.                        -->
+  <inherits name='com.google.gwt.user.User'/>
+
+  <!-- Inherit the i18n google stuff.                             -->
+  <inherits name="com.google.gwt.i18n.I18N"/>
+
+  <!-- Inherit the XML stuff of GWT                               -->
+  <inherits name="com.google.gwt.xml.XML"/>
+
+  <!-- Inherit the Resource module of GWT                         -->
+  <inherits name="com.google.gwt.resources.Resources"/>
+
+  <inherits name="de.intevation.artifacts.common.Common"/>
+
+  <!-- Inherit the smartgwt Enterprise Blue Theme.                -->
+  <inherits name="com.smartgwt.SmartGwtNoTheme" />
+  <inherits name="com.smartclient.theme.enterpriseblue.EnterpriseBlue"/>
+  <inherits name="com.smartclient.theme.enterpriseblue.EnterpriseBlueResources"/>
+
+  <!-- Other module inherits                                      -->
+
+  <!-- Specify the app entry point class.                         -->
+  <entry-point class='de.intevation.flys.client.client.FLYS'/>
+
+  <!-- Specify the paths for translatable code                    -->
+  <source path='client'/>
+  <source path='shared'/>
+
+  <extend-property name="locale" values="de,en"/>
+</module>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/Config.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,84 @@
+package de.intevation.flys.client.client;
+
+import com.google.gwt.i18n.client.LocaleInfo;
+
+import com.google.gwt.xml.client.Document;
+import com.google.gwt.xml.client.Node;
+
+
+/**
+ * A class that is used to handle the global configuration of this client. You
+ * can retrieve an instance of this class using the <code>getInstance</code>
+ * methods. <b>NOTE:</b> the configuration is initialized using {@link
+ * getInstance(Document)} the first time.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class Config {
+
+    /** The instance of the configuration. */
+    protected static Config INSTANCE;
+
+    /** The xml document that contains the configuration options.*/
+    protected Document config;
+
+
+    /**
+     * Get an instance by using {@link getInstance(Document)} or {@link
+     * getInstance()}.
+     */
+    private Config(Document config) {
+        this.config = config;
+    }
+
+
+    /**
+     * Returns an instance of this class and initializes the configuration of
+     * this has not been done so far.
+     *
+     * @param config The client configuration.
+     *
+     * @return an instance of this Config class.
+     */
+    public static Config getInstance(Document config) {
+        if (INSTANCE == null) {
+            INSTANCE = new Config(config);
+        }
+
+        return INSTANCE;
+    }
+
+
+    /**
+     * Returns an instance of this class. If it has not been initialized with a
+     * valid configuration, null is returned.
+     *
+     * @return an instance of this class or null, if the Config has not been
+     * initialized using {@link getInstance(Document)} so far.
+     */
+    public static Config getInstance() {
+        return INSTANCE;
+    }
+
+
+    /**
+     * Returns the URL of the artifact server.
+     *
+     * @return the artifact server url.
+     */
+    public String getServerUrl() {
+        Node server = config.getElementsByTagName("server").item(0);
+        return server.getFirstChild().getNodeValue();
+    }
+
+
+    /**
+     * Returns the name of the current locale.
+     *
+     * @return the name of the current locale.
+     */
+    public String getLocale() {
+        return LocaleInfo.getCurrentLocale().getLocaleName();
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/FLYS.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,313 @@
+package de.intevation.flys.client.client;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.xml.client.XMLParser;
+
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.layout.HLayout;
+
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.CollectionItem;
+import de.intevation.flys.client.shared.model.River;
+import de.intevation.flys.client.shared.model.User;
+
+import de.intevation.flys.client.client.services.ArtifactService;
+import de.intevation.flys.client.client.services.ArtifactServiceAsync;
+import de.intevation.flys.client.client.services.DescribeCollectionService;
+import de.intevation.flys.client.client.services.DescribeCollectionServiceAsync;
+import de.intevation.flys.client.client.services.GetArtifactService;
+import de.intevation.flys.client.client.services.GetArtifactServiceAsync;
+import de.intevation.flys.client.client.services.RiverService;
+import de.intevation.flys.client.client.services.RiverServiceAsync;
+import de.intevation.flys.client.client.services.UserService;
+import de.intevation.flys.client.client.services.UserServiceAsync;
+import de.intevation.flys.client.client.ui.CollectionView;
+import de.intevation.flys.client.client.ui.FLYSHeader;
+import de.intevation.flys.client.client.ui.FLYSView;
+import de.intevation.flys.client.client.ui.FLYSWorkspace;
+import de.intevation.flys.client.client.ui.MainMenu;
+import de.intevation.flys.client.client.ui.ProjectList;
+import de.intevation.flys.client.client.ui.FLYSFooter;
+
+
+/**
+ * Entry point classes define <code>onModuleLoad()</code>.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYS implements EntryPoint {
+
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    /** The UserService used to retrieve information about the current user. */
+    protected UserServiceAsync userService = GWT.create(UserService.class);
+
+    /** The RiverService used to retrieve the supported rivers of the server.*/
+    protected RiverServiceAsync riverService = GWT.create(RiverService.class);
+
+    /** The ArtifactService used to communicate with the Artifact server. */
+    protected ArtifactServiceAsync artifactService =
+        GWT.create(ArtifactService.class);
+
+    /** The ArtifactService used to communicate with the Artifact server. */
+    protected DescribeCollectionServiceAsync describeCollectionService =
+        GWT.create(DescribeCollectionService.class);
+
+    /** The GetArtifactService used to open an existing collection. */
+    protected GetArtifactServiceAsync getArtifactService =
+        GWT.create(GetArtifactService.class);
+
+
+    /** The menu bar at the top of the application.*/
+    protected MainMenu menu;
+
+    /** The content window. It takes the whole space beneath the menu bar.*/
+    protected FLYSView view;
+
+    /** The project list that displays the projects of the user.*/
+    protected ProjectList projectList;
+
+    /** The FLYSWorkspace.*/
+    protected FLYSWorkspace workspace;
+
+    /** The footer. */
+    protected FLYSFooter footer;
+
+    /** The user who is currently logged in.*/
+    protected User currentUser;
+
+    /** The list of rivers supported by the server.*/
+    protected River[] rivers;
+
+
+    /**
+     * This is the entry point method.
+     */
+    public void onModuleLoad() {
+
+        VLayout vertical = new VLayout();
+        vertical.setLayoutMargin(1);
+        vertical.setWidth100();
+        vertical.setHeight100();
+
+        HLayout spacerBar = new HLayout();
+        spacerBar.setWidth("100%");
+        spacerBar.setHeight("20px");
+        spacerBar.setStyleName("bgBlueLight");
+
+        menu = new MainMenu(this);
+        view = new FLYSView();
+        footer = new FLYSFooter(this);
+
+        vertical.addMember(new FLYSHeader());
+        vertical.addMember(menu);
+        vertical.addMember(spacerBar);
+        vertical.addMember(view);
+        vertical.addMember(footer);
+
+        RootPanel.get("app").add(vertical);
+
+        initConfiguration();
+
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        getRivers();
+
+        userService.getCurrentUser(url, locale, new AsyncCallback<User>() {
+            public void onFailure(Throwable caught) {
+                GWT.log("Could not find a logged in user.");
+                SC.warn(MSG.getString(caught.getMessage()));
+            }
+
+            public void onSuccess(User user) {
+                GWT.log("Found a user. Set '"+ user.getName() + "'");
+                setCurrentUser(user);
+
+                menu.setCurrentUser(user);
+
+                projectList = new ProjectList(FLYS.this, user);
+                workspace   = new FLYSWorkspace();
+                view.setProjectList(projectList);
+                view.setFLYSWorkspace(workspace);
+
+                readRivers();
+            }
+        });
+    }
+
+
+    /**
+     * This method should be called at system start. It initialzes the client
+     * configuration.
+     */
+    protected void initConfiguration() {
+        String xml = FLYSResources.INSTANCE.initialConfiguration().getText();
+        Config.getInstance(XMLParser.parse(xml));
+    }
+
+
+    /**
+     * Returns the user that is currently logged in.
+     *
+     * @return the current user.
+     */
+    public User getCurrentUser() {
+        return currentUser;
+    }
+
+
+    /**
+     * Sets the current user.
+     */
+    public void setCurrentUser(User user) {
+        currentUser = user;
+    }
+
+
+    /**
+     * Returns the project list.
+     */
+    public ProjectList getProjectList() {
+        return projectList;
+    }
+
+
+    /**
+     * Returns a list of rivers supported by the artifact server.
+     *
+     * @return a list of rivers supported by the artifact server.
+     */
+    public River[] getRivers() {
+        return rivers;
+    }
+
+
+    protected void readRivers() {
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        GWT.log("Fetch rivers from server '" + url + "'");
+
+        riverService.list(url, locale, new AsyncCallback<River[]>() {
+            public void onFailure(Throwable caught) {
+                GWT.log("Could not recieve a list of rivers.");
+                SC.warn(MSG.getString(caught.getMessage()));
+            }
+
+            public void onSuccess(River[] newRivers) {
+                GWT.log("Retrieved " + newRivers.length + " new rivers.");
+                rivers = newRivers;
+            }
+        });
+    }
+
+
+    /**
+     * This method creates a new CollectionView and adds it to the workspace.
+     * <b>NOTE</b>The user needs to be logged in and there need to at least one
+     * river - otherwise a warning is displayed and no CollectionView is
+     * created.
+     */
+    public void newProject() {
+        if (getCurrentUser() == null) {
+            SC.warn(MSG.error_not_logged_in());
+            return;
+        }
+
+        if (getRivers() == null) {
+            SC.warn(MSG.error_no_rivers_found());
+            readRivers();
+
+            return;
+        }
+
+        CollectionView view = new CollectionView(this);
+        workspace.addView(view);
+
+        view.addCollectionChangeHandler(getProjectList());
+    }
+
+
+    public void openProject(String collectionID) {
+        if (collectionID == null) {
+            return;
+        }
+
+        GWT.log("Open existing project: " + collectionID);
+
+        Config config       = Config.getInstance();
+        final String url    = config.getServerUrl();
+        final String locale = config.getLocale();
+
+        describeCollectionService.describe(collectionID, url, locale,
+            new AsyncCallback<Collection>() {
+                public void onFailure(Throwable caught) {
+                    SC.warn(MSG.getString(caught.getMessage()));
+                }
+
+                public void onSuccess(Collection c) {
+                    final Collection collection = c;
+                    final CollectionItem   item = c.getItem(0);
+
+                    if (item == null) {
+                        SC.warn(MSG.error_load_parameterization());
+                        return;
+                    }
+
+                    getArtifactService.getArtifact(
+                        url,
+                        locale,
+                        item.identifier(),
+                        item.hash(),
+                        new AsyncCallback<Artifact>() {
+                            public void onFailure(Throwable caught) {
+                                SC.warn(MSG.getString(caught.getMessage()));
+                            }
+
+                            public void onSuccess(Artifact artifact) {
+                                CollectionView view = new CollectionView(
+                                    FLYS.this, collection, artifact);
+
+                                view.addCollectionChangeHandler(
+                                    getProjectList());
+
+                                workspace.addView(view);
+                            }
+                    });
+
+                }
+        });
+    }
+
+
+    /**
+     * Create a new Artifact.
+     */
+    public void newArtifact(String factory) {
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        artifactService.create(url, locale, factory,
+            new AsyncCallback<Artifact>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not create the new artifact.");
+                    SC.warn(MSG.getString(caught.getMessage()));
+                }
+
+                public void onSuccess(Artifact artifact) {
+                    GWT.log("Successfully created a new artifact.");
+                }
+        });
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/FLYSConstants.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,221 @@
+package de.intevation.flys.client.client;
+
+import com.google.gwt.i18n.client.ConstantsWithLookup;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface FLYSConstants extends ConstantsWithLookup {
+
+    String title();
+
+    String fullname();
+
+    String user();
+
+    String guest();
+
+    String projects();
+
+    String open_project();
+
+    String delete_project();
+
+    String manage_projects();
+
+    String logout();
+
+    String switch_language();
+
+    String info();
+
+    String warning();
+
+    String warning_language();
+
+    String no_projects();
+
+    String load_projects();
+
+    String empty_table();
+
+    String date_format();
+
+    String datetime_format();
+
+    String new_project();
+
+    String new_calculation();
+
+    String module_selection();
+
+    String river_selection();
+
+    String winfo();
+
+    String minfo();
+
+    String map();
+
+    String chart();
+
+    String fix();
+
+    String next();
+
+    String river_km();
+
+    String location_distance_state();
+
+    String location();
+
+    String distance();
+
+    String unitFrom();
+
+    String unitTo();
+
+    String unitWidth();
+
+    String unitLocation();
+
+    String wrongFormat();
+
+    String description();
+
+    String from();
+
+    String to();
+
+    String riverside();
+
+    String calcTableTitle();
+
+    String wqTitle();
+
+    String wqadaptedTitle();
+
+    String unitWNN();
+
+    String wqW();
+
+    String wqQ();
+
+    String wqSingle();
+
+    String wqRange();
+
+    String unitWSingle();
+
+    String unitWFrom();
+
+    String unitWTo();
+
+    String unitWStep();
+
+    String unitQSingle();
+
+    String unitQFrom();
+
+    String unitQTo();
+
+    String unitQStep();
+
+    String footerHome();
+
+    String footerContact();
+
+    String footerImpressum();
+
+    String buttonNext();
+
+    String imageBack();
+
+    String imageSave();
+
+    // OUTPUT TYPES
+
+    String discharge_curve();
+
+    String computed_discharge_curve();
+
+    String longitudinal_section();
+
+    String duration_curve();
+
+    String name();
+
+    String type();
+
+    String wq_value();
+
+    String discharge_longitudinal_section();
+
+    // EXPORTS
+
+    String waterlevel_export();
+
+    String discharge_longitudinal_section_export();
+
+    String computed_dischargecurve_export();
+
+    String dataexport();
+
+    String csv();
+
+    String wst();
+
+    // ERRORS
+
+    String error_create_artifact();
+
+    String error_describe_artifact();
+
+    String error_feed_data();
+
+    String error_advance_artifact();
+
+    String error_add_artifact();
+
+    String error_create_collection();
+
+    String error_describe_collection();
+
+    String error_no_rivers_found();
+
+    String error_no_such_user();
+
+    String error_not_logged_in();
+
+    String error_load_parameterization();
+
+    String error_feed_no_data();
+
+    String error_feed_from_out_of_range();
+
+    String error_feed_to_out_of_range();
+
+    String error_feed_from_bigger_to();
+
+    String error_feed_invalid_wq_mode();
+
+    String error_feed_number_format_float();
+
+    String error_feed_invalid_calculation_mode();
+
+    String error_feed_no_calculation_mode();
+
+    String error_feed_no_such_river();
+
+    String error_feed_no_river_selected();
+
+    String error_feed_no_wq_mode_selected();
+
+    String error_feed_q_values_invalid();
+
+    String error_feed_w_values_invalid();
+
+    String error_no_meta_data_found();
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/FLYSConstants.properties	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,109 @@
+title = FLYS-3.0
+fullname = Flusshydrologische Software
+user = User:
+guest = guest
+logout = Logout
+projects = Projects
+open_project = Open Project
+delete_project = Delete Project
+manage_projects = Manage Projects
+switch_language = German
+info = Info
+warning = Attention
+warning_language = Choosing a new language starts a new session. Open windows will be closed.
+no_projects = No existing calculations found.
+load_projects = Load calculations...
+empty_table = Loading data...
+date_format = yyyy-MM-dd
+datetime_format = yyyy-MM-dd HH:mm
+new_project = New Project
+new_calculation = New Calculation
+module_selection = Module
+river_selection = Rivers
+winfo = WINFO
+minfo = MINFO
+map = New Map
+chart = New Chart
+fix = TODO FIX ANALYSE TODO
+next = Next
+location_distance_state = Choose calculation location/range
+location = Location
+distance = Range
+unitFrom = km
+unitTo = km a
+unitWidth = m
+unitLocation = km
+wrongFormat = Wrong format
+description = Description
+from = From
+to = To
+riverside = Riverside
+name = Name
+type = Type
+wq_value = W/Q/D
+river_km = River-Km
+
+wqTitle = Input for W/Q Data
+wqadaptedTitle = Input for W/Q Data
+calcTableTitle = Calculated Output
+wqW = W at Gauge [cm]
+wqQ = Q [m³/s]
+wqSingle = Single values
+wqRange = Range
+unitWNN = W [NN+m]
+unitWSingle = cm
+unitWFrom = cm&nbsp;-
+unitWTo = cm&nbsp;a
+unitWStep = cm
+unitQSingle = m³/s
+unitQFrom = m³/s&nbsp;-
+unitQTo = m³/s&nbsp;a
+unitQStep = m³/s
+footerHome = Home
+footerContact = Kontakt
+footerImpressum = Impressum
+
+buttonNext = Next
+imageBack = images/back_en.png
+imageSave = images/save.gif
+
+discharge_curve = Discharge Curves at Gauges
+computed_discharge_curve = Discharge Curve
+longitudinal_section = Longitudinal Section Curve
+duration_curve = Duration Curve
+discharge_longitudinal_section = Discharge Longitudinal Section
+
+waterlevel_export = Waterlevel Export
+computed_dischargecurve_export = Discharge Curve Export
+discharge_longitudinal_section_export = Discharge Longitudinal Section Export
+dataexport = Datenexport
+csv = CSV
+wst = WST
+
+error_create_artifact = Error while creating new project.
+error_describe_artifact = Error while loading the parameterization.
+error_feed_data = Error while inserting new data.
+error_advance_artifact = Error while changing the project's state.
+error_add_artifact = Error while inserting new data.
+error_create_collection = Error while creating a new collection.
+error_describe_collection = Error while fetching the projects state.
+error_no_rivers_found = Error while reading supported rivers.
+error_no_such_user = Error - no such user found.
+error_not_logged_in = You need to log in before you are allowed to start your work.
+error_load_parameterization = Could not load the parameterization.
+
+error_feed_no_data = No input data found.
+error_feed_from_out_of_range = The lower value is bigger than the upper value.
+error_feed_to_out_of_range = The upper value is out or the valid range.
+error_feed_from_bigger_to = The lower value is bigger than the upper value.
+error_feed_invalid_wq_mode = Invalid WQ-Mode selected.
+error_feed_number_format_float = The inserted value is no floating point number.
+error_feed_invalid_calculation_mode = The chosen calculation method is invalid.
+error_feed_no_calculation_mode = No calculation method chosen.
+error_feed_no_such_river = The selected river is not provided.
+error_feed_no_river_selected = No river selected.
+error_feed_no_wq_mode_selected = No W/Q mode selected.
+error_feed_q_values_invalid = Selected Q values are outside the valid range.
+error_feed_w_values_invalid = Selected W values are outside the valid range.
+
+error_no_meta_data_found = No meta data found.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,109 @@
+title = FLYS-3.0
+fullname = Flusshydrologische Software
+user = Benutzer:
+guest = Gast
+logout = Abmelden
+projects = Projekte
+open_project = Projekt \u00d6ffnen
+delete_project = Projekt l\u00f6schen
+manage_projects = Projekte verwalten
+switch_language = Englisch
+info = Info
+warning = Achtung
+warning_language = Das \u00c4ndern der Sprache startet eine neue Sitzung des Programms. Alle offenen Fenster werden geschlossen.
+no_projects = Keine alten Berechnungen gefunden.
+load_projects = Lade Berechnungen...
+empty_table = Lade Daten...
+date_format = dd.MM.yyyy
+datetime_format = dd.MM.yyyy HH:mm
+new_project = Neues Projekt
+new_calculation = Neue Berechnung
+module_selection = Modul
+river_selection = Gewässer
+winfo = WINFO
+minfo = MINFO
+map = Neue Karte
+chart = Neues Diagramm
+fix = Fixierungsanalyse
+next = Weiter
+location_distance_state = Berechnungsort/strecke wählen
+location = Ort
+distance = Strecke
+unitFrom = km&nbsp;-
+unitTo = km a
+unitWidth = m
+unitLocation = km
+wrongFormat = Falsches Format
+description = Beschreibung
+from = von
+to = bis
+riverside = Flussseite
+name = Name
+type = Typ
+wq_value = W/Q/D
+river_km = Fluss-Km
+
+wqTitle = Eingabe für W/Q Daten
+wqadaptedTitle = Eingabe für W/Q Daten
+calcTableTitle = Berechnungsausgabe
+wqW = W am Pegel [cm]
+wqQ = Q [m³/s]
+wqSingle = Einzelwerte
+wqRange = Wertebereich
+unitWNN = W [NN+m]
+unitWSingle = cm
+unitWFrom = cm&nbsp;-
+unitWTo = cm&nbsp;a
+unitWStep = cm
+unitQSingle = m³/s
+unitQFrom = m³/s&nbsp;-
+unitQTo = m³/s&nbsp;a
+unitQStep = m³/s
+footerHome = Home
+footerContact = Kontakt
+footerImpressum = Impressum
+
+buttonNext = Ãœbernehmen
+imageBack = images/back_de.png
+imageSave = images/save.gif
+
+discharge_curve = Abflusskurven an Pegeln
+computed_discharge_curve = Abflusskurve
+longitudinal_section = L\u00e4ngsschnitt
+duration_curve = Dauerlinie
+discharge_longitudinal_section = W bei ungleichwertigem Abflussl\u00e4ngsschnitt
+
+waterlevel_export = Wasserstand/Wasserspiegellagen Export
+computed_dischargecurve_export = Abflusskurven Export
+discharge_longitudinal_section_export = Abflussl\u00e4ngsschnitt Export
+dataexport = Datenexport
+csv = CSV
+wst = WST
+
+error_create_artifact = Fehler beim Erstellen eines neuen Projekts.
+error_describe_artifact = Fehler beim Laden der Parametrisierung.
+error_feed_data = Fehler beim Hinzuf\u00fcgen neuer Daten.
+error_advance_artifact = Fehler beim Wechseln des Zustands des Projektes.
+error_add_artifact = Fehler beim Hinzuf\u00fcgen neuer Daten.
+error_create_collection = Fehler beim Erstellen einer neuen Collection.
+error_describe_collection = Fehler beim Laden des Projektzustandes.
+error_no_rivers_found = Fehler beim Lesen der unterst\u00fctzten Fl\u00fcsse.
+error_no_such_user = Fehler - Kein Benutzer vorhanden.
+error_not_logged_in = Sie m\u00fcssen sich erst einloggen um mit der Arbeit beginnen zu k\u00f6nnen.
+error_load_parameterization = Fehler beim Laden der Parametrisierung.
+
+error_feed_no_data = Keine Eingabedaten gefunden.
+error_feed_from_out_of_range = Der untere Wert liegt au\u00dferhalb des g\u00fcltigen Wertebereiches.
+error_feed_to_out_of_range = Der obere Wert liegt au\u00dferhalb des g\u00fcltigen Wertebereiches.
+error_feed_from_bigger_to = Der untere Wert ist gr\u00f6\u00dfer als der obere Wert.
+error_feed_invalid_wq_mode = Ung\u00fcltiger Modus f\u00fcr die WQ-Eingabe gew\u00e4hlt.
+error_feed_number_format_float = Der eingegebene Wert ist keine Flie\u00dfkommazahl.
+error_feed_invalid_calculation_mode = Die gew\u00e4hlte Berechnungsart ist ung\u00fcltig.
+error_feed_no_calculation_mode = Es ist keine Berechnungsart gew\u00e4hlt.
+error_feed_no_such_river = Der gew\u00e4hlte Fluss wird nicht unterst\u00fctzt.
+error_feed_no_river_selected = Es wurde kein Fluss gew\u00e4hlt.
+error_feed_no_wq_mode_selected = Es ist kein W/Q Modus gew\u00e4hlt.
+error_feed_q_values_invalid = Die gew\u00e4hlten Q Werte liegen au\u00dferhalb des g\u00fcltigen Bereiches.
+error_feed_w_values_invalid = Die gew\u00e4hlten W Werte liegen au\u00dferhalb des g\u00fcltigen Bereiches.
+
+error_no_meta_data_found = Keine Meta-Daten gefunden.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,97 @@
+title = FLYS-3.0
+fullname = Flusshydrologische Software
+user = User:
+guest = guest
+logout = Logout
+projects = Projects
+open_project = Open Project
+delete_project = Delete Project
+manage_projects = Manage Projects
+switch_language = German
+info = Info
+warning = Attention
+warning_language = Choosing a new language starts a new session. Open windows will be closed.
+no_projects = No existing calculations found.
+load_projects = Load calculations...
+empty_table = Loading data...
+date_format = yyyy-MM-dd
+datetime_format = yyyy-MM-dd HH:mm
+new_project = New Project
+new_calculation = New Calculation
+module_selection = Module
+river_selection = Rivers
+winfo = WINFO
+minfo = MINFO
+map = New Map
+chart = New Chart
+fix = TODO FIX ANALYSE TODO
+next = Next
+location_distance_state = Choose calculation location/range
+location = Location
+distance = Range
+unitFrom = km
+unitTo = km a
+unitWidth = m
+unitLocation = km
+wrongFormat = Wrong format
+description = Description
+from = From
+to = To
+riverside = Riverside
+name = Name
+type = Type
+wq_value = W/Q/D
+river_km = River-Km
+
+wqTitle = Input for W/Q Data
+wqadaptedTitle = Input for W/Q Data
+calcTableTitle = Calculated Output
+wqW = W at Gauge [cm]
+wqQ = Q [m³/s]
+wqSingle = Single values
+wqRange = Range
+untiWNN = W [NN+m]
+unitWSingle = cm
+unitWFrom = cm&nbsp;-
+unitWTo = cm&nbsp;a
+unitWStep = cm
+unitQSingle = m³/s
+unitQFrom = m³/s&nbsp;-
+unitQTo = m³/s&nbsp;a
+unitQStep = m³/s
+footerHome = Home
+footerContact = Kontakt
+footerImpressum = Impressum
+
+buttonNext = Next
+imageBack = images/back_en.png
+
+discharge_curve = Discharge Curves at Gauges
+computed_discharge_curve = Discharge Curve
+longitudinal_section = Longitudinal Section Curve
+duration_curve = Duration Curve
+waterlevel_export = Waterlevel Export
+discharge_longitudinal_section = Discharge Longitudinal Section
+
+error_create_artifact = Error while creating new project.
+error_describe_artifact = Error while loading the parameterization.
+error_feed_data = Error while inserting new data.
+error_advance_artifact = Error while changing the project's state.
+error_add_artifact = Error while inserting new data.
+error_create_collection = Error while creating a new collection.
+error_describe_collection = Error while fetching the projects state.
+error_no_rivers_found = Error while reading supported rivers.
+error_no_such_user = Error - no such user.
+error_not_logged_in = You need to log in before you are allowed to start your work.
+error_load_parameterization = Could not load the parameterization.
+
+error_feed_no_data = No input data found.
+error_feed_from_out_of_range = The lower value is bigger than the upper value.
+error_feed_to_out_of_range = The upper value is out or the valid range.
+error_feed_from_bigger_to = The lower value is bigger than the upper value.
+error_feed_invalid_wq_mode = Invalid WQ-Mode selected.
+error_feed_number_format_float = The inserted value is no floating point number.
+error_feed_invalid_calculation_mode = The chosen calculation method is invalid.
+error_feed_no_calculation_mode = No calculation method chosen.
+error_feed_no_such_river = The selected river is not provided.
+error_feed_no_river_selected = No river selected.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/FLYSImages.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,28 @@
+package de.intevation.flys.client.client;
+
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.ImageResource;
+
+
+/**
+ * This interface grants access to the images used in this application.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface FLYSImages extends ClientBundle {
+
+    @Source("images/bfg_logo.gif")
+    ImageResource logoBfg();
+
+    @Source("images/flys_logo.gif")
+    ImageResource logoFlys();
+
+    @Source("images/gewkarte.png")
+    ImageResource riverMap();
+
+    @Source("images/marker_red.png")
+    ImageResource markerRed ();
+
+    @Source("images/marker_green.png")
+    ImageResource markerGreen ();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/FLYSResources.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,21 @@
+package de.intevation.flys.client.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.TextResource;
+
+
+/**
+ * A {@link ClientBundle} that is used to handle resources in this client.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface FLYSResources extends ClientBundle {
+
+    public static final FLYSResources INSTANCE =
+        GWT.create(FLYSResources.class);
+
+        @Source("config.xml")
+        public TextResource initialConfiguration();
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/config.xml	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,3 @@
+<config>
+    <server>http://localhost:8181</server>
+</config>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/event/CollectionChangeEvent.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,29 @@
+package de.intevation.flys.client.client.event;
+
+import de.intevation.flys.client.shared.model.Collection;
+
+
+/**
+ * This events stores references to the old collection and the new one.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CollectionChangeEvent {
+
+    protected Collection oldCollection;
+    protected Collection newCollection;
+
+    public CollectionChangeEvent(Collection old, Collection newArt) {
+        oldCollection = old;
+        newCollection = newArt;
+    }
+
+    public Collection getOldValue() {
+        return oldCollection;
+    }
+
+    public Collection getNewValue() {
+        return newCollection;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/CollectionChangeHandler.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,18 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * Handler interface for CollectionChangeEvent events.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface CollectionChangeHandler {
+
+    /**
+     * Called when a CollectionChangeEvent is fired.
+     *
+     * @param event The CollectionChangeEvent.
+     */
+    public void onCollectionChange(CollectionChangeEvent event);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/HasCollectionChangeHandlers.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,16 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface HasCollectionChangeHandlers {
+
+    /**
+     * Adds a new CollectionChangeHandler.
+     *
+     * @param handler The new CollectionChangeHandler.
+     */
+    public void addCollectionChangeHandler(CollectionChangeHandler handler);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/HasParameterChangeHandler.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,16 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface HasParameterChangeHandler {
+
+    /**
+     * Adds a new ParameterChangeHandler.
+     *
+     * @param handler The new ParameterChangeHandler.
+     */
+    public void addParameterChangeHandler(ParameterChangeHandler handler);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/HasStepBackHandlers.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,13 @@
+package de.intevation.flys.client.client.event;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface HasStepBackHandlers extends Serializable {
+
+    public void addStepBackHandler(StepBackHandler handler);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/HasStepForwardHandlers.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,13 @@
+package de.intevation.flys.client.client.event;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface HasStepForwardHandlers extends Serializable {
+
+    public void addStepForwardHandler(StepForwardHandler handler);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/ParameterChangeEvent.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,29 @@
+package de.intevation.flys.client.client.event;
+
+import de.intevation.flys.client.shared.model.Artifact;
+
+
+/**
+ * This events stores references to the old artifact and the new one.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ParameterChangeEvent {
+
+    protected Artifact oldArtifact;
+    protected Artifact newArtifact;
+
+    public ParameterChangeEvent(Artifact old, Artifact newArt) {
+        oldArtifact = old;
+        newArtifact = newArt;
+    }
+
+    public Artifact getOldValue() {
+        return oldArtifact;
+    }
+
+    public Artifact getNewValue() {
+        return newArtifact;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/ParameterChangeHandler.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,18 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * Handler interface for ParameterChangeEvent events.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface ParameterChangeHandler {
+
+    /**
+     * Called when a ParameterChangeEvent is fired.
+     *
+     * @param event The ParameterChangeEvent.
+     */
+    public void onParameterChange(ParameterChangeEvent event);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/StepBackEvent.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,34 @@
+package de.intevation.flys.client.client.event;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class StepBackEvent implements Serializable {
+
+    /** The identifier of the target state.*/
+    protected String target;
+
+
+    /**
+     * Creates a new StepBackEvent with the identifier of the target state.
+     *
+     * @param target The identifier of the target state.
+     */
+    public StepBackEvent(String target) {
+        this.target = target;
+    }
+
+
+    /**
+     * A method to retrieve the target identifier.
+     *
+     * @return the target identifier.
+     */
+    public String getTarget() {
+        return target;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/StepBackHandler.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,13 @@
+package de.intevation.flys.client.client.event;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface StepBackHandler extends Serializable {
+
+    public void onStepBack(StepBackEvent event);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/StepForwardEvent.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,36 @@
+package de.intevation.flys.client.client.event;
+
+import java.io.Serializable;
+
+import de.intevation.flys.client.shared.model.Data;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class StepForwardEvent implements Serializable {
+
+    /** The selected data.*/
+    protected Data[] data;
+
+    /**
+     * Creates a new StepForwardEvent with the Data that has been selected in
+     * the UI.
+     *
+     * @param data The selected data.
+     */
+    public StepForwardEvent(Data[] data) {
+        this.data = data;
+    }
+
+
+    /**
+     * A method to retrieve the data stored in the event.
+     *
+     * @return the data.
+     */
+    public Data[] getData() {
+        return data;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/event/StepForwardHandler.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,13 @@
+package de.intevation.flys.client.client.event;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface StepForwardHandler extends Serializable {
+
+    public void onStepForward(StepForwardEvent event);
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
Binary file flys-client/src/main/java/de/intevation/flys/client/client/images/bfg_logo.gif has changed
Binary file flys-client/src/main/java/de/intevation/flys/client/client/images/flys_logo.gif has changed
Binary file flys-client/src/main/java/de/intevation/flys/client/client/images/gewkarte.png has changed
Binary file flys-client/src/main/java/de/intevation/flys/client/client/images/marker_green.png has changed
Binary file flys-client/src/main/java/de/intevation/flys/client/client/images/marker_red.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/services/AddArtifactService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,36 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.Collection;
+
+
+/**
+ * This interface describes the service to add an existing artifact to an
+ * existing collection.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("add-artifact")
+public interface AddArtifactService extends RemoteService {
+
+    /**
+     * Adds an artifact to a collection.
+     *
+     * @param collection The Collection that should be extended.
+     * @param artifact   The artifact that should be added.
+     * @param url        The url of the artifact server.
+     *
+     * @return the Collection after the operation.
+     */
+    Collection add(
+        Collection collection,
+        Artifact   artifact,
+        String     url,
+        String     locale)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/AddArtifactServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,21 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.Collection;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface AddArtifactServiceAsync {
+
+    public void add(
+        Collection collection,
+        Artifact   artifact,
+        String     url,
+        String     locale,
+        AsyncCallback<Collection> callback);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/AdvanceService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,34 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+
+/**
+ * This interface provides artifact specific operation ADVANCE.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("advance")
+public interface AdvanceService extends RemoteService {
+
+    /**
+     * This method inserts new data into the an existing artifact.
+     *
+     * @param serverUrl The url of the artifact server.
+     * @param locale The locale used for the request.
+     * @param artifact The artifact.
+     * @param target The identifier of the target state.
+     *
+     * @return the artifact which description might have been changed.
+     */
+    public Artifact advance(
+        String   serverUrl,
+        String   locale,
+        Artifact artifact,
+        String   target)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/AdvanceServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,22 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.Artifact;
+
+
+/**
+ * This interface provides artifact specific operation ADVANCE.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface AdvanceServiceAsync {
+
+    public void advance(
+        String                  serverUrl,
+        String                  locale,
+        Artifact                artifact,
+        String                  target,
+        AsyncCallback<Artifact> callback);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/ArtifactService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,30 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+
+/**
+ * This interface provides artifact specific services as CREATE, DESCRIBE, FEED,
+ * ADVANCE and OUT.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("artifact")
+public interface ArtifactService extends RemoteService {
+
+    /**
+     * This method creates a new artifact based on the given <i>factory</i>.
+     *
+     * @param serverUrl The url of the artifact server.
+     * @param locale The locale used for the request.
+     * @param factory The factory that should be used for the artifact creation.
+     *
+     * @return the new artifact.
+     */
+    public Artifact create(String serverUrl, String locale, String factory)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/ArtifactServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,22 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.Artifact;
+
+
+/**
+ * This interface provides artifact specific services as CREATE, DESCRIBE, FEED,
+ * ADVANCE and OUT.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface ArtifactServiceAsync {
+
+    public void create(
+        String serverUrl,
+        String locale,
+        String factory,
+        AsyncCallback<Artifact> callback);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/CSVExportService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,29 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import java.util.List;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+
+/**
+ * This service is used to fetch a list of DistanceInfoObjects from artifact
+ * server for a specific river.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+@RemoteServiceRelativePath("csv")
+public interface CSVExportService extends RemoteService {
+
+    /**
+     * This method returns a list of DistanceInfoObjects for a specific river.
+     */
+    List getCSV(
+        String serverUrl,
+        String locale,
+        String uuid,
+        String name)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/CSVExportServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,19 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public interface CSVExportServiceAsync {
+
+    void getCSV(
+        String url,
+        String locale,
+        String uuid,
+        String name,
+        AsyncCallback<List> cb);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/CreateCollectionService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,27 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Collection;
+
+
+/**
+ * This interface describes the service for creating new collections.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("create-collection")
+public interface CreateCollectionService extends RemoteService {
+
+    /**
+     * This method creates a new collection in the artifact server and returns
+     * the uuid of this collection.
+     *
+     * @return the uuid of the created collection.
+     */
+    Collection create(String serverUrl, String locale, String ownerId)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/CreateCollectionServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,21 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.Collection;
+
+
+/**
+ * This interface describes the service for creating new collections.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface CreateCollectionServiceAsync {
+
+    void create(
+        String serverUrl,
+        String locale,
+        String owner,
+        AsyncCallback<Collection> callback);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/DescribeCollectionService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,31 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Collection;
+
+
+/**
+ * This service describes an operation the fetches the DESCRIBE document of a
+ * specific collection and returns a Collection.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("describe-collection")
+public interface DescribeCollectionService extends RemoteService {
+
+    /**
+     * Adds an artifact to a collection.
+     *
+     * @param uuid The uuid of the desired collection.
+     * @param url  The url of the artifact server.
+     * @param locale The name of the locale used for the request.
+     *
+     * @return the Collection after the operation.
+     */
+    Collection describe(String uuid, String url, String locale)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/DescribeCollectionServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,19 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.Collection;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface DescribeCollectionServiceAsync {
+
+    public void describe(
+        String uuid,
+        String url,
+        String locale,
+        AsyncCallback<Collection> callback);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/DistanceInfoService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,27 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.DistanceInfoObject;
+
+/**
+ * This service is used to fetch a list of DistanceInfoObjects from artifact
+ * server for a specific river.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("distanceinfo")
+public interface DistanceInfoService extends RemoteService {
+
+    /**
+     * This method returns a list of DistanceInfoObjects for a specific river.
+     */
+    DistanceInfoObject[] getDistanceInfo(
+        String serverUrl,
+        String locale,
+        String river)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/DistanceInfoServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,19 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.DistanceInfoObject;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface DistanceInfoServiceAsync {
+
+    void getDistanceInfo(
+        String url,
+        String locale,
+        String river,
+        AsyncCallback<DistanceInfoObject[]> cb);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/GetArtifactService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,31 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+
+/**
+ * This interface provides a method to retrieve an artifact based on its uuid.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("getartifact")
+public interface GetArtifactService extends RemoteService {
+
+    /**
+     * This method inserts new data into the an existing artifact.
+     *
+     * @param url The url of the artifact server.
+     * @param locale The string representation of a locale that is used for the
+     * request.
+     * @param uuid The artifact's identifier.
+     * @param hash The artifact's hash.
+     *
+     * @return the artifact.
+     */
+    Artifact getArtifact(String url, String locale, String uuid, String hash)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/GetArtifactServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,20 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.Artifact;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface GetArtifactServiceAsync {
+
+    public void getArtifact(
+        String                  serverUrl,
+        String                  locale,
+        String                  uuid,
+        String                  hash,
+        AsyncCallback<Artifact> callback);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/MetaDataService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,18 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+
+import de.intevation.flys.client.shared.model.DataCageTree;
+
+@RemoteServiceRelativePath("meta-data")
+public interface MetaDataService extends RemoteService
+{
+    DataCageTree getMetaData(String serverUrl, String locale, String name)
+        throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/MetaDataServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,16 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.DataCageTree;
+
+public interface MetaDataServiceAsync {
+    void getMetaData(
+        String              serverUrl,
+        String              locale,
+        String              name, 
+        AsyncCallback<DataCageTree> cb);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/RiverService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,29 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.River;
+
+/**
+ * This interface provides a method to list the supported rivers of the artifact
+ * server.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("rivers")
+public interface RiverService extends RemoteService {
+
+    /**
+     * This method returns a list of rivers provided by the artifact server.
+     *
+     * @param serverUrl The url of the artifact server.
+     * @param locale The locale used for the request.
+     *
+     * @return a list of rivers provided by the artifact server.
+     */
+    public River[] list(String serverUrl, String locale)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/RiverServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,21 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.River;
+
+
+/**
+ * This interface provides a method to list the supported rivers of the artifact
+ * server.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface RiverServiceAsync {
+
+    public void list(
+        String serverUrl,
+        String locale,
+        AsyncCallback<River[]> callback);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/StepForwardService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,35 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.Data;
+
+/**
+ * This interface provides artifact specific operation FEED.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("forward")
+public interface StepForwardService extends RemoteService {
+
+    /**
+     * This method inserts new data into the an existing artifact.
+     *
+     * @param serverUrl The url of the artifact server.
+     * @param locale The locale used for the request.
+     * @param artifact The artifact.
+     * @param data The data that should be inserted.
+     *
+     * @return the artifact which description might have been changed.
+     */
+    public Artifact go(
+        String   serverUrl,
+        String   locale,
+        Artifact artifact,
+        Data[]   data)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/StepForwardServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,23 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.Data;
+
+
+/**
+ * This interface provides artifact specific operation FEED.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface StepForwardServiceAsync {
+
+    public void go(
+        String                  serverUrl,
+        String                  locale,
+        Artifact                artifact,
+        Data[]                  data,
+        AsyncCallback<Artifact> callback);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/UserCollectionsService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,32 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.model.Collection;
+
+
+/**
+ * This interface describes a method that retrieves a list of Collections owned
+ * by a specified user.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("user-collections")
+public interface UserCollectionsService extends RemoteService {
+
+    /**
+     * This method retrieves the user that is currently logged in.
+     *
+     * @param serverUrl The url of the artifact server.
+     * @param locale The locale used for the request object.
+     * @param userid The identifier of the owner.
+     *
+     * @return the current {@link User}.
+     */
+    Collection[] getUserCollections(
+        String serverUrl,
+        String locale,
+        String userid);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/UserCollectionsServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,22 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.Collection;
+
+
+/**
+ * This interface describes a method that retrieves a list of Collections owned
+ * by a specified user.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface UserCollectionsServiceAsync {
+
+    void getUserCollections(
+        String serverUrl,
+        String locale,
+        String userid,
+        AsyncCallback<Collection[]> callback);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/UserService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,28 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.AuthenticationException;
+import de.intevation.flys.client.shared.model.User;
+
+
+/**
+ * This interface describes services for the user.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("user")
+public interface UserService extends RemoteService {
+
+    /**
+     * This method retrieves the user that is currently logged in.
+     *
+     * @param serverUrl The url of the artifact server.
+     *
+     * @return the current {@link User}.
+     */
+    User getCurrentUser(String serverUrl, String locale)
+    throws AuthenticationException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/UserServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,20 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.User;
+
+
+/**
+ * This interface describes services for the user.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface UserServiceAsync {
+
+    void getCurrentUser(
+        String serverUrl,
+        String locale,
+        AsyncCallback<User> callback);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/WQInfoService.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,29 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.WQInfoObject;
+
+/**
+ * This service is used to fetch a list of DistanceInfoObjects from artifact
+ * server for a specific river.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+@RemoteServiceRelativePath("mainvalues")
+public interface WQInfoService extends RemoteService {
+
+    /**
+     * This method returns a list of DistanceInfoObjects for a specific river.
+     */
+    WQInfoObject[] getWQInfo(
+        String serverUrl,
+        String locale,
+        String river,
+        double start,
+        double end)
+    throws ServerException;
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/services/WQInfoServiceAsync.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,21 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.WQInfoObject;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public interface WQInfoServiceAsync {
+
+    void getWQInfo(
+        String url,
+        String locale,
+        String river,
+        double start,
+        double end,
+        AsyncCallback<WQInfoObject[]> cb);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,238 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Img;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.events.ClickHandler;
+import com.smartgwt.client.widgets.Button;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.event.HasStepBackHandlers;
+import de.intevation.flys.client.client.event.HasStepForwardHandlers;
+import de.intevation.flys.client.client.event.StepBackEvent;
+import de.intevation.flys.client.client.event.StepBackHandler;
+import de.intevation.flys.client.client.event.StepForwardEvent;
+import de.intevation.flys.client.client.event.StepForwardHandler;
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.Artifact;
+
+/**
+ * An abstract UIProvider that provides some basic methods.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class AbstractUIProvider
+implements   UIProvider, HasStepForwardHandlers, ClickHandler,
+             HasStepBackHandlers
+{
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+
+    /** The StepForwardHandlers.*/
+    protected List<StepForwardHandler> forwardHandlers;
+
+    /** The StepForwardHandlers.*/
+    protected List<StepBackHandler> backHandlers;
+
+    /** The container that is used to position helper widgets.*/
+    protected Canvas helperContainer;
+
+    /** The artifact that contains status information.*/
+    protected Artifact artifact;
+
+    /**
+     * Creates a new UIProvider instance of this class.
+     */
+    public AbstractUIProvider() {
+        forwardHandlers = new ArrayList<StepForwardHandler>();
+        backHandlers    = new ArrayList<StepBackHandler>();
+    }
+
+
+    /**
+     * Appends a StepBackHandler that wants to listen to StepBackEvents.
+     *
+     * @param handler A new StepBackHandler.
+     */
+    public void addStepBackHandler(StepBackHandler handler) {
+        if (handler != null) {
+            backHandlers.add(handler);
+        }
+    }
+
+
+    /**
+     * Appends a StepForwardHandler that wants to listen to StepForwardEvents.
+     *
+     * @param handler A new StepForwardHandler.
+     */
+    public void addStepForwardHandler(StepForwardHandler handler) {
+        if (handler != null) {
+            forwardHandlers.add(handler);
+        }
+    }
+
+
+    /**
+     * This method is called after the user has clicked one of the buttons to
+     * step back to a previous state.
+     *
+     * @param e The StepBackEvent.
+     */
+    protected void fireStepBackEvent(StepBackEvent e) {
+        GWT.log("AbstractUIProvider - fireStepBackEvent() handlers: " + backHandlers.size());
+        for (StepBackHandler handler: backHandlers) {
+            handler.onStepBack(e);
+        }
+    }
+
+
+    /**
+     * This method is called after the user has clicked on the 'next' button to
+     * step to the next state.
+     *
+     * @param e The StepForwardEvent.
+     */
+    protected void fireStepForwardEvent(StepForwardEvent e) {
+        GWT.log("AbstractUIProvider - fireStepForwardEvent() handlers: " + forwardHandlers.size());
+        for (StepForwardHandler handler: forwardHandlers) {
+            handler.onStepForward(e);
+        }
+    }
+
+
+    /**
+     * This method is used to listen to click events on the 'next' button. The
+     * fireStepForwardEvent() method is called here.
+     *
+     * @param e The click event.
+     */
+    public void onClick(ClickEvent e) {
+        Data[] data = getData();
+
+        fireStepForwardEvent(new StepForwardEvent(data));
+    }
+
+
+    /**
+     * Creates the 'next' button to step forward to the next state.
+     *
+     * @return the 'next' button.
+     */
+    protected Canvas getNextButton() {
+        Button next = new Button(MSG.buttonNext());
+        next.addClickHandler(this);
+
+        return next;
+    }
+
+
+    /**
+     * Creates the 'back' button to step back to a previous state.
+     *
+     * @param targetState The identifier of the target state.
+     *
+     * @return the 'back' button.
+     */
+    protected Canvas getBackButton(final String targetState) {
+        String url = GWT.getHostPageBaseURL() + MSG.imageBack();
+        Img back   = new Img(url, 16, 16);
+
+        back.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                fireStepBackEvent(new StepBackEvent(targetState));
+            }
+        });
+
+        return back;
+    }
+
+
+    /**
+     * This method injects a container that is used to position helper widgets.
+     *
+     * @param helperContainer A container that is used to position helper
+     * widgets.
+     */
+    public void setContainer(Canvas helperContainer) {
+        this.helperContainer = helperContainer;
+    }
+
+
+    /**
+     * This method injects an artifact that contains the status information.
+     *
+     * @param art An artifact containing status information.
+     */
+    public void setArtifact(Artifact art) {
+        this.artifact = art;
+    }
+
+
+    /**
+     * This method greps the Data with name <i>name</i> from the list and
+     * returns it.
+     *
+     * @param items A list of Data.
+     * @param name The name of the Data that we are searching for.
+     *
+     * @return the Data with the name <i>name</i>.
+     */
+    protected Data getData(List<Data> data, String name) {
+        for (Data d: data) {
+            if (name.equals(d.getLabel())) {
+                return d;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * This method greps the DataItem with name <i>name</i> from the list and
+     * returns it.
+     *
+     * @param items A list of DataItems.
+     * @param name The name of the DataItem that we are searching for.
+     *
+     * @return the DataItem with the name <i>name</i>.
+     */
+    protected DataItem getDataItem(DataItem[] items, String name) {
+        for (DataItem item: items) {
+            if (name.equals(item.getLabel())) {
+                return item;
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * This method needs to be implemented by concrete subclasses. It should
+     * create a new Canvas object with a representation of <i>data</i>.
+     *
+     * @param data The data that should be displayed.
+     *
+     * @return a Canvas object that displays <i>data</i>.
+     */
+    public abstract Canvas create(DataList data);
+
+
+    /**
+     * This method needs to be implemented by concrete subclasses. It should
+     * return the selected data.
+     *
+     * @return the selected data.
+     */
+    protected abstract Data[] getData();
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,213 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.Date;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.Alignment;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Img;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.IButton;
+
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.events.ClickHandler;
+import com.smartgwt.client.widgets.events.CloseClickHandler;
+import com.smartgwt.client.widgets.events.CloseClientEvent;
+import com.smartgwt.client.widgets.events.ResizedEvent;
+import com.smartgwt.client.widgets.events.ResizedHandler;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.OutputMode;
+import de.intevation.flys.client.client.Config;
+
+import de.intevation.flys.client.shared.model.Artifact;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartOutputTab extends OutputTab implements ResizedHandler {
+
+    public static final int DEFAULT_CHART_WIDTH  = 600;
+    public static final int DEFAULT_CHART_HEIGHT = 500;
+
+    /** The canvas that wraps the chart toolbar.*/
+    protected Canvas tbarPanel;
+
+    /** The canvas that wraps the theme editor.*/
+    protected Canvas left;
+
+    /** The canvas that wraps the chart.*/
+    protected Canvas right;
+
+
+    /**
+     * The default constructor to create a new ChartOutputTab.
+     *
+     * @param title The title of this tab.
+     * @param collection The Collection which this chart belongs to.
+     * @param mode The OutputMode.
+     */
+    public ChartOutputTab(
+        String         title, 
+        Collection     collection,
+        OutputMode     mode,
+        CollectionView collectionView
+    ){
+        super(title, collection, mode);
+
+        tbarPanel = new Canvas();
+        left      = new Canvas();
+        right     = new Canvas();
+
+        tbarPanel.setBorder("1px solid black");
+        tbarPanel.setHeight(30);
+        left.setBorder("1px solid black");
+        left.setWidth("25%");
+        right.setWidth("*");
+
+        VLayout vLayout = new VLayout();
+        vLayout.setMembersMargin(2);
+
+        HLayout hLayout = new HLayout();
+        hLayout.setWidth100();
+        hLayout.setHeight100();
+        hLayout.setMembersMargin(10);
+
+        hLayout.addMember(left);
+        hLayout.addMember(right);
+
+        right.addChild(createChartPanel());
+        left.addChild(createThemeControlPanel());
+        tbarPanel.addChild(
+            createTBarPanel(collectionView));
+
+        vLayout.addMember(tbarPanel);
+        vLayout.addMember(hLayout);
+
+        setPane(vLayout);
+
+        right.addResizedHandler(this);
+    }
+
+
+    /**
+     * This method is called after the chart panel has resized. It removes the
+     * chart - if existing - and requests a new one with adjusted size.
+     *
+     * @param event The resize event.
+     */
+    public void onResized(ResizedEvent event) {
+        Canvas[] children = right.getChildren();
+        for (Canvas child: children) {
+            right.removeChild(child);
+        }
+
+        right.addChild(createChartPanel(right.getWidth(), right.getHeight()));
+    }
+
+
+    protected Canvas createTBarPanel(
+        final CollectionView collectionView
+    ) {
+        HLayout hLayout = new HLayout();
+        hLayout.setWidth100();
+        hLayout.setHeight100();
+        hLayout.setMembersMargin(10);
+
+        Label datacage = new Label("Datenkorb");
+        datacage.setHeight(20);
+        datacage.setBackgroundColor("#BED730");
+        datacage.setBorder("1px solid black");
+        datacage.setAlign(Alignment.CENTER);
+
+        hLayout.setMargin(5);
+        hLayout.addMember(datacage);
+        datacage.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                GWT.log("Clicked 'datacage' button.");
+                openDatacageWindow(
+                    collectionView.getArtifact());
+            }
+        });
+
+        return hLayout;
+    }
+
+    protected void openDatacageWindow(Artifact artifact) {
+        final DatacageWindow dc = new DatacageWindow(artifact);
+        dc.addCloseClickHandler(new CloseClickHandler() {
+            public void onCloseClick(CloseClientEvent event) {
+                dc.destroy();
+            }
+        });
+        dc.show();
+    }
+
+
+    protected Canvas createChartPanel() {
+        return createChartPanel(DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT);
+    }
+
+
+    protected Canvas createChartPanel(int width, int height) {
+        Img chart  = getChartImg(width, height);
+        chart.setWidth100();
+        chart.setHeight100();
+
+        return chart;
+
+    }
+
+
+    protected Canvas createThemeControlPanel() {
+        Label label = new Label("Themensteuerung");
+        label.setHeight(25);
+        label.setMargin(5);
+
+        return label;
+    }
+
+
+    /**
+     * Builds the chart image and returns it.
+     *
+     * @param width The chart width.
+     * @param height The chart height.
+     *
+     * @return the chart image.
+     */
+    protected Img getChartImg(int width, int height) {
+        return new Img(getImgUrl(width, height));
+    }
+
+
+    /**
+     * Builds the URL that points to the chart image.
+     *
+     * @param width The width of the requested chart.
+     * @param height The height of the requested chart.
+     *
+     * @return the URL to the chart image.
+     */
+    protected String getImgUrl(int width, int height) {
+        Config config = Config.getInstance();
+
+        String imgUrl = GWT.getModuleBaseURL();
+        imgUrl += "chart";
+        imgUrl += "?uuid=" + collection.identifier();
+        imgUrl += "&type=" + mode.getName();
+        imgUrl += "&server=" + config.getServerUrl();
+        imgUrl += "&locale=" + config.getLocale();
+        imgUrl += "&timestamp=" + new Date().getTime();
+        imgUrl += "&width=" + Integer.toString(width);
+        imgUrl += "&height=" + Integer.toString(height);
+
+        return imgUrl;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/CollectionView.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,423 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Window;
+import com.smartgwt.client.widgets.layout.Layout;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.tab.TabSet;
+
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.ArtifactDescription;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.ExportMode;
+import de.intevation.flys.client.shared.model.OutputMode;
+import de.intevation.flys.client.shared.model.User;
+
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.FLYS;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.event.HasCollectionChangeHandlers;
+import de.intevation.flys.client.client.event.CollectionChangeEvent;
+import de.intevation.flys.client.client.event.CollectionChangeHandler;
+import de.intevation.flys.client.client.event.ParameterChangeEvent;
+import de.intevation.flys.client.client.event.ParameterChangeHandler;
+import de.intevation.flys.client.client.services.AddArtifactService;
+import de.intevation.flys.client.client.services.AddArtifactServiceAsync;
+import de.intevation.flys.client.client.services.CreateCollectionService;
+import de.intevation.flys.client.client.services.CreateCollectionServiceAsync;
+import de.intevation.flys.client.client.services.DescribeCollectionService;
+import de.intevation.flys.client.client.services.DescribeCollectionServiceAsync;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CollectionView
+extends      Window
+implements   CollectionChangeHandler, HasCollectionChangeHandlers,
+             ParameterChangeHandler
+{
+    /** The ArtifactService used to communicate with the Artifact server. */
+    protected CreateCollectionServiceAsync createCollectionService =
+        GWT.create(CreateCollectionService.class);
+
+    /** The AddArtifactService used to add an artifact to a collection. */
+    protected AddArtifactServiceAsync addArtifactService =
+        GWT.create(AddArtifactService.class);
+
+    /** The DescribeCollectionService used to update the existing collection. */
+    protected DescribeCollectionServiceAsync describeCollectionService =
+        GWT.create(DescribeCollectionService.class);
+
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants messages = GWT.create(FLYSConstants.class);
+
+    /** The FLYS instance used to call services.*/
+    protected FLYS flys;
+
+    /** The ParameterList.*/
+    protected ParameterList parameterList;
+
+    /** The list of ValueChangeHandlers.*/
+    protected List<CollectionChangeHandler> handlers;
+
+    /** The collection to be displayed.*/
+    protected Collection collection;
+
+    /** The artifact that handles the parameterization.*/
+    protected Artifact artifact;
+
+    protected TabSet tabs;
+
+    /** The output tab.*/
+    protected Map<String, OutputTab> outputTabs;
+
+    /** The layout.*/
+    protected Layout layout;
+
+
+    /**
+     * This constructor creates a new CollectionView that is used to display the
+     * <i>collection</i>.
+     *
+     * @param collection The collection to be displayed.
+     */
+    public CollectionView(FLYS flys) {
+        this.flys          = flys;
+        this.tabs          = new TabSet();
+        this.outputTabs    = new HashMap<String, OutputTab>();
+        this.handlers      = new ArrayList<CollectionChangeHandler>();
+        this.layout        = new VLayout();
+        this.parameterList = new ParameterList(
+            flys, this, messages.new_project());
+
+        addCollectionChangeHandler(this);
+        addCollectionChangeHandler(parameterList);
+
+        parameterList.addParameterChangeHandler(this);
+
+        init();
+    }
+
+
+    public CollectionView(FLYS flys, Collection collection, Artifact artifact) {
+        this.flys          = flys;
+        this.tabs          = new TabSet();
+        this.outputTabs    = new HashMap<String, OutputTab>();
+        this.handlers      = new ArrayList<CollectionChangeHandler>();
+        this.layout        = new VLayout();
+        this.parameterList = new ParameterList(
+            flys,
+            this,
+            messages.getString(artifact.getName()),
+            artifact);
+
+        addCollectionChangeHandler(this);
+        addCollectionChangeHandler(parameterList);
+
+        parameterList.addParameterChangeHandler(this);
+
+        init();
+
+        setCollection(collection);
+        setArtifact(artifact);
+    }
+
+
+    /**
+     * This method handles the initial layout stuff.
+     */
+    protected void init() {
+        setWidth(750);
+        setHeight(600);
+
+        layout.setWidth100();
+
+        setCanDragReposition(true);
+        setCanDragResize(true);
+        setShowMaximizeButton(true);
+        setKeepInParentRect(true);
+
+        setTitle("");
+
+        addItem(layout);
+
+        layout.addMember(tabs);
+        tabs.addTab(parameterList);
+    }
+
+
+    /**
+     * This method triggers the CreateCollectionService to create a new
+     * collection in the artifact server.
+     *
+     * @param ownerId The uuid of the user that should own the new collection.
+     */
+    protected void createNewCollection(String ownerId) {
+        GWT.log("CollectionView.createNewCollection");
+
+        Config config       = Config.getInstance();
+        final String url    = config.getServerUrl();
+        final String locale = config.getLocale();
+
+        createCollectionService.create(
+            url, locale, ownerId,
+            new AsyncCallback<Collection>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not create the new collection.");
+                    SC.warn(messages.getString(caught.getMessage()));
+                }
+
+                public void onSuccess(Collection collection) {
+                    GWT.log("Successfully created a new collection.");
+
+                    Artifact artifact = getArtifact();
+                    addArtifactService.add(collection, artifact, url, locale,
+                        new AsyncCallback<Collection>() {
+
+                            public void onFailure(Throwable caught) {
+                                GWT.log("An error occured while adding artifact.");
+                                SC.warn(messages.getString(caught.getMessage()));
+                            }
+
+                            public void onSuccess(Collection newCollection) {
+                                GWT.log("Successfully added artifact.");
+                                setCollection(newCollection);
+                            }
+                        }
+                    );
+                }
+            });
+    }
+
+
+    protected FLYS getFlys() {
+        return flys;
+    }
+
+
+
+    /**
+     * This method registers a new ValueChangeHandler.
+     *
+     * @param handler The new ValueChangeHandler.
+     */
+    public void addCollectionChangeHandler(CollectionChangeHandler handler) {
+        if (handler != null) {
+            handlers.add(handler);
+        }
+    }
+
+
+
+    /**
+     * This method calls the <code>onValueChange()</code> method of all
+     * registered ValueChangeHanders.
+     */
+    protected void fireCollectionChangeEvent(
+        Collection old, Collection newCol)
+    {
+        for (CollectionChangeHandler handler: handlers) {
+            handler.onCollectionChange(new CollectionChangeEvent(old, newCol));
+        }
+    }
+
+
+    /**
+     * This method returns true, if the Collection is new and no plugins has
+     * been chosen.
+     *
+     * @return true, if the Collection is new.
+     */
+    public boolean isNew() {
+        return collection.getItemLength() == 0 ? true : false;
+    }
+
+
+    /**
+     * Returns the artifact that is used for the parameterization.
+     *
+     * @return the artifact that is used for the parameterization.
+     */
+    protected Artifact getArtifact() {
+        return artifact;
+    }
+
+
+    /**
+     * Set the current artifact that is the master of the parameterization.
+     *
+     * @param artifact The new artifact.
+     */
+    protected void setArtifact(Artifact artifact) {
+        this.artifact = artifact;
+    }
+
+
+    /**
+     * Implements the onCollectionChange() method to do update the GUI after the
+     * parameterization has changed.
+     *
+     * @param event The ParameterChangeEvent.
+     */
+    public void onParameterChange(ParameterChangeEvent event) {
+        GWT.log("CollectionView.onParameterChange");
+
+        Artifact art             = event.getNewValue();
+        ArtifactDescription desc = art.getArtifactDescription();
+        OutputMode[] outs        = desc.getOutputModes();
+
+        setArtifact(art);
+        clearOutputTabs();
+
+        if (parameterList.hasPanel()) {
+            parameterList.removePanel();
+        }
+        Collection c = getCollection();
+
+        if (outs != null && c == null) {
+            User user = getFlys().getCurrentUser();
+            createNewCollection(user.identifier());
+        }
+        else if (outs != null && c != null) {
+            Config config = Config.getInstance();
+            String url    = config.getServerUrl();
+            String locale = config.getLocale();
+
+            describeCollectionService.describe(c.identifier(), url, locale,
+                new AsyncCallback<Collection>() {
+                    public void onFailure(Throwable caught) {
+                        GWT.log("Could not DESCRIBE collection.");
+                        SC.warn(messages.getString(caught.getMessage()));
+                    }
+
+
+                    public void onSuccess(Collection newCollection) {
+                        GWT.log("Successfully DESCRIBED collection.");
+                        setCollection(newCollection);
+                    }
+                }
+            );
+        }
+        else {
+            updateView();
+        }
+    }
+
+
+    /**
+     * Returns the collection of displayed by this view.
+     *
+     * @return the collection of this view.
+     */
+    protected Collection getCollection() {
+        return collection;
+    }
+
+
+    /**
+     * Set the current collection.
+     *
+     * @param collection The new collection.
+     */
+    protected void setCollection(Collection collection) {
+        Collection tmp  = this.collection;
+        this.collection = collection;
+
+        fireCollectionChangeEvent(tmp, this.collection);
+    }
+
+
+    public void onCollectionChange(CollectionChangeEvent event) {
+        Collection newCol = event.getNewValue();
+
+        Map<String, OutputMode> outs = newCol.getOutputModes();
+
+        Set<String> keys = outs.keySet();
+        for (String outname: keys) {
+            addOutputTab(outname, outs.get(outname));
+        }
+
+        updateView();
+    }
+
+
+    /**
+     * Adds a new tab for the OutputMode <i>out</i>.
+     *
+     * @param name The name and title of the output.
+     */
+    protected void addOutputTab(String name, OutputMode out) {
+        if (out instanceof ExportMode) {
+            ExportMode export = (ExportMode) out;
+
+            if (export.getFacet("csv") != null) {
+                TableDataPanel p = new TableDataPanel();
+                p.setUuid(collection.identifier());
+                p.setName(out.getName());
+                parameterList.setPanel (p);
+            }
+
+            return;
+        }
+
+        GWT.log("Add new output tab for '" + name + "'");
+
+        // TODO Source this out to a factory that creates the different
+        // OutputTabs.
+        String title = messages.getString(name);
+        OutputTab tab = new ChartOutputTab(
+            title, getCollection(), out, this);
+        outputTabs.put(name, tab);
+    }
+
+
+    /**
+     * Removes all output mode tabs from tab bar.
+     */
+    protected void clearOutputTabs() {
+        GWT.log("Clear OutputTabs.");
+
+        int num = tabs.getNumTabs();
+
+        for (int i = num-1; i >= 1; i--) {
+            tabs.removeTab(i);
+        }
+
+        outputTabs.clear();
+    }
+
+
+    /**
+     * Update the view (refresh the list of old and current data).
+     */
+    protected void updateView() {
+        GWT.log("CollectionView.updateView()");
+        updateOutputTabs();
+    }
+
+
+    /**
+     * This method is used to update the tabs to show specific output modes.
+     */
+    protected void updateOutputTabs() {
+        GWT.log("Update output tabs.");
+        if (outputTabs != null) {
+            Set<String> keys = outputTabs.keySet();
+
+            for (String key: keys) {
+                tabs.addTab(outputTabs.get(key));
+            }
+        }
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,180 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import com.google.gwt.core.client.GWT;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.util.SC;
+
+import com.smartgwt.client.widgets.Window;
+
+import com.smartgwt.client.widgets.tree.Tree;
+import com.smartgwt.client.widgets.tree.TreeNode;
+import com.smartgwt.client.widgets.tree.TreeGrid;
+
+import com.smartgwt.client.widgets.layout.Layout;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.ArtifactDescription;
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.DataItem;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.Config;
+
+import de.intevation.flys.client.client.services.MetaDataService;
+import de.intevation.flys.client.client.services.MetaDataServiceAsync;
+
+import de.intevation.flys.client.shared.model.DataCageTree;
+import de.intevation.flys.client.shared.model.DataCageNode;
+
+public class DatacageWindow
+extends      Window
+{
+    protected MetaDataServiceAsync metaDataService =
+        GWT.create(MetaDataService.class);
+
+    protected FLYSConstants messages =
+        GWT.create(FLYSConstants.class);
+
+
+    protected Artifact artifact;
+
+    protected TreeGrid treeGrid;
+
+    protected Layout layout;
+
+
+    public DatacageWindow(Artifact artifact) {
+        this.artifact = artifact;
+
+        setWidth(200);
+        setHeight(300);
+
+        layout = new VLayout();
+        layout.setWidth100();
+
+        addItem(layout);
+
+        treeGrid = new TreeGrid();  
+        treeGrid.setLoadDataOnDemand(false);          
+        treeGrid.setWidth100();  
+        treeGrid.setHeight100();
+
+        layout.addMember(treeGrid);
+
+        String river =  findRiver();
+        setTitle("Datenkorb: " + river);  
+        setShowMinimizeButton(false);  
+        setIsModal(true);  
+        setShowModalMask(true);  
+        setCanDragResize(true);
+
+        centerInPage();
+
+        triggerTreeBuilding(river);
+    }
+
+    protected void triggerTreeBuilding(String river) {
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        metaDataService.getMetaData(
+            url, locale, river,
+            new AsyncCallback<DataCageTree>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not load meta data.");
+                    SC.warn(messages.getString(caught.getMessage()));
+                }
+
+                public void onSuccess(DataCageTree tree) {
+                    GWT.log("Successfully loaded meta data.");
+                    buildTree(tree);
+                }
+            });
+    }
+
+    protected void buildTree(DataCageTree tree) {
+        treeGrid.setData(convert(tree));
+    }
+
+    protected static TreeNode convert(DataCageNode node) {
+        List<DataCageNode> children = node.getChildren();
+
+        TreeNode [] cs;
+
+        if (children != null) {
+            ArrayList<TreeNode> nc = new ArrayList<TreeNode>(children.size());
+
+            for (DataCageNode dcn: children) {
+                nc.add(convert(dcn));
+            }
+
+            cs = nc.toArray(new TreeNode[nc.size()]);
+        }
+        else {
+            cs = new TreeNode[0];
+        }
+
+        return new TreeNode(node.getName(), cs);
+    }
+
+    protected static Tree convert(DataCageTree tree) {
+        DataCageNode root = tree.getRoot();
+
+        List<DataCageNode> children = root.getChildren();
+
+        TreeNode [] cs;
+
+        if (children != null) {
+            ArrayList<TreeNode> nc = new ArrayList<TreeNode>(children.size());
+
+            if (nc != null) {
+                for (DataCageNode dcn: children) {
+                    nc.add(convert(dcn));
+                }
+            }
+            cs = nc.toArray(new TreeNode[nc.size()]);
+        }
+        else {
+            cs = new TreeNode[0];
+        }
+
+        TreeNode rn = new TreeNode(root.getName(), cs);
+
+        Tree r = new Tree();
+        r.setRoot(rn);
+
+        return r;
+    }
+
+    protected String findRiver() {
+
+        ArtifactDescription adescr = artifact.getArtifactDescription();
+        DataList [] data = adescr.getOldData();
+
+        if (data != null && data.length > 0) {
+            for (int i = 0; i < data.length; i++) {
+                DataList dl = data[i];
+                if (dl.getState().equals("state.winfo.river")) {
+                    for (int j = dl.size()-1; j >= 0; --j) {
+                        Data d = dl.get(j);
+                        DataItem [] di = d.getItems();
+                        if (di != null && di.length == 1) {
+                           return d.getItems()[0].getStringValue();
+                        }
+                    }
+                }
+            }
+        }
+
+        return "";
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,217 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.Map;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.NumberFormat;
+
+import com.smartgwt.client.types.TitleOrientation;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.form.fields.StaticTextItem;
+import com.smartgwt.client.widgets.form.fields.TextItem;
+import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
+
+import de.intevation.flys.client.client.FLYSConstants;
+
+public class DoubleArrayPanel
+extends      DynamicForm
+{
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);
+
+    protected TextItem ti;
+
+    /** The constant input field name.*/
+    public static final String FIELD_NAME = "doublearray";
+
+
+    public DoubleArrayPanel(
+        String title,
+        double[] values,
+        BlurHandler handler)
+    {
+        this(title, values, handler, TitleOrientation.RIGHT);
+    }
+
+
+    /**
+     * Creates a new form with a single input field that displays an array of
+     * double values.
+     *
+     * @param name The name of the TextItem.
+     * @param title The title of the TextItem.
+     * @param values The double values that should be displayed initially.
+     * @param handler The BlurHandler that is used to valide the input.
+     */
+    public DoubleArrayPanel(
+        String title,
+        double[] values,
+        BlurHandler handler,
+        TitleOrientation titleOrientation)
+    {
+        ti                 = new TextItem(FIELD_NAME);
+        StaticTextItem sti = new StaticTextItem("staticarray");
+
+        ti.setShowTitle(false);
+        sti.setShowTitle(false);
+        sti.setValue(title);
+
+        ti.addBlurHandler(handler);
+
+        if (titleOrientation == TitleOrientation.RIGHT) {
+            setFields(ti, sti);
+        }
+        else {
+            setFields(sti, ti);
+        }
+
+        setTitleOrientation(titleOrientation);
+        setNumCols(2);
+
+        if (values == null) {
+            return;
+        }
+
+        NumberFormat f = NumberFormat.getDecimalFormat();
+
+        StringBuilder text = new StringBuilder();
+        boolean firstItem  = true;
+
+        for (double val: values) {
+            if (!firstItem) {
+                text.append(" ");
+            }
+
+            text.append(f.format(val));
+
+            firstItem = false;
+        }
+
+        ti.setValue(text.toString());
+    }
+
+
+    /**
+     * This method takes the double array to set the values to the textbox.
+     *
+     * @param values The double values.
+     */
+    public void setValues(double[] values) {
+        NumberFormat f = NumberFormat.getDecimalFormat();
+
+        StringBuilder text = new StringBuilder();
+        boolean firstItem  = true;
+        if (values != null) {
+            for (double val: values) {
+                if (!firstItem) {
+                    text.append(" ");
+                }
+
+                text.append(f.format(val));
+
+                firstItem = false;
+            }
+        }
+
+        ti.clearValue();
+        ti.setValue(text.toString());
+    }
+
+
+    /**
+     * This method validates the entered text in the location input field. If
+     * there are values that doesn't represent a valid location, an error is
+     * displayed.
+     *
+     * @param item The FormItem.
+     */
+    protected boolean validateForm(FormItem item) {
+        if (item instanceof StaticTextItem) {
+            return true;
+        }
+
+        boolean  valid = true;
+        String   value = (String) item.getValue();
+
+        if (value == null) {
+            return valid;
+        }
+
+        String[] parts = value.split(" ");
+
+        if (parts == null) {
+            return valid;
+        }
+
+        NumberFormat f = NumberFormat.getDecimalFormat();
+        Map errors     = getErrors();
+
+        try {
+            for (String part: parts) {
+                double location = f.parse(part);
+            }
+
+            errors.remove(item.getFieldName());
+        }
+        catch (NumberFormatException nfe) {
+            errors.put(item.getFieldName(), MESSAGES.wrongFormat());
+
+            valid = false;
+        }
+
+        setErrors(errors, true);
+
+        return valid;
+    }
+
+
+    /**
+     * This method returns the double array that has been entered in
+     * <i>item</i>.
+     *
+     * @param item The item that contains the desired values.
+     *
+     * @return the values as double array.
+     */
+    public double[] getInputValues(FormItem item) {
+        String value = (String) item.getValue();
+
+        if (value == null) {
+            return null;
+        }
+
+        String[] parts  = value.split(" ");
+
+        if (parts == null) {
+            return null;
+        }
+
+        double[] values = new double[parts.length];
+
+        NumberFormat f = NumberFormat.getDecimalFormat();
+
+        int i = 0;
+        for (String part: parts) {
+            try {
+                values[i++] = f.parse(part);
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing
+            }
+        }
+
+        return values;
+    }
+
+
+    /**
+     * Returns the double values of this panel.
+     *
+     * @return the double values of this panel.
+     */
+    public double[] getInputValues() {
+        return getInputValues(ti);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/DoubleRangePanel.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,208 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.Map;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.NumberFormat;
+
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.FloatItem;
+import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.form.fields.StaticTextItem;
+import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
+
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+
+/**
+ * This class creates a DynamicForm with three input fields.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DoubleRangePanel
+extends      DynamicForm
+{
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);
+
+
+    /** The constant name of the input field to enter the start of a distance.*/
+    public static final String FIELD_FROM = "from";
+
+    /** The constant name of the input field to enter the end of a distance.*/
+    public static final String FIELD_TO = "to";
+
+    /** The constant name of the input field to enter the step width of a
+     * distance.*/
+    public static final String FIELD_WIDTH = "step";
+
+    /** The textboxes */
+    protected FloatItem fromItem;
+    protected FloatItem toItem;
+    protected FloatItem stepItem;
+
+
+    /**
+     * Creates a new form with a single input field that displays an array of
+     * double values.
+     *
+     * @param name The name of the TextItem.
+     * @param title The title of the TextItem.
+     * @param values The double values that should be displayed initially.
+     * @param handler The BlurHandler that is used to valide the input.
+     */
+    public DoubleRangePanel(
+        String titleFrom, String titleTo, String titleStep,
+        double from, double to, double step,
+        int width,
+        BlurHandler handler)
+    {
+        fromItem = new FloatItem(FIELD_FROM);
+        toItem   = new FloatItem(FIELD_TO);
+        stepItem = new FloatItem(FIELD_WIDTH);
+
+        fromItem.addBlurHandler(handler);
+        toItem.addBlurHandler(handler);
+        stepItem.addBlurHandler(handler);
+
+        NumberFormat f = NumberFormat.getDecimalFormat();
+
+        fromItem.setValue(f.format(from));
+        toItem.setValue(f.format(to));
+        stepItem.setValue(f.format(step));
+
+        StaticTextItem fromText = new StaticTextItem("staticFrom");
+        fromText.setValue(titleFrom);
+        fromText.setShowTitle(false);
+        fromItem.setShowTitle(false);
+
+        StaticTextItem toText = new StaticTextItem("staticTo");
+        toText.setValue(titleTo);
+        toText.setShowTitle(false);
+        toItem.setShowTitle(false);
+
+        StaticTextItem stepText = new StaticTextItem("staticStep");
+        stepText.setValue(titleStep);
+        stepText.setShowTitle(false);
+        stepItem.setShowTitle(false);
+
+        int itemWidth = width / 6;
+        fromItem.setWidth(itemWidth);
+        fromText.setWidth(itemWidth);
+        toItem.setWidth(itemWidth);
+        toText.setWidth(itemWidth);
+        stepItem.setWidth(itemWidth);
+        stepText.setWidth(itemWidth);
+
+        setFields(fromItem, fromText, toItem, toText, stepItem, stepText);
+        setFixedColWidths(false);
+        setNumCols(6);
+        setWidth(width);
+        setAlign(Alignment.CENTER);
+    }
+
+
+    /**
+     * This method takes distances values and sets them to the textboxes
+     * visualizied by this widget.
+     *
+     * @param from  The from value.
+     * @param to    The to value.
+     * @param steps The max steps.
+     */
+    public void setValues(double from, double to, double steps) {
+        NumberFormat f = NumberFormat.getDecimalFormat();
+
+        fromItem.setValue(f.format(from));
+        toItem.setValue(f.format(to));
+        stepItem.setValue(f.format(steps));
+    }
+
+    /**
+     * This method validates the entered text in the input fields. If
+     * there are values that doesn't represent a valid float, an error is
+     * displayed.
+     *
+     * @param item The FormItem.
+     */
+    protected boolean validateForm(FormItem item) {
+        if (item instanceof StaticTextItem) {
+            return true;
+        }
+
+        boolean valid = true;
+
+        String v = (String) item.getValue();
+
+        NumberFormat f = NumberFormat.getDecimalFormat();
+        Map errors     = getErrors();
+
+        try {
+            double value = f.parse(v);
+
+            errors.remove(item.getFieldName());
+        }
+        catch (NumberFormatException nfe) {
+            errors.put(item.getFieldName(), MESSAGES.wrongFormat());
+
+            item.focusInItem();
+
+            valid = false;
+        }
+
+        setErrors(errors, true);
+
+        return valid;
+    }
+
+
+    /**
+     * Returns the double value of <i>value</i>.
+     *
+     * @return the double value of <i>value</i>.
+     */
+    protected double getDouble(String value) {
+        NumberFormat f = NumberFormat.getDecimalFormat();
+
+        return f.parse(value);
+    }
+
+
+    /**
+     * Returns the start value.
+     *
+     * @return the start value.
+     */
+    public double getFrom() {
+        String v = getValueAsString(FIELD_FROM);
+
+        return getDouble(v);
+    }
+
+
+    /**
+     * Returns the end value.
+     *
+     * @return the end value.
+     */
+    public double getTo() {
+        String v = getValueAsString(FIELD_TO);
+
+        return getDouble(v);
+    }
+
+
+    /**
+     * Returns the step width.
+     *
+     * @return the step width.
+     */
+    public double getStep() {
+        String v = getValueAsString(FIELD_WIDTH);
+
+        return getDouble(v);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/ExportPanel.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,136 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.HTMLPane;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.ExportMode;
+import de.intevation.flys.client.shared.model.Facet;
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+/**
+ * A panel that displays an download icon for all available export modes of a
+ * Collection.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ExportPanel extends VLayout {
+
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    protected Collection       c;
+    protected List<ExportMode> exports;
+
+    /** This layout will store a list of available export types.*/
+    protected HLayout container;
+
+
+    public ExportPanel(Collection c, List<ExportMode> exports) {
+        super();
+
+        this.c         = c;
+        this.exports   = exports;
+        this.container = new HLayout();
+
+        Label title = new Label(MSG.dataexport());
+        title.setHeight(15);
+        title.setStyleName("fontNormalSmallUnderlined");
+
+        addMember(title);
+        addMember(createExportItems());
+
+        setHeight(45);
+        setMembersMargin(5);
+    }
+
+
+    /**
+     * This method is used to create an item (created by createExportButton) for
+     * each facet for each export mode.
+     *
+     * @return a horizontal list of buttons.
+     */
+    protected HLayout createExportItems() {
+        HLayout layout = new HLayout();
+
+        for (ExportMode mode: exports) {
+            String      name   = mode.getName();
+            List<Facet> facets = mode.getFacets();
+
+            for (Facet facet: facets) {
+                layout.addMember(createExportButton(name, facet.getName()));
+            }
+        }
+
+        return layout;
+    }
+
+
+    /**
+     * This method is used to create a button (with click handler) for a
+     * concrete export mode / type.
+     *
+     * @param name The name of the export.
+     * @param facet The name of the export type (e.g. CSV, WST).
+     *
+     * @return an image with click handler.
+     */
+    protected Canvas createExportButton(String name, String facet) {
+        String url  = getExportUrl(name, facet);
+        String iUrl = GWT.getHostPageBaseURL() + MSG.imageSave();
+
+        HTMLPane pane = new HTMLPane();
+        pane.setContents("<a href='" + url + "'><img src='" + iUrl + "'></a>");
+        pane.setWidth(30);
+        pane.setTooltip(getTooltipText(name, facet));
+
+        return pane;
+    }
+
+
+    /**
+     * Creates the URL used to trigger an export.
+     *
+     * @param name The name of the export.
+     * @param facet The name of the export type (e.g. CSV, WST).
+     *
+     * @return the export URL.
+     */
+    protected String getExportUrl(String name, String facet) {
+        Config config = Config.getInstance();
+
+        String url = GWT.getModuleBaseURL();
+        url += "export";
+        url += "?uuid=" + c.identifier();
+        url += "&mode=" + name;
+        url += "&type=" + facet;
+        url += "&server=" + config.getServerUrl();
+        url += "&locale=" + config.getLocale();
+
+        return url;
+    }
+
+
+    /**
+     * Creates a text used as tooltip for a specific export and type.
+     *
+     * @param name The name of the export.
+     * @param facet The name of the export type (e.g. CSV, WST).
+     *
+     * @return a tooltip text.
+     */
+    protected String getTooltipText(String name, String facet) {
+        return MSG.getString(name) + " | " + MSG.getString(facet);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/FLYSFooter.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,89 @@
+package de.intevation.flys.client.client.ui;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.layout.HLayout;
+
+import de.intevation.flys.client.client.FLYS;
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class FLYSFooter extends HLayout {
+
+    /** The interface that provides i18n messages. */
+    private FLYSConstants messages = GWT.create(FLYSConstants.class);
+
+    /** An instance to FLYS.*/
+    protected FLYS flys;
+
+    /** The button to open the project list.*/
+    protected Label home;
+
+    /** The button to switch between the english and german version.*/
+    protected Label contact;
+
+    /** The button to open an info panel.*/
+    protected Label impressum;
+
+    /**
+     * The default constructor for creating a new MainMenu.
+     */
+    public FLYSFooter(FLYS flys) {
+        this.flys     = flys;
+
+        home    = new Label(messages.footerHome());
+        contact  = new Label(messages.footerContact());
+        impressum = new Label(messages.footerImpressum());
+
+        init();
+    }
+
+
+    /**
+     * This method is called by the constructor after creating the necessary
+     * components. It initializes layout specific stuff like width, height,
+     * colors and so on and appends the components.
+     */
+    protected void init() {
+        setStyleName("bgBlueDark");
+        setHeight("25px");
+        setLayoutMargin(5);
+
+        home.setStyleName("fontLightSmall");
+        contact.setStyleName("fontLightSmall");
+        impressum.setStyleName("fontLightSmall");
+
+        HLayout panel = new HLayout();
+        panel.setWidth("100%");
+        panel.setMembersMargin(5);
+        panel.setAlign(Alignment.RIGHT);
+        panel.addMember(home);
+        panel.addMember(contact);
+        panel.addMember(impressum);
+        home.setAlign(Alignment.RIGHT);
+        contact.setAlign(Alignment.RIGHT);
+        impressum.setAlign(Alignment.RIGHT);
+
+        home.setWidth(70);
+        contact.setWidth(70);
+        impressum.setWidth(80);
+
+        addMember(panel);
+    }
+
+
+    /**
+     * Returns the FLYS instance stored in this class.
+     *
+     * @return the flys instance.
+     */
+    protected FLYS getFlys() {
+        return flys;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/FLYSHeader.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,93 @@
+package de.intevation.flys.client.client.ui;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ImageResource;
+
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.widgets.Img;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.client.FLYSImages;
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYSHeader extends HLayout {
+
+    /** The interface that provides the image resources. */
+    private FLYSImages IMAGES = GWT.create(FLYSImages.class);
+
+    /** The interface that provides the message resources. */
+    private FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);
+
+    /** The height used for this header.*/
+    public static final int HEIGHT = 75;
+
+    /** The height used for the images.*/
+    public static final int IMG_HEIGHT = 50;
+
+
+    public FLYSHeader() {
+        init();
+    }
+
+    public void init() {
+        setWidth100();
+        setHeight(HEIGHT);
+        setLayoutLeftMargin(5);
+        setLayoutRightMargin(5);
+
+        ImageResource flysRes = IMAGES.logoFlys();
+        ImageResource bfgRes  = IMAGES.logoBfg();
+
+        Img flys = new Img(
+            flysRes.getURL(),
+            calcWidth(flysRes,IMG_HEIGHT),
+            IMG_HEIGHT);
+
+        Img bfg  = new Img(
+            bfgRes.getURL(),
+            calcWidth(bfgRes, HEIGHT),
+            HEIGHT);
+
+        Label fullname = new Label(MESSAGES.fullname());
+        fullname.setHeight(HEIGHT - IMG_HEIGHT);
+        fullname.setStyleName ("fontNormalMid");
+
+        VLayout left = new VLayout();
+        left.addMember(flys);
+        left.addMember(fullname);
+
+        HLayout right = new HLayout();
+        right.setAlign(Alignment.RIGHT);
+        right.addMember(bfg);
+
+        addMember(left);
+        addMember(right);
+    }
+
+
+    /**
+     * This method calculates the wight of an image relative to the given
+     * height.
+     *
+     * @param res The ImageResource that points to the image.
+     * @param height The pre-defined height.
+     *
+     * @return the calculated width that should be used for the image.
+     */
+    protected int calcWidth(ImageResource res, int height) {
+        int widthOrig  = res.getWidth();
+        int heightOrig = res.getHeight();
+
+        double factor = (double)heightOrig / height;
+        double width  = (double)widthOrig / factor;
+
+        return (int) width * 10 / 10;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/FLYSView.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,79 @@
+package de.intevation.flys.client.client.ui;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.layout.HLayout;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYSView extends Canvas {
+
+    /** The project list displaying the projects of a user.*/
+    protected ProjectList projectList;
+
+    /** The workspace that handles the artifact collection views.*/
+    protected FLYSWorkspace workspace;
+
+    /** The layout provided by this widget.*/
+    protected HLayout layout;
+
+
+    /**
+     * The default constructor for creating a new empty FLYSView. After creating
+     * the components, {@link init()} is called to do the layout work.
+     */
+    public FLYSView() {
+        layout = new HLayout();
+
+        init();
+    }
+
+
+    /**
+     * This method is called to do the layout work.
+     */
+    protected void init() {
+        setWidth("100%");
+        setHeight("*");
+
+        setStyleName("bgWhite");
+        setBorder("1px solid #808080");
+
+        layout.setHeight("100%");
+        layout.setWidth("100%");
+
+        addChild(layout);
+    }
+
+
+    /**
+     * Set the current project list. Previous ProjectLists are replaced by the
+     * new one.
+     */
+    public void setProjectList(ProjectList projectList) {
+        if (this.projectList != null) {
+            removeChild(this.projectList);
+        }
+
+        this.projectList = projectList;
+        layout.addMember(this.projectList);
+    }
+
+
+    /**
+     * Set the current FLYSWorkspace. Previous workspaces are replaced by the
+     * new one.
+     *
+     * @param workspaces The new FLYSWorkspace.
+     */
+    public void setFLYSWorkspace(FLYSWorkspace workspace) {
+        if (this.workspace != null) {
+            removeChild(this.workspace);
+        }
+
+        this.workspace = workspace;
+        layout.addMember(this.workspace);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/FLYSWorkspace.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,51 @@
+package de.intevation.flys.client.client.ui;
+
+import com.smartgwt.client.widgets.Canvas;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYSWorkspace extends Canvas {
+
+    /** The maximal number of windows that fit into the browser view when an
+     * offset is used to move windows initially.*/
+    public static int MAX_WINDOWS = 10;
+
+    /** The number of pixels used to move windows.*/
+    public static int WINDOW_OFFSET = 20;
+
+
+    /** A map that contains the open CollectionViews. */
+    protected List<CollectionView> views;
+
+
+    /**
+     * The default constructor creates an empty FLYSWorkspace with no
+     * CollectionViews opened.
+     */
+    public FLYSWorkspace() {
+        views = new ArrayList<CollectionView>();
+    }
+
+
+    /**
+     * This method adds a new CollectionView to this workspace and stores a
+     * reference in {@link views}.
+     *
+     * @param collectionView A new CollectionView.
+     */
+    public void addView(CollectionView collectionView) {
+        int num    = views != null ? views.size() : 0;
+        int factor = num % MAX_WINDOWS;
+
+        collectionView.moveTo(factor * WINDOW_OFFSET, factor * WINDOW_OFFSET);
+
+        views.add(collectionView);
+        addChild(collectionView);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/LinkSelection.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,174 @@
+package de.intevation.flys.client.client.ui;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.LinkItem;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.form.fields.events.ClickHandler;
+import com.smartgwt.client.widgets.form.fields.events.ClickEvent;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.client.event.StepForwardEvent;
+
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+/**
+ * This UIProvider displays the DataItems of the Data object in a list of links.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class LinkSelection
+extends      MapSelection
+{
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants messages = GWT.create(FLYSConstants.class);
+
+    /** The combobox.*/
+    protected DynamicForm form;
+
+    /** The selected river*/
+    protected Data river;
+
+    /**
+     * This method currently returns a
+     * {@link com.smartgwt.client.widgets.form.DynamicForm} that contains all
+     * data items in a list of links stored in <i>data</i>.
+     *
+     * @param data The {@link Data} object.
+     *
+     * @return a combobox.
+     */
+    public Canvas create(DataList data) {
+        VLayout v = new VLayout();
+        v.setMembersMargin(10);
+        v.setAlign(VerticalAlignment.TOP);
+        if (data.getState() == null) {
+            Canvas module = super.createWidget(data);
+            v.addMember(module);
+        }
+        Canvas content = createWidget(data);
+        v.addMember(content);
+
+        return v;
+    }
+
+
+    public Canvas createOld(DataList dataList) {
+        HLayout layout  = new HLayout();
+        VLayout vLayout = new VLayout();
+        layout.setWidth("400px");
+
+        Label label = new Label(dataList.getLabel());
+        label.setWidth("200px");
+
+        int size = dataList.size();
+        for (int i = 0; i < size; i++) {
+            Data data        = dataList.get(i);
+            DataItem[] items = data.getItems();
+
+            for (DataItem item: items) {
+                HLayout hLayout = new HLayout();
+
+                hLayout.addMember(label);
+                hLayout.addMember(new Label(item.getLabel()));
+
+                vLayout.addMember(hLayout);
+                vLayout.setWidth("130px");
+            }
+        }
+
+        Canvas back = getBackButton(dataList.getState());
+
+        layout.addMember(label);
+        layout.addMember(vLayout);
+        layout.addMember(back);
+
+        return layout;
+    }
+
+
+    /**
+     * This method creates the content of the widget.
+     *
+     * @param data The {@link Data} object.
+     *
+     * @return a list of links
+     */
+    protected Canvas createWidget(DataList data) {
+        GWT.log("LinkSelection - create()");
+
+        VLayout layout   = new VLayout();
+        layout.setAlign(VerticalAlignment.TOP);
+        layout.setHeight(25);
+
+
+        form = new DynamicForm();
+        VLayout formLayout = new VLayout();
+
+        formLayout.setLayoutLeftMargin(60);
+        int size = data.size();
+
+        for (int i = 0; i < size; i++) {
+            Data d = data.get(i);
+
+            Label label = new Label(d.getDescription());
+            label.setValign(VerticalAlignment.TOP);
+            label.setHeight(20);
+
+            for (DataItem item: d.getItems()) {
+                LinkItem link = new LinkItem("river");
+                link.setLinkTitle(item.getLabel());
+                link.setTarget(item.getStringValue());
+                link.setShowTitle(false);
+                DynamicForm f = new DynamicForm();
+                f.setItems(link);
+                formLayout.addMember(f);
+                link.addClickHandler(new ClickHandler() {
+                    public void onClick(ClickEvent event) {
+                        DataItem item = new DefaultDataItem(
+                            ((LinkItem)event.getItem()).getLinkTitle(),
+                            null,
+                            ((LinkItem)event.getItem()).getLinkTitle());
+
+                            river = new DefaultData(
+                                "river",
+                                null,
+                                null,
+                                new DataItem [] {item});
+                        fireStepForwardEvent (new StepForwardEvent (getData()));
+                    }
+                });
+
+            }
+            label.setWidth(50);
+
+            layout.addMember(label);
+            layout.addMember(formLayout);
+        }
+        layout.setAlign(VerticalAlignment.TOP);
+
+        return layout;
+    }
+
+
+    protected Data[] getData() {
+        Data[] module = super.getData();
+        if (module != null) {
+            return new Data[] {module[0], river};
+        }
+        else {
+            return new Data[] {river};
+        }
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,898 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
+import com.smartgwt.client.widgets.form.fields.events.BlurEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
+import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
+import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
+import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
+import com.smartgwt.client.widgets.grid.events.CellClickHandler;
+import com.smartgwt.client.widgets.grid.events.CellClickEvent;
+
+import com.smartgwt.client.widgets.tab.TabSet;
+import com.smartgwt.client.widgets.tab.Tab;
+import com.smartgwt.client.data.Record;
+
+import com.smartgwt.client.types.ListGridFieldType;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.shared.model.DistanceInfoObject;
+import de.intevation.flys.client.shared.model.DistanceInfoRecord;
+import de.intevation.flys.client.shared.model.ArtifactDescription;
+
+import de.intevation.flys.client.client.services.DistanceInfoService;
+import de.intevation.flys.client.client.services.DistanceInfoServiceAsync;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.FLYSImages;
+import de.intevation.flys.client.client.Config;
+
+
+/**
+ * This UIProvider creates a widget to enter locations or a distance.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LocationDistancePanel
+extends      AbstractUIProvider
+implements   ChangeHandler, BlurHandler
+{
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);
+
+    /** The interface that provides the image resources. */
+    private FLYSImages IMAGES = GWT.create(FLYSImages.class);
+
+    /** The DistanceInfoService used to retrieve locations about rivers.*/
+    protected DistanceInfoServiceAsync distanceInfoService =
+        GWT.create(DistanceInfoService.class);
+
+    /** The constant name of the input field to enter locations.*/
+    public static final String FIELD_LOCATION = "location";
+
+    /** The constant name of the input field to enter distance.*/
+    public static final String FIELD_DISTANCE = "distance";
+
+    /** The constant name of the input field to enter the start of a distance.*/
+    public static final String FIELD_FROM = "from";
+
+    /** The constant name of the input field to enter the end of a distance.*/
+    public static final String FIELD_TO = "to";
+
+    /** The constant name of the input field to enter the step width of a
+     * distance.*/
+    public static final String FIELD_WIDTH = "width";
+
+    public static final int WIDTH = 250;
+
+
+    /** The radio group for input mode selection.*/
+    protected DynamicForm mode;
+
+    /** A container that will contain the location or the distance panel.*/
+    protected HLayout container;
+
+    /** The min value for a distance.*/
+    protected double min;
+
+    /** The max value for a distance.*/
+    protected double max;
+
+    /** The 'from' value entered in the distance mode.*/
+    protected double from;
+
+    /** The 'to' value entered in the distance mode.*/
+    protected double to;
+
+    /** The 'step' value entered in the distance mode.*/
+    protected double step;
+
+    /** The values entered in the location mode.*/
+    protected double[] values;
+
+    /** The input panel for locations */
+    protected DoubleArrayPanel locationPanel;
+
+    /** The input panel for distances */
+    protected DoubleRangePanel distancePanel;
+
+    /** The tab set containing the location and distance table*/
+    protected TabSet inputTables;
+
+    /** The distance table. */
+    protected ListGrid distanceTable;
+
+    /** The locations table */
+    protected ListGrid locationsTable;
+
+    /** The locations table for distance input. */
+    protected ListGrid locationDistanceTable;
+
+    /** The table data. */
+    protected DistanceInfoObject[] tableData;
+
+    /**
+     * Creates a new LocationDistancePanel instance.
+     */
+    public LocationDistancePanel() {
+        distanceTable  = new ListGrid();
+        locationsTable = new ListGrid();
+        locationDistanceTable = new ListGrid();
+        locationDistanceTable.setShowHeaderContextMenu(false);
+        distanceTable.setShowHeaderContextMenu(false);
+        locationsTable.setShowHeaderContextMenu(false);
+    }
+
+
+    /**
+     * This method creates a widget that contains a label, a panel with
+     * checkboxes to switch the input mode between location and distance input,
+     * and a the mode specific panel.
+     *
+     * @param data The data that might be inserted.
+     *
+     * @return a panel.
+     */
+    public Canvas create(DataList data) {
+        VLayout layout = new VLayout();
+        layout.setMembersMargin(10);
+
+        initDefaults(data);
+
+        Label label   = new Label(MESSAGES.location_distance_state());
+        Canvas widget = createWidget(data);
+        Canvas submit = getNextButton();
+        createDistanceInputPanel();
+
+        createDistanceTable();
+        createLocationTable();
+        createLocationTableDistance ();
+
+        widget.setHeight(50);
+        label.setHeight(25);
+
+        layout.addMember(label);
+        layout.addMember(widget);
+        layout.addMember(submit);
+
+        return layout;
+    }
+
+
+    /**
+     * This method creates a table that contains the distance values.
+     */
+    protected void createDistanceTable() {
+        distanceTable.setWidth100();
+        distanceTable.setShowRecordComponents(true);
+        distanceTable.setShowRecordComponentsByCell(true);
+        distanceTable.setHeight100();
+        distanceTable.setEmptyMessage(MESSAGES.empty_table());
+
+        ListGridField addDistance = new ListGridField ("", "");
+        addDistance.setType (ListGridFieldType.ICON);
+        addDistance.setWidth ("30");
+        addDistance.addRecordClickHandler (new RecordClickHandler () {
+            public void onRecordClick (RecordClickEvent e) {
+                if (!isLocationMode ()) {
+                    Record r = e.getRecord();
+                    double min = r.getAttributeAsDouble("from");
+                    double max = r.getAttributeAsDouble("to");
+                    setDistanceValues(min, max);
+                }
+                else {
+                    double[] selected;
+                    Record r = e.getRecord();
+                    double min = r.getAttributeAsDouble("from");
+                    double max = r.getAttributeAsDouble("to");
+                    if (getLocationValues() != null) {
+                        double[] val = getLocationValues();
+                        selected = new double[val.length + 2];
+                        for(int i = 0; i < val.length; i++){
+                            selected[i] = val[i];
+                        }
+                        selected[val.length] = min;
+                        selected[val.length + 1] = max;
+                    }
+                    else {
+                        selected = new double[2];
+                        selected[0] = min;
+                        selected[1] = max;
+                    }
+                setLocationValues(selected);
+                }
+            }
+        });
+        addDistance.setCellIcon (IMAGES.markerGreen ().getURL ());
+
+        ListGridField ddescr = new ListGridField("description",
+                MESSAGES.description());
+        ddescr.setType(ListGridFieldType.TEXT);
+        ddescr.setWidth("*");
+        ListGridField from = new ListGridField("from", MESSAGES.from());
+        from.setType(ListGridFieldType.TEXT);
+        from.setWidth(75);
+        ListGridField to = new ListGridField("to", MESSAGES.to());
+        to.setType(ListGridFieldType.TEXT);
+        to.setWidth(75);
+        ListGridField dside = new ListGridField("riverside",
+                MESSAGES.riverside());
+        dside.setType(ListGridFieldType.TEXT);
+        dside.setWidth(60);
+
+        distanceTable.setFields(addDistance, ddescr, from, to, dside);
+    }
+
+
+    /**
+     * This method creates a table that contains the location values.
+     */
+    protected void createLocationTable() {
+        locationsTable.setWidth100();
+        locationsTable.setShowRecordComponents(true);
+        locationsTable.setShowRecordComponentsByCell(true);
+        locationsTable.setHeight100();
+        locationsTable.setEmptyMessage(MESSAGES.empty_table());
+
+        ListGridField addLocation = new ListGridField ("", "");
+        addLocation.setType (ListGridFieldType.ICON);
+        addLocation.setWidth ("30");
+        addLocation.addRecordClickHandler (new RecordClickHandler () {
+            public void onRecordClick (RecordClickEvent e) {
+                ListGridRecord[] records = locationsTable.getSelection();
+                double[] selected;
+                if (getLocationValues() != null) {
+                    double[] val = getLocationValues();
+                    selected = new double[val.length + 1];
+                    for(int i = 0; i < val.length; i++){
+                        selected[i] = val[i];
+                    }
+                    selected[val.length] =
+                        records[0].getAttributeAsDouble("from");
+                }
+                else {
+                    selected = new double[1];
+                    selected[0] = records[0].getAttributeAsDouble("from");
+                }
+                setLocationValues(selected);
+            }
+        });
+        addLocation.setCellIcon (IMAGES.markerGreen ().getURL ());
+
+        ListGridField ldescr = new ListGridField("description",
+                MESSAGES.description());
+        ldescr.setType(ListGridFieldType.TEXT);
+        ldescr.setWidth("*");
+        ListGridField lside = new ListGridField("riverside",
+                MESSAGES.riverside());
+        lside.setType(ListGridFieldType.TEXT);
+        lside.setWidth(60);
+        ListGridField loc = new ListGridField("from", MESSAGES.location());
+        loc.setType(ListGridFieldType.TEXT);
+        loc.setWidth(80);
+        locationsTable.setFields(addLocation, ldescr, loc, lside);
+    }
+
+
+    /**
+     * This method creates a table that contains the location values.
+     */
+    protected void createLocationTableDistance (){
+        locationDistanceTable = null;
+        locationDistanceTable = new ListGrid ();
+        locationDistanceTable.setWidth100();
+        locationDistanceTable.setShowRecordComponents(true);
+        locationDistanceTable.setShowRecordComponentsByCell(true);
+        locationDistanceTable.setHeight100();
+        locationDistanceTable.setEmptyMessage(MESSAGES.empty_table());
+
+        ListGridField addfrom = new ListGridField ("", "");
+        addfrom.setType (ListGridFieldType.ICON);
+        addfrom.setWidth ("30");
+        addfrom.setCellIcon (IMAGES.markerGreen ().getURL ());
+
+        ListGridField addto2 = new ListGridField ("", "");
+        addto2.setType (ListGridFieldType.ICON);
+        addto2.setWidth ("30");
+        addto2.setCellIcon (IMAGES.markerRed ().getURL ());
+
+
+        ListGridField ldescr = new ListGridField("description",
+                MESSAGES.description());
+        ldescr.setType(ListGridFieldType.TEXT);
+        ldescr.setWidth("*");
+        ListGridField lside = new ListGridField("riverside",
+                MESSAGES.riverside());
+        lside.setType(ListGridFieldType.TEXT);
+        lside.setWidth(60);
+        ListGridField loc = new ListGridField("from", MESSAGES.location());
+        loc.setType(ListGridFieldType.TEXT);
+        loc.setWidth(80);
+        locationDistanceTable.addCellClickHandler (new CellClickHandler () {
+            public void onCellClick (CellClickEvent e) {
+                if (e.getColNum() == 0) {
+                    Record r = e.getRecord ();
+                    double fromvalue = r.getAttributeAsDouble ("from");
+                    double tovalue = getTo ();
+                    setDistanceValues (fromvalue, tovalue);
+                }
+                else if (e.getColNum() == 1) {
+                    Record r = e.getRecord ();
+                    double fromvalue = getFrom ();
+                    double tovalue = r.getAttributeAsDouble ("to");
+                    setDistanceValues (fromvalue, tovalue);
+                }
+            }
+        });
+        locationDistanceTable.setFields(addfrom, addto2, ldescr, loc, lside);
+    }
+
+
+    public Canvas createOld(DataList dataList) {
+        List<Data> items = dataList.getAll();
+
+        Data dFrom = getData(items, "ld_from");
+        Data dTo   = getData(items, "ld_to");
+        Data dStep = getData(items, "ld_step");
+
+        DataItem[] from = dFrom.getItems();
+        DataItem[] to   = dTo.getItems();
+        DataItem[] step = dStep.getItems();
+
+        HLayout layout = new HLayout();
+        layout.setWidth("400px");
+
+        Label   label  = new Label(dataList.getLabel());
+        label.setWidth("200px");
+
+        StringBuilder sb = new StringBuilder();
+        sb.append(from[0].getLabel());
+        sb.append(" " + MESSAGES.unitFrom() + " ");
+        sb.append(to[0].getLabel());
+        sb.append(" " + MESSAGES.unitTo() + " ");
+        sb.append(step[0].getLabel());
+        sb.append(" " + MESSAGES.unitWidth());
+
+        Canvas back = getBackButton(dataList.getState());
+
+        Label selected = new Label(sb.toString());
+        selected.setWidth("130px");
+
+        layout.addMember(label);
+        layout.addMember(selected);
+        layout.addMember(back);
+
+        return layout;
+    }
+
+
+    /**
+     * This method reads the default values defined in the DataItems of the Data
+     * objects in <i>list</i>.
+     *
+     * @param list The DataList container that stores the Data objects.
+     */
+    protected void initDefaults(DataList list) {
+        Data f = getData(list.getAll(), "ld_from");
+        Data t = getData(list.getAll(), "ld_to");
+        Data s = getData(list.getAll(), "ld_step");
+
+        DataItem[] fItems = f.getItems();
+        DataItem[] tItems = t.getItems();
+        DataItem[] sItems = s.getItems();
+
+        min  = Double.valueOf(fItems[0].getStringValue());
+        max  = Double.valueOf(tItems[0].getStringValue());
+        step = Double.valueOf(sItems[0].getStringValue());
+
+        this.from = min;
+        this.to   = max;
+        this.step = step;
+    }
+
+
+    /**
+     * This method greps the Data with name <i>name</i> from the list and
+     * returns it.
+     *
+     * @param items A list of Data.
+     * @param name The name of the Data that we are searching for.
+     *
+     * @return the Data with the name <i>name</i>.
+     */
+    protected Data getData(List<Data> data, String name) {
+        for (Data d: data) {
+            if (name.equals(d.getLabel())) {
+                return d;
+            }
+        }
+
+        return null;
+    }
+
+
+    protected Canvas createWidget(DataList data) {
+        VLayout layout       = new VLayout();
+        container            = new HLayout();
+        Canvas checkboxPanel = createRadioButtonPanel();
+
+        // the initial view will display the location input mode
+        locationPanel = new DoubleArrayPanel(
+                MESSAGES.unitLocation(),
+                getLocationValues(),
+                this);
+        container.addMember(locationPanel);
+
+        layout.addMember(checkboxPanel);
+        layout.addMember(container);
+
+        container.setMembersMargin(30);
+
+        inputTables   = new TabSet();
+        Tab locations = new Tab(MESSAGES.location());
+        Tab distances = new Tab(MESSAGES.distance());
+
+        inputTables.setWidth100();
+        inputTables.setHeight100();
+
+        locations.setPane(locationsTable);
+        distances.setPane(distanceTable);
+
+        inputTables.addTab(locations);
+        inputTables.addTab(distances);
+
+        helperContainer.addChild(inputTables);
+
+        return layout;
+    }
+
+
+    /**
+     * This method returns the selected data.
+     *
+     * @return the selected/inserted data.
+     */
+    public Data[] getData() {
+        // XXX If we have entered a value and click right afterwards on the
+        // 'next' button, the BlurEvent is not fired, and the values are not
+        // saved. So, we gonna save those values explicitly.
+        if (isLocationMode()) {
+            Canvas member = container.getMember(0);
+            if (member instanceof DoubleArrayPanel) {
+                DoubleArrayPanel form = (DoubleArrayPanel) member;
+                saveLocationValues(form);
+            }
+        }
+        else {
+            Canvas member = container.getMember(0);
+            if (member instanceof DoubleRangePanel) {
+                DoubleRangePanel form = (DoubleRangePanel) member;
+                saveDistanceValues(form);
+            }
+        }
+
+        return new Data[] { getDataFrom(), getDataTo(), getDataStep() };
+    }
+
+
+    /**
+     * Returns the Data object for the 'from' attribute.
+     *
+     * @return the Data object for the 'from' attribute.
+     */
+    protected Data getDataFrom() {
+        String value  = Double.valueOf(getFinalFrom()).toString();
+        DataItem item = new DefaultDataItem("ld_from", "ld_from", value);
+        return new DefaultData(
+            "ld_from", null, null, new DataItem[] { item });
+    }
+
+
+    /**
+     * Returns the Data object for the 'to' attribute.
+     *
+     * @return the Data object for the 'to' attribute.
+     */
+    protected Data getDataTo() {
+        String value  = Double.valueOf(getFinalTo()).toString();
+        DataItem item = new DefaultDataItem("ld_to", "ld_to", value);
+        return new DefaultData(
+            "ld_to", null, null, new DataItem[] { item });
+    }
+
+
+    /**
+     * Returns the Data object for the 'step' attribute.
+     *
+     * @return the Data object for the 'step' attribute.
+     */
+    protected Data getDataStep() {
+        String value  = Double.valueOf(getFinalStep()).toString();
+        DataItem item = new DefaultDataItem("ld_step","ld_step", value);
+        return new DefaultData(
+            "ld_step", null, null, new DataItem[] { item });
+    }
+
+
+    /**
+     * Returns the value of 'from' depending on the selected input mode.
+     *
+     * @return the value of 'from' depending on the selected input mode.
+     */
+    protected double getFinalFrom() {
+        if (isLocationMode()) {
+            double[] values = getLocationValues();
+            double   value  = Double.MAX_VALUE;
+
+            for (double v: values) {
+                value = value < v ? value : v;
+            }
+
+            return value;
+        }
+        else {
+            return getFrom();
+        }
+    }
+
+
+    /**
+     * Returns the value of 'to' depending on the selected input mode.
+     *
+     * @return the value of 'to' depending on the selected input mode.
+     */
+    protected double getFinalTo() {
+        if (isLocationMode()) {
+            double[] values = getLocationValues();
+            double   value  = Double.MIN_VALUE;
+
+            for (double v: values) {
+                value = value > v ? value : v;
+            }
+
+            return value;
+        }
+        else {
+            return getTo();
+        }
+    }
+
+
+    /**
+     * Returns the value of 'step' depending on the selected input mode.
+     *
+     * @return the value of 'step' depending on the selected input mode.
+     */
+    protected double getFinalStep() {
+        if (isLocationMode()) {
+            // we have no field to enter the 'step' attribute in the location
+            // mode
+            return 0.0;
+        }
+        else {
+            return getStep();
+        }
+    }
+
+
+    /**
+     * Determines the current input mode.
+     *
+     * @return true, if 'location' is the current input mode, otherwise false.
+     */
+    public boolean isLocationMode() {
+        String inputMode = mode.getValueAsString("mode");
+
+        return inputMode.equals(FIELD_LOCATION) ? true : false;
+    }
+
+
+    /**
+     * This method switches the input mode between location and distance input.
+     *
+     * @param event The click event fired by a RadioButtonGroupItem.
+     */
+    public void onChange(ChangeEvent event) {
+        String value = (String) event.getValue();
+
+        if (value == null) {
+            return;
+        }
+        if (value.equals(FIELD_LOCATION)) {
+            locationPanel = new DoubleArrayPanel(
+                MESSAGES.unitLocation(),
+                getLocationValues(),
+                this);
+
+            container.removeMembers(container.getMembers());
+            container.addMember(locationPanel);
+            while (inputTables.getNumTabs() > 0) {
+                inputTables.removeTab(0);
+            }
+            Tab t1 = new Tab (MESSAGES.location());
+            createLocationTable();
+            t1.setPane(locationsTable);
+            inputTables.addTab(t1);
+            createDistanceTable();
+            Tab t2 = new Tab (MESSAGES.distance());
+            t2.setPane(distanceTable);
+            inputTables.addTab(t2);
+            updateDistanceInfo(tableData);
+
+            helperContainer.addChild(inputTables);
+            inputTables.selectTab(0);
+        }
+        else {
+            distancePanel = new DoubleRangePanel(
+                MESSAGES.unitFrom(), MESSAGES.unitTo(), MESSAGES.unitWidth(),
+                getFrom(), getTo(), getStep(),
+                250,
+                this);
+
+            container.removeMembers(container.getMembers());
+            container.addMember(distancePanel);
+            while (inputTables.getNumTabs () > 0) {
+                inputTables.removeTab(0);
+            }
+            Tab t1 = new Tab(MESSAGES.location ());
+            createLocationTableDistance ();
+            t1.setPane(locationDistanceTable);
+            inputTables.addTab(t1);
+            createDistanceTable ();
+            Tab t2 = new Tab(MESSAGES.distance ());
+            t2.setPane(distanceTable);
+            inputTables.addTab(t2);
+            if (tableData != null) {
+                updateDistanceInfo(tableData);
+            }
+
+            helperContainer.addChild(inputTables);
+            inputTables.selectTab(1);
+        }
+    }
+
+
+    /**
+     * This method is used to validate the inserted data in the form fields.
+     *
+     * @param event The BlurEvent that gives information about the FormItem that
+     * has been modified and its value.
+     */
+    public void onBlur(BlurEvent event) {
+        FormItem item = event.getItem();
+        String  field = item.getFieldName();
+
+        if (field == null) {
+            return;
+        }
+
+        if (field.equals(DoubleArrayPanel.FIELD_NAME)) {
+            DoubleArrayPanel p = (DoubleArrayPanel) event.getForm();
+
+            saveLocationValue(p, item);
+        }
+        else {
+            DoubleRangePanel p = (DoubleRangePanel) event.getForm();
+
+            saveDistanceValue(p, item);
+        }
+    }
+
+
+
+    /**
+     * Validates and stores all values entered in the location mode.
+     *
+     * @param p The DoubleArrayPanel.
+     */
+    protected void saveLocationValues(DoubleArrayPanel p) {
+        FormItem[] formItems = p.getFields();
+
+        for (FormItem item: formItems) {
+            if (item.getFieldName().equals(DoubleArrayPanel.FIELD_NAME)) {
+                saveLocationValue(p, item);
+            }
+        }
+    }
+
+
+    /**
+     * Validates and stores all values entered in the distance mode.
+     *
+     * @param p The DoubleRangePanel.
+     */
+    protected void saveDistanceValues(DoubleRangePanel p) {
+        FormItem[] formItems = p.getFields();
+
+        for (FormItem item: formItems) {
+            saveDistanceValue(p, item);
+        }
+    }
+
+
+    /**
+     * Validates and stores a value entered in the location mode.
+     *
+     * @param p The DoubleArrayPanel.
+     * @param item The item that needs to be validated.
+     */
+    protected void saveLocationValue(DoubleArrayPanel p, FormItem item) {
+        if (p.validateForm(item)) {
+            setLocationValues(p.getInputValues(item));
+        }
+    }
+
+
+    /**
+     * Validates and stores value entered in the distance mode.
+     *
+     * @param p The DoubleRangePanel.
+     * @param item The item that needs to be validated.
+     */
+    protected void saveDistanceValue(DoubleRangePanel p, FormItem item) {
+        if (p.validateForm(item)) {
+            setFrom(p.getFrom());
+            setTo(p.getTo());
+            setStep(p.getStep());
+        }
+    }
+
+
+    /**
+     * This method creates the panel that contains the checkboxes to switch
+     * between the input mode 'location' and 'distance'.
+     *
+     * @return the checkbox panel.
+     */
+    protected Canvas createRadioButtonPanel() {
+        mode = new DynamicForm();
+
+        RadioGroupItem radio = new RadioGroupItem("mode");
+        radio.setShowTitle(false);
+        radio.setVertical(false);
+
+        LinkedHashMap values = new LinkedHashMap();
+        values.put(FIELD_LOCATION, MESSAGES.location());
+        values.put(FIELD_DISTANCE, MESSAGES.distance());
+
+        LinkedHashMap initial = new LinkedHashMap();
+        initial.put("mode", FIELD_LOCATION);
+
+        radio.setValueMap(values);
+        radio.addChangeHandler(this);
+
+        mode.setFields(radio);
+        mode.setValues(initial);
+
+        return mode;
+    }
+
+
+    protected void createDistanceInputPanel() {
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale ();
+        String river  = "";
+
+        ArtifactDescription adescr = artifact.getArtifactDescription();
+        DataList[] data = adescr.getOldData();
+
+        if (data != null && data.length > 0) {
+            for (int i = 0; i < data.length; i++) {
+                DataList dl = data[i];
+                if (dl.getState().equals("state.winfo.river")) {
+                    for (int j = 0; j < dl.size(); j++) {
+                        Data d = dl.get(j);
+                        DataItem[] di = d.getItems();
+                        if (di != null && di.length == 1) {
+                           river = d.getItems()[0].getStringValue();
+                        }
+                    }
+                }
+            }
+        }
+
+        distanceInfoService.getDistanceInfo(url, locale, river,
+            new AsyncCallback<DistanceInfoObject[]>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not recieve distance informations.");
+                    GWT.log(caught.getMessage());
+                }
+
+                public void onSuccess(DistanceInfoObject[] di) {
+                    int num = di != null ? di.length :0;
+                    GWT.log("Recieved " + num + " distance informations.");
+
+                    if (num == 0) {
+                        return;
+                    }
+                    tableData = di;
+                    updateDistanceInfo(di);
+                }
+            }
+        );
+    }
+
+
+    protected void updateDistanceInfo(DistanceInfoObject[] di) {
+        int i = 0;
+        for (DistanceInfoObject dio: di) {
+            if (dio.getTo() != null) {
+                distanceTable.addData(new DistanceInfoRecord(dio));
+            }
+            else {
+                locationsTable.addData(new DistanceInfoRecord(dio));
+                locationDistanceTable.addData(new DistanceInfoRecord(dio));
+            }
+        }
+        return;
+    }
+
+
+    protected double getFrom() {
+        return from;
+    }
+
+
+    protected void setFrom(double from) {
+        this.from = from;
+    }
+
+
+    protected double getTo() {
+        return to;
+    }
+
+
+    protected void setTo(double to) {
+        this.to = to;
+    }
+
+
+    protected double getStep() {
+        return step;
+    }
+
+
+    protected void setStep(double step) {
+        this.step = step;
+    }
+
+
+    protected double[] getLocationValues() {
+        return values;
+    }
+
+
+    protected void setLocationValues(double[] values) {
+        this.values = values;
+        locationPanel.setValues(values);
+    }
+
+
+    protected void setDistanceValues (double from, double to) {
+        setFrom(from);
+        setTo(to);
+        distancePanel.setValues(from, to, getStep());
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/MainMenu.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,268 @@
+package de.intevation.flys.client.client.ui;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.LocaleInfo;
+import com.google.gwt.user.client.Window;
+
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.util.BooleanCallback;
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.events.ClickEvent;
+import com.smartgwt.client.widgets.events.ClickHandler;
+import com.smartgwt.client.widgets.layout.HLayout;
+
+import de.intevation.flys.client.client.FLYS;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.shared.model.User;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MainMenu extends HLayout {
+
+    /** The interface that provides i18n messages. */
+    private FLYSConstants messages = GWT.create(FLYSConstants.class);
+
+    /** An instance to FLYS.*/
+    protected FLYS flys;
+
+    /** The user that is currently logged in. */
+    protected User currentUser;
+
+    /** The label that displays the current logged in user. */
+    protected Label userText;
+
+    /** The button to log the current user out.*/
+    protected Label logout;
+
+    /** The button to add new projects.*/
+    protected Label newProject;
+
+    /** The button to open the project list.*/
+    protected Label projectList;
+
+    /** The button to switch between the english and german version.*/
+    protected Label language;
+
+    /** The button to open an info panel.*/
+    protected Label info;
+
+    /**
+     * The default constructor for creating a new MainMenu.
+     */
+    public MainMenu(FLYS flys) {
+        this.flys     = flys;
+
+        String guest = messages.user() + " " + messages.guest();
+
+        userText    = new Label(guest);
+        newProject  = new Label(messages.new_project());
+        projectList = new Label(messages.manage_projects());
+        logout      = new Label(messages.logout());
+        language    = new Label(messages.switch_language());
+        info        = new Label(messages.info());
+
+        newProject.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                GWT.log("Clicked 'New Project' button.");
+                createNewProject();
+            }
+        });
+
+        projectList.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                GWT.log("Clicked 'Open ProjectList' button.");
+                ProjectList list = getFlys().getProjectList();
+                if (list.isVisible())
+                    list.hide();
+                else
+                    list.show();
+            }
+        });
+
+        logout.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                GWT.log("Clicked 'logout' button.");
+                GWT.log("IMPLEMENT the 'logout' function.");
+            }
+        });
+
+        language.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                LocaleInfo info            = LocaleInfo.getCurrentLocale();
+                final String currentLocale = info.getLocaleName();
+                final String newLocale     = currentLocale.equals("de")
+                    ? "en"
+                    : "de";
+
+                SC.confirm(messages.warning(), messages.warning_language(),
+                    new BooleanCallback() {
+                        public void execute(Boolean value) {
+                            if (value) {
+                                switchLanguage(currentLocale, newLocale);
+                            }
+                        }
+                    });
+            }
+        });
+
+        info.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                GWT.log("Clicked 'info' button.");
+                GWT.log("IMPLEMENT the 'open info panel' function.");
+            }
+        });
+
+        init();
+    }
+
+
+    /**
+     * This method is called by the constructor after creating the necessary
+     * components. It initializes layout specific stuff like width, height,
+     * colors and so on and appends the components.
+     */
+    protected void init() {
+        setStyleName("bgBlueDark");
+        setHeight("25px");
+        setLayoutMargin(5);
+
+        newProject.setStyleName("fontLightSmall");
+        projectList.setStyleName("fontLightSmall");
+        userText.setStyleName("fontLightSmall");
+        newProject.setStyleName("fontLightSmall");
+        logout.setStyleName("fontLightSmall");
+        language.setStyleName("fontLightSmall");
+        info.setStyleName("fontLightSmall");
+
+        projectList.setWidth("140px");
+
+        HLayout leftPanel = new HLayout();
+        leftPanel.setWidth("80%");
+        leftPanel.setMembersMargin(5);
+        leftPanel.addMember(newProject);
+        leftPanel.addMember(projectList);
+
+        userText.setAlign(Alignment.RIGHT);
+        logout.setAlign(Alignment.RIGHT);
+        info.setAlign(Alignment.RIGHT);
+        language.setAlign(Alignment.RIGHT);
+
+        userText.setWidth(200);
+        logout.setWidth(70);
+        info.setWidth(40);
+        language.setWidth(70);
+
+        HLayout rightPanel = new HLayout();
+        rightPanel.setAlign(Alignment.RIGHT);
+        rightPanel.setMembersMargin(3);
+        rightPanel.setLayoutRightMargin(5);
+        rightPanel.addMember(userText);
+        rightPanel.addMember(logout);
+        rightPanel.addMember(language);
+        rightPanel.addMember(info);
+
+        addMember(leftPanel);
+        addMember(rightPanel);
+    }
+
+
+    /**
+     * Returns the FLYS instance stored in this class.
+     *
+     * @return the flys instance.
+     */
+    protected FLYS getFlys() {
+        return flys;
+    }
+
+
+    /**
+     * Set the current {@link User} and call {@link updateCurrentUser()}
+     * afterwards.
+     *
+     * @param user the new user.
+     */
+    public void setCurrentUser(User currentUser) {
+        this.currentUser = currentUser;
+
+        updateCurrentUser();
+    }
+
+
+    /**
+     * Update the text field that shows the current user. If no user is
+     * currently logged in, the text will display {@link FLYSConstants.guest()}.
+     */
+    public void updateCurrentUser() {
+        String name = currentUser != null
+            ? currentUser.getName()
+            : messages.guest();
+
+        GWT.log("Update the current user: " + name);
+
+        String username = messages.user() + " " + name;
+        userText.setContents(username);
+    }
+
+
+    /**
+     * Create a new project by calling FLYS.newProject().
+     */
+    protected void createNewProject() {
+        flys.newProject();
+    }
+
+
+    /**
+     * This method triggers the language switch between the <i>currentLocale</i>
+     * and the <i>newLocale</i>. The switch is done by replacing a "locale="
+     * parameter in the url of the application. We could use the GWT UrlBuilder
+     * class to create a new URL, but - in my eyes - this class is a bit
+     * inconsistens in its implementation.
+     *
+     * @param currentLocale The current locale string (e.g. "en").
+     * @param newLocale The new locale string (e.g. "de").
+     */
+    protected void switchLanguage(String currentLocale, String newLocale) {
+        String newLocation = Window.Location.getHref();
+
+        String oldLocation = newLocation;
+
+        if (newLocation.endsWith("/")) {
+            newLocation = newLocation.substring(0, newLocation.length()-1);
+        }
+
+        String replace     = null;
+        String replaceWith = null;
+
+        if (newLocation.indexOf("&locale=") >= 0) {
+            replace = currentLocale.equals("de")
+                ? "&locale=de"
+                : "&locale=en";
+
+            replaceWith = "&locale=" + newLocale;
+        }
+        else if (newLocation.indexOf("?locale=") >= 0) {
+            replace = currentLocale.equals("de")
+                ? "?locale=de"
+                : "?locale=en";
+
+            replaceWith = "?locale=" + newLocale;
+        }
+        else {
+            newLocation += newLocation.indexOf("?") >= 0
+                ? "&locale=" + newLocale
+                : "?locale=" + newLocale;
+        }
+
+        if (replace != null && replaceWith != null) {
+            newLocation = newLocation.replace(replace, replaceWith);
+        }
+
+        Window.open(newLocation, "_self", "");
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/MapSelection.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,79 @@
+package de.intevation.flys.client.client.ui;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ImageResource;
+
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Img;
+import com.smartgwt.client.widgets.layout.HLayout;
+
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.client.FLYSImages;
+import de.intevation.flys.client.client.event.HasStepForwardHandlers;
+import de.intevation.flys.client.shared.model.Data;
+
+
+/**
+ * This UIProvider displays the DataItems contained in the Data object in a
+ * combo box as SelectProvider does. Furthermore, there is a map displayed that
+ * lets the user choose a river by selecting it on the map.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MapSelection extends SelectProvider {
+
+    /** The interface that provides the image resources. */
+    private FLYSImages IMAGES = GWT.create(FLYSImages.class);
+
+    protected ModuleSelection moduleSelection;
+
+    public MapSelection() {
+    }
+
+
+    /**
+     * This method currently returns a
+     * {@link com.smartgwt.client.widgets.form.DynamicForm} that contains all
+     * data items in a combobox stored in <i>data</i>.<br>
+     *
+     * <b>TODO: The map panel for the river selection needs to be
+     * implemented!</b>
+     *
+     * @param data The {@link DataList} object.
+     *
+     * @return a combobox.
+     */
+    protected Canvas createWidget(DataList data) {
+        GWT.log("MapSelection - create()");
+
+        HLayout h = new HLayout();
+        h.setAlign(VerticalAlignment.TOP);
+        h.setHeight(100);
+        moduleSelection = new ModuleSelection();
+
+        Canvas form  = moduleSelection.create(data);
+        form.setWidth(250);
+        form.setLayoutAlign(VerticalAlignment.TOP);
+
+        ImageResource mapRes = IMAGES.riverMap();
+        Img map              = new Img(mapRes.getURL(), 400, 452);
+
+        // TODO implement event handling in the river map
+
+        h.addMember(form);
+        helperContainer.addChild(map);
+
+        return h;
+    }
+
+    protected Data[] getData() {
+        if (moduleSelection != null) {
+            return moduleSelection.getData();
+        }
+        else {
+            return 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-client/src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,150 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.LinkedHashMap;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.client.event.HasStepForwardHandlers;
+
+import de.intevation.flys.client.client.FLYSConstants;
+
+/**
+ * The ModuleSelection combines the river selection and the module selection in
+ * one widget. It will display a vertical splitted widget - the upper part will
+ * render checkboxes for each module, the lower one will display a combobox at
+ * the left and a map panel on the right to choose the river.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ModuleSelection extends MapSelection {
+
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);
+
+
+    /** Constant field name for the plugin selection.*/
+    public static final String FIELD_PLUGIN = "plugin";
+
+    /** Constant value for the WINFO plugin.*/
+    public static final String FIELD_PLUGIN_WINFO = "winfo";
+
+    /** Constant value for the MINFO plugin.*/
+    public static final String FIELD_PLUGIN_MINFO = "minfo";
+
+    /** Constant value for the MAP plugin.*/
+    public static final String FIELD_PLUGIN_MAP   = "map";
+
+    /** Constant value for the CHART plugin.*/
+    public static final String FIELD_PLUGIN_CHART = "chart";
+
+    /** Constant value for the FIX plugin.*/
+    public static final String FIELD_PLUGIN_FIX   = "fix";
+
+
+    /** The module checkboxes.*/
+    protected RadioGroupItem radio;
+
+
+    /**
+     * The default constructor.
+     */
+    public ModuleSelection() {
+    }
+
+
+    /**
+     * This method returns a widget that renders the checkboxes for each module
+     * and the MapSelection that lets the user choose the river.
+     *
+     * @param data The provided rivers.
+     *
+     * @return the module selection combined with the river selection.
+     */
+    public Canvas create(DataList data) {
+        VLayout newLayout = new VLayout();
+        newLayout.setMembersMargin(10);
+        newLayout.setAlign(VerticalAlignment.TOP);
+        Canvas moduleSelection = createWidget();
+
+        moduleSelection.setHeight(100);
+        newLayout.setHeight(70);
+        newLayout.addMember(moduleSelection);
+
+        return newLayout;
+    }
+
+
+    /**
+     * Creates a widget that displays a checkbox for each module.
+     *
+     * @return a widget with checkboxes.
+     */
+    protected Canvas createWidget() {
+        HLayout layout = new HLayout();
+
+        Label      label = new Label(MESSAGES.module_selection());
+        DynamicForm form = new DynamicForm();
+
+        radio = new RadioGroupItem("plugin");
+
+        label.setWidth(50);
+        label.setHeight(25);
+
+        LinkedHashMap values = new LinkedHashMap();
+        values.put(FIELD_PLUGIN_WINFO, messages.winfo());
+        values.put(FIELD_PLUGIN_MINFO, messages.minfo());
+        values.put(FIELD_PLUGIN_FIX, messages.fix());
+        values.put(FIELD_PLUGIN_CHART, messages.chart());
+        values.put(FIELD_PLUGIN_MAP, messages.map());
+
+        radio.setShowTitle(false);
+        radio.setVertical(true);
+        radio.setValueMap(values);
+
+        LinkedHashMap initial = new LinkedHashMap();
+        initial.put(FIELD_PLUGIN, FIELD_PLUGIN_WINFO);
+
+        form.setFields(radio);
+        form.setValues(initial);
+
+        layout.addMember(label);
+        layout.addMember(form);
+
+        return layout;
+    }
+
+
+    /**
+     * This method prepares the data of two widgets - the module selection and
+     * the river selection. The returning field will contain the Data that
+     * represents the module selection at first position, the second position
+     * stores the Data object that represents the river selection.
+     *
+     * @return the Data that was chosen in this widget.
+     */
+    protected Data[] getData() {
+
+        String module = radio.getValueAsString();
+
+        DataItem[] items = new DefaultDataItem[1];
+        items[0]         = new DefaultDataItem(module, module, module);
+
+        Data       data  = new DefaultData("module", null, null, items);
+
+        return new Data[] {data};
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/OutputTab.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,36 @@
+package de.intevation.flys.client.client.ui;
+
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.tab.Tab;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.OutputMode;
+
+
+public class OutputTab extends Tab {
+
+    /** The OutputMode that should be displayed in this tab.*/
+    protected OutputMode mode;
+
+    /** The Collection that should be displayed in this tab.*/
+    protected Collection collection;
+
+
+    /**
+     * The default constructor that creates a new Tab for displaying a specific
+     * OutputMode of a Collection.
+     *
+     * @param title The title of the tab.
+     * @param collection The collection that need to be displayed.
+     * @param outputmode The OutputMode that need to be displayed.
+     */
+    public OutputTab(String title, Collection collection, OutputMode mode) {
+        super(title);
+
+        this.collection = collection;
+        this.mode       = mode;
+
+        setPane(new Label("Implement concrete subclasses to vary the 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-client/src/main/java/de/intevation/flys/client/client/ui/ParameterList.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,550 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.tab.Tab;
+
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.ArtifactDescription;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.shared.model.ExportMode;
+import de.intevation.flys.client.shared.model.OutputMode;
+import de.intevation.flys.client.shared.model.River;
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.FLYS;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.event.CollectionChangeEvent;
+import de.intevation.flys.client.client.event.CollectionChangeHandler;
+import de.intevation.flys.client.client.event.HasParameterChangeHandler;
+import de.intevation.flys.client.client.event.HasStepBackHandlers;
+import de.intevation.flys.client.client.event.HasStepForwardHandlers;
+import de.intevation.flys.client.client.event.ParameterChangeEvent;
+import de.intevation.flys.client.client.event.ParameterChangeHandler;
+import de.intevation.flys.client.client.event.StepBackEvent;
+import de.intevation.flys.client.client.event.StepBackHandler;
+import de.intevation.flys.client.client.event.StepForwardEvent;
+import de.intevation.flys.client.client.event.StepForwardHandler;
+import de.intevation.flys.client.client.services.AdvanceService;
+import de.intevation.flys.client.client.services.AdvanceServiceAsync;
+import de.intevation.flys.client.client.services.ArtifactService;
+import de.intevation.flys.client.client.services.ArtifactServiceAsync;
+import de.intevation.flys.client.client.services.StepForwardService;
+import de.intevation.flys.client.client.services.StepForwardServiceAsync;
+
+
+public class ParameterList
+extends      Tab
+implements   StepBackHandler, StepForwardHandler, ParameterChangeHandler,
+             HasParameterChangeHandler, CollectionChangeHandler
+{
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    /** The ArtifactService used to communicate with the Artifact server. */
+    protected ArtifactServiceAsync artifactService =
+        GWT.create(ArtifactService.class);
+
+    /** The StepForwardService used to put data into an existing artifact. */
+    protected StepForwardServiceAsync forwardService =
+        GWT.create(StepForwardService.class);
+
+    /** The StepForwardService used to put data into an existing artifact. */
+    protected AdvanceServiceAsync advanceService =
+        GWT.create(AdvanceService.class);
+
+
+    /** The list of ParameterizationChangeHandler.*/
+    protected List<ParameterChangeHandler> parameterHandlers;
+
+    protected FLYS flys;
+
+    protected CollectionView cView;
+
+    protected Artifact artifact;
+
+    protected List<DataList> old;
+    protected DataList   current;
+
+    protected UIProvider uiProvider;
+
+    protected VLayout topLayout;
+    protected VLayout oldItems;
+    protected VLayout currentItems;
+    protected VLayout exportModes;
+    protected Canvas  helperPanel;
+    protected Canvas  tablePanel;
+
+    public ParameterList(FLYS flys, CollectionView cView, String title) {
+        super(title);
+
+        this.cView = cView;
+        this.flys  = flys;
+
+        parameterHandlers = new ArrayList<ParameterChangeHandler>();
+        old               = new ArrayList<DataList>();
+        topLayout         = new VLayout();
+        oldItems          = new VLayout();
+        currentItems      = new VLayout();
+        exportModes       = new VLayout();
+
+        addParameterChangeHandler(this);
+
+        init();
+    }
+
+
+    public ParameterList(
+        FLYS           flys,
+        CollectionView cView,
+        String         title,
+        Artifact       artifact)
+    {
+        super(title);
+
+        this.cView    = cView;
+        this.flys     = flys;
+        this.artifact = artifact;
+
+        parameterHandlers = new ArrayList<ParameterChangeHandler>();
+        old               = new ArrayList<DataList>();
+        topLayout         = new VLayout();
+        oldItems          = new VLayout();
+        currentItems      = new VLayout();
+        exportModes       = new VLayout();
+
+        init();
+
+        addParameterChangeHandler(this);
+
+        setArtifact(artifact);
+    }
+
+
+    protected void init() {
+        HLayout rootLayout = new HLayout();
+        tablePanel = new Canvas();
+        rootLayout.setMembersMargin(20);
+
+        VLayout left = new VLayout();
+
+        if (old == null || old.size() == 0) {
+            oldItems.setHeight(0);
+        }
+
+        oldItems.setMembersMargin(10);
+        currentItems.setAlign(VerticalAlignment.TOP);
+
+        left.setMembersMargin(20);
+        left.setWidth(300);
+
+        left.addMember(oldItems);
+        left.addMember(currentItems);
+        left.addMember(exportModes);
+
+        // This canvas is used to render helper widgets
+        helperPanel = new Canvas();
+        helperPanel.setWidth("*");
+        helperPanel.setHeight100();
+
+        rootLayout.addMember(left);
+        rootLayout.addMember(helperPanel);
+
+        topLayout.addMember(rootLayout);
+        if (artifact == null) {
+            Canvas moduleSelection = renderNew();
+            moduleSelection.setLayoutAlign(VerticalAlignment.TOP);
+            currentItems.addMember(moduleSelection);
+        }
+
+        setPane(topLayout);
+    }
+
+
+    protected void setArtifact(Artifact artifact) {
+        Artifact tmp  = this.artifact;
+        this.artifact = artifact;
+
+        fireParameterChangeEvent(tmp, this.artifact);
+    }
+
+
+    /**
+     * This method registers a new ParameterChangeHandler.
+     *
+     * @param handler The new ParameterChangeHandler.
+     */
+    public void addParameterChangeHandler(ParameterChangeHandler handler) {
+        if (handler != null) {
+            parameterHandlers.add(handler);
+        }
+    }
+
+
+    /**
+     * This method calls the <code>onParameterChange()</code> method of all
+     * registered ParameterChangeHandler.
+     */
+    protected void fireParameterChangeEvent(Artifact old, Artifact newArt) {
+        for (ParameterChangeHandler handler: parameterHandlers) {
+            handler.onParameterChange(new ParameterChangeEvent(old, newArt));
+        }
+    }
+
+
+    /**
+     * This method creates a Canvas displaying the plugins of FLYS combined with
+     * a widget to select a river.
+     *
+     * @return a Canvas that displays the supported plugins and rivers of FLYS.
+     */
+    protected Canvas renderNew() {
+        River[] rivers   = flys.getRivers();
+        DataItem[] items = new DataItem[rivers.length];
+
+        int i = 0;
+        for (River river: rivers) {
+            String name = river.getName();
+            items[i++]  = new DefaultDataItem(name, null, name);
+        }
+
+        Data data = new DefaultData(
+            "river",
+            MSG.river_selection(),
+            null,
+            items);
+
+        LinkSelection widget         = new LinkSelection();
+        HasStepForwardHandlers handler = (HasStepForwardHandlers) widget;
+
+        widget.setContainer(helperPanel);
+
+        handler.addStepForwardHandler(new StepForwardHandler() {
+            public void onStepForward(StepForwardEvent event) {
+                Data[] data = event.getData();
+
+                DataItem[] moduleItems = data[0].getItems();
+                DataItem[] riversItems = data[1].getItems();
+
+                String module = moduleItems[0].getStringValue();
+                String river  = riversItems[0].getStringValue();
+
+                if (module == null) {
+                    // TODO throw / show error!
+                    return;
+                }
+
+                String newTitle = MSG.getString(module);
+                setTitle(newTitle);
+
+                Config config       = Config.getInstance();
+                final String url    = config.getServerUrl();
+                final String locale = config.getLocale();
+
+                final Data[] feedData  = new Data[] { data[1] };
+
+                artifactService.create(
+                    url, locale, module.toLowerCase(),
+                    new AsyncCallback<Artifact>() {
+                        public void onFailure(Throwable caught) {
+                            GWT.log("Could not create the new artifact.");
+                            SC.warn(MSG.getString(caught.getMessage()));
+                        }
+
+                        public void onSuccess(Artifact artifact) {
+                            GWT.log("Successfully created a new artifact.");
+
+                            forwardService.go(url, locale, artifact, feedData,
+                            new AsyncCallback<Artifact>() {
+                                public void onFailure(Throwable caught) {
+                                    GWT.log("Could not feed the artifact.");
+                                    SC.warn(caught.getMessage());
+                                }
+
+                                public void onSuccess(Artifact artifact) {
+                                    GWT.log("Successfully feed the artifact.");
+                                    setArtifact(artifact);
+                                }
+                            });
+                        }
+                });
+            }
+        });
+
+        DataList list = new DataList();
+        list.add(data);
+
+        return widget.create(list);
+    }
+
+
+    protected void clearOldData() {
+        old.clear();
+    }
+
+
+    public void addOldData(DataList old) {
+        if (old != null) {
+            this.old.add(old);
+        }
+
+        refreshOld();
+    }
+
+
+    public void addOldDatas(DataList[] old) {
+        if (old != null && old.length > 0) {
+            for (DataList o: old) {
+                if (!exists(o)) {
+                    GWT.log("Data '" + o.getLabel() + "' is new.");
+                    addOldData(o);
+                }
+            }
+
+            return;
+        }
+
+        addOldData(null);
+    }
+
+
+    public boolean exists(DataList data) {
+        String stateName = data.getState();
+
+        for (DataList o: old) {
+            if (stateName.equals(o.getState())) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+
+    public void setCurrentData(DataList current, UIProvider uiProvider) {
+        this.current    = current;
+        this.uiProvider = uiProvider;
+
+        refreshCurrent();
+    }
+
+
+    public void refreshOld() {
+        // XXX maybe we should not remove all items but just add the newest item
+        // to the list.
+        oldItems.removeMembers(oldItems.getMembers());
+
+        for (DataList dataList: old) {
+            HLayout h = new HLayout();
+            h.setAlign(VerticalAlignment.TOP);
+            h.setHeight(20);
+
+            String     provider   = dataList.getUIProvider();
+            UIProvider uiprovider = UIProviderFactory.getProvider(provider);
+            ((HasStepBackHandlers) uiprovider).addStepBackHandler(this);
+
+            oldItems.addMember(uiprovider.createOld(dataList));
+        }
+
+        int minHeight = oldItems.getMinHeight();
+        if (minHeight <= 20) {
+            oldItems.setHeight(20);
+        }
+        else {
+            oldItems.setHeight(minHeight);
+        }
+    }
+
+
+    /**
+     * This method refreshes the part displaying the data of the current state.
+     * The UI is created using the UIProvider stored in the Data object.
+     */
+    public void refreshCurrent() {
+        currentItems.removeMembers(currentItems.getMembers());
+
+        if (current != null && uiProvider != null) {
+            Canvas c = uiProvider.create(current);
+            c.setLayoutAlign(VerticalAlignment.TOP);
+
+            currentItems.addMember(c);
+        }
+        else {
+            currentItems.setHeight(1);
+        }
+    }
+
+
+    /**
+     * This method is called if the user clicks on the 'next' button to advance
+     * to the next state.
+     *
+     * @param event The StepForwardEvent.
+     */
+    public void onStepForward(StepForwardEvent event) {
+        GWT.log("CollectionView - onStepForward()");
+
+        Config config    = Config.getInstance();
+        String serverUrl = config.getServerUrl();
+        String locale    = config.getLocale();
+
+        forwardService.go(serverUrl, locale, artifact, event.getData(),
+            new AsyncCallback<Artifact>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not feed the artifact.");
+                    SC.warn(MSG.getString(caught.getMessage()));
+                }
+
+                public void onSuccess(Artifact artifact) {
+                    GWT.log("Successfully feed the artifact.");
+                    setArtifact(artifact);
+                }
+        });
+    }
+
+
+    /**
+     * This method is used to remove all old items from this list after the user
+     * has clicked the step back button.
+     *
+     * @param e The StepBackEvent that holds the identifier of the target state.
+     */
+    public void onStepBack(StepBackEvent e) {
+        final String target    = e.getTarget();
+
+        Config config          = Config.getInstance();
+        final String serverUrl = config.getServerUrl();
+        final String locale    = config.getLocale();
+
+        advanceService.advance(serverUrl, locale, artifact, target,
+            new AsyncCallback<Artifact>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not go back to '" + target + "'");
+                    SC.warn(MSG.getString(caught.getMessage()));
+                }
+
+                public void onSuccess(Artifact artifact) {
+                    GWT.log("Successfully step back to '" + target + "'");
+
+                    old.clear();
+                    oldItems.removeMembers(oldItems.getMembers());
+
+                    setArtifact(artifact);
+                }
+            }
+        );
+    }
+
+
+    /**
+     * Implements the onCollectionChange() method to do update the GUI after the
+     * parameterization has changed.
+     *
+     * @param event The ParameterChangeEvent.
+     */
+    public void onParameterChange(ParameterChangeEvent event) {
+        GWT.log("ParameterList.onParameterChange");
+
+        for (Canvas c: helperPanel.getChildren()) {
+            helperPanel.removeChild(c);
+        }
+
+        Artifact art             = event.getNewValue();
+        ArtifactDescription desc = art.getArtifactDescription();
+
+        DataList currentData = desc.getCurrentData();
+        if (currentData != null) {
+            // the user has to enter some attributes
+            String uiProvider   = currentData.getUIProvider();
+            UIProvider provider = UIProviderFactory.getProvider(uiProvider);
+
+            provider.setContainer(helperPanel);
+            provider.setArtifact(art);
+
+            ((HasStepForwardHandlers) provider).addStepForwardHandler(this);
+            ((HasStepBackHandlers) provider).addStepBackHandler(this);
+
+            setCurrentData(currentData, provider);
+        }
+        else {
+            // we have reached a final state with no more user input
+            setCurrentData(null, null);
+        }
+
+        addOldDatas(desc.getOldData());
+    }
+
+
+    public void onCollectionChange(CollectionChangeEvent event) {
+        Collection c = event.getNewValue();
+
+        Map<String, OutputMode> outs = c.getOutputModes();
+        Set<String>             keys = outs.keySet();
+
+        List<ExportMode> exports = new ArrayList<ExportMode>();
+
+        for (String outname: keys) {
+            OutputMode out = outs.get(outname);
+
+            if (out instanceof ExportMode) {
+                exports.add((ExportMode) out);
+            }
+        }
+
+        updateExportModes(c, exports);
+    }
+
+
+    protected void updateExportModes(Collection c, List<ExportMode> exports) {
+        int num = exports != null ? exports.size() : 0;
+        GWT.log("Update export modes: " + num);
+
+        exportModes.removeMembers(exportModes.getMembers());
+
+        if (exports.size() > 0) {
+            exportModes.addMember(new ExportPanel(c, exports));
+        }
+    }
+
+
+    /**
+     * Adds a table to the parameterlist to show calculated data.
+     *
+     * @param table The table data panel.
+     */
+    public void setPanel (TableDataPanel table) {
+        tablePanel = table.create();
+        tablePanel.setHeight100();
+        tablePanel.setWidth100();
+        helperPanel.addChild(tablePanel);
+    }
+
+
+    /**
+     * Removes the table from the parameter list.
+     */
+    public void removePanel() {
+        topLayout.removeMember(tablePanel);
+    }
+
+
+    public boolean hasPanel() {
+        return topLayout.hasMember(tablePanel);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/ProjectList.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,304 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.Date;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.types.ListGridFieldType;
+import com.smartgwt.client.types.SortDirection;
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.grid.CellFormatter;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.grid.events.CellDoubleClickEvent;
+import com.smartgwt.client.widgets.grid.events.CellDoubleClickHandler;
+import com.smartgwt.client.widgets.grid.events.RowContextClickEvent;
+import com.smartgwt.client.widgets.grid.events.RowContextClickHandler;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.menu.Menu;
+import com.smartgwt.client.widgets.menu.MenuItem;
+import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent;
+import com.smartgwt.client.widgets.menu.events.ClickHandler;
+import com.smartgwt.client.types.VerticalAlignment;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.CollectionRecord;
+import de.intevation.flys.client.shared.model.User;
+
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.FLYS;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.event.CollectionChangeEvent;
+import de.intevation.flys.client.client.event.CollectionChangeHandler;
+import de.intevation.flys.client.client.services.UserCollectionsService;
+import de.intevation.flys.client.client.services.UserCollectionsServiceAsync;
+
+
+/**
+ * The project list shows a list of projects of a specific user.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ProjectList
+extends      VLayout
+implements   CollectionChangeHandler
+{
+    /** The interface that provides i18n messages. */
+    private FLYSConstants messages = GWT.create(FLYSConstants.class);
+
+    /** The UserService used to retrieve information about the current user. */
+    protected UserCollectionsServiceAsync userCollectionsService =
+        GWT.create(UserCollectionsService.class);
+
+    /** A pointer to the FLYS instance.*/
+    protected FLYS flys;
+
+    /** The user whose projects should be displayed.*/
+    protected User user;
+
+    /** The grid that contains the project rows.*/
+    protected ListGrid grid;
+
+    /**
+     * The default constructor that creates a new ProjectList for a specific
+     * user.
+     *
+     * @param user The user.
+     */
+    public ProjectList(FLYS flys, User user) {
+        this.flys = flys;
+        this.user = user;
+
+        grid = new ListGrid();
+        initGrid();
+        init();
+
+        updateUserCollections();
+    }
+
+
+    protected void initGrid() {
+        grid.setEmptyMessage(messages.no_projects());
+        grid.setLoadingDataMessage(messages.load_projects());
+        grid.setShowRecordComponents(true);
+        grid.setShowRecordComponentsByCell(true);
+        grid.setCanRemoveRecords(false);
+        grid.setShowHeader(false);
+        grid.setWidth100();
+        grid.setHeight100();
+        grid.setSortDirection(SortDirection.DESCENDING);
+        grid.setSortField(0);
+
+        ListGridField date = buildDateField();
+        ListGridField name = buildNameField();
+
+        grid.setFields(date, name);
+
+        grid.addCellDoubleClickHandler(new CellDoubleClickHandler() {
+            public void onCellDoubleClick(CellDoubleClickEvent e) {
+                CollectionRecord record = (CollectionRecord) e.getRecord();
+                String uuid = record != null
+                    ? record.getCollection().identifier()
+                    : "";
+                getFlys().openProject(uuid);
+            }
+        });
+
+        grid.addRowContextClickHandler(new RowContextClickHandler() {
+            public void onRowContextClick(RowContextClickEvent event) {
+                CollectionRecord record = (CollectionRecord) event.getRecord();
+
+                Menu menu = createContextMenu(record);
+                grid.setContextMenu(menu);
+                menu.showContextMenu();
+
+                event.cancel();
+            }
+        });
+    }
+
+
+    public FLYS getFlys() {
+        return flys;
+    }
+
+
+    /**
+     * Creates a new context menu that interacts with a CollectionRecord.
+     *
+     * @param record The selected record.
+     *
+     * @return the context menu with operations that interact with
+     * <i>record</i>.
+     */
+    protected Menu createContextMenu(final CollectionRecord record) {
+        Menu menu = new Menu();
+
+        MenuItem open = new MenuItem(messages.open_project());
+        open.addClickHandler(new ClickHandler() {
+            public void onClick(MenuItemClickEvent evt) {
+                getFlys().openProject(record.getCollection().identifier());
+            }
+        });
+
+        MenuItem del = new MenuItem(messages.delete_project());
+        del.addClickHandler(new ClickHandler() {
+            public void onClick(MenuItemClickEvent evt) {
+                SC.warn("Removing projects is not implemented.");
+            }
+        });
+
+        menu.addItem(open);
+        menu.addItem(del);
+
+        return menu;
+    }
+
+
+    /**
+     * The init() method handles the layout stuff for this widget.
+     */
+    protected void init() {
+        setWidth(300);
+        setHeight100();
+        setShowResizeBar(true);
+        setShowEdges(false);
+        setLayoutMargin(0);
+        setLayoutAlign(VerticalAlignment.TOP);
+
+        Label title = new Label(messages.projects());
+        title.setHeight("20");
+        title.setMargin(5);
+        title.setWidth100();
+        title.setStyleName("fontLightSmall");
+
+        Canvas titleWrapper = new Canvas();
+        titleWrapper.setStyleName("bgBlueDark");
+        titleWrapper.setWidth100();
+        titleWrapper.setHeight("20px");
+        titleWrapper.addChild(title);
+
+        Canvas gridWrapper = new Canvas();
+        gridWrapper.setPadding(0);
+        titleWrapper.setWidth100();
+        gridWrapper.addChild(grid);
+
+        addMember(titleWrapper);
+        addMember(gridWrapper);
+    }
+
+
+    public void onCollectionChange(CollectionChangeEvent event) {
+        GWT.log("ProjectList.onCollectionChange");
+
+        updateUserCollections();
+    }
+
+
+    protected void updateUserCollections() {
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        userCollectionsService.getUserCollections(url, locale, user.identifier(),
+            new AsyncCallback<Collection[]>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not recieve a list of user collections.");
+                    GWT.log(caught.getMessage());
+                }
+
+                public void onSuccess(Collection[] collections) {
+                    int num = collections != null ? collections.length : 0;
+
+                    GWT.log("Received " + num + " user collections.");
+
+                    if (num == 0) {
+                        return;
+                    }
+
+                    updateGrid(collections);
+                }
+            }
+        );
+    }
+
+
+    protected void clearGrid() {
+        ListGridRecord[] records = grid.getRecords();
+
+        for (ListGridRecord record: records) {
+            grid.removeData(record);
+        }
+    }
+
+
+    protected void updateGrid(Collection[] collections) {
+        clearGrid();
+
+        for (Collection c: collections) {
+            grid.addData(new CollectionRecord(c));
+        }
+    }
+
+
+    /**
+     * Builds the field in the grid that displays the creation time of a
+     * project.
+     *
+     * @return the grid field.
+     */
+    protected ListGridField buildDateField() {
+        ListGridField date = new ListGridField("creationTime", "creationTime");
+        date.setType(ListGridFieldType.DATE);
+
+        date.setCellFormatter(new CellFormatter() {
+            public String format(Object value, ListGridRecord rec, int r, int c) {
+                if (value == null) {
+                    return null;
+                }
+
+                DateTimeFormat dtf = DateTimeFormat.getFormat(
+                    messages.datetime_format());
+
+                return dtf.format((Date)value);
+            }
+        });
+
+        date.setWidth(105);
+        date.setAlign(Alignment.LEFT);
+
+        return date;
+    }
+
+
+    /**
+     * Builds the field in the grid that displays the name of a project.
+     *
+     * @return the grid field.
+     */
+    protected ListGridField buildNameField() {
+        ListGridField name = new ListGridField("name", "name");
+        name.setType(ListGridFieldType.TEXT);
+
+        name.setCellFormatter(new CellFormatter() {
+            public String format(Object value, ListGridRecord record, int row, int col) {
+                String n = (String) value;
+                int len  = n.length();
+                int sec  = len - 15;
+                return n.substring(0, 14) + "..." + n.substring(sec, len-1);
+            }
+        });
+
+        name.setWidth(195);
+        name.setAlign(Alignment.LEFT);
+
+        return name;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,177 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.ComboBoxItem;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+/**
+ * This UIProvider displays the DataItems of the Data object in a combo box.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SelectProvider
+extends      AbstractUIProvider
+{
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants messages = GWT.create(FLYSConstants.class);
+
+    /** The combobox.*/
+    protected DynamicForm form;
+
+
+    /**
+     * This method currently returns a
+     * {@link com.smartgwt.client.widgets.form.DynamicForm} that contains all
+     * data items in a combobox stored in <i>data</i>.
+     *
+     * @param data The {@link Data} object.
+     *
+     * @return a combobox.
+     */
+    public Canvas create(DataList data) {
+        VLayout v = new VLayout();
+        v.setMembersMargin(10);
+
+        Canvas content = createWidget(data);
+        Canvas button  = getNextButton();
+
+        v.addMember(content);
+        v.addMember(button);
+
+        return v;
+    }
+
+
+    public Canvas createOld(DataList dataList) {
+        HLayout layout  = new HLayout();
+        VLayout vLayout = new VLayout();
+        layout.setWidth("400px");
+
+        Label label = new Label(dataList.getLabel());
+        label.setWidth("200px");
+
+        int size = dataList.size();
+        for (int i = 0; i < size; i++) {
+            Data data        = dataList.get(i);
+            DataItem[] items = data.getItems();
+
+            for (DataItem item: items) {
+                HLayout hLayout = new HLayout();
+
+                hLayout.addMember(label);
+                hLayout.addMember(new Label(item.getLabel()));
+
+                vLayout.addMember(hLayout);
+                vLayout.setWidth("130px");
+            }
+        }
+
+        Canvas back = getBackButton(dataList.getState());
+
+        layout.addMember(label);
+        layout.addMember(vLayout);
+        layout.addMember(back);
+
+        return layout;
+    }
+
+
+    /**
+     * This method creates the content of the widget.
+     *
+     * @param data The {@link Data} object.
+     *
+     * @return a combobox.
+     */
+    protected Canvas createWidget(DataList data) {
+        GWT.log("SelectProvider - create()");
+
+        VLayout layout   = new VLayout();
+        layout.setAlign(VerticalAlignment.TOP);
+        layout.setHeight(25);
+
+        LinkedHashMap initial = new LinkedHashMap();
+
+        form = new DynamicForm();
+
+        int size = data.size();
+
+        for (int i = 0; i < size; i++) {
+            Data d = data.get(i);
+
+            Label label = new Label(d.getDescription());
+            label.setValign(VerticalAlignment.TOP);
+            label.setHeight(20);
+
+            ComboBoxItem combobox = new ComboBoxItem(d.getLabel());
+
+            LinkedHashMap<String, String> it = new LinkedHashMap<String, String>();
+
+            boolean firstItem = true;
+
+            for (DataItem item: d.getItems()) {
+                if (firstItem) {
+                    initial.put(d.getLabel(), item.getStringValue());
+                    firstItem = false;
+                }
+
+                it.put(item.getStringValue(), item.getLabel());
+            }
+
+            label.setWidth(50);
+            combobox.setValueMap(it);
+            combobox.setShowTitle(false);
+            form.setItems(combobox);
+
+            layout.addMember(label);
+            layout.addMember(form);
+        }
+
+        form.setValues(initial);
+
+        layout.setAlign(VerticalAlignment.TOP);
+
+        return layout;
+    }
+
+
+    protected Data[] getData() {
+        Map values    = form.getValues();
+        Iterator keys = values.keySet().iterator();
+
+        Data[] list = new Data[values.size()];
+        int       i = 0;
+
+        while (keys.hasNext()) {
+            String fieldname = (String) keys.next();
+            String selection = (String) values.get(fieldname);
+
+            DataItem item    = new DefaultDataItem(fieldname, null, selection);
+
+            list[i++] = new DefaultData(
+                fieldname, null, null, new DataItem[] { item });
+        }
+
+        return list;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,350 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
+import com.smartgwt.client.widgets.form.fields.events.BlurEvent;
+import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
+import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
+
+import com.smartgwt.client.types.ListGridFieldType;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.shared.model.DistanceInfoObject;
+import de.intevation.flys.client.shared.model.DistanceInfoRecord;
+import de.intevation.flys.client.shared.model.ArtifactDescription;
+
+import de.intevation.flys.client.client.services.DistanceInfoService;
+import de.intevation.flys.client.client.services.DistanceInfoServiceAsync;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.FLYSImages;
+import de.intevation.flys.client.client.Config;
+
+
+/**
+ * This UIProvider creates a widget to enter locations.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class SingleLocationPanel
+extends      AbstractUIProvider
+{
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);
+
+    /** The interface that provides the image resources. */
+    private FLYSImages IMAGES = GWT.create(FLYSImages.class);
+
+    /** The DistanceInfoService used to retrieve locations about rivers.*/
+    protected DistanceInfoServiceAsync distanceInfoService =
+        GWT.create(DistanceInfoService.class);
+
+    /** A container that will contain the location or the distance panel.*/
+    protected HLayout container;
+
+    /** The values entered in the location mode.*/
+    protected double[] values;
+
+    /** The input panel for locations */
+    protected DoubleArrayPanel locationPanel;
+
+    /** The locations table */
+    protected ListGrid locationTable;
+
+    /** The table data. */
+    protected DistanceInfoObject[] tableData;
+
+    /**
+     * Creates a new LocationDistancePanel instance.
+     */
+    public SingleLocationPanel() {
+        locationTable = new ListGrid();
+        locationTable.setShowHeaderContextMenu(false);
+    }
+
+
+    /**
+     * This method creates a widget that contains a label, a panel with
+     * checkboxes to switch the input mode between location and distance input,
+     * and a the mode specific panel.
+     *
+     * @param data The data that might be inserted.
+     *
+     * @return a panel.
+     */
+    public Canvas create(DataList data) {
+        VLayout layout = new VLayout();
+        layout.setMembersMargin(10);
+
+        initDefaults(data);
+
+        Label label   = new Label(MESSAGES.location ());
+        Canvas widget = createWidget(data);
+        Canvas submit = getNextButton();
+
+        createLocationTable();
+
+        widget.setHeight(50);
+        label.setHeight(25);
+
+        layout.addMember(label);
+        layout.addMember(widget);
+        layout.addMember(submit);
+
+        return layout;
+    }
+
+
+    /**
+     * This method creates a table that contains the location values.
+     */
+    protected void createLocationTable() {
+        locationTable.setWidth100();
+        locationTable.setShowRecordComponents(true);
+        locationTable.setShowRecordComponentsByCell(true);
+        locationTable.setHeight100();
+        locationTable.setEmptyMessage(MESSAGES.empty_table());
+
+        ListGridField addLocation = new ListGridField ("", "");
+        addLocation.setType (ListGridFieldType.ICON);
+        addLocation.setWidth ("30");
+        addLocation.addRecordClickHandler (new RecordClickHandler () {
+            public void onRecordClick (RecordClickEvent e) {
+                ListGridRecord[] records = locationTable.getSelection();
+                double[] selected = new double[1];
+                selected[0] = records[0].getAttributeAsDouble("from");
+                setLocationValues(selected);
+            }
+        });
+        addLocation.setCellIcon (IMAGES.markerGreen ().getURL ());
+
+        ListGridField ldescr = new ListGridField("description",
+                MESSAGES.description());
+        ldescr.setType(ListGridFieldType.TEXT);
+        ldescr.setWidth("*");
+        ListGridField lside = new ListGridField("riverside",
+                MESSAGES.riverside());
+        lside.setType(ListGridFieldType.TEXT);
+        lside.setWidth(60);
+        ListGridField loc = new ListGridField("from", MESSAGES.location());
+        loc.setType(ListGridFieldType.TEXT);
+        loc.setWidth(80);
+        locationTable.setFields(addLocation, ldescr, loc, lside);
+    }
+
+
+    public Canvas createOld(DataList dataList) {
+        List<Data> items = dataList.getAll();
+        Data dLocation = getData(items, "ld_locations");
+        DataItem[] loc = dLocation.getItems();
+
+        HLayout layout = new HLayout();
+        layout.setWidth("400px");
+
+        Label   label  = new Label(dataList.getLabel());
+        label.setWidth("200px");
+
+        Canvas back = getBackButton(dataList.getState());
+
+        Label selected = new Label(loc[0].getLabel());
+        selected.setWidth("130px");
+
+        layout.addMember(label);
+        layout.addMember(selected);
+        layout.addMember(back);
+
+        return layout;
+    }
+
+
+    /**
+     * This method reads the default values defined in the DataItems of the Data
+     * objects in <i>list</i>.
+     *
+     * @param list The DataList container that stores the Data objects.
+     */
+    protected void initDefaults(DataList list) {
+    }
+
+
+    /**
+     * This method greps the Data with name <i>name</i> from the list and
+     * returns it.
+     *
+     * @param items A list of Data.
+     * @param name The name of the Data that we are searching for.
+     *
+     * @return the Data with the name <i>name</i>.
+     */
+    protected Data getData(List<Data> data, String name) {
+        for (Data d: data) {
+            if (name.equals(d.getLabel())) {
+                return d;
+            }
+        }
+
+        return null;
+    }
+
+
+    protected Canvas createWidget(DataList data) {
+        VLayout layout       = new VLayout();
+        container            = new HLayout();
+
+        // the initial view will display the location input mode
+        locationPanel = new DoubleArrayPanel(
+                MESSAGES.unitLocation(),
+                getLocationValues(),
+                new BlurHandler(){public void onBlur(BlurEvent be) {}});
+        container.addMember(locationPanel);
+
+        layout.addMember(container);
+
+        container.setMembersMargin(30);
+
+        helperContainer.addChild(locationTable);
+        createInputPanel();
+        return layout;
+    }
+
+
+    /**
+     * This method returns the selected data.
+     *
+     * @return the selected/inserted data.
+     */
+    public Data[] getData() {
+        saveLocationValues(locationPanel);
+        double[] values = getLocationValues();
+        Data[] data = new Data[values.length];
+        DataItem item = new DefaultDataItem();
+        for (int i = 0; i < values.length; i++) {
+            item = new DefaultDataItem(
+                "ld_locations",
+                "ld_locations",
+                Double.valueOf(values[i]).toString());
+            data[i] = new DefaultData(
+                "ld_locations",
+                null,
+                null,
+                new DataItem[] {item});
+        }
+        return data;
+    }
+
+
+
+
+    /**
+     * Validates and stores all values entered in the location mode.
+     *
+     * @param p The DoubleArrayPanel.
+     */
+    protected void saveLocationValues(DoubleArrayPanel p) {
+        FormItem[] formItems = p.getFields();
+
+        for (FormItem item: formItems) {
+            if (item.getFieldName().equals(DoubleArrayPanel.FIELD_NAME)) {
+                saveLocationValue(p, item);
+            }
+        }
+    }
+
+
+    /**
+     * Validates and stores a value entered in the location mode.
+     *
+     * @param p The DoubleArrayPanel.
+     * @param item The item that needs to be validated.
+     */
+    protected void saveLocationValue(DoubleArrayPanel p, FormItem item) {
+        if (p.validateForm(item)) {
+            setLocationValues(p.getInputValues(item));
+        }
+    }
+
+
+    protected void createInputPanel() {
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale ();
+        String river  = "";
+
+        ArtifactDescription adescr = artifact.getArtifactDescription();
+        DataList[] data = adescr.getOldData();
+
+        if (data != null && data.length > 0) {
+            for (int i = 0; i < data.length; i++) {
+                DataList dl = data[i];
+                if (dl.getState().equals("state.winfo.river")) {
+                    for (int j = 0; j < dl.size(); j++) {
+                        Data d = dl.get(j);
+                        DataItem[] di = d.getItems();
+                        if (di != null && di.length == 1) {
+                           river = d.getItems()[0].getStringValue();
+                           break;
+                        }
+                    }
+                }
+            }
+        }
+
+        distanceInfoService.getDistanceInfo(url, locale, river,
+            new AsyncCallback<DistanceInfoObject[]>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not recieve location informations.");
+                    GWT.log(caught.getMessage());
+                }
+
+                public void onSuccess(DistanceInfoObject[] di) {
+                    int num = di != null ? di.length :0;
+                    GWT.log("Recieved " + num + " location informations.");
+
+                    if (num == 0) {
+                        return;
+                    }
+                    tableData = di;
+                    updateLocationInfo(di);
+                }
+            }
+        );
+    }
+
+
+    protected void updateLocationInfo(DistanceInfoObject[] di) {
+        int i = 0;
+        for (DistanceInfoObject dio: di) {
+            if (dio.getTo() == null) {
+                locationTable.addData(new DistanceInfoRecord(dio));
+            }
+        }
+        return;
+    }
+
+
+    protected double[] getLocationValues() {
+        return values;
+    }
+
+
+    protected void setLocationValues(double[] values) {
+        this.values = values;
+        locationPanel.setValues(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-client/src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,159 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+
+import de.intevation.flys.client.shared.model.DataList;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.Config;
+
+import de.intevation.flys.client.client.services.CSVExportService;
+import de.intevation.flys.client.client.services.CSVExportServiceAsync;
+
+/**
+ * This UIProvider creates a widget that displays calculated data in a table.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class TableDataPanel
+{
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);
+
+    /** The DistanceInfoService used to retrieve locations about rivers.*/
+    protected CSVExportServiceAsync exportService =
+        GWT.create(CSVExportService.class);
+
+    /** A container that will contain the location or the distance panel.*/
+    protected VLayout container;
+
+    /** The export type.*/
+    protected String name;
+
+    /** The UUID of the collection */
+    protected String uuid;
+
+    /** The table*/
+    protected ListGrid dataTable;
+
+
+    /**
+     * Creates a new LocationDistancePanel instance.
+     */
+    public TableDataPanel() {
+        container = new VLayout();
+        dataTable = new ListGrid();
+        name      = "";
+    }
+
+
+    /**
+     * This method creates a widget that contains a table
+     *
+     * @param data The data that might be inserted.//Use this?
+     *
+     * @return a panel.
+     */
+    public Canvas create() {
+        Config config    = Config.getInstance();
+        String url       = config.getServerUrl();
+        String locale    = config.getLocale ();
+        dataTable.setEmptyMessage(MESSAGES.empty_table());
+        dataTable.setShowHeaderContextMenu(false);
+
+        exportService.getCSV(url, locale, uuid, name,
+            new AsyncCallback<List>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not recieve csv.");
+                    GWT.log(caught.getMessage());
+                }
+
+                public void onSuccess(List l) {
+                    GWT.log("Recieved csv with " + l.size() + " lines.");
+                    setData(l);
+                }
+            }
+        );
+
+        Label l = new Label (MESSAGES.calcTableTitle());
+        l.setHeight(20);
+
+        container.addMember(l);
+        container.addMember(dataTable);
+
+        return container;
+    }
+
+
+    public void setName(String name) {
+      this.name = name;
+    }
+
+    public void setUuid(String uuid) {
+      this.uuid = uuid;
+    }
+
+
+    public Canvas createOld(DataList dataList) {
+        return null;
+    }
+
+
+    protected Canvas createWidget(DataList data) {
+        return null;
+    }
+
+
+    /**
+     * This method sets the data to a dynmic table.
+     *
+     * @param list List if String[] containing the data.
+     */
+    public void setData(List list) {
+        String[] header = (String[])list.get(0);
+        ListGridField[] fields = new ListGridField[header.length];
+        for(int i = 0; i < header.length; i++) {
+            ListGridField f = new ListGridField(String.valueOf(i));
+            fields[i] = f;
+            f.setTitle(header[i]);
+        }
+
+        if (header.length == 2) {
+            dataTable.setFields(fields[0], fields[1]);
+        }
+        else if(header.length == 3) {
+            dataTable.setFields(fields[0], fields[1], fields[2]);
+        }
+        else if(header.length == 4) {
+            dataTable.setFields(fields[0], fields[1], fields[2], fields[3]);
+        }
+        else if(header.length == 5) {
+            dataTable.setFields(
+                fields[0],
+                fields[1],
+                fields[2],
+                fields[3],
+                fields[4]);
+        }
+
+        for(int i = 1; i < list.size(); i++) {
+            String[] sItem = (String[])list.get(i);
+            ListGridRecord r = new ListGridRecord();
+            for(int j = 0; j < sItem.length; j++) {
+                r.setAttribute(String.valueOf(j), sItem[j]);
+            }
+            dataTable.addData(r);
+        }
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/UIProvider.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,55 @@
+package de.intevation.flys.client.client.ui;
+
+import java.io.Serializable;
+
+import com.smartgwt.client.widgets.Canvas;
+
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.Artifact;
+
+/**
+ * This interface describes a method that creates a Canvas element displaying
+ * DataItems for a current state of the artifact.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface UIProvider extends Serializable {
+
+    /**
+     * This method creates a Canvas element showing the DataItems in
+     * <i>data</i>.
+     *
+     * @param data The DataList object.
+     *
+     * @return the Canvas showing the Data.
+     */
+    public Canvas create(DataList data);
+
+
+    /**
+     * This method creates a Canvas element showing the old Data objects in the
+     * DataList <i>data</i>.
+     *
+     * @param dataList The DataList which elements should be displayed.
+     *
+     * @return a Canvas displaying the Data.
+     */
+    public Canvas createOld(DataList dataList);
+
+
+    /**
+     * This method injects a container that is used to position helper widgets.
+     *
+     * @param container A container that is used to position helper widgets.
+     */
+    public void setContainer(Canvas container);
+
+
+    /**
+     * Sets an artifact that contains the status data information for a project.
+     *
+     * @param artifact The artifact containing status information.
+     */
+    public void setArtifact(Artifact artifact);
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/UIProviderFactory.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,35 @@
+package de.intevation.flys.client.client.ui;
+
+public class UIProviderFactory {
+
+    private UIProviderFactory() {
+    }
+
+    public static UIProvider getProvider(String uiProvider) {
+      System.out.println("Provider: " + uiProvider);
+        if (uiProvider == null || uiProvider.equals("")) {
+            return new SelectProvider();
+        }
+        else if (uiProvider.equals("select_with_map")) {
+            return new MapSelection();
+        }
+        else if (uiProvider.equals("location_distance_panel")) {
+            return new LocationDistancePanel();
+        }
+        else if (uiProvider.equals("location_panel")) {
+            return new SingleLocationPanel();
+        }
+        else if (uiProvider.equals("wq_panel")) {
+            return new WQInputPanel();
+        }
+        else if (uiProvider.equals("wq_panel_adapted")) {
+            return new WQAdaptedInputPanel();
+        }
+        else if (uiProvider.equals("river_panel")) {
+            return new LinkSelection();
+        }
+        else {
+            return new SelectProvider();
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/ui/WQAdaptedInputPanel.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,333 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.TitleOrientation;
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
+import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
+import com.smartgwt.client.widgets.form.fields.events.BlurEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
+import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+
+import de.intevation.flys.client.client.FLYSConstants;
+
+
+/**
+ * This UIProvider creates a widget to enter W or Q data for discharge
+ * longitudinal section computations.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQAdaptedInputPanel
+extends      AbstractUIProvider
+implements   ChangeHandler, BlurHandler
+{
+    public static final String FIELD_WQ_MODE = "wq_mode";
+    public static final String FIELD_WQ_W    = "W";
+    public static final String FIELD_WQ_Q    = "Q";
+
+    public static final String GAUGE_SEPARATOR = ":";
+
+    public static final String GAUGE_PART_SEPARATOR = ";";
+
+    public static final String VALUE_SEPARATOR = ",";
+
+    public static final int ROW_HEIGHT = 20;
+
+
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+    /** Stores the input panels related to their keys.*/
+    protected Map<String, DoubleArrayPanel> wqranges;
+
+    /** The RadioGroupItem that determines the w/q input mode.*/
+    protected DynamicForm modes;
+
+
+
+    public WQAdaptedInputPanel() {
+        wqranges = new HashMap<String, DoubleArrayPanel>();
+    }
+
+
+    public Canvas create(DataList data) {
+        Canvas submit = getNextButton();
+        Canvas widget = createWidget(data);
+        Label  label  = new Label(MSG.wqadaptedTitle());
+
+        label.setHeight(25);
+
+        VLayout layout = new VLayout();
+        layout.setMembersMargin(10);
+        layout.setWidth(350);
+
+        layout.addMember(label);
+        layout.addMember(widget);
+        layout.addMember(submit);
+
+        return layout;
+    }
+
+
+    public Canvas createOld(DataList dataList) {
+        List<Data> all = dataList.getAll();
+        Data    wqData = getData(all, "wq_values");
+        Data    wqMode = getData(all, "wq_mode");
+
+        Canvas back = getBackButton(dataList.getState());
+
+        HLayout valLayout  = new HLayout();
+        HLayout modeLayout = new HLayout();
+        VLayout vlayout    = new VLayout();
+
+        Label wqLabel   = new Label(dataList.getLabel());
+        Label modeLabel = new Label("");
+
+        wqLabel.setValign(VerticalAlignment.TOP);
+
+        wqLabel.setWidth(200);
+        wqLabel.setHeight(25);
+        modeLabel.setHeight(25);
+        modeLabel.setWidth(200);
+
+        valLayout.addMember(wqLabel);
+        valLayout.addMember(createOldWQValues(wqData));
+        valLayout.addMember(back);
+        modeLayout.addMember(modeLabel);
+
+        vlayout.addMember(valLayout);
+        vlayout.addMember(modeLayout);
+
+        return vlayout;
+    }
+
+
+    protected Canvas createOldWQValues(Data wqData) {
+        VLayout layout = new VLayout();
+
+        DataItem item  = wqData.getItems()[0];
+        String   value = item.getStringValue();
+
+        String[] gauges = value.split(GAUGE_SEPARATOR);
+
+        for (String gauge: gauges) {
+            HLayout h = new HLayout();
+
+            String[] parts  = gauge.split(GAUGE_PART_SEPARATOR);
+            String[] values = parts[2].split(VALUE_SEPARATOR);
+
+            Label l = new Label(parts[0] + " - " + parts[1] + ": ");
+
+            StringBuilder sb = new StringBuilder();
+            boolean    first = true;
+
+            for (String v: values) {
+                if (!first) {
+                    sb.append(", ");
+                }
+
+                sb.append(v);
+
+                first = false;
+            }
+
+            Label v = new Label(sb.toString());
+
+            l.setWidth(65);
+            v.setWidth(65);
+
+            h.addMember(l);
+            h.addMember(v);
+
+            layout.addMember(h);
+        }
+
+        return layout;
+    }
+
+
+    protected Canvas createWidget(DataList dataList) {
+        VLayout layout = new VLayout();
+
+        Canvas mode = createMode(dataList);
+        Canvas list = createList(dataList);
+
+        DataItem[] items = getWQItems(dataList);
+        int listHeight   = ROW_HEIGHT * items.length;
+
+        mode.setHeight(25);
+        mode.setWidth(200);
+
+        layout.addMember(mode);
+        layout.addMember(list);
+
+        layout.setHeight(25 + listHeight);
+        layout.setWidth(350);
+
+        return layout;
+    }
+
+
+    protected Canvas createList(DataList dataList) {
+        VLayout layout = new VLayout();
+
+        DataItem[] items = getWQItems(dataList);
+
+        for (DataItem item: items) {
+            String title = item.getLabel();
+
+            DoubleArrayPanel dap = new DoubleArrayPanel(
+                createLineTitle(title), null, this, TitleOrientation.LEFT);
+
+            wqranges.put(title, dap);
+
+            layout.addMember(dap);
+        }
+
+        layout.setHeight(items.length * ROW_HEIGHT);
+
+        return layout;
+    }
+
+
+    protected DataItem[] getWQItems(DataList dataList) {
+        List<Data> data = dataList.getAll();
+
+        for (Data d: data) {
+            String name = d.getLabel();
+
+            if (name.equals(FIELD_WQ_MODE)) {
+                continue;
+            }
+
+            return d.getItems();
+        }
+
+        return null;
+    }
+
+
+    protected Canvas createMode(DataList dataList) {
+        RadioGroupItem wq = new RadioGroupItem(FIELD_WQ_MODE);
+        wq.setShowTitle(false);
+        wq.setVertical(false);
+        wq.setWidth(200);
+
+        LinkedHashMap wqValues = new LinkedHashMap();
+        wqValues.put(FIELD_WQ_W, MSG.wqW());
+        wqValues.put(FIELD_WQ_Q, MSG.wqQ());
+
+        wq.setValueMap(wqValues);
+
+        modes = new DynamicForm();
+        modes.setFields(wq);
+        modes.setWidth(200);
+
+        LinkedHashMap initial = new LinkedHashMap();
+        initial.put(FIELD_WQ_MODE, FIELD_WQ_W);
+        modes.setValues(initial);
+
+        return modes;
+    }
+
+
+    public String createLineTitle(String key) {
+        String[] splitted = key.split(";");
+
+        return splitted[0] + " - " + splitted[1];
+    }
+
+
+    public Data[] getData() {
+        Data mode   = getWQMode();
+        Data values = getWQValues();
+
+        return new Data[] { mode, values };
+    }
+
+
+    protected Data getWQMode() {
+        String wqMode = modes.getValueAsString(FIELD_WQ_MODE);
+        DataItem item = new DefaultDataItem("wq_mode", "wq_mode", wqMode);
+        Data mode = new DefaultData(
+            "wq_mode", null, null, new DataItem[] { item });
+
+        return mode;
+    }
+
+
+    protected Data getWQValues() {
+        String wqvalue = null;
+
+        Iterator<String> iter = wqranges.keySet().iterator();
+        while (iter.hasNext()) {
+            String           key = iter.next();
+            DoubleArrayPanel dap = wqranges.get(key);
+
+            double[] values = dap.getInputValues();
+            if (wqvalue == null) {
+                wqvalue = createValueString(key, values);
+            }
+            else {
+                wqvalue += GAUGE_SEPARATOR + createValueString(key, values);
+            }
+        }
+
+        DataItem valueItem = new DefaultDataItem(
+            "wq_values", "wq_values", wqvalue);
+        Data values = new DefaultData(
+            "wq_values", null, null, new DataItem[] { valueItem });
+
+        return values;
+    }
+
+
+    protected String createValueString(String key, double[] values) {
+        StringBuilder sb = new StringBuilder();
+
+        boolean first = true;
+
+        for (double value: values) {
+            if (!first) {
+                sb.append(",");
+            }
+
+            sb.append(Double.toString(value));
+
+            first = false;
+        }
+
+        return key + ";" + sb.toString();
+    }
+
+
+    public void onChange(ChangeEvent event) {
+        // TODO IMPLEMENT ME
+    }
+
+
+    public void onBlur(BlurEvent event) {
+        DoubleArrayPanel dap = (DoubleArrayPanel) event.getForm();
+        dap.validateForm(event.getItem());
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,1166 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.data.Record;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.form.DynamicForm;
+import com.smartgwt.client.widgets.form.fields.FormItem;
+import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
+import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
+import com.smartgwt.client.widgets.form.fields.events.BlurEvent;
+import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
+import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+import com.smartgwt.client.widgets.grid.events.CellClickHandler;
+import com.smartgwt.client.widgets.grid.events.CellClickEvent;
+
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.shared.model.WQInfoObject;
+import de.intevation.flys.client.shared.model.WQInfoRecord;
+import de.intevation.flys.client.shared.model.ArtifactDescription;
+import com.smartgwt.client.types.ListGridFieldType;
+import com.smartgwt.client.types.SelectionStyle;
+
+import de.intevation.flys.client.client.services.WQInfoService;
+import de.intevation.flys.client.client.services.WQInfoServiceAsync;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.FLYSImages;
+import de.intevation.flys.client.client.Config;
+
+
+/**
+ * This UIProvider creates a widget to enter W or Q data.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQInputPanel
+extends      AbstractUIProvider
+implements   ChangeHandler, BlurHandler
+{
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MESSAGE = GWT.create(FLYSConstants.class);
+
+    /** The interface that provides the image resources. */
+    private FLYSImages IMAGES = GWT.create(FLYSImages.class);
+
+    /** The DistanceInfoService used to retrieve locations about rivers.*/
+    protected WQInfoServiceAsync wqInfoService =
+        GWT.create(WQInfoService.class);
+
+    /** The constant field name for choosing w or q mode.*/
+    public static final String FIELD_WQ = "wq";
+
+    /** The constant field value for W input mode.*/
+    public static final String FIELD_WQ_W = "W";
+
+    /** The constant field value for Q input mode.*/
+    public static final String FIELD_WQ_Q = "Q";
+
+    /** The constant field name for choosing single values or range.*/
+    public static final String FIELD_MODE = "mode";
+
+    /** The constant field value for single input mode.*/
+    public static final String FIELD_MODE_SINGLE = "single";
+
+    /** The constant field value for range input mode.*/
+    public static final String FIELD_MODE_RANGE = "range";
+
+    /** The constant value that determines the width of the left panel.*/
+    public static final int WIDTH_LEFT = 200;
+
+
+    /** The container that manages the w and q panels.*/
+    protected HLayout container;
+
+    /** The RadioGroupItem that determines the w/q input mode.*/
+    protected DynamicForm modes;
+
+    /** The min values for the 'from' property in the W-Range input mode.*/
+    protected double minW;
+
+    /** The min values for the 'from' property in the Q-Range input mode.*/
+    protected double minQ;
+
+    /** The max values for the 'from' property in the W-Range input mode.*/
+    protected double maxW;
+
+    /** The max values for the 'from' property in the Q-Range input mode.*/
+    protected double maxQ;
+
+    /** The 'from' value entered in the range W mode.*/
+    protected double fromW;
+
+    /** The 'to' value entered in the range W mode.*/
+    protected double toW;
+
+    /** The 'step' value entered in the range W mode.*/
+    protected double stepW;
+
+    /** The values entered in the single W mode.*/
+    protected double[] valuesW;
+
+    /** The 'from' value entered in the range Q mode.*/
+    protected double fromQ;
+
+    /** The 'to' value entered in the range Q mode.*/
+    protected double toQ;
+
+    /** The 'step' value entered in the range Q mode.*/
+    protected double stepQ;
+
+    /** The values entered in the single Q mode.*/
+    protected double[] valuesQ;
+
+    /** The input panel for W values*/
+    protected DoubleArrayPanel wArrayPanel;
+
+    /** The input panel for q values*/
+    protected DoubleArrayPanel qArrayPanel;
+
+    /** The input panel for w range*/
+    protected DoubleRangePanel wRangePanel;
+
+    /** The input panel for q range*/
+    protected DoubleRangePanel qRangePanel;
+
+    /** The WQ Data*/
+    protected WQInfoObject[] tableData;
+
+    /** The WQ Input Table*/
+    protected ListGrid wqTable;
+
+    /** The WQ input table for ranges.*/
+    protected ListGrid wqRangeTable;
+
+    /**
+     * Creates a new WQInputPanel instance.
+     */
+    public WQInputPanel() {
+        wqTable      = new ListGrid();
+        wqRangeTable = new ListGrid();
+        wqTable.setShowHeaderContextMenu(false);
+        wqRangeTable.setShowHeaderContextMenu(false);
+    }
+
+
+    /**
+     * This method calls createWidget and puts a 'next' button to the bottom.
+     *
+     * @param data The data that is displayed.
+     *
+     * @return the widget.
+     */
+    public Canvas create(DataList data) {
+        initDefaults(data);
+
+
+        Canvas  widget = createWidget(data);
+        Canvas  submit = getNextButton();
+        Label   label  = new Label(MESSAGE.wqTitle());
+
+        label.setHeight(25);
+
+        VLayout layout = new VLayout();
+        layout.setMembersMargin(10);
+
+        layout.addMember(label);
+        layout.addMember(widget);
+        layout.addMember(submit);
+
+        return layout;
+    }
+
+
+    public Canvas createOld(DataList dataList) {
+        List<Data> items = dataList.getAll();
+
+        Data dMode      = getData(items, "wq_mode");
+        Data dSelection = getData(items, "wq_selection");
+        Data dSingle    = getData(items, "wq_single");
+        Data dFrom      = getData(items, "wq_from");
+        Data dTo        = getData(items, "wq_to");
+        Data dStep      = getData(items, "wq_step");
+
+        DataItem[] mode = dMode.getItems();
+        String strMode  = mode[0].getStringValue();
+        boolean wMode   = strMode.equals(FIELD_WQ_W);
+
+        HLayout layout = new HLayout();
+        layout.setWidth("400px");
+
+        Label label  = new Label(dataList.getLabel());
+        label.setWidth("200px");
+
+        String text = null;
+
+        DataItem[] selItem = dSelection.getItems();
+        String     sel     = selItem != null
+            ? selItem[0].getStringValue()
+            : "";
+
+        if (sel.equals("single")) {
+            DataItem[] single = dSingle.getItems();
+
+            text = wMode
+                ? createWString(single[0])
+                : createQString(single[0]);
+        }
+        else {
+            DataItem[] from = dFrom.getItems();
+            DataItem[] to   = dTo.getItems();
+            DataItem[] step = dStep.getItems();
+
+            text = wMode
+                ? createWString(from[0], to[0], step[0])
+                : createQString(from[0], to[0], step[0]);
+        }
+
+        Label selected = new Label(text);
+        selected.setWidth("130px");
+
+        Canvas back = getBackButton(dataList.getState());
+
+        layout.addMember(label);
+        layout.addMember(selected);
+        layout.addMember(back);
+
+        return layout;
+    }
+
+
+    protected void createWQInputPanel() {
+        wqTable.setWidth100();
+        wqTable.setShowRecordComponents(true);
+        wqTable.setShowRecordComponentsByCell(true);
+        wqTable.setHeight100();
+        wqTable.setEmptyMessage(MESSAGE.empty_table());
+
+        wqRangeTable.setWidth100();
+        wqRangeTable.setShowRecordComponents(true);
+        wqRangeTable.setShowRecordComponentsByCell(true);
+        wqRangeTable.setHeight100();
+        wqRangeTable.setEmptyMessage(MESSAGE.empty_table());
+
+        ListGridField addWQ = new ListGridField("", "");
+        addWQ.setType(ListGridFieldType.ICON);
+        addWQ.setWidth(30);
+
+        CellClickHandler cch = new CellClickHandler() {
+            public void onCellClick(CellClickEvent e) {
+                if (e.getColNum() == 0 && e.getRecord().getEnabled ()) {
+                    Record r = e.getRecord ();
+                    double val = r.getAttributeAsDouble("value");
+                    if(isWMode() && !isRangeMode()) {
+                        double[] in = wArrayPanel.getInputValues();
+                        int size = 0;
+                        if (in == null) {
+                            double[] vals = new double[1];
+                            vals[0] = val;
+                            wArrayPanel.setValues(vals);
+                            setSingleW(vals);
+                        }
+                        else {
+                            double[] vals = new double[in.length+1];
+                            for(int i = 0; i < in.length; i++) {
+                                vals[i] = in[i];
+                            }
+                            vals[in.length] = val;
+                            wArrayPanel.setValues(vals);
+                            setSingleW(vals);
+                        }
+                    }
+                    else if (!isWMode() && !isRangeMode()) {
+                        double[] in = qArrayPanel.getInputValues();
+                        int size = 0;
+                        if (in == null) {
+                            double[] vals = new double[1];
+                            vals[0] = val;
+                            qArrayPanel.setValues(vals);
+                            setSingleQ(vals);
+                        }
+                        else {
+                            double[] vals = new double[in.length+1];
+                            for(int i = 0; i < in.length; i++) {
+                                vals[i] = in[i];
+                            }
+                            vals[in.length] = val;
+                            qArrayPanel.setValues(vals);
+                            setSingleQ(vals);
+                        }
+                    }
+                }
+            }
+        };
+
+        CellClickHandler cchRange = new CellClickHandler() {
+            public void onCellClick(CellClickEvent e) {
+                if (e.getColNum() == 0 && e.getRecord().getEnabled ()) {
+                    Record r = e.getRecord ();
+                    double val = r.getAttributeAsDouble("value");
+                    if(isWMode() && isRangeMode()) {
+                        wRangePanel.setValues(
+                            val,
+                            wRangePanel.getTo(),
+                            wRangePanel. getStep());
+                    }
+                    else if (!isWMode() && isRangeMode()) {
+                        qRangePanel.setValues(
+                            val,
+                            qRangePanel.getTo(),
+                            qRangePanel.getStep());
+                    }
+                }
+                if (e.getColNum() == 1 && e.getRecord().getEnabled()) {
+                    Record r = e.getRecord ();
+                    double val = r.getAttributeAsDouble("value");
+                    if (isWMode() && isRangeMode()) {
+                         wRangePanel.setValues(
+                             wRangePanel.getFrom(),
+                             val,
+                             wRangePanel.getStep());
+                    }
+                    else if(!isWMode() && isRangeMode()) {
+                        qRangePanel.setValues(
+                            qRangePanel.getFrom(),
+                            val,
+                            qRangePanel.getStep());
+                    }
+                }
+            }
+        };
+        wqTable.addCellClickHandler(cch);
+        wqRangeTable.addCellClickHandler(cchRange);
+        addWQ.setCellIcon (IMAGES.markerGreen().getURL());
+
+        ListGridField addWQMin = new ListGridField("", "");
+        addWQMin.setType(ListGridFieldType.ICON);
+        addWQMin.setWidth(30);
+        ListGridField addWQMax = new ListGridField("", "");
+        addWQMax.setType(ListGridFieldType.ICON);
+        addWQMax.setWidth(30);
+
+        wqRangeTable.addCellClickHandler(cch);
+        addWQMin.setCellIcon(IMAGES.markerGreen().getURL());
+        addWQMax.setCellIcon(IMAGES.markerRed().getURL());
+
+        ListGridField name = new ListGridField("name", MESSAGE.name());
+        name.setType(ListGridFieldType.TEXT);
+        name.setWidth("*");
+        ListGridField type = new ListGridField("type", MESSAGE.type());
+        type.setType(ListGridFieldType.TEXT);
+        type.setWidth("50");
+        ListGridField value = new ListGridField("value", MESSAGE.wq_value());
+        value.setType(ListGridFieldType.TEXT);
+        type.setWidth("50");
+
+        ListGridField name_r = new ListGridField("name", MESSAGE.name());
+        name_r.setType(ListGridFieldType.TEXT);
+        name_r.setWidth("*");
+        ListGridField type_r = new ListGridField("type", MESSAGE.type());
+        type_r.setType(ListGridFieldType.TEXT);
+        type_r.setWidth("50");
+        ListGridField value_r = new ListGridField("value", MESSAGE.wq_value());
+        value_r.setType(ListGridFieldType.TEXT);
+        type_r.setWidth("50");
+
+        wqTable.setSelectionType(SelectionStyle.SINGLE);
+        wqRangeTable.setSelectionType(SelectionStyle.SINGLE);
+        wqTable.setFields(addWQ, name, type, value);
+        wqRangeTable.setFields(addWQMin, addWQMax, name_r, type_r, value_r);
+    }
+
+    /**
+     * This method reads the default values defined in the DataItems of the Data
+     * objects in <i>list</i>.
+     *
+     * @param list The DataList container that stores the Data objects.
+     */
+    protected void initDefaults(DataList list) {
+        Data f = getData(list.getAll(), "wq_from");
+        Data t = getData(list.getAll(), "wq_to");
+        Data s = getData(list.getAll(), "wq_step");
+
+        DataItem fQItem = getDataItem(f.getItems(), "minQ");
+        DataItem fWItem = getDataItem(f.getItems(), "minW");
+        DataItem tQItem = getDataItem(t.getItems(), "maxQ");
+        DataItem tWItem = getDataItem(t.getItems(), "maxW");
+        DataItem sQItem = getDataItem(s.getItems(), "stepQ");
+        DataItem sWItem = getDataItem(s.getItems(), "stepW");
+
+        minW  = Double.valueOf(fWItem.getStringValue());
+        maxW  = Double.valueOf(tWItem.getStringValue());
+        stepW = Double.valueOf(sWItem.getStringValue());
+
+        minQ  = Double.valueOf(fQItem.getStringValue());
+        maxQ  = Double.valueOf(tQItem.getStringValue());
+        stepQ = Double.valueOf(sQItem.getStringValue());
+
+        this.fromW = minW;
+        this.toW   = maxW;
+        this.stepW = stepW;
+
+        this.fromQ = minQ;
+        this.toQ   = maxQ;
+        this.stepQ = stepQ;
+    }
+
+
+    protected String createWString(DataItem from, DataItem to, DataItem step) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(from.getLabel());
+        sb.append(" " + MESSAGE.unitWFrom() + " ");
+        sb.append(to.getLabel());
+        sb.append(" " + MESSAGE.unitWTo() + " ");
+        sb.append(step.getLabel());
+        sb.append(" " + MESSAGE.unitWStep());
+
+        return sb.toString();
+    }
+
+
+    protected String createWString(DataItem single) {
+        return single.getLabel();
+    }
+
+
+    protected String createQString(DataItem from, DataItem to, DataItem step) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(from.getLabel());
+        sb.append(" " + MESSAGE.unitQFrom() + " ");
+        sb.append(to.getLabel());
+        sb.append(" " + MESSAGE.unitQTo() + " ");
+        sb.append(step.getLabel());
+        sb.append(" " + MESSAGE.unitQStep());
+
+        return sb.toString();
+    }
+
+
+    protected String createQString(DataItem single) {
+        return single.getLabel();
+    }
+
+
+    /**
+     * This method creates the whole widget. There is a panel on the left, that
+     * allows the user to enter values manually by keyboard. On the right, there
+     * is a table that allows the user to enter values by mouse click.
+     *
+     * @param data The data that is displayed in the table on the right.
+     *
+     * @return the widget.
+     */
+    protected Canvas createWidget(DataList data) {
+        VLayout layout  = new VLayout();
+        container       = new HLayout();
+        Canvas modeForm = createModePanel();
+
+        container.setMembersMargin(30);
+        createWQInputTable();
+        createWQInputPanel();
+        // the initial panel is the Single-W panel.
+        double[] values = getSingleW();
+        wArrayPanel = new DoubleArrayPanel(
+            MESSAGE.unitWSingle(), values, this);
+        container.addMember(wArrayPanel);
+        helperContainer.addChild(wqTable);
+        layout.addMember(modeForm);
+        layout.addMember(container);
+
+        return layout;
+    }
+
+
+    /**
+     * This method creates the mode panel. It contains two radio button panels
+     * that allows the user to switch the input mode between w/q and
+     * single/range input.
+     *
+     * @return a panel.
+     */
+    protected Canvas createModePanel() {
+        RadioGroupItem wq = new RadioGroupItem(FIELD_WQ);
+        wq.setShowTitle(false);
+        wq.setVertical(false);
+        wq.setWidth(WIDTH_LEFT);
+
+        RadioGroupItem mode = new RadioGroupItem(FIELD_MODE);
+        mode.setShowTitle(false);
+        mode.setVertical(false);
+        mode.setWidth(WIDTH_LEFT);
+
+        LinkedHashMap wqValues = new LinkedHashMap();
+        wqValues.put(FIELD_WQ_W, MESSAGE.wqW());
+        wqValues.put(FIELD_WQ_Q, MESSAGE.wqQ());
+
+        LinkedHashMap modeValues = new LinkedHashMap();
+        modeValues.put(FIELD_MODE_SINGLE, MESSAGE.wqSingle());
+        modeValues.put(FIELD_MODE_RANGE, MESSAGE.wqRange());
+
+        wq.setValueMap(wqValues);
+        mode.setValueMap(modeValues);
+
+        wq.addChangeHandler(this);
+        mode.addChangeHandler(this);
+
+        modes = new DynamicForm();
+        modes.setFields(wq, mode);
+        modes.setWidth(WIDTH_LEFT);
+        modes.setNumCols(1);
+
+        LinkedHashMap initial = new LinkedHashMap();
+        initial.put(FIELD_WQ, FIELD_WQ_W);
+        initial.put(FIELD_MODE, FIELD_MODE_SINGLE);
+        modes.setValues(initial);
+
+        return modes;
+    }
+
+
+    /**
+     * This method returns the selected data.
+     *
+     * @return the selected/inserted data.
+     */
+    public Data[] getData() {
+        // XXX If we have entered a value and click right afterwards on the
+        // 'next' button, the BlurEvent is not fired, and the values are not
+        // saved. So, we gonna save those values explicitly.
+        if (!isRangeMode()) {
+            Canvas member = container.getMember(0);
+            if (member instanceof DoubleArrayPanel) {
+                DoubleArrayPanel form = (DoubleArrayPanel) member;
+                if (isWMode()) {
+                    saveSingleWValues(form);
+                }
+                else {
+                    saveSingleQValues(form);
+                }
+            }
+
+            return new Data[] {
+                getDataMode(),
+                getDataSelectionMode(),
+                getDataSingle() };
+        }
+        else {
+            Canvas member = container.getMember(0);
+            if (member instanceof DoubleRangePanel) {
+                DoubleRangePanel form = (DoubleRangePanel) member;
+
+                if (isWMode()) {
+                    saveRangeWValues(form);
+                }
+                else {
+                    saveRangeQValues(form);
+                }
+            }
+
+            return new Data[] {
+                getDataMode(),
+                getDataSelectionMode(),
+                getDataFrom(),
+                getDataTo(),
+                getDataStep() };
+        }
+    }
+
+
+    /**
+     * Returns the Data object for the 'mode' attribute.
+     *
+     * @return the Data object for the 'mode' attribute.
+     */
+    protected Data getDataMode() {
+        String wqMode = modes.getValueAsString(FIELD_WQ);
+        DataItem item = new DefaultDataItem("wq_mode", "wq_mode", wqMode);
+        return new DefaultData(
+            "wq_mode", null, null, new DataItem[] { item });
+    }
+
+
+    /**
+     * Returns the Data object for the 'mode' attribute.
+     *
+     * @return the Data object for the 'mode' attribute.
+     */
+    protected Data getDataSelectionMode() {
+        String wqSelection = modes.getValueAsString(FIELD_MODE);
+        DataItem item = new DefaultDataItem(
+            "wq_selection", "wq_selection", wqSelection);
+
+        return new DefaultData(
+            "wq_selection", null, null, new DataItem[] { item });
+    }
+
+
+    /**
+     * Returns the data object for the 'single' attribute.
+     *
+     * @return the Data object for the 'single' attribute.
+     */
+    protected Data getDataSingle() {
+        double[] values  = getFinalSingle();
+        StringBuilder sb = new StringBuilder();
+        for (double value: values) {
+            sb.append(Double.toString(value));
+            sb.append(" ");
+        }
+
+        DataItem item = new DefaultDataItem(
+            "wq_single", "wq_single", sb.toString());
+
+        return new DefaultData(
+            "wq_single", null, null, new DataItem[] { item });
+    }
+
+
+    /**
+     * Returns the Data object for the 'from' attribute.
+     *
+     * @return the Data object for the 'from' attribute.
+     */
+    protected Data getDataFrom() {
+        String value  = Double.valueOf(getFinalFrom()).toString();
+        DataItem item = new DefaultDataItem("wq_from", "wq_from", value);
+        return new DefaultData(
+            "wq_from", null, null, new DataItem[] { item });
+    }
+
+
+    /**
+     * Returns the Data object for the 'to' attribute.
+     *
+     * @return the Data object for the 'to' attribute.
+     */
+    protected Data getDataTo() {
+        String value  = Double.valueOf(getFinalTo()).toString();
+        DataItem item = new DefaultDataItem("wq_to", "wq_to", value);
+        return new DefaultData(
+            "wq_to", null, null, new DataItem[] { item });
+    }
+
+
+    /**
+     * Returns the Data object for the 'step' attribute.
+     *
+     * @return the Data object for the 'step' attribute.
+     */
+    protected Data getDataStep() {
+        String value  = Double.valueOf(getFinalStep()).toString();
+        DataItem item = new DefaultDataItem("wq_step","wq_step", value);
+        return new DefaultData(
+            "wq_step", null, null, new DataItem[] { item });
+    }
+
+
+    protected double[] getFinalSingle() {
+        boolean wMode = isWMode();
+
+        return wMode ? getSingleW() : getSingleQ();
+    }
+
+
+    /**
+     * Returns the value of 'from' depending on the selected input mode.
+     *
+     * @return the value of 'from' depending on the selected input mode.
+     */
+    protected double getFinalFrom() {
+        boolean wMode     = isWMode();
+        boolean rangeMode = isRangeMode();
+
+        if (rangeMode) {
+            return wMode ? getFromW() : getFromQ();
+
+        }
+        else {
+            double[] values = wMode ? getSingleW() : getSingleQ();
+            double   value  = Double.MAX_VALUE;
+
+            for (double v: values) {
+                value = value < v ? value : v;
+            }
+
+            return value;
+        }
+    }
+
+
+    /**
+     * Returns the value of 'to' depending on the selected input mode.
+     *
+     * @return the value of 'to' depending on the selected input mode.
+     */
+    protected double getFinalTo() {
+        boolean wMode     = isWMode();
+        boolean rangeMode = isRangeMode();
+
+        if (rangeMode) {
+            return wMode ? getToW() : getToQ();
+
+        }
+        else {
+            double[] values = wMode ? getSingleW() : getSingleQ();
+            double   value  = Double.MIN_VALUE;
+
+            for (double v: values) {
+                value = value > v ? value : v;
+            }
+
+            return value;
+        }
+    }
+
+
+    /**
+     * Returns the value of 'step' depending on the selected input mode.
+     *
+     * @return the value of 'step' depending on the selected input mode.
+     */
+    protected double getFinalStep() {
+        boolean wMode     = isWMode();
+        boolean rangeMode = isRangeMode();
+
+        if (rangeMode) {
+            return wMode ? getStepW() : getStepQ();
+        }
+        else {
+            // we have no field to enter the 'step' attribute in the
+            // single mode
+            return 0d;
+        }
+    }
+
+
+    /**
+     * Determines the range/single mode.
+     *
+     * @return true if the range mode is activated.
+     */
+    public boolean isRangeMode() {
+        String rMode = modes.getValueAsString(FIELD_MODE);
+
+        return rMode.equals(FIELD_MODE_RANGE) ? true : false;
+    }
+
+
+    /**
+     * Determines the w/q mode.
+     *
+     * @return true, if the W mode is activated.
+     */
+    public boolean isWMode() {
+        String wq = modes.getValueAsString(FIELD_WQ);
+
+        return wq.equals(FIELD_WQ_W) ? true : false;
+    }
+
+
+    /**
+     * This method changes the lower panel with the input fields depending on
+     * the combination of the two radio button panels.
+     *
+     * @param event The ChangeEvent.
+     */
+    public void onChange(ChangeEvent event) {
+        DynamicForm form = event.getForm();
+        FormItem    item = event.getItem();
+
+        String wqMode    = null;
+        String inputMode = null;
+
+        if (item.getFieldName().equals(FIELD_MODE)) {
+            wqMode    = form.getValueAsString(FIELD_WQ);
+            inputMode = (String) event.getValue();
+        }
+        else {
+            wqMode    = (String) event.getValue();
+            inputMode = form.getValueAsString(FIELD_MODE);
+        }
+
+        updatePanels(wqMode, inputMode);
+    }
+
+
+    protected void updatePanels(String wqMode, String inputMode) {
+        container.removeMembers(container.getMembers());
+        GWT.log("updating Panel and Table");
+        if (wqMode.equals(FIELD_WQ_W)) {
+            if (inputMode.equals(FIELD_MODE_SINGLE)) {
+                // Single W mode
+                double[] values = getSingleW();
+
+                wArrayPanel = new DoubleArrayPanel(
+                    MESSAGE.unitWSingle(), values, this);
+
+                container.addMember(wArrayPanel);
+                helperContainer.addChild(wqTable);
+                int size = wqTable.getRecords().length;
+                for(int i = 0; i < size; i++) {
+                    ListGridRecord r = wqTable.getRecord(i);
+                    String type = r.getAttributeAsString("type");
+                    if (type.equals("W")) {
+                        wqTable.getRecord(i).setEnabled(true);
+                    }
+                    else if(type.equals("D") || type.equals("Q")) {
+                        wqTable.getRecord(i).setEnabled(false);
+                    }
+                }
+            }
+            else {
+                // Range W mode
+                double from = getFromW();
+                double to   = getToW();
+                double step = getStepW();
+
+                wRangePanel = new DoubleRangePanel(
+                    MESSAGE.unitWFrom(), MESSAGE.unitWTo(), MESSAGE.unitWStep(),
+                    from, to, step,
+                    250,
+                    this);
+                container.addMember(wRangePanel);
+                helperContainer.addChild(wqRangeTable);
+                int size = wqRangeTable.getRecords().length;
+                for(int i = 0; i < size; i++) {
+                    ListGridRecord r = wqRangeTable.getRecord(i);
+                    String type = r.getAttributeAsString("type");
+                    if (type.equals("W")) {
+                        wqRangeTable.getRecord(i).setEnabled(true);
+                    }
+                    else if(type.equals("D") || type.equals("Q")) {
+                        wqRangeTable.getRecord(i).setEnabled(false);
+                    }
+                }
+            }
+        }
+        else {
+            if (inputMode.equals(FIELD_MODE_SINGLE)) {
+                // Single Q mode
+                double[] values = getSingleQ();
+
+                qArrayPanel = new DoubleArrayPanel(
+                    MESSAGE.unitQSingle(), values, this);
+                container.addMember(qArrayPanel);
+                helperContainer.addChild(wqTable);
+                int size = wqTable.getRecords().length;
+                for(int i = 0; i < size; i++) {
+                    ListGridRecord r = wqTable.getRecord(i);
+                    String type = r.getAttributeAsString("type");
+                    if (type.equals("W")) {
+                        wqTable.getRecord(i).setEnabled(false);
+                    }
+                    else if(type.equals("D") || type.equals("Q")) {
+                        wqTable.getRecord(i).setEnabled(true);
+                    }
+            }
+
+            }
+            else {
+                // Range Q mode
+                double from = getFromQ();
+                double to   = getToQ();
+                double step = getStepQ();
+
+                qRangePanel = new DoubleRangePanel(
+                    MESSAGE.unitQFrom(), MESSAGE.unitQTo(), MESSAGE.unitQStep(),
+                    from, to, step,
+                    250,
+                    this);
+                container.addMember(qRangePanel);
+                helperContainer.addChild(wqRangeTable);
+                int size = wqRangeTable.getRecords().length;
+                for(int i = 0; i < size; i++) {
+                    ListGridRecord r = wqRangeTable.getRecord(i);
+                    String type = r.getAttributeAsString("type");
+                    if (type.equals("W")) {
+                        wqRangeTable.getRecord(i).setEnabled(false);
+                    }
+                    else if(type.equals("D") || type.equals("Q")) {
+                        wqRangeTable.getRecord(i).setEnabled(true);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * This method is called if the value of one of the input fields might have
+     * changed. The entered values are validated and stored.
+     *
+     * @param event The BlurEvent.
+     */
+    public void onBlur(BlurEvent event) {
+        DynamicForm form = event.getForm();
+        FormItem    item = event.getItem();
+
+        String wqMode    = (String) modes.getValue(FIELD_WQ);
+        String inputMode = (String) modes.getValue(FIELD_MODE);
+
+        if (wqMode.equals(FIELD_WQ_W)) {
+            if (inputMode.equals(FIELD_MODE_SINGLE)) {
+                DoubleArrayPanel p = (DoubleArrayPanel) form;
+                saveSingleWValue(p, item);
+            }
+            else {
+                DoubleRangePanel p = (DoubleRangePanel) form;
+                saveRangeWValue(p, item);
+            }
+        }
+        else {
+            if (inputMode.equals(FIELD_MODE_SINGLE)) {
+                DoubleArrayPanel p = (DoubleArrayPanel) form;
+                saveSingleQValue(p, item);
+            }
+            else {
+                DoubleRangePanel p = (DoubleRangePanel) form;
+                saveRangeQValue(p, item);
+            }
+        }
+    }
+
+
+    protected void saveSingleWValues(DoubleArrayPanel p) {
+        FormItem[] formItems = p.getFields();
+
+        for (FormItem item: formItems) {
+            if (item.getFieldName().equals(DoubleArrayPanel.FIELD_NAME)) {
+                saveSingleWValue(p, item);
+            }
+        }
+    }
+
+
+    protected void saveSingleQValues(DoubleArrayPanel p) {
+        FormItem[] formItems = p.getFields();
+
+        for (FormItem item: formItems) {
+            if (item.getFieldName().equals(DoubleArrayPanel.FIELD_NAME)) {
+                saveSingleQValue(p, item);
+            }
+        }
+    }
+
+
+    protected void saveSingleWValue(DoubleArrayPanel p, FormItem item) {
+        if (p.validateForm(item)) {
+            setSingleW(p.getInputValues(item));
+        }
+    }
+
+
+    protected void saveSingleQValue(DoubleArrayPanel p, FormItem item) {
+        if (p.validateForm(item)) {
+            setSingleQ(p.getInputValues(item));
+        }
+    }
+
+
+    protected void saveRangeWValues(DoubleRangePanel p) {
+        FormItem[] formItems = p.getFields();
+
+        for (FormItem item: formItems) {
+            saveRangeWValue(p, item);
+        }
+    }
+
+
+    protected void saveRangeQValues(DoubleRangePanel p) {
+        FormItem[] formItems = p.getFields();
+
+        for (FormItem item: formItems) {
+            saveRangeQValue(p, item);
+        }
+    }
+
+
+    protected void saveRangeWValue(DoubleRangePanel p, FormItem item) {
+        if (p.validateForm(item)) {
+            setFromW(p.getFrom());
+            setToW(p.getTo());
+            setStepW(p.getStep());
+        }
+    }
+
+
+    protected void saveRangeQValue(DoubleRangePanel p, FormItem item) {
+        if (p.validateForm(item)) {
+            setFromQ(p.getFrom());
+            setToQ(p.getTo());
+            setStepQ(p.getStep());
+        }
+    }
+
+
+    protected double[] getSingleQ() {
+        return valuesQ;
+    }
+
+
+    protected void setSingleQ(double[] values) {
+        valuesQ = values;
+    }
+
+
+    protected double getFromQ() {
+        return fromQ;
+    }
+
+
+    protected void setFromQ(double fromQ) {
+        this.fromQ = fromQ;
+    }
+
+
+    protected double getToQ() {
+        return toQ;
+    }
+
+
+    protected void setToQ(double toQ) {
+        this.toQ = toQ;
+    }
+
+
+    protected double getStepQ() {
+        return stepQ;
+    }
+
+
+    protected void setStepQ(double stepQ) {
+        this.stepQ = stepQ;
+    }
+
+
+    protected double[] getSingleW() {
+        return valuesW;
+    }
+
+
+    protected void setSingleW(double[] values) {
+        valuesW = values;
+    }
+
+
+    protected double getFromW() {
+        return fromW;
+    }
+
+
+    protected void setFromW(double fromW) {
+        this.fromW = fromW;
+    }
+
+
+    protected double getToW() {
+        return toW;
+    }
+
+
+    protected void setToW(double toW) {
+        this.toW = toW;
+    }
+
+
+    protected double getStepW() {
+        return stepW;
+    }
+
+
+    protected void setStepW(double stepW) {
+        this.stepW = stepW;
+    }
+
+
+    protected void createWQInputTable() {
+        Config config    = Config.getInstance();
+        String url       = config.getServerUrl();
+        String locale    = config.getLocale ();
+        String river     = "";
+        String value_min = "";
+        String value_max = "";
+
+        ArtifactDescription adescr = artifact.getArtifactDescription();
+        DataList[] data = adescr.getOldData();
+
+        //TODO: get the information about old data.
+        if (data != null && data.length > 0) {
+            for (int i = 0; i < data.length; i++) {
+                DataList dl = data[i];
+                if (dl.getState().equals("state.winfo.river")) {
+                    for (int j = 0; j < dl.size(); j++) {
+                        Data d = dl.get(j);
+                        DataItem[] di = d.getItems();
+                        if (di != null && di.length == 1) {
+                           river = d.getItems()[0].getStringValue();
+                        }
+                    }
+                }
+                else if (dl.getState().equals("state.winfo.location_distance")){
+                    for (int j = 0; j < dl.size(); j++) {
+                        Data d = dl.get(j);
+                        DataItem[] di = d.getItems();
+                        if (di != null && di.length >= 1 && j == 0) {
+                            value_max = d.getItems()[0].getStringValue();
+                        }
+                        else if (di != null &&
+                                 di.length >= 1 &&
+                                 j == dl.size() - 1) {
+                            value_min = d.getItems()[0].getStringValue();
+                        }
+                    }
+                }
+            }
+        }
+
+        double from = Double.valueOf(value_min);
+        double to = Double.valueOf(value_max);
+        wqInfoService.getWQInfo(url, locale, river, from, to,
+            new AsyncCallback<WQInfoObject[]>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not recieve wq informations.");
+                    GWT.log(caught.getMessage());
+                }
+
+                public void onSuccess(WQInfoObject[] wqi) {
+                    int num = wqi != null ? wqi.length :0;
+                    GWT.log("Recieved " + num + " wq informations.");
+
+                    if (num == 0) {
+                        return;
+                    }
+                    tableData = wqi;
+                    addWQInfo(wqi);
+                    updatePanels(FIELD_WQ_W, FIELD_MODE_SINGLE);
+                }
+            }
+        );
+    }
+
+
+    protected void addWQInfo (WQInfoObject[] wqi) {
+        int i = 0;
+        for(WQInfoObject wi: wqi) {
+            WQInfoRecord rec = new WQInfoRecord (wi);
+            wqTable.addData(rec);
+            wqRangeTable.addData(rec);
+        }
+        return;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/AddArtifactServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,61 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.client.services.AddArtifactService;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class AddArtifactServiceImpl
+extends      DescribeCollectionServiceImpl
+implements   AddArtifactService
+{
+    public static final String ERROR_ADD_ARTIFACT = "error_add_artifact";
+
+    public Collection add(
+        Collection collection,
+        Artifact   artifact,
+        String     url,
+        String     locale)
+    throws ServerException
+    {
+        System.out.println("AddArtifactServiceImpl.add");
+
+        Document add = ClientProtocolUtils.newAddArtifactDocument(
+            artifact.getUuid(), null);
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            Document response = (Document) client.doCollectionAction(
+                add, collection.identifier(), new DocumentResponseHandler());
+
+            Collection c = parseCollection(response);
+
+            if (c == null) {
+                throw new ServerException(ERROR_ADD_ARTIFACT);
+            }
+
+            return c;
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+
+        }
+
+        throw new ServerException(ERROR_ADD_ARTIFACT);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/AdvanceServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,81 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.client.services.AdvanceService;
+
+
+/**
+ * This interface provides artifact specific operation ADVANCE.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class AdvanceServiceImpl
+extends      RemoteServiceServlet
+implements   AdvanceService
+{
+    public static final String XPATH_RESULT = "/art:result/text()";
+
+    public static final String OPERATION_FAILURE = "FAILED";
+
+    public static final String ERROR_ADVANCE_ARTIFACT = "error_advance_artifact";
+
+
+    public Artifact advance(
+        String   serverUrl,
+        String   locale,
+        Artifact artifact,
+        String   target)
+    throws ServerException
+    {
+        System.out.println("AdvanceServiceImpl.advance");
+
+        Document advance = ClientProtocolUtils.newAdvanceDocument(
+            artifact.getUuid(),
+            artifact.getHash(),
+            target);
+
+        HttpClient client = new HttpClientImpl(serverUrl, locale);
+
+        try {
+            Document description = (Document) client.advance(
+                new de.intevation.artifacts.httpclient.objects.Artifact(
+                    artifact.getUuid(),
+                    artifact.getHash()),
+                advance,
+                new DocumentResponseHandler());
+
+            if (description == null) {
+                throw new ServerException(ERROR_ADVANCE_ARTIFACT);
+            }
+
+            String result = XMLUtils.xpathString(
+                description,
+                XPATH_RESULT,
+                ArtifactNamespaceContext.INSTANCE);
+
+            if (result == null || !result.equals(OPERATION_FAILURE)) {
+                return (Artifact) new FLYSArtifactCreator().create(description);
+            }
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_ADVANCE_ARTIFACT);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,359 @@
+package de.intevation.flys.client.server;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.client.shared.model.ArtifactDescription;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.shared.model.DataList;
+import de.intevation.flys.client.shared.model.DefaultArtifactDescription;
+import de.intevation.flys.client.shared.model.DefaultData;
+import de.intevation.flys.client.shared.model.DefaultDataItem;
+import de.intevation.flys.client.shared.model.DefaultOutputMode;
+import de.intevation.flys.client.shared.model.OutputMode;
+
+
+/**
+ * This factory class helps creating an {@link ArtifactDescription} based on the
+ * DESCRIBE document of an artifact returned by the artifact server. Use the
+ * {@link createArtifactDescription(org.w3c.dom.Document)} method with the
+ * DESCRIBE document to create such an {@link ArtifactDescription}.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ArtifactDescriptionFactory {
+
+    public static final String XPATH_STATE_NAME = "@art:name";
+
+    public static final String XPATH_UIPROVIDER = "@art:uiprovider";
+
+    public static final String XPATH_REACHABLE_STATE = "art:state";
+
+    public static final String XPATH_STATIC_STATE_NODE = "art:state";
+
+    public static final String XPATH_STATIC_DATA_NODE = "art:data";
+
+    public static final String XPATH_STATIC_ITEM_NODE = "art:item";
+
+    /**
+     * This method creates the {@link ArtifactDescription} of the DESCRIBE
+     * document <i>doc</i>.
+     *
+     * @param doc A DESCRIBE document.
+     *
+     * @return the {@link ArtifactDescription}.
+     */
+    public static ArtifactDescription createArtifactDescription(Document doc) {
+        System.out.println("ArtifactDescriptionFactory - create()");
+
+        Node currentState = ClientProtocolUtils.getCurrentState(doc);
+        Node staticNode   = ClientProtocolUtils.getStaticUI(doc);
+        Node dynamicNode  = ClientProtocolUtils.getDynamicUI(doc);
+        Node reachable    = ClientProtocolUtils.getReachableStates(doc);
+        NodeList outputs  = ClientProtocolUtils.getOutputModes(doc);
+
+        String state = (String) XMLUtils.xpath(
+            currentState,
+            XPATH_STATE_NAME,
+            XPathConstants.STRING,
+            ArtifactNamespaceContext.INSTANCE);
+        System.out.println("Current state name: " + state);
+
+        DataList currentData = extractCurrentData(dynamicNode, state);
+        DataList[] old       = extractOldData(staticNode);
+        String[] states      = extractReachableStates(reachable);
+        OutputMode[] outs        = extractOutputModes(outputs);
+
+        return new DefaultArtifactDescription(
+            old,
+            currentData,
+            state,
+            states,
+            outs);
+    }
+
+
+    /**
+     * This method extracts the data that the user is able to enter in the
+     * current state of the artifact.
+     *
+     * @param dynamicNode The dynamic node of the DESCRIBE document.
+     * @param state The name of the current state.
+     *
+     * @return A {@link Data} object that represents the data which might be
+     * entered by the user in the current state or null, if no data might be
+     * entered.
+     */
+    protected static DataList extractCurrentData(Node dynamicNode, String state) {
+        System.out.println("ArtifactDescriptionFactory - extractCurrentData()");
+
+        NodeList data     = ClientProtocolUtils.getSelectNode(dynamicNode);
+        String uiProvider = extractUIProvider(dynamicNode);
+
+        if (data == null || data.getLength() == 0) {
+            return null;
+        }
+
+        int dataNum   = data.getLength();
+        DataList list = new DataList(state, dataNum, uiProvider);
+
+        for (int i = 0; i < dataNum; i++) {
+            Node d = data.item(i);
+
+            NodeList choices  = ClientProtocolUtils.getItemNodes(d);
+            String   label    = ClientProtocolUtils.getLabel(d);
+            String   name     = XMLUtils.xpathString(
+                d, "@art:name", ArtifactNamespaceContext.INSTANCE);
+
+            DataItem[] dataItems = extractCurrentDataItems(choices);
+
+            list.add(new DefaultData(name, label, null, dataItems));
+        }
+
+        return list;
+    }
+
+
+    /**
+     * This method extract the {@link DataItem}s of the DESCRIBE document.
+     *
+     * @param items The items in the DESCRIBE document.
+     *
+     * @return the {@link DataItem}s.
+     */
+    protected static DataItem[] extractCurrentDataItems(NodeList items) {
+        System.out.println(
+            "ArtifactDescriptionFactory - extractCurrentDataItems()");
+
+        if (items == null || items.getLength() == 0) {
+            System.out.println("No data items found.");
+            return null;
+        }
+
+        int count = items.getLength();
+
+        List<DataItem> dataItems = new ArrayList<DataItem>(count);
+
+        for (int i = 0; i < count; i++) {
+            Node item    = items.item(i);
+            String label = ClientProtocolUtils.getLabel(item);
+            String value = ClientProtocolUtils.getValue(item);
+
+            dataItems.add(new DefaultDataItem(label, null, value));
+        }
+
+        return (DataItem[]) dataItems.toArray(new DataItem[count]);
+    }
+
+
+    /**
+     * This method extracts the data objects from the data node of the static ui
+     * part of the DESCRIBE document.
+     *
+     * @param staticNode The static ui node of the DESCRIBE.
+     *
+     * @return the DataList objects.
+     */
+    protected static DataList[] extractOldData(Node staticNode) {
+        System.out.println("ArtifactDescriptionFactory - extractOldData()");
+
+        NodeList stateNodes = (NodeList) XMLUtils.xpath(
+            staticNode,
+            XPATH_STATIC_STATE_NODE,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (stateNodes == null || stateNodes.getLength() == 0) {
+            System.out.println("No old items found.");
+            return null;
+        }
+
+        int count       = stateNodes.getLength();
+        DataList[] data = new DataList[count];
+
+        for (int i = 0; i < count; i++) {
+            Node tmp = stateNodes.item(i);
+
+            String name = XMLUtils.xpathString(
+                tmp, "@art:name", ArtifactNamespaceContext.INSTANCE);
+            String uiprovider = XMLUtils.xpathString(
+                tmp, "@art:uiprovider", ArtifactNamespaceContext.INSTANCE);
+            String label = XMLUtils.xpathString(
+                tmp, "@art:label", ArtifactNamespaceContext.INSTANCE);
+
+            NodeList dataNodes = (NodeList) XMLUtils.xpath(
+                tmp,
+                XPATH_STATIC_DATA_NODE,
+                XPathConstants.NODESET,
+                ArtifactNamespaceContext.INSTANCE);
+
+            if (dataNodes == null || dataNodes.getLength() == 0) {
+                continue;
+            }
+
+            int size      = dataNodes.getLength();
+            DataList list = new DataList(name, size, uiprovider, label);
+
+            for (int j = 0; j < size; j++) {
+                Node dataNode = dataNodes.item(j);
+
+                String dName = XMLUtils.xpathString(
+                    dataNode, "@art:name", ArtifactNamespaceContext.INSTANCE);
+                String dType = XMLUtils.xpathString(
+                    dataNode, "@art:type", ArtifactNamespaceContext.INSTANCE);
+
+                DataItem[] items = extractOldDataItems(dataNode);
+
+                list.add(new DefaultData(dName, dName, dType, items));
+
+                data[i] = list;
+            }
+        }
+
+        return data;
+    }
+
+
+    /**
+     * This method extracts the data items from the data nodes that are placed
+     * in the static ui part of the DESCRIBE document.
+     *
+     * @param dataNode A data node that contains items.
+     *
+     * @return a list of DataItems.
+     */
+    protected static DataItem[] extractOldDataItems(Node dataNode) {
+        NodeList itemList = (NodeList) XMLUtils.xpath(
+            dataNode,
+            XPATH_STATIC_ITEM_NODE,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (itemList == null || itemList.getLength() == 0) {
+            System.out.println("No old data items found.");
+            return null;
+        }
+
+        int count = itemList.getLength();
+
+        DataItem[] items = new DataItem[count];
+
+        for (int i = 0; i < count; i++) {
+            Node tmp = itemList.item(i);
+
+            String value = XMLUtils.xpathString(
+                tmp, "@art:value", ArtifactNamespaceContext.INSTANCE);
+            String label = XMLUtils.xpathString(
+                tmp, "@art:label", ArtifactNamespaceContext.INSTANCE);
+
+            items[i] = new DefaultDataItem(label, label, value);
+        }
+
+        return items;
+    }
+
+
+    /**
+     * This method extracts the UIProvider specified by the data node.
+     *
+     * @param data The data node.
+     *
+     * @return the UIProvider that is specified in the data node.
+     */
+    protected static String extractUIProvider(Node ui) {
+        return (String) XMLUtils.xpath(
+            ui,
+            XPATH_UIPROVIDER,
+            XPathConstants.STRING,
+            ArtifactNamespaceContext.INSTANCE);
+    }
+
+
+    /**
+     * This method extracts the reachable states of the current artifact.
+     *
+     * @param reachable The reachable states node.
+     *
+     * @return an array with identifiers of reachable states.
+     */
+    protected static String[] extractReachableStates(Node reachable) {
+        System.out.println("ArtifactDescriptionFactory - extractReachableStates()");
+
+        NodeList list = (NodeList) XMLUtils.xpath(
+            reachable,
+            XPATH_REACHABLE_STATE,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (list == null || list.getLength() == 0) {
+            return null;
+        }
+
+        int count = list.getLength();
+
+        String[] states = new String[count];
+
+        for (int i = 0; i < count; i++) {
+            Node state = list.item(i);
+
+            String name = XMLUtils.xpathString(
+                state, "@art:name", ArtifactNamespaceContext.INSTANCE);
+
+            states[i] = name;
+        }
+
+        return states;
+    }
+
+
+    /**
+     * This method extract available output modes of the the current artifact.
+     *
+     * @param outputs A list of nodes that contain information about output
+     * modes.
+     *
+     * @return an array of Output modes.
+     */
+    protected static OutputMode[] extractOutputModes(NodeList outputs) {
+        System.out.println("ArtifactDescriptionFactory - extractOutputModes()");
+
+        if (outputs == null || outputs.getLength() == 0) {
+            return null;
+        }
+
+        int size = outputs.getLength();
+
+        List<OutputMode> outs = new ArrayList<OutputMode>(size);
+
+        for (int i = 0; i < size; i++) {
+            Node out = outputs.item(i);
+
+            String name = XMLUtils.xpathString(
+                out, "@art:name", ArtifactNamespaceContext.INSTANCE);
+            String desc = XMLUtils.xpathString(
+                out, "@art:description", ArtifactNamespaceContext.INSTANCE);
+            String mimeType = XMLUtils.xpathString(
+                out, "@art:mime-type", ArtifactNamespaceContext.INSTANCE);
+
+            if (name != null) {
+                outs.add(new DefaultOutputMode(name, desc, mimeType));
+            }
+            else {
+                System.err.println("Found an invalid output mode.");
+            }
+        }
+
+        return (OutputMode[]) outs.toArray(new OutputMode[size]);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/ArtifactServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,51 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.client.services.ArtifactService;
+
+
+/**
+ * This interface provides artifact specific services as CREATE, DESCRIBE, FEED,
+ * ADVANCE and OUT.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ArtifactServiceImpl
+extends      RemoteServiceServlet
+implements   ArtifactService
+{
+    /** The error message key that is thrown if an error occured while artifact
+     * creation.*/
+    public static final String ERROR_CREATE_ARTIFACT = "error_create_artifact";
+
+
+    public Artifact create(String serverUrl, String locale, String factory)
+    throws ServerException
+    {
+        System.out.println("ArtifactServiceImpl.create");
+
+        Document create   = ClientProtocolUtils.newCreateDocument(factory);
+        HttpClient client = new HttpClientImpl(serverUrl, locale);
+
+        try {
+            return (Artifact) client.create(create, new FLYSArtifactCreator());
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_CREATE_ARTIFACT);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/CSVExportServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,74 @@
+package de.intevation.flys.client.server;
+
+import java.util.List;
+
+import java.io.Reader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import au.com.bytecode.opencsv.CSVReader;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.client.services.CSVExportService;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class CSVExportServiceImpl
+extends      RemoteServiceServlet
+implements   CSVExportService
+{
+    public static final String ERROR_NO_EXPORT_FOUND =
+        "error_no_export_found";
+
+    public List getCSV(
+        String url,
+        String locale,
+        String uuid,
+        String name)
+    throws ServerException
+    {
+        System.out.println("CSVExportServiceImpl.getCSV");
+
+        Document requestDoc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            requestDoc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element action = ec.create("action");
+        ec.addAttr(action, "type", "csv", true);
+        ec.addAttr(action, "name", name, true);
+
+        requestDoc.appendChild(action);
+
+        HttpClient client = new HttpClientImpl(url, locale);
+        try {
+            InputStream in = client.collectionOut(requestDoc, uuid, "export");
+            Reader reader       = new InputStreamReader (in, "UTF-8");
+            CSVReader csvReader = new CSVReader (reader);
+
+            return csvReader.readAll ();
+        }
+        catch (IOException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_NO_EXPORT_FOUND);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/ChartOutputServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,121 @@
+package de.intevation.flys.client.server;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+
+/**
+ * This service is used to request a chart from the artifact server. The
+ * response is directed directly to the output stream, so the image that is
+ * retrieved is displayed in the UI afterwards.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartOutputServiceImpl
+extends      HttpServlet
+{
+    /** The default chart width if no value is specified in the request.*/
+    public static final int DEFAULT_CHART_WIDTH  = 600;
+
+    /** The default chart height if no value is specified in the request.*/
+    public static final int DEFAULT_CHART_HEIGHT = 400;
+
+
+    public void doGet(HttpServletRequest req, HttpServletResponse resp) {
+        System.out.println("ChartOutputServiceImpl.doGet");
+
+        try {
+            OutputStream out = resp.getOutputStream();
+
+            String serverUrl = req.getParameter("server");
+            String uuid      = req.getParameter("uuid");
+            String type      = req.getParameter("type");
+            String locale    = req.getParameter("locale");
+
+            Document request = ClientProtocolUtils.newOutCollectionDocument(
+                uuid, type, type, getChartAttributes(req));
+
+            HttpClient client = new HttpClientImpl(serverUrl, locale);
+            client.collectionOut(request, uuid, "chart", out);
+
+            out.close();
+            out.flush();
+        }
+        catch (IOException ioe) {
+            System.err.println(ioe.getMessage());
+        }
+        catch (Exception e) {
+            System.err.println(e.getMessage());
+        }
+    }
+
+
+    /**
+     * This method returns a document which might contain parameters to adjust
+     * chart settings. The document is created using the information that are
+     * contained in the request object.
+     *
+     * @param req The request document.
+     *
+     * @return a document to adjust chart settings.
+     */
+    protected Document getChartAttributes(HttpServletRequest req) {
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator ec = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        appendChartSize(req, doc, ec);
+
+        return doc;
+    }
+
+
+    /**
+     * This method extracts the size (width/height) of a chart from request
+     * object and append those values - if they exist - to the attribute
+     * document used to adjust chart settings.
+     *
+     * @param req The request object that might contain the chart size.
+     * @param doc The attribute document used to adjust chart settings.
+     * @param ec The ElementCreator that might be used to create new Elements.
+     */
+    protected void appendChartSize(
+        HttpServletRequest req,
+        Document           doc,
+        ElementCreator     ec)
+    {
+        Element size = ec.create("size");
+
+        String width  = req.getParameter("width");
+        String height = req.getParameter("height");
+
+        if (width == null || height == null) {
+            width  = Integer.toString(DEFAULT_CHART_WIDTH);
+            height = Integer.toString(DEFAULT_CHART_HEIGHT);
+        }
+
+        ec.addAttr(size, "width", width, true);
+        ec.addAttr(size, "height", height, true);
+
+        doc.appendChild(size);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/CreateCollectionServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,71 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.DefaultCollection;
+import de.intevation.flys.client.client.services.CreateCollectionService;
+
+
+/**
+ * This interface provides the createCollection service to create new
+ * collections in the artifact server.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CreateCollectionServiceImpl
+extends      RemoteServiceServlet
+implements   CreateCollectionService
+{
+    /** XPath to figure out the uuid of the created collection.*/
+    public static final String XPATH_COLLECTION_UUID =
+        "/art:result/art:artifact-collection/@art:uuid";
+
+    /** Error message key that is thrown if an error occured while creating
+     *  a new collection.*/
+    public static final String ERROR_CREATE_COLLECTION =
+        "error_create_collection";
+
+
+    public Collection create(String serverUrl, String locale, String ownerId)
+    throws ServerException
+    {
+        System.out.println("Start creating a new collection.");
+
+        Document create   =
+            ClientProtocolUtils.newCreateCollectionDocument(null);
+        HttpClient client = new HttpClientImpl(serverUrl, locale);
+
+        try {
+            Document doc = (Document) client.createCollection(
+                create, ownerId, new DocumentResponseHandler());
+
+            String uuid = XMLUtils.xpathString(
+                doc, XPATH_COLLECTION_UUID, ArtifactNamespaceContext.INSTANCE);
+
+            if (uuid == null || uuid.trim().length() == 0) {
+                throw new ServerException(ERROR_CREATE_COLLECTION);
+            }
+
+            return new DefaultCollection(uuid);
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_CREATE_COLLECTION);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,273 @@
+package de.intevation.flys.client.server;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.CollectionItem;
+import de.intevation.flys.client.shared.model.DefaultCollection;
+import de.intevation.flys.client.shared.model.DefaultCollectionItem;
+import de.intevation.flys.client.shared.model.DefaultFacet;
+import de.intevation.flys.client.shared.model.DefaultOutputMode;
+import de.intevation.flys.client.shared.model.ExportMode;
+import de.intevation.flys.client.shared.model.Facet;
+import de.intevation.flys.client.shared.model.OutputMode;
+import de.intevation.flys.client.client.services.DescribeCollectionService;
+
+
+/**
+ * This service implements a method that queries the DESCRIBE document of a
+ * specific collection and returns a Collection object with the information of
+ * the document.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DescribeCollectionServiceImpl
+extends      RemoteServiceServlet
+implements   DescribeCollectionService
+{
+    /** The error message key that is thrown if an error occured while
+     * describe() a Collection.*/
+    public static final String ERROR_DESCRIBE_COLLECTION =
+        "error_describe_collection";
+
+    public static final String XPATH_FACETS = "art:facets/art:facet";
+
+
+    public Collection describe(String uuid, String serverUrl, String locale)
+    throws ServerException
+    {
+        System.out.println("DescribeCollectionServiceImpl.describe");
+
+        Document describe = ClientProtocolUtils.newDescribeCollectionDocument(
+            uuid);
+
+        HttpClient client = new HttpClientImpl(serverUrl, locale);
+
+        try {
+            Document response = (Document) client.doCollectionAction(
+                describe, uuid, new DocumentResponseHandler());
+
+            Collection c = parseCollection(response);
+
+            if (c == null) {
+                throw new ServerException(ERROR_DESCRIBE_COLLECTION);
+            }
+
+            return c;
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_DESCRIBE_COLLECTION);
+    }
+
+
+    /**
+     * This method takes the DESCRIBE document of the Collections describe()
+     * operation and extracts the information about the collection itself and
+     * the collection items.
+     *
+     * @param description The DESCRIBE document of the Collections describe()
+     * operation.
+     *
+     * @return a Collection with CollectionItems.
+     */
+    protected Collection parseCollection(Document description) {
+        System.out.println("AddArtifactServiceImpl.parseCollection");
+
+        if (description == null) {
+            System.err.println("The DESCRIBE of the Collection is null!");
+            return null;
+        }
+
+        String uuid = XMLUtils.xpathString(
+            description,
+            "art:artifact-collection/@art:uuid",
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (uuid == null || uuid.equals("")) {
+            System.err.println("Found an invalid Collection!");
+            return null;
+        }
+
+        Collection c = new DefaultCollection(uuid);
+
+        NodeList items = (NodeList) XMLUtils.xpath(
+            description,
+            "art:artifact-collection/art:artifacts/art:artifact",
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (items == null || items.getLength() == 0) {
+            System.out.println("No collection item found for this collection.");
+
+            return c;
+        }
+
+        int size = items.getLength();
+
+        for (int i = 0; i < size; i++) {
+            CollectionItem item = parseCollectionItem(items.item(i));
+
+            if (item != null) {
+                c.addItem(item);
+            }
+        }
+
+        System.out.println(
+            "Found " + c.getItemLength() + " collection items " +
+            "for the Collection '" + c.identifier() + "'.");
+
+        return c;
+    }
+
+
+    /**
+     * This method extracts the CollectionItem from <i>node</i> with its output
+     * modes. The output modes are parsed using the parseOutputModes() method.
+     *
+     * @param node A node that contains information about a CollectionItem.
+     *
+     * @return a CollectionItem.
+     */
+    protected CollectionItem parseCollectionItem(Node node) {
+        System.out.println("AddArtifactServiceImpl.parseCollectionItem");
+
+        if (node == null) {
+            System.err.println("The node for parsing CollectionItem is null!");
+            return null;
+        }
+
+        String uuid = XMLUtils.xpathString(
+            node, "@art:uuid", ArtifactNamespaceContext.INSTANCE);
+
+        String hash = XMLUtils.xpathString(
+            node, "@art:hash", ArtifactNamespaceContext.INSTANCE);
+
+        if (uuid == null || uuid.equals("")) {
+            System.err.println("Found an invalid CollectionItem!");
+        }
+
+        Node outputmodes = (Node) XMLUtils.xpath(
+            node,
+            "art:outputmodes",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        List<OutputMode> modes = parseOutputModes(outputmodes);
+
+        return new DefaultCollectionItem(uuid, hash, modes);
+    }
+
+
+    /**
+     * This method extracts the OutputModes available for a specific
+     * CollectionItem and returns them as list.
+     *
+     * @param node The root node of the outputmodes list.
+     *
+     * @return a list of OutputModes.
+     */
+    protected List<OutputMode> parseOutputModes(Node node) {
+        System.out.println("AddArtifactServiceImpl.parseOutputModes");
+
+        if (node == null) {
+            System.err.println("The node for parsing OutputModes is null!");
+            return null;
+        }
+
+        NodeList list = (NodeList) XMLUtils.xpath(
+            node,
+            "art:output",
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (list == null || list.getLength() == 0) {
+            System.err.println("No outputmode nodes found!");
+            return null;
+        }
+
+        int size = list.getLength();
+
+        List<OutputMode> modes = new ArrayList<OutputMode>(size);
+
+        for (int i = 0; i < size; i++) {
+            Node   tmp  = list.item(i);
+            String name = XMLUtils.xpathString(
+                tmp, "@art:name", ArtifactNamespaceContext.INSTANCE);
+            String desc = XMLUtils.xpathString(
+                tmp, "@art:description", ArtifactNamespaceContext.INSTANCE);
+            String mime = XMLUtils.xpathString(
+                tmp, "@art:mime-type", ArtifactNamespaceContext.INSTANCE);
+
+            if (name == null || name.equals("")) {
+                System.err.println("Found an invalid output mode.");
+
+                continue;
+            }
+
+            OutputMode outmode = null;
+            List<Facet> fs     = extractFacets(tmp);
+
+            if (name.indexOf("export") > -1) {
+                outmode = new ExportMode(name, desc, mime, fs);
+            }
+            else {
+                outmode = new DefaultOutputMode(name, desc, mime, fs);
+            }
+
+            modes.add(outmode);
+        }
+
+        return modes;
+    }
+
+
+    protected static List<Facet> extractFacets(Node outmode) {
+        System.out.println("ArtifactDescriptionFactory - extractFacets()");
+
+        NodeList facetList = (NodeList) XMLUtils.xpath(
+            outmode,
+            XPATH_FACETS,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = facetList != null ? facetList.getLength() : 0;
+
+        List<Facet> facets = new ArrayList<Facet>(num);
+
+        for (int i = 0; i < num; i++) {
+            Node facetNode = facetList.item(i);
+
+            String name = XMLUtils.xpathString(
+                facetNode, "@art:name", ArtifactNamespaceContext.INSTANCE);
+
+            if (name != null && name.length() > 0) {
+                facets.add(new DefaultFacet(name));
+            }
+        }
+
+        return facets;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/DistanceInfoServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,164 @@
+package de.intevation.flys.client.server;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.client.services.DistanceInfoService;
+import de.intevation.flys.client.shared.model.DistanceInfoObject;
+import de.intevation.flys.client.shared.model.DistanceInfoObjectImpl;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DistanceInfoServiceImpl
+extends      RemoteServiceServlet
+implements   DistanceInfoService
+{
+    public static final String ERROR_NO_DISTANCEINFO_FOUND =
+        "error_no_distanceinfo_found";
+
+    public static final String XPATH_DISTANCES = "art:distances/art:distance";
+
+
+    public DistanceInfoObject[] getDistanceInfo(
+        String url,
+        String locale,
+        String river)
+    throws ServerException
+    {
+        System.out.println("DistanceInfoServiceImpl.getDistanceInfo");
+
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element riverEl = ec.create("river");
+
+        riverEl.setTextContent(river);
+
+        doc.appendChild(riverEl);
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            Document result = client.callService(url, "distanceinfo", doc);
+
+            System.out.println("Extract distance info objects now.");
+            DistanceInfoObject[] objects = extractDistanceInfoObjects(result);
+
+            if (objects != null && objects.length > 0) {
+                return objects;
+            }
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_NO_DISTANCEINFO_FOUND);
+    }
+
+
+    /**
+     * Extracts all distance info objects from <i>result</i> document.
+     *
+     * @param result The document retrieved by the server.
+     *
+     * @return a list of DistanceInfoObjects.
+     */
+    protected DistanceInfoObject[] extractDistanceInfoObjects(Document result)
+    throws    ServerException
+    {
+        NodeList list = (NodeList) XMLUtils.xpath(
+            result,
+            XPATH_DISTANCES,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (list == null || list.getLength() == 0) {
+            System.err.println("No distance info found.");
+
+            throw new ServerException(ERROR_NO_DISTANCEINFO_FOUND);
+        }
+
+        int num = list.getLength();
+        System.out.println("Response contains " + num + " objects.");
+
+        List<DistanceInfoObject> objects =
+            new ArrayList<DistanceInfoObject>(num);
+
+        for (int i = 0; i < num; i++) {
+            DistanceInfoObject obj = buildDistanceInfoObject(list.item(i));
+
+            if (obj != null) {
+                objects.add(obj);
+            }
+        }
+
+        System.out.println("Retrieved " + objects.size() + " distances.");
+
+        return (DistanceInfoObject[])
+            objects.toArray(new DistanceInfoObject[num]);
+    }
+
+
+    /**
+     * Extracts information for a single distance info object and intializes an
+     * DistanceInfoObject with them.
+     *
+     * @param node The node that contains the information.
+     *
+     * @return a valid DistanceInfoObject.
+     */
+    protected DistanceInfoObject buildDistanceInfoObject(Node node) {
+        String desc = XMLUtils.xpathString(
+            node, "@art:description", ArtifactNamespaceContext.INSTANCE);
+
+        String from = XMLUtils.xpathString(
+            node, "@art:from", ArtifactNamespaceContext.INSTANCE);
+
+        String to = XMLUtils.xpathString(
+            node, "@art:to", ArtifactNamespaceContext.INSTANCE);
+
+        String riverside = XMLUtils.xpathString(
+            node, "@art:riverside", ArtifactNamespaceContext.INSTANCE);
+
+        if (desc != null && from != null) {
+            try {
+                return new DistanceInfoObjectImpl(
+                    desc,
+                    new Double(from),
+                    to != null && to.trim().length() > 0 ? new Double(to) : null,
+                    riverside);
+            }
+            catch (NumberFormatException nfe) {
+                System.err.println(nfe.getLocalizedMessage());
+            }
+        }
+
+        System.err.println("Invalid distance info object found.");
+
+        return 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-client/src/main/java/de/intevation/flys/client/server/ExportServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,58 @@
+package de.intevation.flys.client.server;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+
+/**
+ * This service is used to request a data export from the artifact server. The
+ * response is directed directly to the output stream, so that a file dialog is
+ * opened.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ExportServiceImpl
+extends      HttpServlet
+{
+    public void doGet(HttpServletRequest req, HttpServletResponse resp) {
+        System.out.println("ExportServiceImpl.doGet");
+
+        try {
+            OutputStream out = resp.getOutputStream();
+
+            String serverUrl = req.getParameter("server");
+            String uuid      = req.getParameter("uuid");
+            String mode      = req.getParameter("mode");
+            String type      = req.getParameter("type");
+            String locale    = req.getParameter("locale");
+            String fn        = mode + "." + type;
+
+            resp.setHeader("Content-Disposition", "attachment;filename=" + fn);
+
+            System.out.println("Request " + type + " export.");
+            Document request = ClientProtocolUtils.newOutCollectionDocument(
+                uuid, mode, type);
+
+            HttpClient client = new HttpClientImpl(serverUrl, locale);
+            client.collectionOut(request, uuid, mode, out);
+
+            out.close();
+            out.flush();
+        }
+        catch (IOException ioe) {
+            System.err.println(ioe.getMessage());
+        }
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/FLYSArtifactCreator.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,97 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+
+import de.intevation.artifacts.httpclient.utils.ArtifactCreator;
+
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.DefaultArtifact;
+import de.intevation.flys.client.shared.model.WINFOArtifact;
+
+
+/**
+ * An implementation of an {@link ArtifactCreator}. This class uses the document
+ * that is returned by the artifact server to parse important information (like
+ * uuid, hash) and returns a new {@link Artifact} instance.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FLYSArtifactCreator implements ArtifactCreator {
+
+    /** The XPath to the artifact's uuid.*/
+    public static final String XPATH_UUID = "/art:result/art:uuid/@art:value";
+
+    /** The XPath to the artifact's hash value.*/
+    public static final String XPATH_HASH = "/art:result/art:hash/@art:value";
+
+    /** The XPath to the artifact's name value.*/
+    public static final String XPATH_NAME = "/art:result/art:name/@art:value";
+
+
+    /**
+     * Creates a new instance of an {@link ArtifactCreator}.
+     */
+    public FLYSArtifactCreator() {
+    }
+
+
+    /**
+     * This concreate implementation returns an instance of {@link Artifact}
+     * that is used in the FLYS GWT Client code.
+     *
+     * @param doc A document that describes the artifact that has been created
+     * in the artifact server.
+     *
+     * @return an instance if {@link Artifact}.
+     */
+    public Object create(Document doc) {
+        Artifact artifact = extractArtifact(doc);
+        artifact.setArtifactDescription(
+            ArtifactDescriptionFactory.createArtifactDescription(doc));
+
+        return artifact;
+    }
+
+
+    /**
+     * This method extracts the UUID und HASH information of the returned
+     * artifact document.
+     *
+     * @param doc The result of the CREATE operation.
+     *
+     * @return an instance of an {@link Artifact}.
+     */
+    protected Artifact extractArtifact(Document doc) {
+        System.out.println("FLYSArtifactCreator - extractArtifact()");
+
+        String uuid = XMLUtils.xpathString(
+            doc, XPATH_UUID, ArtifactNamespaceContext.INSTANCE);
+
+        String hash = XMLUtils.xpathString(
+            doc, XPATH_HASH, ArtifactNamespaceContext.INSTANCE);
+
+        String name = XMLUtils.xpathString(
+            doc, XPATH_NAME, ArtifactNamespaceContext.INSTANCE);
+
+        System.out.println("NEW Artifact UUID: " + uuid);
+        System.out.println("NEW Artifact HASH: " + hash);
+        System.out.println("NEW Artifact NAME: " + name);
+
+        if (name == null) {
+            return new DefaultArtifact(uuid, hash);
+        }
+
+        name = name.trim();
+
+        if (name.length() > 0 && name.equals("winfo")) {
+            System.out.println("+++++ NEW WINFO ARTIFACT.");
+            return new WINFOArtifact(uuid, hash);
+        }
+
+        return new DefaultArtifact(uuid, hash);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/GetArtifactServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,80 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.client.services.GetArtifactService;
+
+
+/**
+ * This service provides a method that returns an artifact based on its
+ * identifier.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class GetArtifactServiceImpl
+extends      RemoteServiceServlet
+implements   GetArtifactService
+{
+    public static final String ERROR_DESCRIBE_ARTIFACT =
+        "error_describe_artifact";
+
+    public static final String XPATH_RESULT = "/art:result/text()";
+
+    public static final String OPERATION_FAILURE = "FAILED";
+
+
+    public Artifact getArtifact(
+        String url,
+        String locale,
+        String uuid,
+        String hash)
+    throws ServerException
+    {
+        System.out.println("GetArtifactServiceImpl.getArtifact");
+
+        Document describe = ClientProtocolUtils.newDescribeDocument(
+            uuid, hash, true);
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            Document description = (Document) client.describe(
+                new de.intevation.artifacts.httpclient.objects.Artifact(
+                    uuid, hash),
+                describe,
+                new DocumentResponseHandler());
+
+            if (description == null) {
+                throw new ServerException(ERROR_DESCRIBE_ARTIFACT);
+            }
+
+            String result = XMLUtils.xpathString(
+                description,
+                XPATH_RESULT,
+                ArtifactNamespaceContext.INSTANCE);
+
+            if (result == null || !result.equals(OPERATION_FAILURE)) {
+                return (Artifact) new FLYSArtifactCreator().create(description);
+            }
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_DESCRIBE_ARTIFACT);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/MetaDataServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,69 @@
+package de.intevation.flys.client.server;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+
+import de.intevation.flys.client.client.services.MetaDataService;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+import de.intevation.flys.client.shared.model.DataCageTree;
+import de.intevation.flys.client.shared.model.DataCageNode;
+
+import de.intevation.flys.client.server.meta.Converter;
+
+public class MetaDataServiceImpl
+extends      RemoteServiceServlet
+implements   MetaDataService
+{
+    public static final String ERROR_NO_META_DATA_FOUND =
+        "error_no_meta_data_found";
+
+    public DataCageTree getMetaData(String url, String locale, String river) 
+    throws ServerException
+    {
+        System.out.println("MetaDataService.getDistanceInfo");
+
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element riverEl = ec.create("river");
+
+        riverEl.setTextContent(river);
+
+        doc.appendChild(riverEl);
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            Converter converter = new Converter();
+            return converter.convert(client.callService(url, "metadata", doc));
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_NO_META_DATA_FOUND);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/RiverServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,94 @@
+package de.intevation.flys.client.server;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Node;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.DefaultRiver;
+import de.intevation.flys.client.shared.model.River;
+import de.intevation.flys.client.client.services.RiverService;
+
+
+/**
+ * This interface provides a method to list the supported rivers of the artifact
+ * server.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class RiverServiceImpl
+extends      RemoteServiceServlet
+implements   RiverService
+{
+    /** The XPath string that points to the rivers in the resulting document.*/
+    public static final String XPATH_RIVERS = "/art:rivers/art:river";
+
+    /** The error message key that is thrown if an error occured while reading
+     * the supported rivers from server.*/
+    public static final String ERROR_NO_RIVERS_FOUND = "error_no_rivers_found";
+
+
+    public River[] list(String serverUrl, String locale)
+    throws ServerException
+    {
+        Document doc      = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        doc.appendChild(ec.create("action"));
+
+        HttpClient client = new HttpClientImpl(serverUrl, locale);
+
+        try {
+            Document res = client.callService(serverUrl, "rivers", doc);
+
+            NodeList rivers = (NodeList) XMLUtils.xpath(
+                res,
+                XPATH_RIVERS,
+                XPathConstants.NODESET,
+                ArtifactNamespaceContext.INSTANCE);
+
+            if (rivers == null || rivers.getLength() == 0) {
+                throw new ServerException(ERROR_NO_RIVERS_FOUND);
+            }
+
+            int count = rivers.getLength();
+
+            List<River> theRivers = new ArrayList<River>(count);
+
+            for (int i = 0; i < count; i++) {
+                Node tmp = rivers.item(i);
+
+                String name = XMLUtils.xpathString(
+                    tmp, "@art:name", ArtifactNamespaceContext.INSTANCE);
+
+                theRivers.add(new DefaultRiver(name));
+            }
+
+            return (River[]) theRivers.toArray(new River[count]);
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_NO_RIVERS_FOUND);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/StepForwardServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,172 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+import de.intevation.artifacts.httpclient.http.response.DocumentResponseHandler;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Artifact;
+import de.intevation.flys.client.shared.model.ArtifactDescription;
+import de.intevation.flys.client.shared.model.Data;
+import de.intevation.flys.client.shared.model.DataItem;
+import de.intevation.flys.client.client.services.StepForwardService;
+
+
+/**
+ * This interface provides a method that bundles the artifact specific
+ * operations FEED and ADVANCE.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class StepForwardServiceImpl
+extends      AdvanceServiceImpl
+implements   StepForwardService
+{
+    /** XPath that points to the result type of a feed or advance operation.*/
+    public static final String XPATH_RESULT = "/art:result/@art:type";
+
+    /** XPath that points to the result type of a feed or advance operation.*/
+    public static final String XPATH_RESULT_MSG = "/art:result/text()";
+
+    /** A constant that marks errors.*/
+    public static final String OPERATION_FAILURE = "FAILURE";
+
+    /** The error message key that is thrown if an error occured while feeding
+     * new data.*/
+    public static final String ERROR_FEED_DATA = "error_feed_data";
+
+
+    /**
+     * This method wraps the artifact operations FEED and ADVANCE. FEED is
+     * always triggerd, ADVANCE only, if there is at least one reachable state.
+     *
+     * @param url The url of the artifact server.
+     * @param locale The locale used for the request.
+     * @param artifact The artifact that needs to be fed.
+     * @param data An array of Data objects that contain the information that
+     *
+     * @return the modified artifact.
+     */
+    public Artifact go(String url, String locale, Artifact artifact, Data[] data)
+    throws ServerException
+    {
+        System.out.println("StepForwardServiceImpl.go");
+
+        Artifact afterFeed = feed(url, locale, artifact, data);
+
+        if (afterFeed == null) {
+            System.err.println("StepForwardService.feed() - FAILED");
+            throw new ServerException(ERROR_FEED_DATA);
+        }
+
+        ArtifactDescription desc = afterFeed.getArtifactDescription();
+        String[] reachable       = desc.getReachableStates();
+
+        if (reachable == null || reachable.length == 0) {
+            System.out.println("Did not find any reachable state.");
+            return afterFeed;
+        }
+
+        // We use the first reachable state as default target, maybe we need to
+        // change this later.
+        return advance(url, locale, afterFeed, reachable[0]);
+    }
+
+
+    /**
+     * This method triggers the FEED operation.
+     *
+     * @param url The url of the artifact server.
+     * @param artifact The artifact that needs to be fed.
+     * @param data An array of Data objects that contain the information that
+     * are used for the FEED operation.
+     *
+     * @return a new artifact parsed from the description of FEED.
+     */
+    protected Artifact feed(
+        String   url,
+        String   locale,
+        Artifact artifact,
+        Data[]   data)
+    throws    ServerException
+    {
+        System.out.println("StepForwardServiceImpl.feed");
+
+        Document feed = ClientProtocolUtils.newFeedDocument(
+            artifact.getUuid(),
+            artifact.getHash(),
+            createKVP(data));
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            Document description = (Document) client.feed(
+                new de.intevation.artifacts.httpclient.objects.Artifact(
+                    artifact.getUuid(),
+                    artifact.getHash()),
+                feed,
+                new DocumentResponseHandler());
+
+            if (description == null) {
+                System.err.println("StepForwardService.feed() - FAILED");
+                throw new ServerException(ERROR_FEED_DATA);
+            }
+
+            String result = XMLUtils.xpathString(
+                description,
+                XPATH_RESULT,
+                ArtifactNamespaceContext.INSTANCE);
+
+            if (result == null || !result.equals(OPERATION_FAILURE)) {
+                System.out.println("StepForwardService.feed() - SUCCESS");
+                return (Artifact) new FLYSArtifactCreator().create(description);
+            }
+            else if (result != null && result.equals(OPERATION_FAILURE)) {
+                String msg = XMLUtils.xpathString(
+                    description,
+                    XPATH_RESULT_MSG,
+                    ArtifactNamespaceContext.INSTANCE);
+                throw new ServerException(msg);
+            }
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        System.err.println("StepForwardService.feed() - FAILED");
+        throw new ServerException(ERROR_FEED_DATA);
+    }
+
+
+    /**
+     * This method creates an array of key/value pairs from an array of Data
+     * objects. The string array is used as parameter for the feed() operation.
+     *
+     * @param data The data that should be transformed into the string array.
+     *
+     * @return a string array that contains key/value pairs.
+     */
+    protected String[][] createKVP(Data[] data) {
+        String[][] kvp = new String[data.length][];
+
+        int i = 0;
+
+        for (Data d: data) {
+            DataItem[] items = d.getItems();
+            String key       = d.getLabel();
+            String value     = items[0].getStringValue();
+
+            kvp[i++] = new String[] { key, value };
+        }
+
+        return kvp;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/UserCollectionsServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,113 @@
+package de.intevation.flys.client.server;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.DefaultCollection;
+import de.intevation.flys.client.client.services.UserCollectionsService;
+
+
+/**
+ * This service returns a list of collections owned by a specified user.
+ * <b>NOTE:</b> The Collections returned by this service provide no information 
+ * about the CollectionItems or OutputModes of the Collection. You need to fetch
+ * these information explicitly using another service.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class UserCollectionsServiceImpl
+extends      RemoteServiceServlet
+implements   UserCollectionsService
+{
+    public Collection[] getUserCollections(
+        String serverUrl,
+        String locale,
+        String userid)
+    {
+        System.out.println("UserCollectionsServiceImpl.getUserCollections");
+
+        HttpClient client = new HttpClientImpl(serverUrl, locale);
+
+        try {
+            Document result = client.listUserCollections(userid);
+
+            NodeList list = (NodeList) XMLUtils.xpath(
+                result,
+                "/art:artifact-collections/art:artifact-collection",
+                XPathConstants.NODESET,
+                ArtifactNamespaceContext.INSTANCE);
+
+            if (list == null || list.getLength() == 0) {
+                System.out.println("No collection found for user: " + userid);
+                return null;
+            }
+
+            int num = list.getLength();
+
+            List<Collection> all = new ArrayList<Collection>(num);
+
+            for (int i = 0; i < num; i++) {
+                Collection c = createCollection(list.item(i));
+
+                if (c != null) {
+                    all.add(c);
+                }
+            }
+
+            System.out.println("User has " + all.size() + " collections.");
+
+            return (Collection[]) all.toArray(new Collection[all.size()]);
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        System.err.println("No user collections found.");
+        return null;
+    }
+
+
+    /**
+     * Extracts a SimpleCollection from <i>node</i>.
+     *
+     * @param node Contains information about a collection.
+     *
+     * @return a list of Simplecollections.
+     */
+    protected Collection createCollection(Node node) {
+        String creationStr = XMLUtils.xpathString(
+            node, "@art:creation", ArtifactNamespaceContext.INSTANCE);
+
+        String name = XMLUtils.xpathString(
+            node, "@art:name", ArtifactNamespaceContext.INSTANCE);
+
+        String uuid = XMLUtils.xpathString(
+            node, "@art:uuid", ArtifactNamespaceContext.INSTANCE);
+
+        if (uuid != null) {
+            long time = Long.parseLong(creationStr);
+            return new DefaultCollection(uuid, name, new Date(time));
+        }
+
+        System.err.println("Found an invalid Collection.");
+        return 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-client/src/main/java/de/intevation/flys/client/server/UserServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,68 @@
+package de.intevation.flys.client.server;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+import de.intevation.flys.client.client.services.UserService;
+import de.intevation.flys.client.shared.exceptions.AuthenticationException;
+import de.intevation.flys.client.shared.model.DefaultUser;
+import de.intevation.flys.client.shared.model.User;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class UserServiceImpl
+extends      RemoteServiceServlet
+implements   UserService
+{
+    public static final String ERROR_NO_SUCH_USER = "error_no_such_user";
+
+    public User getCurrentUser(String serverUrl, String locale)
+    throws AuthenticationException
+    {
+        HttpClient client = new HttpClientImpl(serverUrl);
+
+        try {
+            Document users = (Document) client.listUsers();
+
+            String XPATH_USERS = "/art:users/art:user";
+
+            NodeList theUsers = (NodeList) XMLUtils.xpath(
+                users,
+                XPATH_USERS,
+                XPathConstants.NODESET,
+                ArtifactNamespaceContext.INSTANCE);
+
+            if (theUsers != null && theUsers.getLength() > 0) {
+                Node user = theUsers.item(0);
+
+                String uuid = XMLUtils.xpathString(
+                    user, "@art:uuid", ArtifactNamespaceContext.INSTANCE);
+                String name = XMLUtils.xpathString(
+                    user, "@art:name", ArtifactNamespaceContext.INSTANCE);
+
+                return new DefaultUser(uuid, name);
+            }
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        System.err.println("No users existing in the server.");
+        throw new AuthenticationException(ERROR_NO_SUCH_USER);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/server/WQInfoServiceImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,172 @@
+package de.intevation.flys.client.server;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifacts.httpclient.exceptions.ConnectionException;
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.client.services.WQInfoService;
+import de.intevation.flys.client.shared.model.WQInfoObject;
+import de.intevation.flys.client.shared.model.WQInfoObjectImpl;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQInfoServiceImpl
+extends      RemoteServiceServlet
+implements   WQInfoService
+{
+    public static final String ERROR_NO_WQINFO_FOUND =
+        "error_no_wqinfo_found";
+
+    public static final String XPATH_WQS = "art:service/art:mainvalues/art:mainvalue";
+
+
+    public WQInfoObject[] getWQInfo(
+        String url,
+        String locale,
+        String river,
+        double from,
+        double to)
+    throws ServerException
+    {
+        System.out.println("WQInfoServiceImpl.getWQInfo");
+
+        Document doc = XMLUtils.newDocument();
+
+        XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element mainvalues = ec.create("mainvalues");
+        Element riverEl = ec.create("river");
+        Element startEl = ec.create("start");
+        Element endEl   = ec.create("end");
+
+        riverEl.setTextContent(river);
+        startEl.setTextContent(Double.valueOf(from).toString());
+        endEl.setTextContent(Double.valueOf(to).toString());
+
+        mainvalues.appendChild(riverEl);
+        mainvalues.appendChild(startEl);
+        mainvalues.appendChild(endEl);
+
+        doc.appendChild(mainvalues);
+
+        HttpClient client = new HttpClientImpl(url, locale);
+
+        try {
+            Document result = client.callService(url, "mainvalues", doc);
+
+            System.out.println("Extract wq info objects now.");
+            WQInfoObject[] objects = extractWQInfoObjects(result);
+
+            if (objects != null && objects.length > 0) {
+                return objects;
+            }
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_NO_WQINFO_FOUND);
+    }
+
+
+    /**
+     * Extracts all wq info objects from <i>result</i> document.
+     *
+     * @param result The document retrieved by the server.
+     *
+     * @return a list of WQInfoObjects.
+     */
+    protected WQInfoObject[] extractWQInfoObjects(Document result)
+    throws    ServerException
+    {
+        NodeList list = (NodeList) XMLUtils.xpath(
+            result,
+            XPATH_WQS,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (list == null || list.getLength() == 0) {
+            System.err.println("No wq info found.");
+
+            throw new ServerException(ERROR_NO_WQINFO_FOUND);
+        }
+
+        int num = list.getLength();
+        System.out.println("Response contains " + num + " objects.");
+
+        List<WQInfoObject> objects =
+            new ArrayList<WQInfoObject>(num);
+
+        for (int i = 0; i < num; i++) {
+            WQInfoObject obj = buildWQInfoObject(list.item(i));
+
+            if (obj != null) {
+                objects.add(obj);
+            }
+        }
+
+        System.out.println("Retrieved " + objects.size() + " wq values");
+
+        return (WQInfoObject[])
+            objects.toArray(new WQInfoObject[num]);
+    }
+
+
+    /**
+     * Extracts information for a single wq info object and intializes an
+     * WQInfoObject with them.
+     *
+     * @param node The node that contains the information.
+     *
+     * @return a valid WQInfoObject.
+     */
+    protected WQInfoObject buildWQInfoObject(Node node) {
+        String name = XMLUtils.xpathString(
+            node, "@name", ArtifactNamespaceContext.INSTANCE);
+
+        String type = XMLUtils.xpathString(
+            node, "@type", ArtifactNamespaceContext.INSTANCE);
+
+        String value = XMLUtils.xpathString(
+            node, "@value", ArtifactNamespaceContext.INSTANCE);
+
+
+        if (name != null && type != null) {
+            try {
+                return new WQInfoObjectImpl(
+                    name,
+                    type,
+                    new Double(value));
+            }
+            catch (NumberFormatException nfe) {
+                System.err.println(nfe.getLocalizedMessage());
+            }
+        }
+
+        System.err.println("Invalid wq info object found.");
+
+        return 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-client/src/main/java/de/intevation/flys/client/server/meta/Converter.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,110 @@
+package de.intevation.flys.client.server.meta;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import de.intevation.flys.client.shared.model.DataCageTree;
+import de.intevation.flys.client.shared.model.DataCageNode;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+
+public class Converter
+{
+    public interface NodeConverter
+    {
+        DataCageNode convert(Element node, Converter converter);
+
+    } // interface NodeConverter
+
+    public static class NameConverter implements NodeConverter {
+        public DataCageNode convert(Element node, Converter converter) {
+            System.err.println("NameConverter called");
+            DataCageNode out = new DataCageNode(node.getAttribute("name"));
+            converter.convertChildren(out, node);
+            return out;
+        }
+    } // class NameConverter
+
+    public static class I18NConverter implements NodeConverter {
+        public DataCageNode convert(Element node, Converter converter) {
+            System.err.println("I18NConverter called");
+            DataCageNode out =
+                new DataCageNode("${" + node.getLocalName() + "}");
+            converter.convertChildren(out, node);
+            return out;
+        }
+    } // I18NConverter
+
+    protected Map<String, NodeConverter> converters;
+
+    protected void convertChildren(DataCageNode parent, Element sub) {
+        System.err.println("convertChildren called");
+        NodeList children = sub.getChildNodes();
+        for (int i = 0, N = children.getLength(); i < N; ++i) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                String name = child.getLocalName();
+                NodeConverter converter = converters.get(name);
+                if (converter != null) {
+                    DataCageNode son = converter.convert(
+                        (Element)child, this);
+                    parent.addChild(son);
+                }
+            }
+        } // for all children
+    }
+
+    public Converter() {
+        converters = new HashMap<String, NodeConverter>();
+        registerConverters();
+    }
+
+    protected void registerConverters() {
+        System.err.println("register converters called");
+        converters.put("datacage",                    new NameConverter());
+        converters.put("river",                       new NameConverter());
+        converters.put("gauge",                       new NameConverter());
+        converters.put("historical",                  new NameConverter());
+        converters.put("discharge-table-nn",          new I18NConverter());
+        converters.put("discharge-table-gauge",       new I18NConverter());
+        converters.put("fixations",                   new I18NConverter());
+        converters.put("fixation",                    new I18NConverter());
+        converters.put("columns",                     new I18NConverter());
+        converters.put("column",                      new NameConverter());
+        converters.put("flood-protections",           new I18NConverter());
+        converters.put("flood-protection",            new I18NConverter());
+        converters.put("flood-water-marks",           new I18NConverter());
+        converters.put("flood-water-mark",            new I18NConverter());
+        converters.put("water-levels",                new I18NConverter());
+        converters.put("water-level",                 new I18NConverter());
+        converters.put("extra-longitudinal-sections", new I18NConverter());
+        converters.put("extra-longitudinal-section",  new I18NConverter());
+        converters.put("longitudinal-section",        new I18NConverter());
+    }
+
+    public DataCageTree convert(Document document) {
+        System.err.println("convert called");
+
+        ArrayList<DataCageNode> roots = new ArrayList<DataCageNode>();
+        NodeList nodes = document.getChildNodes();
+        for (int i = 0, N = nodes.getLength(); i < N; ++i) {
+            Node node = nodes.item(i);
+            if (node.getNodeType() == Node.ELEMENT_NODE) {
+                System.err.println("search for name: " + node.getLocalName());
+                NodeConverter converter = converters.get(node.getLocalName());
+                if (converter != null) {
+                    roots.add(converter.convert((Element)node, this));
+                }
+            }
+        }
+        return roots.isEmpty()
+            ? new DataCageTree()
+            : new DataCageTree(roots.get(0));
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/FieldVerifier.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,42 @@
+package de.intevation.flys.client.shared;
+
+/**
+ * <p>
+ * FieldVerifier validates that the name the user enters is valid.
+ * </p>
+ * <p>
+ * This class is in the <code>shared</code> package because we use it in both
+ * the client code and on the server. On the client, we verify that the name is
+ * valid before sending an RPC request so the user doesn't have to wait for a
+ * network round trip to get feedback. On the server, we verify that the name is
+ * correct to ensure that the input is correct regardless of where the RPC
+ * originates.
+ * </p>
+ * <p>
+ * When creating a class that is used on both the client and the server, be sure
+ * that all code is translatable and does not use native JavaScript. Code that
+ * is note translatable (such as code that interacts with a database or the file
+ * system) cannot be compiled into client side JavaScript. Code that uses native
+ * JavaScript (such as Widgets) cannot be run on the server.
+ * </p>
+ */
+public class FieldVerifier {
+
+  /**
+   * Verifies that the specified name is valid for our service.
+   * 
+   * In this example, we only require that the name is at least four
+   * characters. In your application, you can use more complex checks to ensure
+   * that usernames, passwords, email addresses, URLs, and other fields have the
+   * proper syntax.
+   * 
+   * @param name the name to validate
+   * @return true if valid, false if invalid
+   */
+  public static boolean isValidName(String name) {
+    if (name == null) {
+      return false;
+    }
+    return name.length() > 3;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/shared/exceptions/AuthenticationException.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,23 @@
+package de.intevation.flys.client.shared.exceptions;
+
+import java.io.Serializable;
+
+
+/**
+ * This exception class is used if an error occured while user authentication.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class AuthenticationException
+extends      Exception
+implements   Serializable
+{
+    public AuthenticationException() {
+    }
+
+
+    public AuthenticationException(String msg) {
+        super(msg);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/exceptions/ServerException.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,17 @@
+package de.intevation.flys.client.shared.exceptions;
+
+import java.io.Serializable;
+
+
+public class ServerException
+extends      Exception
+implements   Serializable
+{
+    public ServerException() {
+    }
+
+
+    public ServerException(String msg) {
+        super(msg);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/shared/model/Artifact.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,52 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+/**
+ * This class represents an artifact for the client. It contains the necessary
+ * information for the client and the communication with the artifact server.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface Artifact extends Serializable {
+
+    /**
+     * Returns the UUID of the artifact.
+     *
+     * @return the UUID.
+     */
+    public String getUuid();
+
+
+    /**
+     * Returns the hash of the artifact.
+     *
+     * @return the hash.
+     */
+    public String getHash();
+
+
+    /**
+     * Returns the name of the artifact.
+     *
+     * @return the name-
+     */
+    public String getName();
+
+    /**
+     * Returns the ArtifactDescription.
+     *
+     * @return the artifact description.
+     */
+    public ArtifactDescription getArtifactDescription();
+
+
+    /**
+     * Sets a new ArtifactDescription.
+     *
+     * @param artifactDescription The new artifact description.
+     */
+    public void setArtifactDescription(ArtifactDescription artifactDescription);
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/ArtifactDescription.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,56 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+/**
+ * The artifact description describes a state of an artifact. There are
+ * operations defined that return former inserted data, possible input values
+ * and output targets that are available in the current state of the artifact.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface ArtifactDescription extends Serializable {
+
+    /**
+     * Returns the data that have been inserted in former states of the
+     * artifact.
+     *
+     * @return the old data of former states.
+     */
+    public DataList[] getOldData();
+
+
+    /**
+     * Returns the data with all its options that might be inserted in the
+     * current state of the artifact.
+     *
+     * @return the current data.
+     */
+    public DataList getCurrentData();
+
+
+    /**
+     * Returns the current state as string.
+     *
+     * @return the current state.
+     */
+    public String getCurrentState();
+
+
+    /**
+     * Returns the reachable states as string.
+     *
+     * @return the reachable states.
+     */
+    public String[] getReachableStates();
+
+
+    /**
+     * Returns the available output modes.
+     * 
+     * @return the available output modes.
+     */
+    public OutputMode[] getOutputModes();
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/Collection.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,31 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+import java.util.Date;
+import java.util.Map;
+
+
+/**
+ * The artifact collection.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface Collection extends Serializable {
+
+    public String identifier();
+
+    public String getName();
+
+    public Date getCreationTime();
+
+    public Date getLastAccess();
+
+    public void addItem(CollectionItem item);
+
+    public int getItemLength();
+
+    public CollectionItem getItem(int idx);
+
+    public Map<String, OutputMode> getOutputModes();
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/CollectionItem.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,49 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * The CollectionItem interface that provides methods to get information about
+ * artifacts and its output modes.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface CollectionItem extends Serializable {
+
+    /**
+     * Returns the identifier of the wrapped artifact.
+     *
+     * @return the identifier of the wrapped artifact.
+     */
+    String identifier();
+
+
+    /**
+     * Returns the hash of the wrapped artifact.
+     *
+     * @return the hash of the wrapped artifact.
+     */
+    String hash();
+
+
+    /**
+     * Returns the output modes of the wrapped artifact.
+     *
+     * @return the output modes of the wrapped artifact.
+     */
+    List<OutputMode> getOutputModes();
+
+
+    /**
+     * Returns the facets of the wrapped artifact for a specific output mode.
+     *
+     * @param outputmode The name of an output mode that is supported by this
+     * item.
+     *
+     * @return the facets of the wrapped artifact for a specific output mode.
+     */
+    List<Facet> getFacets(String outputmode);
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/CollectionRecord.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,85 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.Date;
+
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+
+
+/**
+ * The CollectionRecord is a wrapper to put Collection objects into a ListGrid.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CollectionRecord extends ListGridRecord {
+
+    /** The artifact collection. */
+    protected Collection collection;
+
+
+    /**
+     * The default constructor.
+     *
+     * @param collection The artifact collection.
+     */
+    public CollectionRecord(Collection collection) {
+        this.collection = collection;
+
+        setCreationTime(collection.getCreationTime());
+
+        String name = collection.getName();
+        setName(name != null && name.length() > 0
+            ? name
+            : collection.identifier());
+    }
+
+
+    /**
+     * Sets the creation time.
+     *
+     * @param creationTime The creation time.
+     */
+    public void setCreationTime(Date creationTime) {
+        setAttribute("creationTime", creationTime);
+    }
+
+
+    /**
+     * Returns the date of the creation.
+     *
+     * @return the creation time.
+     */
+    public Date getCreationTime() {
+        return getAttributeAsDate("creationTime");
+    }
+
+
+    /**
+     * Returns the name of the collection.
+     *
+     * @return the name of the collection.
+     */
+    public void setName(String name) {
+        setAttribute("name", name);
+    }
+
+
+    /**
+     * Returns the name of the collection or the uuid if no name is specified.
+     *
+     * @return the name of the collection.
+     */
+    public String getName() {
+        return getAttributeAsString("name");
+    }
+
+
+    /**
+     * Returns the collection objects itself.
+     *
+     * @return the collection object.
+     */
+    public Collection getCollection() {
+        return collection;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/Data.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,46 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+/**
+ * A Data object represents the necessary data of a single state of the
+ * artifact. It might provide several DataItems or just a single DataItem. The
+ * <code>type</code> makes it possible to validate the input in the client.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface Data extends Serializable {
+
+    /**
+     * Returns the label of the item.
+     *
+     * @return the label.
+     */
+    public String getLabel();
+
+
+    /**
+     * Returns the description of the item.
+     *
+     * @return the description.
+     */
+    public String getDescription();
+
+
+    /**
+     * Returns the type of the item.
+     *
+     * @return the type.
+     */
+    public String getType();
+
+
+    /**
+     * Returns the DataItems provided by this Data object.
+     *
+     * @return the DataItems.
+     */
+    public DataItem[] getItems();
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DataCageNode.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,39 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import java.io.Serializable;
+
+public class DataCageNode implements Serializable
+{
+    protected String             name;
+    protected List<DataCageNode> children;
+
+    public DataCageNode() {
+    }
+
+    public DataCageNode(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public void addChild(DataCageNode child) {
+        if (children == null) {
+            children = new ArrayList<DataCageNode>();
+        }
+        children.add(child);
+    }
+
+    public List<DataCageNode> getChildren() {
+        return children;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DataCageTree.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,24 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+public class DataCageTree implements Serializable
+{
+    protected DataCageNode root;
+
+    public DataCageTree() {
+    }
+
+    public DataCageTree(DataCageNode root) {
+        this.root = root;
+    }
+
+    public void setRoot(DataCageNode root) {
+        this.root = root;
+    }
+
+    public DataCageNode getRoot() {
+        return root;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DataItem.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,37 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+/**
+ * A DataItem represents a concrete item that might be selected, chosen or
+ * inserted by the user.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface DataItem extends Serializable {
+
+    /**
+     * Returns the label of the item.
+     *
+     * @return the label.
+     */
+    public String getLabel();
+
+
+    /**
+     * Returns the description of the item.
+     *
+     * @return the description;
+     */
+    public String getDescription();
+
+
+    /**
+     * Returns the value of the item.
+     *
+     * @return the value.
+     */
+    public String getStringValue();
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DataList.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,165 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DataList implements Serializable {
+
+    /** The list of Data objects managed by this list.*/
+    protected List<Data> data;
+
+    /** The name of the state that this list belongs to.*/
+    protected String state;
+
+    /** The name of a UIProvider that is recommended to render this DataList.*/
+    protected String uiprovider;
+
+    /** The label that should be used to label data objects.*/
+    protected String label;
+
+
+    /**
+     * The default constructor that creates a new DataList without Data objects
+     * and no UIProvider.
+     */
+    public DataList() {
+        data = new ArrayList<Data>();
+    }
+
+
+    /**
+     * Constructor.
+     *
+     * @param state The name of the state that this list belongs to.
+     * @param size The initial size of the list.
+     */
+    public DataList(String state, int size) {
+        this.state = state;
+        this.data  = new ArrayList<Data>(size);
+    }
+
+
+    /**
+     * A constructor that creates a new DataList without Data objects and no
+     * UIProvider. Size defines the initial size of the list.
+     *
+     * @param state The name of the state that this list belongs to.
+     * @param size The initial size of the list.
+     * @param uiprovider The UIProvider that should be used to render this list.
+     */
+    public DataList(String state, int size, String uiprovider) {
+        this(state, size);
+        this.uiprovider = uiprovider;
+    }
+
+
+    /**
+     * A constructor that creates a new DataList without Data objects and no
+     * UIProvider. Size defines the initial size of the list.
+     *
+     * @param state The name of the state that this list belongs to.
+     * @param size The initial size of the list.
+     * @param uiprovider The UIProvider that should be used to render this list.
+     * @param label The label.
+     */
+    public DataList(String state, int size, String uiprovider, String label) {
+        this(state, size, uiprovider);
+        this.label = label;
+    }
+
+
+    /**
+     * Adds a new Data object to the list.
+     *
+     * @param obj The Data object.
+     */
+    public void add(Data obj) {
+        if (obj != null) {
+            data.add(obj);
+        }
+    }
+
+
+    /**
+     * Adds a new Data objects to the list.
+     *
+     * @param obj The Data object.
+     */
+    public void add(Data[] obj) {
+        if (obj != null) {
+            for (Data o: obj) {
+                data.add(o);
+            }
+        }
+    }
+
+
+    /**
+     * Returns the Data element at position <i>idx</i>.
+     *
+     * @param idx The position of an element that should be returned.
+     *
+     * @return the Data element at position <i>idx</i>.
+     */
+    public Data get(int idx) {
+        if (idx < size()) {
+            return data.get(idx);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the whole list of Data objects.
+     *
+     * @return the whole list of Data objects.
+     */
+    public List<Data> getAll() {
+        return data;
+    }
+
+    /**
+     * Returns the number of Data objects in the list.
+     *
+     * @param the number of Data objects in the list.
+     */
+    public int size() {
+        return data.size();
+    }
+
+
+    /**
+     * Returns the name of the state that this list belongs to.
+     *
+     * @return the name of the state that this list belongs to.
+     */
+    public String getState() {
+        return state;
+    }
+
+
+    /**
+     * Returns the label for this list.
+     *
+     * @return the label of this list.
+     */
+    public String getLabel() {
+        return label;
+    }
+
+
+    /**
+     * Retrieves the name of a UIProvider or null if no one is recommended.
+     *
+     * @return the name of a UIProvider or null if no one is recommended.
+     */
+    public String getUIProvider() {
+        return uiprovider;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultArtifact.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,65 @@
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * The default implementation of an artifact that might be used in the client.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultArtifact implements Artifact {
+
+    /** The artifact's uuid.*/
+    protected String uuid;
+
+    /** The artifacts hash value.*/
+    protected String hash;
+
+    /** The current artifact description.*/
+    protected ArtifactDescription artifactDescription;
+
+    /**
+     * This constructor should not be used to create new instances of this
+     * class. An empty artifact without uuid and hash will be the result of
+     * this constructor call.
+     */
+    public DefaultArtifact() {
+    }
+
+
+    /**
+     * This constructor creates a new artifact instance with a uuid and a hash.
+     *
+     * @param uuid The artifact's uuid.
+     * @param hash The artifact's hash.
+     */
+    public DefaultArtifact(String uuid, String hash) {
+        this.uuid = uuid;
+        this.hash = hash;
+    }
+
+
+    public String getUuid() {
+        return uuid;
+    }
+
+
+    public String getHash() {
+        return hash;
+    }
+
+
+    public String getName() {
+        return "default";
+    }
+
+
+    public ArtifactDescription getArtifactDescription() {
+        return artifactDescription;
+    }
+
+
+    public void setArtifactDescription(ArtifactDescription description) {
+        this.artifactDescription = description;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultArtifactDescription.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,99 @@
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * The default implementation of an {@link ArtifactDescription}. This class just
+ * implements constructors to create new instances and the necessary methods of
+ * the interface.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultArtifactDescription implements ArtifactDescription {
+
+    /** Data that have been inserted in former states.*/
+    protected DataList[] oldData;
+
+    /** The Data that is allowed to be inserted in the current state.*/
+    protected DataList currentData;
+
+    /** The current state name.*/
+    protected String   currentState;
+
+    /** The names of reachable states.*/
+    protected String[] reachableStates;
+
+    /** The output modes of this state.*/
+    protected OutputMode[] outputModes;
+
+
+    public DefaultArtifactDescription() {
+    }
+
+
+    /**
+     * The default constructor.
+     *
+     * @param old The data that have been inserted in former states.
+     * @param current The data that might be inserted in the current state.
+     * @param state The name of the current state.
+     * @param reachableStates The names of the reachable states.
+     */
+    public DefaultArtifactDescription(
+        DataList[]   old,
+        DataList     current,
+        String       state,
+        String[]     reachableStates)
+    {
+        this.oldData         = old;
+        this.currentData     = current;
+        this.currentState    = state;
+        this.reachableStates = reachableStates;
+    }
+
+
+    /**
+     * The default constructor.
+     *
+     * @param old The data that have been inserted in former states.
+     * @param current The data that might be inserted in the current state.
+     * @param state The name of the current state.
+     * @param reachableStates The names of the reachable states.
+     * @param outputModes The available output modes of this artifact.
+     */
+    public DefaultArtifactDescription(
+        DataList[]   old,
+        DataList     current,
+        String       state,
+        String[]     reachableStates,
+        OutputMode[] outputModes)
+    {
+        this(old, current, state, reachableStates);
+        this.outputModes = outputModes;
+    }
+
+
+    public DataList[] getOldData() {
+        return oldData;
+    }
+
+
+    public DataList getCurrentData() {
+        return currentData;
+    }
+
+
+    public String getCurrentState() {
+        return currentState;
+    }
+
+
+    public String[] getReachableStates() {
+        return reachableStates;
+    }
+
+
+    public OutputMode[] getOutputModes() {
+        return outputModes;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultCollection.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,136 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * The default implementation of a {@link Collection}.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultCollection implements Collection {
+
+    /** The uuid of the collection. */
+    protected String uuid;
+
+    /** The name of the collection.*/
+    protected String name;
+
+    /** The creation time of this collection.*/
+    protected Date creation;
+
+    /** The list of artifacts that are managed by this Collection.*/
+    protected List<CollectionItem> items;
+
+
+    /**
+     * Constructor without arguments is necessary for GWT.
+     */
+    public DefaultCollection() {
+    }
+
+
+    /**
+     * Creates a new DefaultCollection with a UUID.
+     *
+     * @param uuid The UUID.
+     */
+    public DefaultCollection(String uuid) {
+        this.uuid  = uuid;
+        this.items = new ArrayList<CollectionItem>();
+    }
+
+
+    /**
+     * Creates a new DefaultCollection with uuid and name.
+     *
+     * @param uuid The identifier of this collection.
+     * @param name The name of this collection.
+     */
+    public DefaultCollection(String uuid, String name) {
+        this(uuid);
+
+        this.name = name;
+    }
+
+
+    /**
+     * Creates a new DefaultCollection with uuid and name.
+     *
+     * @param uuid The identifier of this collection.
+     * @param name The name of this collection.
+     * @param creation The creation time.
+     */
+    public DefaultCollection(String uuid, String name, Date creation) {
+        this(uuid, name);
+
+        this.creation = creation;
+    }
+
+
+    public String identifier() {
+        return uuid;
+    }
+
+
+    public Date getCreationTime() {
+        return creation;
+    }
+
+
+    public Date getLastAccess() {
+        return new Date();
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public void addItem(CollectionItem item) {
+        if (item != null) {
+            items.add(item);
+        }
+    }
+
+
+    public int getItemLength() {
+        return items.size();
+    }
+
+
+    public CollectionItem getItem(int idx) {
+        if (idx >= getItemLength()) {
+            return null;
+        }
+
+        return items.get(idx);
+    }
+
+
+    public Map<String, OutputMode> getOutputModes() {
+        Map<String, OutputMode> modes = new HashMap<String, OutputMode>();
+
+        for (CollectionItem item: items) {
+            List<OutputMode> itemModes = item.getOutputModes();
+
+            if (itemModes != null) {
+                for (OutputMode itemMode: itemModes) {
+                    String name = itemMode.getName();
+                    if (!modes.containsKey(name)) {
+                        // we dont want duplicated OutputModes in our result.
+                        modes.put(name, itemMode);
+                    }
+                }
+            }
+        }
+
+        return modes;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultCollectionItem.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,73 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.List;
+
+
+/**
+ * The default implementation of a CollectionItem.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultCollectionItem implements CollectionItem {
+
+    /** The identifier that specifies the artifact related to this item.*/
+    protected String identifier;
+
+    /** The hash that specifies the artifact related to this item.*/
+    protected String hash;
+
+    /** The list of output modes supported by the artifact of this item.*/
+    protected List<OutputMode> outputModes;
+
+
+    /**
+     * An empty constructor.
+     */
+    public DefaultCollectionItem() {
+    }
+
+
+    /**
+     * The default constructor to create a new CollectionItem related to an
+     * artifact with output modes.
+     *
+     * @param identifier The identifier of an artifact.
+     * @param outputModes The output modes supported by this item.
+     */
+    public DefaultCollectionItem(
+        String           identifier,
+        String           hash,
+        List<OutputMode> modes) {
+        this.identifier  = identifier;
+        this.hash        = hash;
+        this.outputModes = modes;
+    }
+
+
+    public String identifier() {
+        return identifier;
+    }
+
+
+    public String hash() {
+        return hash;
+    }
+
+
+    public List<OutputMode> getOutputModes() {
+        return outputModes;
+    }
+
+
+    public List<Facet> getFacets(String outputmode) {
+        for (OutputMode mode: outputModes) {
+            if (outputmode.equals(mode.getName())) {
+                // TODO Return facets, but facets are not implemented for
+                // OutputModes yet!
+            }
+        }
+
+        return 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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultData.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,69 @@
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * The default implementation of a {@link Data} item. This class just implements
+ * constructors to create instances and the necessary methods of the interface.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultData implements Data {
+
+    /** The label of this Data object.*/
+    protected String label;
+
+    /** The description.*/
+    protected String description;
+
+    /** The type.*/
+    protected String type;
+
+    /** The DataItems.*/
+    protected DataItem[] items;
+
+
+    public DefaultData() {
+    }
+
+
+    /**
+     * The default constructor to create new DefaultData objects.
+     *
+     * @param label The label.
+     * @param description The description.
+     * @param type The type.
+     * @param items The DataItems.
+     */
+    public DefaultData(
+        String label,
+        String description,
+        String type,
+        DataItem[] items)
+    {
+        this.label       = label;
+        this.description = description;
+        this.type        = type;
+        this.items       = items;
+    }
+
+
+    public String getLabel() {
+        return label;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public String getType() {
+        return type;
+    }
+
+
+    public DataItem[] getItems() {
+        return items;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultDataItem.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,54 @@
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * The default implementation of a {@link DataItem}. This class just implements
+ * constructors to create instances and the necessary methods of the interface.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultDataItem implements DataItem {
+
+    /** The label.*/
+    protected String label;
+
+    /** The description.*/
+    protected String description;
+
+    /** The value.*/
+    protected String value;
+
+
+    public DefaultDataItem() {
+    }
+
+
+    /**
+     * The default constructor to create new instances.
+     *
+     * @param label The label.
+     * @param description The description.
+     * @param value The value.
+     */
+    public DefaultDataItem(String label, String description, String value) {
+        this.label       = label;
+        this.description = description;
+        this.value       = value;
+    }
+
+
+    public String getLabel() {
+        return label;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public String getStringValue() {
+        return value;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultFacet.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,36 @@
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * The default implementation of a Facet.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultFacet implements Facet {
+
+    /** The name of the facet.*/
+    protected String name;
+
+
+    /**
+     * An empty constructor.
+     */
+    public DefaultFacet() {
+    }
+
+
+    /**
+     * The default constructor to create new DefaultFacets.
+     *
+     * @param name The name of the facet.
+     */
+    public DefaultFacet(String name) {
+        this.name = name;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultOutputMode.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,124 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * The default implementation of an Output.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultOutputMode implements OutputMode {
+
+    /** The name of this mode.*/
+    protected String name;
+
+    /** The description of this mode.*/
+    protected String description;
+
+    /** The mime-type of this mode.*/
+    protected String mimeType;
+
+    /** The list of available facets of this export mode.*/
+    protected List<Facet> facets;
+
+
+    /** A convinience constructor.*/
+    public DefaultOutputMode() {
+        facets = new ArrayList<Facet>();
+    }
+
+
+    /**
+     * The default constructor.
+     *
+     * @param name The name of this mode.
+     * @param description The description of this mode.
+     * @param mimeType The mime-type of this mode.
+     */
+    public DefaultOutputMode(String name, String description, String mimeType) {
+        this.name        = name;
+        this.description = description;
+        this.mimeType    = mimeType;
+    }
+
+
+    public DefaultOutputMode(
+        String name,
+        String description,
+        String mimeType,
+        List<Facet> facets)
+    {
+        this(name, description, mimeType);
+        this.facets = facets;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public String getMimeType() {
+        return mimeType;
+    }
+
+
+    /**
+     * Adds a new facet to this export.
+     *
+     * @param facet The new facet.
+     */
+    public void addFacet(Facet facet) {
+        facets.add(facet);
+    }
+
+
+    /**
+     * Returns the number of facets supported by this export.
+     *
+     * @return the number of facets.
+     */
+    public int getFacetCount() {
+        return facets.size();
+    }
+
+
+    /**
+     * Returns the facet at a given position.
+     *
+     * @param idx The position of a facet.
+     *
+     * @return a facet.
+     */
+    public Facet getFacet(int idx) {
+        if (idx < getFacetCount()) {
+            return facets.get(idx);
+        }
+
+        return null;
+    }
+
+
+    public Facet getFacet(String name) {
+        for (Facet facet: facets) {
+            if (name.equals(facet.getName())) {
+                return facet;
+            }
+        }
+
+        return null;
+    }
+
+
+    public List<Facet> getFacets() {
+        return facets;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultRiver.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,50 @@
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * The simpliest default implementation of a River that just stores a name.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultRiver implements River {
+
+    /** The name of the river.*/
+    protected String name;
+
+    /**
+     * The default constructor that creates empty river objects.
+     */
+    public DefaultRiver() {
+    }
+
+
+    /**
+     * This constructor should be used to create new rivers.
+     *
+     * @param name The name of the river.
+     */
+    public DefaultRiver(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * Sets the name of the river.
+     *
+     * @param name The name of the river.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * Returns the name of the river.
+     *
+     * @return the name of the river.
+     */
+    public String getName() {
+        return name;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultUser.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * Trivial implementation of a user. Useful to be subclassed.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultUser
+implements   User
+{
+    /** The identifier of the user.*/
+    protected String identifier;
+
+    /** The name of the user.*/
+    protected String name;
+
+
+    /**
+     * The default constructor.
+     */
+    public DefaultUser() {
+    }
+
+
+    /**
+     * A constructor that creates a new user.
+     *
+     * @param identifier The uuid of the user.
+     * @param name The name of the user.
+     */
+    public DefaultUser(String identifier, String name) {
+        this.identifier = identifier;
+        this.name       = name;
+    }
+
+
+    /**
+     * Returns the identifier of this user.
+     *
+     * @return the identifier of this user.
+     */
+    public String identifier() {
+        return identifier;
+    }
+
+
+    /**
+     * Returns the name of the user.
+     *
+     * @return the name of the user.
+     */
+    public String getName() {
+        return name;
+    }
+
+
+    /**
+     * Set the identifier of the user.
+     *
+     * @param identifier The new identifier.
+     */
+    public void setIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+
+    /**
+     * Set the name of the user.
+     *
+     * @param name The name for this user.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DistanceInfoObject.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,19 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface DistanceInfoObject extends Serializable {
+
+    String getDescription();
+
+    Double getFrom();
+
+    Double getTo();
+
+    String getRiverside();
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DistanceInfoObjectImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,54 @@
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DistanceInfoObjectImpl implements DistanceInfoObject {
+
+    protected String description;
+
+    protected Double from;
+
+    protected Double to;
+
+    protected String riverside;
+
+
+    public DistanceInfoObjectImpl() {
+    }
+
+
+    public DistanceInfoObjectImpl(
+        String description,
+        Double from,
+        Double to,
+        String riverside)
+    {
+        this.description = description;
+        this.from        = from;
+        this.to          = to;
+        this.riverside   = riverside;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public Double getFrom() {
+        return from;
+    }
+
+
+    public Double getTo() {
+        return to;
+    }
+
+
+    public String getRiverside() {
+        return riverside;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/DistanceInfoRecord.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,89 @@
+package de.intevation.flys.client.shared.model;
+
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+
+
+/**
+ * The DistanceInfoRecord is a wrapper to put DistanceInfo objects into
+ * a ListGrid.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class DistanceInfoRecord extends ListGridRecord {
+
+    /** The artifact collection. */
+    protected DistanceInfoObject distanceInfo;
+
+
+    /**
+     * The default constructor.
+     *
+     * @param info The distance info object.
+     */
+    public DistanceInfoRecord(DistanceInfoObject info) {
+        this.distanceInfo = info;
+
+        setDescription(info.getDescription());
+        setFrom(info.getFrom());
+        if (info.getTo() != null)
+            setTo(info.getTo());
+        else
+            setTo(info.getFrom());
+        setRiverside(info.getRiverside());
+    }
+
+
+    /**
+     * Sets the creation time.
+     *
+     * @param creationTime The creation time.
+     */
+    public void setDescription(String description) {
+        setAttribute("description", description);
+    }
+
+
+    /**
+     * Returns the description.
+     *
+     * @return the description.
+     */
+    public String getDescription() {
+        return getAttributeAsString("description");
+    }
+
+
+    public void setFrom(double from) {
+        setAttribute("from", from);
+    }
+
+
+    public double getFrom() {
+        return getAttributeAsDouble("from");
+    }
+
+    public void setTo(double to) {
+        setAttribute("to", to);
+    }
+
+
+    public double getTo() {
+        return getAttributeAsDouble("to");
+    }
+
+
+    public void setRiverside(String side) {
+        setAttribute("riverside", side);
+    }
+
+
+    public String getRiverside() {
+        return getAttributeAsString("riverside");
+    }
+
+
+    public DistanceInfoObject getDistanceInfo() {
+        return distanceInfo;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/ExportMode.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,33 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.List;
+
+
+/**
+ * An derived OutputMode that marks an OutputMode as an export. An export mode
+ * should at least support one (or more) facet which specify the type of export
+ * (e.g. CSV, WST).
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ExportMode extends DefaultOutputMode {
+
+    public ExportMode() {
+    }
+
+
+    public ExportMode(String name, String desc, String mimeType) {
+        super(name, desc, mimeType);
+    }
+
+
+    public ExportMode(
+        String name,
+        String descrition,
+        String mimeType,
+        List<Facet> facets)
+    {
+        super(name, descrition, mimeType, facets);
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/Facet.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,20 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+/**
+ * The interface that provides methods to retrieve information about a Facet.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface Facet extends Serializable {
+
+    /**
+     * Returns the name of a facet.
+     *
+     * @return the name of a facet.
+     */
+    String getName();
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/OutputMode.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,82 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+import java.util.List;
+
+
+/**
+ * This interface describes an output mode of an artifact.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface OutputMode extends Serializable {
+
+    /**
+     * Retrieves the name of this mode.
+     *
+     * @return the name of this mode.
+     */
+    String getName();
+
+
+    /**
+     * Retrieves the description of this mode.
+     *
+     * @return the description of this mode.
+     */
+    String getDescription();
+
+
+    /**
+     * Retrieves the mime-type of this mode.
+     *
+     *
+     * @return the mime-type of this mode.
+     */
+    String getMimeType();
+
+
+    /**
+     * Adds a new facet to this mode.
+     *
+     * @param facet The new facet.
+     */
+    void addFacet(Facet facet);
+
+
+    /**
+     * Returns the number of facets supported by this mode.
+     *
+     * @return the number of facets.
+     */
+    int getFacetCount();
+
+
+    /**
+     * Returns the facet at a given position.
+     *
+     * @param idx The position of a facet.
+     *
+     * @return a facet.
+     */
+    Facet getFacet(int idx);
+
+
+    /**
+     * Returns a facet based on its name.
+     *
+     * @param name The name of the facet.
+     *
+     * @return a facet or null if no such facet is available.
+     */
+    Facet getFacet(String name);
+
+
+    /**
+     * Returns all facets of this mode.
+     *
+     * @return all facets.
+     */
+    List<Facet> getFacets();
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/River.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,12 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface River extends Serializable {
+
+    String getName();
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/User.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2011 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.
+ */
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+public interface User
+extends          Serializable
+{
+    String identifier();
+
+    String getName();
+
+    void setName(String name);
+
+    void setIdentifier(String identifier);
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/WINFOArtifact.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,28 @@
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * The WINFO implementation of an Artifact.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WINFOArtifact extends DefaultArtifact {
+
+    /** The name of this artifact: 'winfo'.*/
+    public static final String NAME = "winfo";
+
+
+    public WINFOArtifact() {
+    }
+
+
+    public  WINFOArtifact(String uuid, String hash) {
+        super(uuid, hash);
+    }
+
+
+    public String getName() {
+        return NAME;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/WQInfoObject.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,17 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public interface WQInfoObject extends Serializable {
+
+    String getName();
+
+    String getType();
+
+    Double getValue();
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/WQInfoObjectImpl.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,45 @@
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class WQInfoObjectImpl implements WQInfoObject {
+
+    protected String name;
+
+    protected String type;
+
+    protected Double value;
+
+
+    public WQInfoObjectImpl() {
+    }
+
+
+    public WQInfoObjectImpl(
+        String name,
+        String type,
+        Double value)
+    {
+        this.name  = name;
+        this.type  = type;
+        this.value = value;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public String getType() {
+        return type;
+    }
+
+
+    public Double getValue() {
+        return value;
+    }
+}
+// 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-client/src/main/java/de/intevation/flys/client/shared/model/WQInfoRecord.java	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,68 @@
+package de.intevation.flys.client.shared.model;
+
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+
+
+/**
+ * The WQInfoRecord is a wrapper to put  WQ Info objects into
+ * a ListGrid.
+ *
+ * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
+ */
+public class WQInfoRecord extends ListGridRecord {
+
+    /** The artifact collection. */
+    protected WQInfoObject wqInfo;
+
+
+    /**
+     * The default constructor.
+     *
+     * @param info The wq info object.
+     */
+    public WQInfoRecord(WQInfoObject info) {
+        this.wqInfo = info;
+
+        setName(info.getName());
+        setType(info.getType());
+        if (info.getValue() != null)
+            setValue(info.getValue());
+        else
+            setValue(info.getValue());
+    }
+
+
+    public void setName(String name) {
+        setAttribute("name", name);
+    }
+
+
+    public String getName() {
+        return getAttributeAsString("name");
+    }
+
+
+    public void setType(String type) {
+        setAttribute("type", type);
+    }
+
+
+    public String getType() {
+        return getAttributeAsString("type");
+    }
+
+    public void setValue(double value) {
+        setAttribute("value", value);
+    }
+
+
+    public double getValue() {
+        return getAttributeAsDouble("value");
+    }
+
+
+    public WQInfoObject getWQInfo() {
+        return wqInfo;
+    }
+}
+// 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-client/src/main/webapp/FLYS.css	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,96 @@
+/** Add css rules here for your application. */
+
+
+/** Example rules used by the template application (remove for your app) */
+h1 {
+  font-size: 2em;
+  font-weight: bold;
+  color: #777777;
+  margin: 40px 0px 70px;
+  text-align: center;
+}
+
+.sendButton {
+  display: block;
+  font-size: 16pt;
+}
+
+/** Most GWT widgets already have a style name defined */
+.gwt-DialogBox {
+  width: 400px;
+}
+
+.dialogVPanel {
+  margin: 5px;
+}
+
+.serverResponseLabelError {
+  color: red;
+}
+
+/** Set ids using widget.getElement().setId("idOfElement") */
+#closeButton {
+  margin: 15px 6px 6px;
+}
+
+
+/** BfG Colors */
+.bgWhite {
+  background-color: #ffffff;
+}
+
+.bgBlueLight {
+  background-color: #cfe1f1;
+}
+
+.bgBlueMid {
+  background-color: #a9c9e6;
+}
+
+.bgBlueDark {
+  background-color: #669fd1;
+}
+
+
+/** Fontstyles */
+.fontLightSmall {
+  font-family: Arial,Verdana,sans-serif;
+  font-size: 10pt;
+  color: #cfe1f1;
+}
+
+.fontLightMid {
+  font-family: Arial,Verdana,sans-serif;
+  font-size: 12pt;
+  color: #cfe1f1;
+}
+
+.fontLightBig {
+  font-family: Arial,Verdana,sans-serif;
+  font-size: 18pt;
+  color: #cfe1f1;
+}
+
+.fontNormalSmall {
+  font-family: Arial,Verdana,sans-serif;
+  font-size: 10pt;
+  color: #a9c9e6;
+}
+
+.fontNormalMid {
+  font-family: Arial,Verdana,sans-serif;
+  font-size: 12pt;
+  color: #a9c9e6;
+}
+
+.fontNormalBig {
+  font-family: Arial,Verdana,sans-serif;
+  font-size: 18pt;
+  color: #a9c9e6;
+}
+
+.fontNormalSmallUnderlined {
+  font-family: Arial,Verdana,sans-serif;
+  font-size: 10pt;
+  text-decoration: underline;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/webapp/FLYS.html	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,52 @@
+<!doctype html>
+<!-- The DOCTYPE declaration above will set the    -->
+<!-- browser's rendering engine into               -->
+<!-- "Standards Mode". Replacing this declaration  -->
+<!-- with a "Quirks Mode" doctype may lead to some -->
+<!-- differences in layout.                        -->
+
+<html>
+  <head>
+    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+
+    <meta name="gwt:property" content="locale=de">
+
+    <!--                                                               -->
+    <!-- Consider inlining CSS to reduce the number of requested files -->
+    <!--                                                               -->
+    <link type="text/css" rel="stylesheet" href="FLYS.css">
+
+    <!--                                           -->
+    <!-- Any title is fine                         -->
+    <!--                                           -->
+    <title>FLYS-3.0</title>
+    
+    <!--                                           -->
+    <!-- This script loads your compiled module.   -->
+    <!-- If you add any GWT meta tags, they must   -->
+    <!-- be added before this line.                -->
+    <!--                                           -->
+    <script type="text/javascript" language="javascript" src="flys/flys.nocache.js"></script>
+  </head>
+
+    <!--                                           -->
+    <!-- The body can have arbitrary html, or      -->
+    <!-- you can leave the body empty if you want  -->
+    <!-- to create a completely dynamic UI.        -->
+    <!--                                           -->
+    <body style="margin:0px">
+
+        <!-- OPTIONAL: include this if you want history support -->
+        <iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
+
+        <!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
+        <noscript>
+            <div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
+                Your web browser must have JavaScript enabled
+                in order for this application to display correctly.
+            </div>
+        </noscript>
+
+        <div id="app"></div>
+    </body>
+</html>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/webapp/WEB-INF/web.xml	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE web-app
+    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
+    "http://java.sun.com/dtd/web-app_2_3.dtd">
+
+<web-app>
+  
+  <!-- Servlets -->
+  <servlet>
+    <servlet-name>user</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.UserServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>user</servlet-name>
+    <url-pattern>/flys/user</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>artifact</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.ArtifactServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>artifact</servlet-name>
+    <url-pattern>/flys/artifact</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>getartifact</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.GetArtifactServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>getartifact</servlet-name>
+    <url-pattern>/flys/getartifact</url-pattern>
+  </servlet-mapping>
+
+
+  <servlet>
+    <servlet-name>create-collection</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.CreateCollectionServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>create-collection</servlet-name>
+    <url-pattern>/flys/create-collection</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>rivers</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.RiverServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>rivers</servlet-name>
+    <url-pattern>/flys/rivers</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>forward</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.StepForwardServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>forward</servlet-name>
+    <url-pattern>/flys/forward</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>advance</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.AdvanceServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>advance</servlet-name>
+    <url-pattern>/flys/advance</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>add-artifact</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.AddArtifactServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>add-artifact</servlet-name>
+    <url-pattern>/flys/add-artifact</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>describe-collection</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.DescribeCollectionServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>describe-collection</servlet-name>
+    <url-pattern>/flys/describe-collection</url-pattern>
+  </servlet-mapping>
+
+    <servlet>
+    <servlet-name>user-collections</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.UserCollectionsServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>user-collections</servlet-name>
+    <url-pattern>/flys/user-collections</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>distanceinfo</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.DistanceInfoServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>distanceinfo</servlet-name>
+    <url-pattern>/flys/distanceinfo</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>meta-data</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.MetaDataServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>meta-data</servlet-name>
+    <url-pattern>/flys/meta-data</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>mainvalues</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.WQInfoServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>mainvalues</servlet-name>
+    <url-pattern>/flys/mainvalues</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>csv</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.CSVExportServiceImpl</servlet-class>
+  </servlet>
+  
+  <servlet-mapping>
+    <servlet-name>csv</servlet-name>
+    <url-pattern>/flys/csv</url-pattern>
+  </servlet-mapping>
+  
+  <servlet>
+    <servlet-name>ChartOutputService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.ChartOutputServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>ChartOutputService</servlet-name>
+    <url-pattern>/flys/chart</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>ExportService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.ExportServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>ExportService</servlet-name>
+    <url-pattern>/flys/export</url-pattern>
+  </servlet-mapping>
+
+  <!-- Default page to serve -->
+  <welcome-file-list>
+    <welcome-file>FLYS.html</welcome-file>
+  </welcome-file-list>
+
+</web-app>
Binary file flys-client/src/main/webapp/images/back.png has changed
Binary file flys-client/src/main/webapp/images/back_de.png has changed
Binary file flys-client/src/main/webapp/images/back_en.png has changed
Binary file flys-client/src/main/webapp/images/next.png has changed
Binary file flys-client/src/main/webapp/images/next_de.png has changed
Binary file flys-client/src/main/webapp/images/next_en.png has changed
Binary file flys-client/src/main/webapp/images/save.gif has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/test/java/de/intevation/flys/client/FLYSJUnit.gwt.xml	Fri Sep 28 12:14:12 2012 +0200
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module>
+  <!-- Inherit our applications main module.                      -->
+  <inherits name='de.intevation.flys.client.FLYS'/>
+
+  <!-- Specify the path to any remote services.                   -->
+  <!--
+  <servlet path="/flys/greet" class="de.intevation.flys.client.server.GreetingServiceImpl" />
+  -->
+
+</module>

http://dive4elements.wald.intevation.org