changeset 774:461b8bd696a7 2.4

merged flys-backend/2.4
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:15 +0200
parents 8d5bd3a08dd1 (diff) ade8e3d3cff8 (current diff)
children 0a56dbf81cc6
files
diffstat 342 files changed, 43579 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:15 2012 +0200
@@ -0,0 +1,3078 @@
+2011-06-28  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 2.4
+
+2011-06-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/WaterlevelExporter.java,
+	  src/main/java/de/intevation/flys/exports/WstWriter.java:
+	  Append the corrected W column (if existing) to the WST export.
+
+2011-06-27  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQ.java:
+	  Guessing increaing w is not based on direct neighbors any more.
+	  The second to be compared with is choosen by random of
+	  the values before the first one. This makes the guessing
+	  more robust against 'plateaus' of equal w values.
+
+2011-06-27  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ATWriter.java: Array
+	  for constructing the spline was too large leading to non-increasing
+	  values.
+
+2011-06-27  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Fix for flys/issue150
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Invert the x axis if its guessed that water is increasing.
+
+2011-06-27  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQ.java(guessWaterIncreasing):
+	  Added a method to guess based on a given factor of the size (default 0.05)
+	  if the water levels are increasing. Needed to determine in which direction
+	  the water level curve should be orientated. Based on random to avoid
+	  running over large dataset each time a diagram is generated.
+
+2011-06-27  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue177 (WINFO: Abflusskurven am Pegel verursachen ein Hängen des Servers)
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  Commented out code to generate time ranges for series names. There
+	  seems to be a problem while loading the discharge tables of a gauge or
+	  while determining the start and/or end time of such discharge tables.
+
+2011-06-26  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/**/*.java: Removed trailing whitespace.
+
+2011-06-26  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Removed dead code.
+
+2011-06-26  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Fix for flys/issue173
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation4.java:
+	  Fixed the way the gauge was found for a given interval.
+	  The old way does not work because it was just tested if
+	  the station point was inside the segments which is not
+	  necessarily true. The obvious solution to simply check
+	  the overlapping intervals does not work either because
+	  the gauge ranges touch each other and so more than
+	  one gauge are returned in these cases. The River.maxOverlap()
+	  is now used to find the gauge with the max overlapping
+	  range.
+
+2011-06-26  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Fix for flys/issue147
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation1.java:
+	  Removed the 'kmUp' flag. It was an left over from former
+	  WSP calculations (w/o ref km) leading to wrong results now.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Don't pass the kmUp flag to the calculation.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Added debug output to see the value of 'wq_free'.
+
+2011-06-26  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Fix for flys/issue86
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  The reference gauge for calculations "am Pegel" was determined
+	  wrong.
+	  
+2011-06-25  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DischargeTables.java:
+	  Conversion w->q was broken. This should fix a number of issues
+	  around "W am Pegel" calculations.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Issue an error report if a w->q conversion fails.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  The gauge to convert w->q with its discharge table was determined wrong.
+
+2011-06-24  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue174 (Diagramm: Q-Linie wird bei initialem Laden des Diagramms
+	  anders dargestellt als bei Ansicht auf gesamten Wertebereich)
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  The chart will now have upper margins again.
+
+2011-06-24  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue172 (Diagramm: Ursprung der Diagramme bei Dauerzahlen)
+
+	* src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java:
+	  Charts of this type will have the lower X value set to "0".
+
+2011-06-23  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ATWriter.java:
+	  Results are now in cm. Made it more robust against corner cases.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Removed superfluous import.
+
+2011-06-23  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml, doc/conf/conf.xml:
+	  Added facet config for AT exports.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java:
+	  Added 'at' facet type.
+
+	* src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java:
+	  Generate AT facets.
+	
+2011-06-23  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ATExporter.java: New.
+	  Exporter for AT facets. Needs testing.
+
+2011-06-23  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ATWriter.java: New.
+	  Writer for AT files. New code because our data model differs
+	  from Desktop-FLYS. Needs testing.
+
+2011-06-23  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue157 (Diagramm: Ursprung berechnete Abflusskurve)
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  Moved the method that adjusts the X-axis to include the "0" value from
+	  ComputedDischargeCurveGenerator to DischargeCurveGenerator. Now, both
+	  charts will include the "0" on the X-axis.
+
+2011-06-23  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQDay.java:
+	  Based on WQ now to make it exportable as AT.
+
+2011-06-23  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQ.java:
+	  New. Base class for WQKms.
+
+	  TODO 1: make it a base class for WQDay, too,
+	  TODO 2: Generate instances of WQ instead of WQKms in "Abflusskurven"
+	          calculations. This will save memory.
+
+	  AT exporter will get instances of this class as data model
+
+	* src/main/java/de/intevation/flys/artifacts/model/NamedObject.java:
+	  Add default constructor to ease inheritance.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java:
+	  Based on WQ now. Removed code allready defined in base class.
+
+2011-06-23  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Add TODOs for error reports
+	  of "Abflusskurven am Pegel". Maybe we don't need them?
+
+	* doc/conf/conf.xml: Configure report for each calculated output type.
+
+2011-06-22  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue164 (Berechnung 4: Umgekehrtes Berechnungsintervall führt zur)
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java:
+	  Create the items with range information for W and Q which allows the
+	  client to validate the user inserted values.
+
+2011-06-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Arguments in filling datastructure were flip. Now
+	  the "Abflusskurve am Pegel" looks correct again.
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  Removed superfluous imports.
+
+2011-06-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Improved situtation on rendering "Abflusskurve am Pegel". Not
+	fully working, yet.
+
+	* doc/conf/artifacts/winfo.xml: Generate facets for the location path, too.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: Misspelled
+	  the facet which prevented the facets from being to the outputs.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java:
+	  Extended to store the hash and the state id of the producing artifact/state
+	  else it results in NPEs because the data is calculated on later (wrong) states.
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java:
+	  Store the state id and the hash in the facet, too.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Add two
+	  method to do calculations for a state the artifact is currently not in
+	  and fetching the current state id.
+
+2011-06-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Generate facets for "Abflusskurven am Pegel"
+	  Not working by now. :-/
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java: New facet type
+	  for "Abflusskurven am Pegel"
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Do calculation
+	  in the artifact not in the output generator.
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java:
+	  Generate the new facets.
+
+	* src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java:
+	  Fetch data from facet.
+
+2011-06-22  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue161 (Diagramm: Q-Achse in W-Längsschnitten immer bei Q=0)
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  The Q axis (which is the second y axis) initially contains the 0 value.
+	  After a zoom action has taken place, this behaviour is no longer
+	  supported.
+
+2011-06-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Draw correction curve again.
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java:
+	  Use correction curve to map plot to axes. Not doing so prevented
+	  the correction curve from being drawn!
+	  Smaller code cleanups and simplifications.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WQCKms.java:
+	  Added methods to directly access the components w, q and c
+	  at a given index.
+
+	* src/main/java/de/intevation/flys/exports/ChartGenerator.java:
+	  Attribute access via DOM instead of XPath.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Code simplification.
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue157 (Diagramm: Ursprung berechnete Abflusskurve)
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Splitted
+	  up zooming for x and y axes to be able to override specific axis
+	  zoom behaviour.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java:
+	  The lower bound of the x axis (which is the Q axis in such chart) is
+	  always 0.
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue90 (Diagramm: Trennung derDiagrammfläche und Achsenaufheben)
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Added a margin between chart data and chart axes.
+
+2011-06-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Fix for flys/issue158
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java:
+	  Checks for right class now.
+	
+2011-06-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Fix for flys/issue154
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java:
+	  Checks for right class now.
+
+2011-06-21  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Only generate 'outs' if they have facets.
+
+	* src/main/java/de/intevation/flys/exports/ChartExportHelper.java:
+	  'boolean ? true : false' <=> 'boolean'
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Added the time-to-live to the DESCRIBE document.
+
+2011-06-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Configured states to generate report facets.
+
+2011-06-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java:
+	  Return the report.
+
+	* src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java:
+	  Generate report facets if there are problems with the calculations.
+	  TODO: Adjust winfo.xml to configure the facets.
+
+2011-06-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ReportGenerator.java:
+	  New. Generator for calculation reports.
+
+	* doc/conf/conf.xml: Added ReportGenerator.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java:
+	  Added type 'report'.
+
+	* src/main/java/de/intevation/flys/artifacts/model/ReportFacet.java:
+	  Specialized facet for serving reports. TODO: Added them to the
+	  calculation states.
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation.java:
+	  Looped through CallMeta for i18n purposes. TODO: do i18n
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Removed superfluous import.
+
+	* src/main/java/de/intevation/flys/exports/AbstractExporter.java:
+	  Added some override annotations.
+
+2011-06-20  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/CalculationResult.java:
+	  New. Used to transport the data and the error report.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/Calculation1.java,
+	  src/main/java/de/intevation/flys/artifacts/model/Calculation2.java,
+	  src/main/java/de/intevation/flys/artifacts/model/Calculation3.java,
+	  src/main/java/de/intevation/flys/artifacts/model/Calculation4.java,
+	  src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/Calculation.java,
+	  src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java,
+	  src/main/java/de/intevation/flys/exports/WaterlevelExporter.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveExporter.java:
+	  Use the CalculationResult now.
+
+2011-06-18  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation1.java:
+	  Allow an explicit reference km to enable calculation "am Pegel".
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  If a calculation "am Pegel" is done, take start km of the
+	  calculation range and find the gauge in which range it is located.
+	  Take the station of the gauge as the reference km. If no gauge
+	  is found the calcualtion falls back to calculation "auf freier Strecke".
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Replaced another inefficient attribute extraction via XPath
+	  with direct DOM access.
+
+2011-06-18  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java,
+	  src/main/java/de/intevation/flys/exports/WaterlevelExporter.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  Removed dead code.
+
+2011-06-18  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/DataFacet.java
+	  src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java,
+	  src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Only generate facets when needed.
+
+	* src/main/java/de/intevation/flys/collections/OutputParser.java:
+	  Removed more XPath for simply accessing attributes of an element.
+
+	* doc/conf/cache.xml: 200 elements in memory for "computed.values" are enough,
+	  LRU as eviction strategy is sufficent.
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java,
+	  src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Use
+	  more 'standard' Java naming conventions.
+
+2011-06-17  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Merged in the facet-slt branch to bring in the 'facet' feature.
+
+	* doc/conf/artifacts/winfo.xml: Fixed some facets.
+
+	* doc/conf/cache.xml: Added a "computed.values" cache to store the
+	  results of the WINFO calculations.
+
+	* src/main/java/de/intevation/flys/artifacts/model/ManagedFacet.java:
+	  Add support for index per facet to make them unique and identifiable.
+
+	* src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java:
+	  New. Inteface to be used to access the facet names of the configuration.
+
+	* src/main/java/de/intevation/flys/artifacts/model/DataFacet.java: New.
+	  A facet to be used to have raw access to the computed data of an artifact.
+	  Useful to export things like CSV and WST.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WaterlevelFacet.java: New.
+	  Specialized facet to access the water level data stored in WQKms arrays.
+
+	* src/main/java/de/intevation/flys/artifacts/model/DurationCurveFacet.java: New.
+	  Specialized facet to access the duration data stored in WQDay data structures.
+	  
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Facets are
+	  now generated dynamically from the current available ones stored with
+	  the artifact.
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java: Added
+	  methods computeAdvance() and computeFeed() called if artifact is fed or
+	  adance. This overwritten in subclasses to do the state depending calculations.
+
+	* src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WaterlevelState.java: These
+	  states overwrites the computeAdvance() and computeFeed() methods to do
+	  the corresponding WINFO calculations.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Calls 
+	  computeAdvance() and computeFeed() if artifact is fed or advanced. Centralized
+	  the caching mechanism.
+
+	* src/main/java/de/intevation/flys/exports/AbstractExporter.java,
+	  src/main/java/de/intevation/flys/collections/AttributeParser.java,
+	  src/main/java/de/intevation/flys/collections/OutputParser.java,
+	  src/main/java/de/intevation/flys/collections/AttributeWriter.java,
+	  src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Adjusted the code to cope with the indices of the facets. Used
+	  DOM to access the attributes instead of XPath. Removed smaller bugs
+	  concerning position generation.
+
+	* src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ChartGenerator.java,
+	  src/main/java/de/intevation/flys/exports/OutGenerator.java:
+	  Forwarded facet references.
+
+	* src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveExporter.java,
+	  src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java,
+	  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/WaterlevelExporter.java:
+	  Uses facets to fetch data and generate output now.
+
+2011-06-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Added a new parameter "wq_free" that
+	  determines the mode of calculation 1. If it is "false" (default),
+	  the calculation should be bound to a gauge.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a
+	  method to retrieve the information about the "wq_free" parameter.
+
+2011-06-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* doc/conf/artifacts/winfo.xml: Added a facet for corrected W in
+	  computation 4.
+
+2011-06-14  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java:
+	  Got rid of namespace in result document.
+
+2011-06-14  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue77 (Diagramm: Beschriftung der Kurven bei Dauerlinien)
+
+	* src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java:
+	  Give the curves in the chart names.
+
+	* 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 titles for duration
+	  chart curves.
+
+2011-06-14  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java:
+	  Write top 'Oberkante' and bottom 'Unterkante' to out going XML
+	  if they exist.
+
+2011-06-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java:
+	  Write the min/max W/Q ranges as art:range elements into the DESCRIBE.
+
+2011-06-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationSelect.java:
+	  This state that is used to retrieve locations will now write the
+	  kilometer range of the selected river into the DESCRIBE document.
+
+2011-06-10  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation1.java:
+	  New. Factored out version of "Wasserspiegellage" calculation.
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java:
+
+	  Removed some dead code.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQCKms.java:
+	  Added Override annotation and used quick access method.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Looped through error reporting use by interpolate.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Used factored out version of calculation 1. Removed dead code.
+
+2011-06-10  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation2.java:
+	  New. Factored out version of "Abflusskurve".
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Loop errors through w/q at km interpolation.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Use factored out version of calculation 2.
+
+2011-06-10  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation3.java:
+	  New. Factored out version of "Dauerzahlen".
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Loop errors through for q->w interpolations.
+	  
+	* src/main/java/de/intevation/flys/artifacts/model/WQDay.java:
+	  Added constructor to directly create with calculated results.
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation.java:
+	  Added method to return the number of problems.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Use factored out version of calculation 3.
+
+2011-06-10  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQKms.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WQCKms.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WQDay.java:
+	  Added methods to remove NaN values.
+
+2011-06-10  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation.java:
+	  New. Base class for calculations. Used to collect problems occuring
+	  during calculation.
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation4.java:
+	  Extends Calculation now. Looped through the problem reports to
+	  base class.
+
+	* src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java:
+	  Looped through the problem reports.
+
+2011-06-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java:
+	  Append the min/max range and a transformation matrix for each axis.
+
+	* src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java:
+	  Instantiate the InfoGeneratorHelper with a XYChartGenerator instance.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Changed the zoom operation. The zoom values defined in the chart request
+	  document are no longer absolute values for a specific axis. Those values
+	  represent percental values for the start and end point of x and y axes.
+	  E.g. a chart has three axes with the following ranges:
+	    - x axis  :  0 - 10
+	    - y axis 1: 20 - 40
+	    - y axis 2: 40 - 90
+	    - zoom values for x: 0.1 - 0.9 (10% - 90%)
+	    - zoom values for y: 0.2 - 0.8 (20% - 80%)
+	  The produced chart will have the following ranges:
+	    - x axis  :  1 - 9
+		- y axis 1: 24 - 36
+		  y axis 2: 50 - 80
+
+2011-06-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java:
+	  Map datasets to axes correctly.
+
+2011-06-08  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation4.java:
+	  Determine the gauges by their station positions. This hopfully
+	  fixes the problem with wrong assigned gauges and invalid segments.
+
+2011-06-08  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/Segment.java,
+	  src/main/java/de/intevation/flys/artifacts/model/Calculation4.java:
+	  Added more debug output.
+
+2011-06-08  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue103 PART 1 (WINFO: Wasserspiegellagenberechnung / Layout-Inkonsistenz)
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Selected values are formatted with the current locale. The static part
+	  of the DESCRIBE document will now contain i18n formatted numbers.
+
+2011-06-08  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue93 (WINFO: Benennung der Berechnungsart korrigieren)
+
+	* src/main/resources/messages_de_DE.properties,
+	  src/main/resources/messages_de.properties: Changed the name of
+	  calculation 4.
+
+2011-06-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Made the range determination more robust against NaN values.
+
+2011-06-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java:
+	  The second y axis is set to position "1". It was set to "2" before, but
+	  in that case, there was no position "1".
+
+2011-06-08  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/WaterlevelExporter.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionExporter.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveExporter.java,
+	  src/main/java/de/intevation/flys/exports/LongitudinalSectionGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveExporter.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveGenerator.java:
+	  s@m³/s@m\\u00b3/s@
+
+2011-06-08  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java:
+	  Only generate an axis element if a axis really exists.
+
+2011-06-07  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java:
+	  Simpified array swapping.
+
+2011-06-07  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Use java.util.List instead of java.util.Vector
+
+	* src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DischargeLongitudinalSection.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Removed superfluous imports.
+
+2011-06-07  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  createItem() is not abstract any longer to avoid code repetitionin sub classes.
+
+	* src/main/java/de/intevation/flys/artifacts/states/ComputedDischargeCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java,
+	  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/DischargeLongitudinalSection.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DurationCurveState.java,
+	  src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java:
+	  Removed duplicated code, inserted default constructors and Override annotations.
+
+2011-06-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java:
+	  Append axes range information to the info document.
+
+2011-06-07  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  If feed() fails do not store invalid values in database.
+
+2011-06-06  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java:
+	  Removed asymmetrical "- 1" from width calculation.
+
+2011-06-05  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation4.java:
+	  Fixed problem when more than one value per segment are given.
+
+2011-06-05  Sascha L. Teichmann <sascha.teichmann@intevation.de>
+
+	Refactored version of "Berechnung 4"
+
+	* src/main/java/de/intevation/flys/artifacts/model/Segment.java:
+	  Added instance fields for a reference point (= location of gauge)
+	  and backup of values (needed for naming).
+
+	* src/main/java/de/intevation/flys/artifacts/model/WQCKms.java:
+	  Added a constructor to be created from a WQKms. This is helpful
+	  if a WQKms is replaced by a back jump correction.
+
+	* src/main/java/de/intevation/flys/artifacts/model/Calculation4.java:
+	  New. Outfactored version of "W bei ungleichmaessigen Abflusslaengsschnitt".
+	  Much cleaner now and it should have a better handling of the corner
+	  cases.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Removed the linear interpolation stuff. It is now in Linear. Removed
+	  the LinearRemap interpolation method because it is not needed any
+	  longer. Added a method to interpolate a given km with a given
+	  function.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Removed the old calc 4 and used the new one.
+
+	* src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java:
+	  Deleted. Not needed any longer.
+
+	* src/main/java/de/intevation/flys/artifacts/math/Function.java:
+	  New. Interface for a uni-variate real function.
+
+	* src/main/java/de/intevation/flys/artifacts/math/Identity.java:
+	  New. Implements Function with f(x) = x
+
+	* src/main/java/de/intevation/flys/artifacts/math/Linear.java:
+	  New. Implements Function with f(x) = m*x + b
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Factored some stuff out to DoubleUtil. Removed some dead code.
+	  Does some rounding correct.
+
+	* src/main/java/de/intevation/flys/utils/DoubleUtil.java: New.
+	  Centralized utils surrounding common double operations.
+
+	* src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java:
+	  Removed superfluous imports.
+
+2011-06-03  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue90(Diagramm: Trennung der Diagrammfläche und Achsen aufheben)
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java:
+	  Determine the ranges of x and y axes. If no zoom ranges are given, we
+	  will determine the min and max xy values in the dataset manually,
+	  because JFreeCharts adds a margin to the left and right of the data
+	  area automatically..
+
+2011-06-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Moved
+	  the chart creation into an own public method. This lets the
+	  ChartInfoGenerator create charts without duplicating code.
+
+	* src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java:
+	  Limited the possible class for generators to XYChartGenerator. This
+	  enables the ChartInfoGenerator class to do the whole chart creation
+	  stuff itself without outsourcing the code to concrete subclasses.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionInfoGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionInfoGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveInfoGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveInfoGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveInfoGenerator.java:
+	  Removed the code to generate charts - this is done in ChartInfoGenerator
+	  now.
+
+2011-06-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ChartGenerator.java: Added new
+	  methods to extract the x and y ranges from request document.
+
+	* src/main/java/de/intevation/flys/exports/XYChartGenerator.java: Added a
+	  method that zooms the chart to the specified x and y ranges.
+
+2011-06-02	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* doc/conf/conf.xml: Set collection ttl to 6 hours.
+
+2011-06-01	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/Segment.java:
+	  New. Parse segments only once.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Expose new parser to artifacts. TODO: Use it.
+
+2011-06-01	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Changed interpolation methods to interpolate to an arbitrary position
+	  in a given double result array as a preparation for segment independent
+	  calculation.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Removed superfluous import.
+
+	* src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java:
+	  Fixed cause for crashing: Run back too far in some siutations.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Refactored range code a bit. Needs more work.
+
+2011-06-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java:
+	  Interchange the lower and upper x value of the chart if the x-axis is
+	  inverted before the matrix values are computed. Now, the matrix is able
+	  to work with charts that have an inverted x-axis.
+
+2011-06-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/exports/ChartInfoGenerator.java: New. A
+	  chart info generator generates a document that contains meta information
+	  for a specific chart. Concrete instances of this abstract class need to
+	  instantiate concrete ChartGenerators and dispatch nearly all methods of
+	  an OutGenerator (init(), doOut(), setMaster()) to this instance. The
+	  generate() method is implemented in the ChartInfoGenerator itself. It
+	  creates a chart with help of the ChartGenerator instance and builds a
+	  document that contains meta information of this chart.
+
+	* src/main/java/de/intevation/flys/exports/InfoGeneratorHelper.java: New.
+	  This helper is used to create the chart info document. At the moment,
+	  the only information that is included in this document is a
+	  transformation matrix to transform image coordinates into chart
+	  coordinates.
+
+	  NOTE: The transformation matrix creation needs some work to support
+	  charts with inverted X axis.
+
+	* src/main/java/de/intevation/flys/exports/LongitudinalSectionInfoGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeLongitudinalSectionInfoGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DurationCurveInfoGenerator.java,
+	  src/main/java/de/intevation/flys/exports/DischargeCurveInfoGenerator.java,
+	  src/main/java/de/intevation/flys/exports/ComputedDischargeCurveInfoGenerator.java:
+	  Concrete instances of ChartInfoGenerator that create the chart info for
+	  the currently supported chart types.
+
+	* doc/conf/conf.xml: Registered new OutGenerators.
+
+2011-05-31  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Added support for the 'type' paramter of the collection's out()
+	  operation.
+
+2011-05-31	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java,
+	  src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Use an explicit reference km for interpolation now.
+
+2011-05-30	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* doc/conf/cache.xml: Introduced cache to store the distance info per river.
+	  This boosts performance in following ab setup from  3.61 to 39.91 requests/secs.
+
+	  $ ab -c 20 -n 1000 -p distances.xml http://127.0.0.1:8181/service/distanceinfo
+
+	  $ cat distances.xml
+	    <?xml version="1.0" encoding="UTF-8"?>
+	    <art:river xmlns:art="http://www.intevation.de/2009/artifacts">Elbe</art:river>
+
+	* src/main/java/de/intevation/flys/artifacts/model/AnnotationsFactory.java: Added
+	  an iterator result to avoid construction expensive interim lists.
+
+	* src/main/java/de/intevation/flys/artifacts/services/DistanceInfoService.java: Use
+	  the cache if configured.
+
+2011-05-30	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	flys/issue82
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Only successful interpolations are named.
+
+2011-05-27	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/math/BackJumpCorrector.java:
+	  Make it work independent of river flow direction.
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java:
+	  Fixed bug in ordering segments
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Re-enabled
+	  calculation of the back jump correction. Fixed more flow direction issues.
+	  
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Removed call of XMLDebug class which is not in the version control.
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Moved the code part that skips themes right after the part that sets the
+	  master artifact for the OutGenerator. We need this master artifact to
+	  display empty charts - master artifact is used to create titles and
+	  axes.
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Deactivated themes are not put into the chart.
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/collections/AttributeParser.java:
+	  Repaired broken XPath expressions to find the output modes in an
+	  attribute document of a collection.
+
+	* src/main/java/de/intevation/flys/collections/AttributeWriter.java: This
+	  writer will now create a document that has a root node art:attribute.
+	  Before these changes, the document's root node was art:outputs which is
+	  part of the attribute document but not the right root node.
+
+	* src/main/java/de/intevation/flys/collections/FLYSArtifactCollection.java:
+	  Adapted some XPath expressions and corrected the the process to create
+	  attribute documents.
+
+2011-05-26	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/math/LinearRemap.java:
+	  Made it work independent of from/to order.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Added method to extract the ranges correctly from data.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Use the correct ranges. Comment out backjump detection temporarily.
+
+2011-05-26	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java:
+	  Generate fields for w/q input depend on flow direction.
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Moved km up question out of loop.
+2011-05-26	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java:
+	  Take the flow direction into account.
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Prevent NPE.
+
+	* src/main/java/de/intevation/flys/artifacts/states/RangeState.java:
+	  Allow to be 'from' greater than 'to' in ranges.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Negate
+	  step if 'from' is greater than 'to'.
+
+2011-05-26	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java:
+	  Round exploded values to a precision of 1e-6.
+
+2011-05-25	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	Qs are now stored in ranges for each column.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory2.java:
+	  Deleted.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java:
+	  Was WstValueTableFactory2.
+
+	* src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java: Fixed
+	  node linking bug. Removed dead code.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Q values are now stored in range trees by each column. The qs of the rows
+	  are removed and the calculations are adjusted. Removed dead code.
+
+2011-05-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java:
+	  Moved cache name to WstValueTableCacheKey. Do not cache null references.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableCacheKey.java:
+	  Moved cache name into this class.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  Store QRangeTree for each column of value table. TODO: Use them!
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory2.java:
+	  Intended as a replacement for WstValueTableFactory, but is work in progress.
+
+	* src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java: Fixed
+	  index errors and added methods to dump as graphviz graph.
+
+2011-05-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/WQSelect.java:
+	  Improved the validation of WQ values.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Use the
+	  correct input data object to determine the selected WQ mode (range or
+	  single input).
+
+2011-05-24  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-40 (part I/II)
+
+	* src/main/java/de/intevation/flys/artifacts/states/DefaultState.java:
+	  Write default values (values already selected by the user before) of the
+	  input data items into DESCRIBE.
+
+2011-05-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java:
+	  Removed needless imports.
+
+2011-05-24  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-85 (part III/III)
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java:
+	  Added a static function that returns the kilometer values (double[])
+	  from locations input (whitespace separated double values).
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Adapted
+	  the getKms() method. It will now return a computed array of kilometers
+	  if we had inserted a range, or it will return the inserted kilometers if
+	  we had inserted locations.
+
+2011-05-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableFactory.java:
+	  Moved cache key to separate class.
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTableCacheKey.java:
+	  New. The new cache key class.
+
+2011-05-24	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/model/QRangeTree.java:
+	  Model to store the q values of a WST column efficiently. First
+	  building block not to store the q values directly aside the
+	  w values.
+
+2011-05-24  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-85 (part I/III)
+
+	* doc/conf/artifacts/winfo.xml: Added two further field 'ld_mode' and
+	  'ld_locations' to the range/locations state to track the selected mode
+	  and locations.
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java:
+	  Added methods to validate the user inserted locations.
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a
+	  method to determine of a range or locations have been inserted.
+
+2011-05-23  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-62 (part II/II)
+
+	* src/main/java/de/intevation/flys/artifacts/WINFOArtifact.java: Dump the
+	  artifacts state/data in DEBUG mode in describe().
+
+	* src/main/java/de/intevation/flys/artifacts/states/LocationDistanceSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/DefaultState.java,
+	  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/LocationSelect.java,
+	  src/main/java/de/intevation/flys/artifacts/states/WQAdapted.java,
+	  src/main/java/de/intevation/flys/artifacts/states/CalculationSelect.java:
+	  States will no longer store data. The only reason for states storing
+	  StateData is to know about the necessary data for this state. If a State
+	  needs to access the user input for a specific StateData object, it needs
+	  to query the FLYSArtifact which stores the data.
+
+2011-05-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/artifacts/FLYSArtifact.java: Added a
+	  method to dump the artifacts state(s)/data.
+
+2011-05-23	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	flys/issue84
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstFactory.java:
+	  Forget to select wst kind.
+	  
+2011-05-20	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	flys/issue81
+
+	* src/main/java/de/intevation/flys/artifacts/model/WstValueTable.java:
+	  weights for kms were swapped. 
+
+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:15 2012 +0200
@@ -0,0 +1,196 @@
+2011-06-27  RELEASE 2.4
+
+    NEW:
+
+        * Finalized the Facet concept: output modes and the output generation
+          are now based on facets. Facets are created dynamically by an Artifact
+          based on the results of a calculation.
+
+        * Introduced a report mechanism that gives feedback of calculation
+          problems.
+
+        * Introduced output modes that generate XML document with calculation
+          report information.
+
+        * Introduced deactivated themes in charts: such themes are not rendered.
+
+        * Introduced a cache to store distance info per river.
+
+        * Introduced output modes that generate XML documents which contain meta
+          information of charts as axes ranges, data ranges and a transformation
+          matrix that allows to transform image coordinates into chart
+          coordinates.
+
+        * Added support for zoom values in Chart output modes.
+
+        * Added support for min/max values in DESCRIBE documents.
+
+        * Added "Oberkante" and "Unterkante" columns to distance info service.
+
+        * Added a new export mode to save data in AT format.
+
+        * Improved performance while storing/loading Q values of WST columns.
+
+        * Improved the WQ values validation for calculation 1 & 4.
+
+        * Improved calculations to work independent of "from"/"to" order of
+          kilometer ranges.
+
+        * Improved the input of WQ values for calculation 1. We distinguish
+          between a selected Q at a given gauge or a selected Q that doesn't
+          base on a given gauge.
+
+        * Allow "from" to be greater than "to" in kilometer ranges.
+
+        * Write default values of the user into the Artifact's DESCRIBE
+          document (flys/issue40).
+
+
+    FIXES:
+
+        * flys/issue62 Artifacts no longer share their data with each other.
+
+        * flys/issue77 Added titles for themes in duration curve charts.
+
+        * flys/issue81
+
+        * flys/issue82 Fixed NPE after a calculation has taken place.
+
+        * flys/issue84
+
+        * flys/issue85 Fixed location input for calculation 1 & 4.
+
+        * flys/issue86 Fixed Q determination based on a given W.
+
+        * flys/issue90 Removed space between chart axes and chart area.
+
+        * flys/issue93 Renamed calculation 4.
+
+        * flys/issue103 Append values selected by the user in the correct format
+          to the DESCRIBE document of Artifacts (uses i18n).
+
+        * flys/issue147
+
+        * flys/issue150 Invert the X axis correctly for charts of type
+          calculation 1 & 4.
+
+        * flys/issue154 Repaired computed discharge curve that broke after the
+          facets had been finalized.
+
+        * flys/issue157 Discharge curve charts (computed an static) will now
+          have a lower x value set to "1".
+
+        * flys/issue161 Longitudinal section chart's second Y axis will
+          initially start at Q=0.
+
+        * flys/issue164 Improved input validation for WQ input of calculation 4.
+
+        * flys/issue172 Duration curve charts will now have a lower x value set
+          to "0".
+
+        * flys/issue173 Fixed broken gauge determination in calculation 4.
+
+        * flys/issue174 Repaired broken upper margin between chart data and
+          chart border in longitudinal section charts.
+
+        * Added missing "Corrected W" facet for results of calculation 4.
+
+        * Map datasets in duration curve charts to the correct axes.
+
+        * Fixed broken XPath to detect output modes in an attribute document of
+          a Collection.
+
+        * Feed operation will no longer save data if the validation of the given
+          values failed.
+
+
+
+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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,215 @@
+<?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"/>
+                    </facets>
+                </outputmode>
+                <!-- TODO: Do we want an error report? -->
+            </outputmodes>
+        </state>
+
+        <state id="state.winfo.location_distance" description="state.winfo.location_distance" state="de.intevation.flys.artifacts.states.LocationDistanceSelect">
+            <data name="ld_mode" type="String" />
+            <data name="ld_locations" type="Double[]" />
+            <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"/>
+                    </facets>
+                </outputmode>
+                <!-- TODO: Do we want an error report? -->
+            </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_free" type="Boolean" />
+            <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.durationcurve_export" mime-type="text/plain">
+                    <facets>
+                        <facet name="csv" description="facet.durationcurve_export.csv" />
+                    </facets>
+                </outputmode>
+                <outputmode name="durationcurve_report" description="output.durationcurve_report" mime-type="text/xml">
+                    <facets>
+                        <facet name="report" description="facet.durationcurve_export.report" />
+                    </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.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>
+                <outputmode name="computed_dischargecurve_report" description="output.computed_dischargecurve_report" mime-type="text/xml">
+                    <facets>
+                        <facet name="report" description="facet.computed_dischargecurve_export.report"/>
+                    </facets>
+                </outputmode>
+                <outputmode name="computed_dischargecurve_at_export" description="output.computed_dischargecurve_at_export" mime-type="text/plain">
+                    <facets>
+                        <facet name="at" description="facet.computed_dischargecurve_export.at"/>
+                    </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>
+                <outputmode name="waterlevel_report" description="output.waterlevel_report" mime-type="text/xml">
+                    <facets>
+                        <facet name="report" description="facet.waterlevel_export.report"/>
+                    </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"/>
+                        <facet name="discharge_longitudinal_section.c"/>
+                    </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.wst" />
+                    </facets>
+                </outputmode>
+                <outputmode name="discharge_longitudinal_section_report" description="output.discharge_longitudinal_section_report" mime-type="text/xml">
+                    <facets>
+                        <facet name="report" description="facet.discharge_longitudinal_section_export.report"/>
+                    </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:15 2012 +0200
@@ -0,0 +1,49 @@
+<?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"
+           />
+
+    <!-- This one is used to cache the distance infos per river -->
+    <cache name="service-distanceinfo"
+           maxElementsInMemory="20"
+           eternal="false"
+           timeToIdleSeconds="360"
+           timeToLiveSeconds="86400"
+           memoryStoreEvictionPolicy="LFU"
+           />
+
+    <!-- This one is used to cache the computed values.-->
+    <cache name="computed.values"
+           maxElementsInMemory="200"
+           eternal="false"
+           timeToLiveSeconds="172800"
+           overflowToDisk="true"
+           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:15 2012 +0200
@@ -0,0 +1,112 @@
+<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="21600000"
+            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="discharge_curve_chartinfo">de.intevation.flys.exports.DischargeCurveInfoGenerator</output-generator>
+        <output-generator name="computed_discharge_curve">de.intevation.flys.exports.ComputedDischargeCurveGenerator</output-generator>
+        <output-generator name="computed_discharge_curve_chartinfo">de.intevation.flys.exports.ComputedDischargeCurveInfoGenerator</output-generator>
+        <output-generator name="longitudinal_section">de.intevation.flys.exports.LongitudinalSectionGenerator</output-generator>
+        <output-generator name="longitudinal_section_chartinfo">de.intevation.flys.exports.LongitudinalSectionInfoGenerator</output-generator>
+        <output-generator name="duration_curve">de.intevation.flys.exports.DurationCurveGenerator</output-generator>
+        <output-generator name="duration_curve_chartinfo">de.intevation.flys.exports.DurationCurveInfoGenerator</output-generator>
+        <output-generator name="discharge_longitudinal_section">de.intevation.flys.exports.DischargeLongitudinalSectionGenerator</output-generator>
+        <output-generator name="discharge_longitudinal_section_chartinfo">de.intevation.flys.exports.DischargeLongitudinalSectionInfoGenerator</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>
+        <!-- error report generators -->
+        <output-generator name="discharge_longitudinal_section_report">de.intevation.flys.exports.ReportGenerator</output-generator>
+        <output-generator name="waterlevel_report">de.intevation.flys.exports.ReportGenerator</output-generator>
+        <output-generator name="computed_dischargecurve_report">de.intevation.flys.exports.ReportGenerator</output-generator>
+        <output-generator name="durationcurve_report">de.intevation.flys.exports.ReportGenerator</output-generator>
+        <!-- AT exporter -->
+        <output-generator name="computed_dischargecurve_at_export">de.intevation.flys.exports.ATExporter</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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,1219 @@
+package de.intevation.flys.artifacts;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.TreeMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.xpath.XPathConstants;
+
+import gnu.trove.TDoubleArrayList;
+
+import net.sf.ehcache.Cache;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+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.Facet;
+import de.intevation.artifactdatabase.state.State;
+import de.intevation.artifactdatabase.state.StateEngine;
+import de.intevation.artifactdatabase.transition.TransitionEngine;
+
+import de.intevation.flys.utils.DoubleUtil;
+
+import de.intevation.flys.model.Gauge;
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.context.FLYSContext;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import de.intevation.flys.artifacts.model.DischargeTables;
+import de.intevation.flys.artifacts.model.RiverFactory;
+import de.intevation.flys.artifacts.model.Segment;
+
+import de.intevation.flys.artifacts.states.DefaultState;
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+import de.intevation.flys.artifacts.states.LocationDistanceSelect;
+
+
+/**
+ * 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);
+
+
+    public static final String COMPUTING_CACHE = "computed.values";
+
+    /** 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 List<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 list of facets supported by this artifact.*/
+    protected Map<String, List<Facet>> facets;
+
+
+    /**
+     * The default constructor that creates an empty FLYSArtifact.
+     */
+    public FLYSArtifact() {
+        data             = new TreeMap<String, StateData>();
+        previousStateIds = new ArrayList<String>();
+        facets           = new HashMap<String, List<Facet>>();
+    }
+
+
+    /**
+     * 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 static 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);
+
+            compute(context, ComputeType.FEED, true);
+
+            return describe(target, context);
+        }
+        catch (IllegalArgumentException iae) {
+            // do not store state if validation fails.
+            context.afterCall(CallContext.NOTHING);
+            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");
+
+            List<String> prev = getPreviousStateIds();
+            prev.add(getCurrentStateId());
+
+            setCurrentStateId(targetState);
+
+            logger.debug("Compute data for state: " + targetState);
+            compute(context, ComputeType.ADVANCE, true);
+
+            return describe(target, context);
+        }
+        else if (isPreviousState(targetState, context)) {
+            logger.info("Advance: Step back to");
+
+            List<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);
+                facets.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.
+     */
+    public 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());
+    }
+
+    protected State getState(Object context, String stateID) {
+        FLYSContext flysContext = getFlysContext(context);
+        StateEngine engine      = (StateEngine) flysContext.get(
+            FLYSContext.STATE_ENGINE_KEY);
+        return engine.getState(stateID);
+    }
+
+
+    /**
+     * Returns the vector of previous state identifiers.
+     *
+     * @return the vector of previous state identifiers.
+     */
+    protected List<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);
+    }
+
+
+    public Facet getNativeFacet(Facet facet) {
+        String name  = facet.getName();
+        int    index = facet.getIndex();
+
+        for (Map.Entry<String, List<Facet>> entry: facets.entrySet()) {
+            for (Facet f: entry.getValue()) {
+                if (f.getIndex() == index && f.getName().equals(name)) {
+                    return f;
+                }
+            }
+        }
+
+        logger.warn("Could not find facet: " + name + " at " + index);
+        return null;
+    }
+
+
+    /**
+     * 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.");
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        for (int i = 0; i < count; i++) {
+            Element node = (Element)nodes.item(i);
+
+            String name  = node.getAttributeNS(uri, "name");
+            String value = node.getAttributeNS(uri, "value");
+
+            if (name.length() > 0 && value.length() > 0) {
+                logger.debug("Save data item for '" + name + "' : " + value);
+
+                addData(name, new DefaultStateData(name, null, null, value));
+            }
+        }
+
+        DefaultState current = (DefaultState) getCurrentState(context);
+        current.validate(this, context);
+    }
+
+
+    /**
+     * 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  = 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.");
+
+        List<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 dMode      = getData("ld_mode");
+        StateData dFrom      = getData("ld_from");
+        StateData dTo        = getData("ld_to");
+        StateData dLocations = getData("ld_locations");
+
+        if (dMode != null) {
+            String mode = (String)dMode.getValue();
+            if ("location".equals(mode)) {
+                double[] locations = getLocations();
+                return new double[] { locations[0], locations[locations.length-1] };
+            }
+            if (dFrom != null && dTo != null) {
+                return getDistanceByRange(dFrom, dTo);
+            }
+        }
+
+        if (dLocations != null) {
+            double[] locations = getLocations();
+            return new double[] { locations[0], locations[locations.length-1] };
+        }
+
+        if (dFrom != null && dTo != null) {
+            return getDistanceByRange(dFrom, dTo);
+        }
+
+        logger.warn("No data found for distance determination!");
+
+        return null;
+    }
+
+
+    /**
+     * Determines the selected mode of distance/range input.
+     *
+     * @return true, if the range mode is selected otherwise false.
+     */
+    public boolean isRange() {
+        StateData mode = getData("ld_mode");
+
+        if (mode == null) {
+            logger.warn("No mode location/range chosen. Defaults to range.");
+            return true;
+        }
+
+        String value = (String) mode.getValue();
+
+        return value.equals("distance");
+    }
+
+
+    /**
+     * 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 DoubleUtil.explode(distance[0], distance[1], step);
+    }
+
+
+    /**
+     * Returns the selected Kms.
+     *
+     * @return the selected kms.
+     */
+    public double[] getKms() {
+        if (isRange()) {
+            double[] distance = getDistance();
+            return getKms(distance);
+
+        }
+        else {
+            return LocationDistanceSelect.getLocations(this);
+        }
+    }
+
+    public double [] getFromToStep() {
+        if (!isRange()) {
+            return null;
+        }
+        double [] fromTo = getDistance();
+
+        if (fromTo == null) {
+            return null;
+        }
+
+        StateData dStep = getData("ld_step");
+        if (dStep == null) {
+            return null;
+        }
+
+        double [] result = new double[3];
+        result[0] = fromTo[0];
+        result[1] = fromTo[1];
+
+        try {
+            String step = (String)dStep.getValue();
+            result[2] = DoubleUtil.round(Double.parseDouble(step) / 1000d);
+        }
+        catch (NumberFormatException nfe) {
+            return null;
+        }
+
+        return result;
+    }
+
+
+    /**
+     * Returns the gauge based on the current distance and river.
+     *
+     * @return the gauge.
+     */
+    public Gauge getGauge() {
+        River    river = getRiver();
+
+        if (river == null) {
+            logger.debug("no river found");
+            return null;
+        }
+
+        double[] dist  = getDistance();
+
+        if (dist == null) {
+            logger.debug("no range found");
+            return null;
+        }
+
+        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();
+        if (river == null) {
+            return null;
+        }
+
+        double [] dist  = getDistance();
+        if (dist == null) {
+            return null;
+        }
+
+        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 dSelection = getData("wq_selection");
+
+        String mode = dMode != null ? (String) dMode.getValue() : "";
+        String sel  = dSelection != null ? (String)dSelection.getValue() : null;
+
+        if (mode.equals("Q")) {
+            if (sel != null && sel.equals("single")) {
+                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 true, if the parameter is set to compute data on a free range.
+     * Otherwise it returns false, which tells the calculation that it is bound
+     * to a gauge.
+     *
+     * @return true, if the calculation should compute on a free range otherwise
+     * false and the calculation is bound to a gauge.
+     */
+    public boolean isFreeQ() {
+        StateData mode  = getData("wq_free");
+        String    value = mode != null ? (String) mode.getValue() : null;
+
+        logger.debug("isFreeQ: " + value);
+
+        if (value == null) {
+            return false;
+        }
+
+        return Boolean.valueOf(value);
+    }
+
+
+    /**
+     * 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;
+        }
+    }
+
+    public List<Segment> getSegments() {
+        StateData wqValues = getData("wq_values");
+        if (wqValues == null) {
+            logger.warn("no wq_values given");
+            return Collections.emptyList();
+        }
+        String input = (String)wqValues.getValue();
+        if (input == null || (input = input.trim()).length() == 0) {
+            logger.warn("wq_values are empty");
+            return Collections.emptyList();
+        }
+        return Segment.parseSegments(input);
+    }
+
+
+    /**
+     * 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) {
+
+        boolean debug = logger.isDebugEnabled();
+
+        if (debug) {
+            logger.debug("FLYSArtifact.getQsForWs");
+        }
+
+        River r = getRiver();
+        if (r == null) {
+            logger.warn("no river found");
+            return null;
+
+        }
+
+        double [] range = getDistance();
+        if (range == null) {
+            logger.warn("no ranges found");
+            return null;
+        }
+
+        if (debug) {
+            logger.debug("range: " + Arrays.toString(range));
+        }
+
+        Gauge g = r.determineGaugeByPosition(range[0]);
+        if (g == null) {
+            logger.warn("no gauge found for km: " + range[0]);
+            return null;
+        }
+
+        if (debug) {
+            logger.debug("convert w->q with gauge '" + g.getName() + "'");
+        }
+
+        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]);
+            if (debug) {
+                logger.debug("w: " + ws[i] + " -> q: " + qs[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 DoubleUtil.explode(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();
+    }
+
+
+    /**
+     * Computes the hash code of the entered values.
+     *
+     * @return a hash code.
+     */
+    @Override
+    public String hash() {
+        Set<Map.Entry<String, StateData>> entries = data.entrySet();
+
+        long hash  = 0L;
+        int  shift = 3;
+
+        for (Map.Entry<String, StateData> entry: entries) {
+            String key   = entry.getKey();
+            Object value = entry.getValue().getValue();
+
+            hash ^= ((long)key.hashCode() << shift)
+                 |  ((long)value.hashCode() << (shift + 3));
+            shift += 2;
+        }
+
+        return getCurrentStateId() + hash;
+    }
+
+
+    /**
+     * Dispatches the computation request to compute(CallContext context, String
+     * hash) with the current hash value of the artifact which is provided by
+     * hash().
+     *
+     * @param context The CallContext.
+     */
+    public Object compute(
+        CallContext context,
+        ComputeType type,
+        boolean     generateFacets
+    ) {
+        return compute(context, hash(), type, generateFacets);
+    }
+
+
+    /**
+     * Dispatches computation requests to the current state which needs to
+     * implement a createComputeCallback(String hash, FLYSArtifact artifact)
+     * method.
+     *
+     * @param context The CallContext.
+     * @param hash The hash value which is used to fetch computed data from
+     * cache.
+     *
+     * @return the computed data.
+     */
+    public Object compute(
+        CallContext context,
+        String      hash,
+        ComputeType type,
+        boolean     generateFacets
+    ) {
+        DefaultState current = (DefaultState) getCurrentState(context);
+        return compute(context, hash, current, type, generateFacets);
+    }
+
+    public Object compute(
+        CallContext context,
+        String      stateID,
+        String      hash,
+        ComputeType type,
+        boolean     generateFacets
+    ) {
+        DefaultState current = stateID == null
+            ? (DefaultState)getCurrentState(context)
+            : (DefaultState)getState(context, stateID);
+
+        if (hash == null) {
+            hash = hash();
+        }
+
+        return compute(context, hash, current, type, generateFacets);
+    }
+
+    public Object compute(
+        CallContext   context,
+        String        key,
+        DefaultState  state,
+        ComputeType   type,
+        boolean       generateFacets
+    ) {
+        String stateID = state.getID();
+
+        List<Facet> fs = generateFacets ? new ArrayList<Facet>() : null;
+
+        try {
+            Cache cache = CacheFactory.getCache(COMPUTING_CACHE);
+
+            Object old = null;
+
+            if (cache != null) {
+                net.sf.ehcache.Element element = cache.get(key);
+                if (element != null) {
+                    logger.debug("Got computation result from cache.");
+                    old = element.getValue();
+                }
+            }
+
+            Object res;
+            switch (type) {
+                case FEED:
+                    res = state.computeFeed(this, key, context, fs, old);
+                    break;
+                case ADVANCE:
+                    res = state.computeAdvance(this, key, context, fs, old);
+                    break;
+                default:
+                    res = null;
+            }
+
+            if (cache != null && old != res && res != null) {
+                logger.debug("Store computation result to cache.");
+                net.sf.ehcache.Element element =
+                    new net.sf.ehcache.Element(key, res);
+                cache.put(element);
+            }
+
+            return res;
+        }
+        finally {
+            if (generateFacets) {
+                if (fs.isEmpty()) {
+                    facets.remove(stateID);
+                }
+                else {
+                    facets.put(stateID, fs);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Method to dump the artifacts state/data.
+     */
+    protected void dumpArtifact() {
+        if (logger.isDebugEnabled()) {
+            logger.debug("++++++++++++++ DUMP ARTIFACT DATA +++++++++++++++++");
+
+            logger.debug("------ DUMP DATA ------");
+            Collection<StateData> allData = data.values();
+
+            for (StateData d: allData) {
+                String name  = d.getName();
+                String value = (String) d.getValue();
+
+                logger.debug("- " + name + ": " + value);
+            }
+
+            logger.debug("------ DUMP PREVIOUS STATES ------");
+            List<String> stateIds = getPreviousStateIds();
+
+            for (String id: stateIds) {
+                logger.debug("- State: " + id);
+            }
+
+            logger.debug("CURRENT STATE: " + getCurrentStateId());
+
+            logger.debug("++++++++++++++ END ARTIFACT DUMP +++++++++++++++++");
+        }
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,609 @@
+package de.intevation.flys.artifacts;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+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.DefaultOutput;
+import de.intevation.artifactdatabase.state.Facet;
+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.model.DischargeTables;
+import de.intevation.flys.artifacts.model.MainValuesFactory;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.artifacts.model.WstValueTable;
+import de.intevation.flys.artifacts.model.WstValueTableFactory;
+import de.intevation.flys.artifacts.model.Calculation;
+import de.intevation.flys.artifacts.model.Calculation1;
+import de.intevation.flys.artifacts.model.Calculation2;
+import de.intevation.flys.artifacts.model.Calculation3;
+import de.intevation.flys.artifacts.model.Calculation4;
+import de.intevation.flys.artifacts.model.CalculationResult;
+import de.intevation.flys.artifacts.model.Segment;
+
+
+/**
+ * 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());
+
+        if (logger.isDebugEnabled()) {
+            dumpArtifact();
+        }
+
+        FLYSContext flysContext = getFlysContext(context);
+
+        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)
+    {
+        List<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);
+            DefaultState state = (DefaultState) engine.getState(stateId);
+
+            List<Output> list = state.getOutputs();
+            if (list == null || list.size() == 0) {
+                logger.debug("-> No output modes for this state.");
+                continue;
+            }
+
+            List<Facet>  fs = facets.get(stateId);
+            if (fs == null || fs.size() == 0) {
+                logger.debug("No facets found.");
+                continue;
+            }
+
+            logger.debug("Found " + fs.size() + " facets in previous states.");
+
+            List<Output> generated = generateOutputs(list, fs);
+
+            ProtocolUtils.appendOutputModes(creator, outs, generated);
+        }
+
+        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());
+
+                    List<Facet>  fs = facets.get(cur.getID());
+                    if (fs != null && fs.size() > 0) {
+                        List<Output> generated = generateOutputs(list, fs);
+
+                        logger.debug("Found " + fs.size() + " current facets.");
+                        if (!generated.isEmpty()) {
+                            ProtocolUtils.appendOutputModes(
+                                creator, outs, generated);
+                        }
+                    }
+                    else {
+                        logger.debug("No facets found for the current state.");
+                    }
+
+                }
+            }
+        }
+        catch (IllegalArgumentException iae) {
+            // state is not valid, so we do not append its outputs.
+        }
+    }
+
+
+    protected List<Output> generateOutputs(List<Output> list, List<Facet> fs) {
+        List<Output> generated = new ArrayList<Output>();
+
+        boolean debug = logger.isDebugEnabled();
+
+        for (Output out: list) {
+            Output o = new DefaultOutput(
+                out.getName(),
+                out.getDescription(),
+                out.getMimeType());
+
+            Set<String> outTypes = new HashSet<String>();
+
+            for (Facet f: out.getFacets()) {
+                if (outTypes.add(f.getName()) && debug) {
+                    logger.debug("configured facet " + f);
+                }
+            }
+
+            boolean facetAdded = false;
+            for (Facet f: fs) {
+                String type = f.getName();
+
+                if (outTypes.contains(type)) {
+                    if (debug) {
+                        logger.debug("Add facet " + f);
+                    }
+                    facetAdded = true;
+                    o.addFacet(f);
+                }
+            }
+
+            if (facetAdded) {
+                generated.add(o);
+            }
+        }
+
+        return generated;
+    }
+
+
+    /**
+     * 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)
+    {
+        List<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);
+
+            ui.appendChild(state.describeStatic(this, 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 CalculationResult getWaterlevelData()
+    {
+        logger.debug("WINFOArtifact.getWaterlevelData");
+
+        River river = getRiver();
+        if (river == null) {
+            return error(new WQKms[0], "No river selected.");
+        }
+
+        double[] kms = getKms();
+        if (kms == null) {
+            return error(new WQKms[0], "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);
+            if (qs == null) {
+                return error(new WQKms[0], "conversion ws to qs failed.");
+            }
+        }
+
+        WstValueTable wst = WstValueTableFactory.getTable(river);
+        if (wst == null) {
+            return error(new WQKms[0], "No Wst found for selected river.");
+        }
+
+
+        double [] range = getDistance();
+        if (range == null) {
+            return error(new WQKms[0], "No range found");
+        }
+
+        double refKm;
+
+        if (isFreeQ()) {
+            refKm = range[0];
+            logger.debug("'free' calculation (km " + refKm + ")");
+        }
+        else {
+            Gauge gauge = river.determineGaugeByPosition(range[0]);
+            if (gauge == null) {
+                return error(
+                    new WQKms[0], "No gauge found for km " + range[0]);
+            }
+
+            refKm = gauge.getStation().doubleValue();
+
+            logger.debug(
+                "reference gauge: " + gauge.getName() + " (km " + refKm + ")");
+        }
+
+        return computeWaterlevelData(kms, qs, ws, wst, refKm);
+    }
+
+    /**
+     * 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 CalculationResult computeWaterlevelData(
+        double []     kms,
+        double []     qs,
+        double []     ws,
+        WstValueTable wst,
+        double        refKm
+    ) {
+        logger.info("WINFOArtifact.computeWaterlevelData");
+
+        Calculation1 calc1 = new Calculation1(kms, qs, ws, refKm);
+
+        return calc1.calculate(wst);
+    }
+
+
+    /**
+     * Returns the data that is computed by a duration curve computation.
+     *
+     * @return the data computed by a duration curve computation.
+     */
+    public CalculationResult getDurationCurveData() {
+        logger.debug("WINFOArtifact.getDurationCurveData");
+
+        River r = getRiver();
+
+        if (r == null) {
+            return error(null, "Cannot determine river.");
+        }
+
+        Gauge g = getGauge();
+
+        if (g == null) {
+           return error(null, "Cannot determine gauge.");
+        }
+
+        double[] locations = getLocations();
+
+        if (locations == null) {
+            return error(null, "Cannot determine location.");
+        }
+
+        WstValueTable wst = WstValueTableFactory.getTable(r);
+        if (wst == null) {
+            return error(null, "No Wst found for selected river.");
+        }
+
+        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 CalculationResult 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];
+
+        Calculation3 calculation = new Calculation3(location, days, qs);
+
+        return calculation.calculate(wst);
+    }
+
+
+    /**
+     * Returns the data that is used to create discharge curves.
+     *
+     */
+    public CalculationResult getDischargeCurveData() {
+
+        River river = getRiver();
+        if (river == null) {
+            return error(new WQKms[0], "no river found");
+        }
+
+        double [] distance = getDistance();
+
+        if (distance == null) {
+            return error(new WQKms[0], "no range found");
+        }
+
+        List<Gauge> gauges = river.determineGauges(distance[0], distance[1]);
+
+        if (gauges.isEmpty()) {
+            return error(new WQKms[0], "no gauges found");
+        }
+
+        String [] names = new String[gauges.size()];
+
+        for (int i = 0; i < names.length; ++i) {
+            names[i] = gauges.get(i).getName();
+        }
+
+        DischargeTables dt = new DischargeTables(river.getName(), names);
+
+        Map<String, double [][]> map = dt.getValues(100d);
+
+        ArrayList<WQKms> res = new ArrayList<WQKms>();
+
+        for (Gauge gauge: gauges) {
+            String name = gauge.getName();
+            double [][] values = map.get(name);
+            if (values == null) {
+                continue;
+            }
+            double [] kms = new double[values[0].length];
+            Arrays.fill(kms, gauge.getStation().doubleValue());
+            res.add(new WQKms(kms, values[0], values[1], name));
+        }
+
+        return new CalculationResult(
+            res.toArray(new WQKms[res.size()]),
+            new Calculation());
+    }
+
+
+    /**
+     * Returns the data that is computed by a discharge curve computation.
+     *
+     * @return the data computed by a discharge curve computation.
+     */
+    public CalculationResult getComputedDischargeCurveData()
+    throws NullPointerException
+    {
+        logger.debug("WINFOArtifact.getComputedDischargeCurveData");
+
+        River r = getRiver();
+
+        if (r == null) {
+            return error(new WQKms[0], "Cannot determine river.");
+        }
+
+        double[] locations = getLocations();
+
+        if (locations == null) {
+            return error(new WQKms[0], "Cannot determine location.");
+        }
+
+        WstValueTable wst = WstValueTableFactory.getTable(r);
+        if (wst == null) {
+            return error(new WQKms[0], "No Wst found for selected river.");
+        }
+
+        return computeDischargeCurveData(wst, locations[0]);
+    }
+
+
+    /**
+     * Computes the data used to create computed discharge curves.
+     *
+     * @param wst The WstValueTable that is used for the interpolation.
+     * @param location The location where the computation should be based on.
+     *
+     * @return an object that contains tuples of W/Q values at the specified
+     * location.
+     */
+    public static CalculationResult computeDischargeCurveData(
+        WstValueTable wst,
+        double location)
+    {
+        logger.info("WINFOArtifact.computeDischargeCurveData");
+
+        Calculation2 calculation = new Calculation2(location);
+
+        return calculation.calculate(wst);
+    }
+
+    protected static final CalculationResult error(Object data, String msg) {
+        return new CalculationResult(data, new Calculation(msg));
+    }
+
+    /**
+     * Returns the data computed by the discharge longitudinal section
+     * computation.
+     *
+     * @return an array of WQKms object - one object for each given Q value.
+     */
+    public CalculationResult getDischargeLongitudinalSectionData() {
+
+        logger.debug("WINFOArtifact.getDischargeLongitudinalSectionData");
+
+        River river = getRiver();
+        if (river == null) {
+            logger.debug("No river selected.");
+            return error(new WQKms[0], "No river selected.");
+        }
+
+        WstValueTable table = WstValueTableFactory.getTable(river);
+        if (table == null) {
+            logger.debug("No wst found for selected river.");
+            return error(new WQKms[0], "No wst found for selected river.");
+        }
+
+        List<Segment> segments = getSegments();
+
+        if (segments == null) {
+            logger.debug("Cannot create segments.");
+            return error(new WQKms[0], "Cannot create segments.");
+        }
+
+        double [] range = getFromToStep();
+
+        if (range == null) {
+            logger.debug("Cannot figure out range.");
+            return error(new WQKms[0], "Cannot figure out range.");
+        }
+
+        Calculation4 calc4 = new Calculation4(segments, river, isQ());
+
+        return calc4.calculate(table, range[0], range[1], range[2]);
+    }
+}
+// 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/cache/CacheFactory.java	Fri Sep 28 12:14:15 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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,300 @@
+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.commons.math.exception.MathIllegalArgumentException;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.artifacts.model.Calculation;
+
+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;
+    }
+
+    public boolean doCorrection(
+        double []   km,
+        double []   ws,
+        Calculation errors
+    ) {
+        boolean wsUp = isIncreasing(ws);
+
+        if (wsUp) {
+            km = swapClone(km);
+            ws = swapClone(ws);
+        }
+
+        boolean kmUp = isIncreasing(km);
+
+        if (!kmUp) {
+            km = sumDiffs(km);
+        }
+
+        if (log.isDebugEnabled()) {
+            log.debug("BackJumpCorrector.doCorrection ------- enter");
+            log.debug("  km increasing: " + isIncreasing(km));
+            log.debug("  ws increasing: " + isIncreasing(ws));
+            log.debug("BackJumpCorrector.doCorrection ------- leave");
+        }
+
+        boolean hasBackJumps = doCorrectionClean(km, ws, errors);
+
+        if (hasBackJumps && wsUp) {
+            // mirror back
+            swap(corrected);
+        }
+
+        return hasBackJumps;
+    }
+
+    protected boolean doCorrectionClean(
+        double []   km,
+        double []   ws,
+        Calculation errors
+    ) {
+        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();
+            }
+
+            if (log.isDebugEnabled()) {
+                for (int j = 0; j < x.length; ++j) {
+                    log.debug("   " + x[j] + " -> " + y[j]);
+                }
+            }
+
+            PolynomialSplineFunction spline;
+
+            try {
+                spline = interpolator.interpolate(x, y);
+            }
+            catch (MathIllegalArgumentException miae) {
+                // TODO: I18N
+                errors.addProblem("creating spline interpolation failed.");
+                log.error(miae);
+                continue;
+            }
+
+            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) {
+                // TODO: I18N
+                errors.addProblem("spline interpolation failed.");
+                log.error("spline interpolation failed", aode);
+            }
+        } // for all km
+
+        return !backjumps.isEmpty();
+    }
+
+    public static final boolean isIncreasing(double [] array) {
+        int inc = 0;
+        int dec = 0;
+        int sweet = (array.length-1)/2;
+        for (int i = 1; i < array.length; ++i) {
+            if (array[i] > array[i-1]) {
+                if (++inc > sweet) {
+                    return true;
+                }
+            }
+            else if (++dec > sweet) {
+                return false;
+            }
+        }
+        return inc > sweet;
+    }
+
+    public static final double [] swap(double [] array) {
+        int lo = 0;
+        int hi = array.length-1;
+        while (hi > lo) {
+            double t  = array[lo];
+            array[lo] = array[hi];
+            array[hi] = t;
+            ++lo;
+            --hi;
+        }
+
+        return array;
+    }
+
+    public static final double [] swapClone(double [] in) {
+        double [] out = new double[in.length];
+
+        for (int j = out.length-1, i = 0; j >= 0;) {
+            out[j--] = in[i++];
+        }
+
+        return out;
+    }
+
+    public static final double [] sumDiffs(double [] in) {
+        double [] out = new double[in.length];
+
+        for (int i = 1; i < out.length; ++i) {
+            out[i] = out[i-1] + Math.abs(in[i-1] - in[i]);
+        }
+
+        return out;
+    }
+}
+// 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/Function.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,6 @@
+package de.intevation.flys.artifacts.math;
+
+public interface Function {
+    double value(double x);
+}
+// 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/Identity.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,15 @@
+package de.intevation.flys.artifacts.math;
+
+public final class Identity
+implements         Function
+{
+    public static final Identity IDENTITY = new Identity();
+
+    public Identity() {
+    }
+
+    public double value(double x) {
+        return x;
+    }
+}
+// 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/Linear.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,68 @@
+package de.intevation.flys.artifacts.math;
+
+public final class Linear
+implements         Function
+{
+    private double m;
+    private double b;
+
+    public Linear(
+        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) {
+            m = 0;
+            b = 0.5*(y1 + y2);
+        }
+        else {
+            m = (y2 - y1)/(x2 - x1);
+            b = y1 - m*x1;
+        }
+    }
+
+    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;
+    }
+
+    @Override
+    public double value(double x) {
+        return m*x + 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;
+        return a + factor*(b-a);
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,53 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+import java.util.Iterator;
+
+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();
+    }
+
+
+    public static Iterator<Annotation> getAnnotationsIterator(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 (Iterator<Annotation>)query.iterate();
+    }
+}
+// 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/Calculation.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,94 @@
+package de.intevation.flys.artifacts.model;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import java.io.Serializable;
+
+import de.intevation.artifacts.CallMeta;
+
+public class Calculation
+implements   Serializable
+{
+    public static class Problem
+    implements          Serializable
+    {
+        protected Double km;
+        protected String msg;
+
+        public Problem() {
+        }
+
+        public Problem(String msg) {
+            this.msg = msg;
+        }
+
+        public Problem(double km, String msg) {
+            this.km  = km;
+            this.msg = msg;
+        }
+
+        public Element toXML(Document document, CallMeta meta) {
+            // TODO: i18n
+            Element problem = document.createElement("problem");
+            if (km != null) {
+                problem.setAttribute("km", String.valueOf(km));
+            }
+            problem.setTextContent(msg);
+            return problem;
+        }
+    } // class Problem
+
+    protected List<Problem> problems;
+
+    public Calculation() {
+    }
+
+    public Calculation(String msg) {
+        addProblem(msg);
+    }
+
+    protected List<Problem> checkProblems() {
+        if (problems == null) {
+            problems = new ArrayList<Problem>();
+        }
+        return problems;
+    }
+
+    public void addProblem(String msg) {
+        checkProblems().add(new Problem(msg));
+    }
+
+    public void addProblem(double km, String msg) {
+        checkProblems().add(new Problem(km, msg));
+    }
+
+    public boolean hasProblems() {
+        return problems != null && !problems.isEmpty();
+    }
+
+    public int numProblems() {
+        return problems != null ? problems.size() : 0;
+    }
+
+    public List<Problem> getProblems() {
+        return problems;
+    }
+
+    public void toXML(Document document, CallMeta meta) {
+
+        Element root = document.createElement("problems");
+
+        if (hasProblems()) {
+            for (Problem problem: problems) {
+                root.appendChild(problem.toXML(document, meta));
+            }
+        }
+
+        document.appendChild(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-artifacts/src/main/java/de/intevation/flys/artifacts/model/Calculation1.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,73 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.ArrayList;
+
+import org.apache.log4j.Logger;
+
+public class Calculation1
+extends      Calculation
+{
+    private static Logger logger = Logger.getLogger(Calculation1.class);
+
+    protected double [] kms;
+    protected double [] qs;
+    protected double [] ws;
+    protected double    refKm;
+
+    public Calculation1() {
+    }
+
+    public Calculation1(
+        double [] kms,
+        double [] qs,
+        double [] ws,
+        double    refKm
+    ) {
+        this.kms   = kms;
+        this.qs    = qs;
+        this.ws    = ws;
+        this.refKm = refKm;
+    }
+
+    public CalculationResult calculate(WstValueTable wst) {
+
+        ArrayList<WQKms> results = new ArrayList<WQKms>();
+
+        String    prefix;
+        double [] origData;
+
+        if (ws != null) { prefix = "W="; origData = ws; }
+        else            { prefix = "Q="; origData = qs; }
+
+        int oldNumProblems = numProblems();
+
+        for (int i = 0; i < qs.length; i++) {
+
+            double [] oqs = new double[kms.length];
+            double [] ows = new double[kms.length];
+
+            boolean success =
+                wst.interpolate(qs[i], refKm, kms, ows, oqs, this) != null;
+
+            int newNumProblems = numProblems();
+
+            if (success) {
+                WQKms result = new WQKms(kms, oqs, ows, prefix + origData[i]);
+                if (oldNumProblems != newNumProblems) {
+                    logger.debug(
+                        qs[i] + " caused " + (newNumProblems-oldNumProblems) +
+                        " new problem(s).");
+                    result.removeNaNs();
+                }
+                results.add(result);
+            }
+
+            oldNumProblems = newNumProblems;
+        }
+
+        return new CalculationResult(
+            results.toArray(new WQKms[results.size()]),
+            this);
+    }
+}
+// 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/Calculation2.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,49 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.Arrays;
+
+import org.apache.log4j.Logger;
+
+public class Calculation2
+extends      Calculation
+{
+    private static Logger logger = Logger.getLogger(Calculation2.class);
+
+    protected double km;
+
+    public Calculation2() {
+    }
+
+    public Calculation2(double km) {
+        this.km = km;
+    }
+
+    public CalculationResult calculate(WstValueTable wst) {
+
+        logger.debug("Calculation2.calculate");
+
+        double [][] wqs = wst.interpolateWQ(km, this);
+
+        if (wqs == null || wqs[0].length == 0) {
+            logger.debug("Cannot compute discharge curve data.");
+            addProblem("Cannot compute discharge curve data.");
+            return new CalculationResult(new WQKms[0], this);
+        }
+
+        double [] ws = wqs[0];
+        double [] qs = wqs[1];
+        double [] kms = new double[ws.length];
+
+        Arrays.fill(kms, km);
+
+        WQKms wqkms = new WQKms(kms, qs, ws, String.valueOf(km));
+
+        if (hasProblems()) {
+            logger.debug("found + "+numProblems()+" problems.");
+            wqkms.removeNaNs();
+        }
+
+        return new CalculationResult(new WQKms[] { wqkms }, this);
+    }
+}
+// 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/Calculation3.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,37 @@
+package de.intevation.flys.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+public class Calculation3
+extends      Calculation
+{
+    private static Logger logger = Logger.getLogger(Calculation3.class);
+
+    protected double    km;
+    protected int    [] days;
+    protected double [] qs;
+
+    public Calculation3() {
+    }
+
+    public Calculation3(double km, int [] days, double [] qs) {
+        this.km   = km;
+        this.days = days;
+        this.qs   = qs;
+    }
+
+    public CalculationResult calculate(WstValueTable wst) {
+
+        double [] ws = wst.interpolateW(km, qs, new double[qs.length], this);
+
+        WQDay wqday = new WQDay(days, ws, qs);
+
+        if (hasProblems()) {
+            logger.debug("calculation caused "+numProblems()+" problem(s).");
+            wqday.removeNaNs();
+        }
+
+        return new CalculationResult(wqday, this);
+    }
+}
+// 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/Calculation4.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,309 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Collections;
+
+import de.intevation.flys.utils.DoubleUtil;
+
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.Gauge;
+
+import de.intevation.flys.artifacts.model.WstValueTable.QPosition;
+
+import de.intevation.flys.artifacts.math.Function;
+import de.intevation.flys.artifacts.math.Linear;
+import de.intevation.flys.artifacts.math.Identity;
+import de.intevation.flys.artifacts.math.BackJumpCorrector;
+
+import org.apache.log4j.Logger;
+
+public class Calculation4
+extends      Calculation
+{
+    private static Logger logger = Logger.getLogger(Calculation4.class);
+
+    public static final double MINIMAL_STEP_WIDTH = 1e-5;
+
+    public static final Comparator<Segment> REF_CMP =
+        new Comparator<Segment>() {
+            public int compare(Segment a, Segment b) {
+                double d = a.referencePoint - b.referencePoint;
+                if (d < 0d) return -1;
+                return d > 0d ? +1 : 0;
+            }
+        };
+
+    protected List<Segment> segments;
+
+    protected boolean       isQ;
+
+    public Calculation4() {
+    }
+
+    public Calculation4(List<Segment> segments, River river, boolean isQ) {
+
+        this.segments = segments;
+        this.isQ      = isQ;
+
+        int numResults = -1;
+
+        // assign reference points
+        for (Segment segment: segments) {
+            Gauge gauge = river.maxOverlap(segment.getFrom(), segment.getTo());
+
+            if (gauge == null) {
+                logger.warn("no gauge found. Defaults to mid point.");
+                segment.setReferencePoint(
+                    0.5*(segment.getFrom()+segment.getTo()));
+            }
+            else {
+                double ref = gauge.getStation().doubleValue();
+                logger.debug(
+                    "reference gauge: " + gauge.getName() +
+                    " (km " + ref + ")");
+                segment.setReferencePoint(ref);
+            }
+
+            double [] values = segment.values;
+
+            if (numResults == -1) {
+                numResults = values.length;
+            }
+            else if (numResults != values.length) {
+                throw new IllegalArgumentException("wrong length of values");
+            }
+
+            // convert to Q if needed
+            if (!isQ && gauge != null) {
+                double [][] table = new DischargeTables(
+                    river.getName(), gauge.getName()).getFirstTable();
+
+                // need the original values for naming
+                segment.backup();
+
+                for (int i = 0; i < values.length; ++i) {
+                    values[i] = DischargeTables.getQForW(table, values[i]);
+                }
+            }
+        } // for all segments
+
+        Collections.sort(segments, REF_CMP);
+    }
+
+    public CalculationResult calculate(
+        WstValueTable table,
+        double from, double to, double step
+    ) {
+        boolean debug = logger.isDebugEnabled();
+
+        if (debug) {
+            logger.debug(
+                "calculate from " + from + " to " + to + " step " + step);
+            logger.debug("# segments: " + segments.size());
+            for (Segment segment: segments) {
+                logger.debug("  " + segment);
+            }
+        }
+
+        if (segments.isEmpty()) {
+            logger.debug("no segments found");
+            // TODO: I18N
+            addProblem("no segments found");
+            return new CalculationResult(new WQKms[0], this);
+        }
+
+        int numResults = segments.get(0).values.length;
+
+        if (numResults < 1) {
+            logger.debug("no values given");
+            // TODO: I18N
+            addProblem("no values given");
+            return new CalculationResult(new WQKms[0], this);
+        }
+
+
+        WQKms [] results = new WQKms[numResults];
+        for (int i = 0; i < results.length; ++i) {
+            results[i] = new WQKms();
+        }
+
+        if (Math.abs(step) < MINIMAL_STEP_WIDTH) {
+            step = MINIMAL_STEP_WIDTH;
+        }
+
+        if (from > to) {
+            step = -step;
+        }
+
+        QPosition [] qPositions = new QPosition[numResults];
+
+        Function [] functions = new Function[numResults];
+
+        double [] out = new double[2];
+
+        Segment sentinel = new Segment(Double.MAX_VALUE);
+        Segment s1 = sentinel, s2 = sentinel;
+
+        for (double pos = from;
+             from < to ? pos <= to : pos >= to;
+             pos = DoubleUtil.round(pos + step)
+        ) {
+            if (pos < s1.referencePoint || pos > s2.referencePoint) {
+                if (debug) {
+                    logger.debug("need to find new interval for " + pos);
+                }
+                // find new interval
+                if (pos <= segments.get(0).referencePoint) {
+                    // before first segment -> "gleichwertig"
+                    if (debug) {
+                        logger.debug("before first segment -> gleichwertig");
+                    }
+                    Segment   first  = segments.get(0);
+                    double [] values = first.values;
+                    double    refPos = first.referencePoint;
+                    for (int i = 0; i < qPositions.length; ++i) {
+                        qPositions[i] = table.getQPosition(
+                            refPos, values[i]);
+                    }
+                    sentinel.setReferencePoint(-Double.MAX_VALUE);
+                    s1 = sentinel;
+                    s2 = segments.get(0);
+                    Arrays.fill(functions, Identity.IDENTITY);
+                }
+                else if (pos >= segments.get(segments.size()-1).referencePoint) {
+                    // after last segment -> "gleichwertig"
+                    if (debug) {
+                        logger.debug("after last segment -> gleichwertig");
+                    }
+                    Segment   last   = segments.get(segments.size()-1);
+                    double [] values = last.values;
+                    double    refPos = last.referencePoint;
+                    for (int i = 0; i < qPositions.length; ++i) {
+                        qPositions[i] = table.getQPosition(
+                            refPos, values[i]);
+                    }
+                    sentinel.setReferencePoint(Double.MAX_VALUE);
+                    s1 = last;
+                    s2 = sentinel;
+                    Arrays.fill(functions, Identity.IDENTITY);
+                }
+                else { // "ungleichwertig"
+                    // find matching interval
+                    if (debug) {
+                        logger.debug("in segments -> ungleichwertig");
+                    }
+                    s1 = s2 = null;
+                    for (int i = 1, N = segments.size(); i < N; ++i) {
+                        Segment si1 = segments.get(i-1);
+                        Segment si  = segments.get(i);
+                        if (debug) {
+                            logger.debug("check " + pos + " in " +
+                                si1.referencePoint + " - " + si.referencePoint);
+                        }
+                        if (pos >= si1.referencePoint
+                        &&  pos <= si. referencePoint) {
+                            s1 = si1;
+                            s2 = si;
+                            break;
+                        }
+                    }
+
+                    if (s1 == null) {
+                        throw new IllegalStateException("no interval found");
+                    }
+
+                    Segment anchor, free;
+
+                    if (from > to) { anchor = s1; free = s2; }
+                    else           { anchor = s2; free = s1; }
+
+                    // build transforms based on "gleichwertiger" phase
+                    for (int i = 0; i < qPositions.length; ++i) {
+                        QPosition qi = table.getQPosition(
+                            anchor.referencePoint,
+                            anchor.values[i]);
+
+                        if ((qPositions[i] = qi) == null) {
+                            // TODO: I18N
+                            addProblem(pos, "cannot find q = " + anchor.values[i]);
+                            functions[i] = Identity.IDENTITY;
+                        }
+                        else {
+                            double qA = table.getQ(qi, anchor.referencePoint);
+                            double qF = table.getQ(qi, free  .referencePoint);
+
+                            functions[i] = Double.isNaN(qA) || Double.isNaN(qF)
+                                ? Identity.IDENTITY
+                                : new Linear(
+                                    qA, qF,
+                                    anchor.values[i], free.values[i]);
+
+                            if (debug) {
+                                logger.debug(
+                                    anchor.referencePoint + ": " +
+                                    qA + " -> " + functions[i].value(qA) +
+                                    " / " + free.referencePoint + ": " +
+                                    qF + " -> " + functions[i].value(qF));
+                            }
+                        }
+                    } // build transforms
+                } // "ungleichwertiges" interval
+            } // find matching interval
+
+            for (int i = 0; i < qPositions.length; ++i) {
+                QPosition qPosition = qPositions[i];
+
+                if (qPosition == null) {
+                    continue;
+                }
+
+                if (table.interpolate(pos, out, qPosition, functions[i])) {
+                    results[i].add(out[0], out[1], pos);
+                }
+                else {
+                    // TODO: I18N
+                    addProblem(pos, "cannot interpolate w/q");
+                }
+            }
+        }
+
+        // Backjump correction
+        for (int i = 0; i < results.length; ++i) {
+            BackJumpCorrector bjc = new BackJumpCorrector();
+
+            double [] ws  = results[i].getWs();
+            double [] kms = results[i].getKms();
+
+            if (bjc.doCorrection(kms, ws, this)) {
+                results[i] = new WQCKms(results[i], bjc.getCorrected());
+            }
+        }
+
+        // name the curves
+        for (int i = 0; i < results.length; ++i) {
+            results[i].setName(createName(i));
+        }
+
+        return new CalculationResult(results, this);
+    }
+
+    protected String createName(int index) {
+        // TODO: I18N
+        StringBuilder sb = new StringBuilder(isQ ? "Q" : "W");
+        sb.append(" benutzerdefiniert (");
+        for (int i = 0, N = segments.size(); i < N; ++i) {
+            if (i > 0) {
+                sb.append("; ");
+            }
+            Segment segment = segments.get(i);
+            sb.append((segment.backup != null
+                ? segment.backup
+                : segment.values)[index]);
+        }
+        sb.append(')');
+        return 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/model/CalculationResult.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,35 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+public class CalculationResult
+implements   Serializable
+{
+    protected Object      data;
+    protected Calculation report;
+
+    public CalculationResult() {
+    }
+
+    public CalculationResult(Object data, Calculation report) {
+        this.data   = data;
+        this.report = report;
+    }
+
+    public Object getData() {
+        return data;
+    }
+
+    public void setData(Object data) {
+        this.data = data;
+    }
+
+    public Calculation getReport() {
+        return report;
+    }
+
+    public void setReport(Calculation report) {
+        this.report = report;
+    }
+}
+// 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/DataFacet.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,43 @@
+package de.intevation.flys.artifacts.model;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+public class DataFacet
+extends      DefaultFacet
+{
+    protected ComputeType type;
+    protected String      hash;
+
+    public DataFacet(String name, String description) {
+        this(name, description, ComputeType.ADVANCE);
+    }
+
+    public DataFacet(String name, String description, ComputeType type) {
+        this(name, description, type, null);
+    }
+
+    public DataFacet(
+        String      name,
+        String      description,
+        ComputeType type,
+        String      hash
+    ) {
+        super(name, description);
+        this.type = type;
+        this.hash = hash;
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+        return flys.compute(
+            context, hash != null ? hash : flys.hash(), type, false);
+    }
+}
+// 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:15 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[1], 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[1][index-1];
+        double w2 = values[1][index  ];
+        double q1 = values[0][index-1];
+        double q2 = values[0][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/DurationCurveFacet.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,36 @@
+package de.intevation.flys.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+
+public class DurationCurveFacet extends DefaultFacet {
+
+    private static Logger logger = Logger.getLogger(DurationCurveFacet.class);
+
+
+    public DurationCurveFacet(String name, String description) {
+        super(0, name, description);
+    }
+
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for duration curve data");
+
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult cr = (CalculationResult)winfo.compute(
+            context, ComputeType.ADVANCE, false);
+
+        return cr.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-artifacts/src/main/java/de/intevation/flys/artifacts/model/FacetTypes.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,25 @@
+package de.intevation.flys.artifacts.model;
+
+public interface FacetTypes {
+
+    String DISCHARGE_LONGITUDINAL_W = "discharge_longitudinal_section.w";
+    String DISCHARGE_LONGITUDINAL_Q = "discharge_longitudinal_section.q";
+    String DISCHARGE_LONGITUDINAL_C = "discharge_longitudinal_section.c";
+
+    String LONGITUDINAL_W = "longitudinal_section.w";
+    String LONGITUDINAL_Q = "longitudinal_section.q";
+
+    String COMPUTED_DISCHARGE_Q = "computed_discharge_curve.q";
+
+    String DISCHARGE_CURVE = "discharge_curve.curve";
+
+    String DURATION_W = "duration_curve.w";
+    String DURATION_Q = "duration_curve.q";
+
+    String CSV = "csv";
+    String WST = "wst";
+    String AT  = "at";
+
+    String REPORT = "report";
+}
+// 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/GaugesFactory.java	Fri Sep 28 12:14:15 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:15 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:15 2012 +0200
@@ -0,0 +1,61 @@
+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;
+
+    /** The index.*/
+    protected int index;
+
+    /** 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,
+        int     index,
+        String  desc,
+        String  uuid,
+        int     pos,
+        int     active)
+    {
+        super(index, 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:15 2012 +0200
@@ -0,0 +1,34 @@
+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() {
+    }
+
+    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/QRangeTree.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,195 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+public class QRangeTree
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(QRangeTree.class);
+
+    public static class Node
+    implements          Serializable
+    {
+        Node left;
+        Node right;
+        Node prev;
+        Node next;
+
+        double a;
+        double b;
+        double q;
+
+        public Node() {
+        }
+
+        public Node(double a, double b, double q) {
+            this.a = a;
+            this.b = b;
+            this.q = q;
+        }
+
+        protected final double interpolatePrev(double pos) {
+            /*
+            f(prev.b) = prev.q
+            f(a)      = q
+
+            prev.q = m*prev.b + n
+            q      = m*a      + n <=> n = q - m*a
+
+            q - prev.q = m*(a - prev.b)
+
+            m = (q - prev.q)/(a - prev.b) # a != prev.b
+            */
+
+            if (a == prev.b) {
+                return 0.5*(q + prev.q);
+            }
+            double m = (q - prev.q)/(a - prev.b);
+            double n = q - m*a;
+            return m*pos + n;
+        }
+
+        protected final double interpolateNext(double pos) {
+            /*
+            f(next.a) = next.q
+            f(b)      = q
+
+            next.q = m*next.a + n
+            q      = m*b      + n <=> n = q - m*b
+
+            q - next.q = m*(b - next.a)
+            m = (q - next.q)/(b - next.a) # b != next.a
+            */
+
+            if (b == next.a) {
+                return 0.5*(q + next.q);
+            }
+            double m = (q - next.q)/(b - next.a);
+            double n = q - m*b;
+            return m*pos + n;
+        }
+
+        public double findQ(double pos) {
+
+            Node current = this;
+            for (;;) {
+                if (pos < current.a) {
+                    if (current.left != null) {
+                        current = current.left;
+                        continue;
+                    }
+                    return current.prev != null
+                        ? current.interpolatePrev(pos)
+                        : Double.NaN;
+                }
+                if (pos > current.b) {
+                    if (current.right != null) {
+                        current = current.right;
+                        continue;
+                    }
+                    return current.next != null
+                        ? current.interpolateNext(pos)
+                        : Double.NaN;
+                }
+                return current.q;
+            }
+        }
+    } // class Node
+
+    protected Node root;
+
+    public QRangeTree() {
+    }
+
+    /** wstQRanges need to be sorted by range.a */
+    public QRangeTree(List<Object []> qRanges, int start, int stop) {
+
+        if (stop <= start) {
+            return;
+        }
+
+        Node [] nodes = new Node[stop-start];
+        for (int i = 0; i < nodes.length; ++i) {
+            Object [] qRange = qRanges.get(i + start);
+            Double q = (Double)qRange[1];
+            Double a = (Double)qRange[2];
+            Double b = (Double)qRange[3];
+
+            nodes[i] = new Node(
+                a != null ? a.doubleValue() : -Double.MAX_VALUE,
+                b != null ? b.doubleValue() :  Double.MAX_VALUE,
+                q.doubleValue());
+        }
+
+        root = wireTree(nodes);
+    }
+
+    protected static Node wireTree(Node [] nodes) {
+        for (int i = 0; i < nodes.length; ++i) {
+            Node node = nodes[i];
+            if (i > 0             ) node.prev = nodes[i-1];
+            if (i < nodes.length-1) node.next = nodes[i+1];
+        }
+
+        return buildTree(nodes, 0, nodes.length-1);
+    }
+
+    protected static Node buildTree(Node [] nodes, int lo, int hi) {
+
+        if (lo > hi) {
+            return null;
+        }
+
+        int mid = (lo + hi) >> 1;
+        Node parent = nodes[mid];
+
+        parent.left  = buildTree(nodes, lo, mid-1);
+        parent.right = buildTree(nodes, mid+1, hi);
+
+        return parent;
+    }
+
+    public double findQ(double pos) {
+        return root != null ? root.findQ(pos) : Double.NaN;
+    }
+
+    private static final String name(Object o) {
+        return String.valueOf(System.identityHashCode(o) & 0xffffffffL);
+    }
+
+    public String toGraph() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("subgraph c");
+        sb.append(name(this));
+        sb.append(" {\n");
+        if (root != null) {
+            java.util.Stack<Node> stack = new java.util.Stack<Node>();
+            stack.push(root);
+            while (!stack.empty()) {
+                Node current = stack.pop();
+                String name = "n" + name(current);
+                sb.append(name);
+                sb.append(" [label=\"");
+                sb.append(current.a).append(", ").append(current.b);
+                sb.append(": ").append(current.q).append("\"]\n");
+                if (current.left != null) {
+                    String leftName = name(current.left);
+                    sb.append(name).append(" -- n").append(leftName).append("\n");
+                    stack.push(current.left);
+                }
+                if (current.right != null) {
+                    String rightName = name(current.right);
+                    sb.append(name).append(" -- n").append(rightName).append("\n");
+                    stack.push(current.right);
+                }
+            }
+        }
+        sb.append("}\n");
+        return 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/model/RangeWithValues.java	Fri Sep 28 12:14:15 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/ReportFacet.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,43 @@
+package de.intevation.flys.artifacts.model;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+
+import org.apache.log4j.Logger;
+
+public class ReportFacet
+extends      DefaultFacet
+implements   FacetTypes
+{
+    private static Logger logger = Logger.getLogger(ReportFacet.class);
+
+    protected ComputeType type;
+
+    public ReportFacet() {
+        this(ComputeType.ADVANCE);
+    }
+
+    public ReportFacet(ComputeType type) {
+        super(0, REPORT, "report");
+        this.type = type;
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("get report data");
+
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult cr = (CalculationResult)winfo.compute(
+            context, type, false);
+
+        return cr.getReport();
+    }
+}
+// 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/RiverFactory.java	Fri Sep 28 12:14:15 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/Segment.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,126 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import java.io.Serializable;
+
+import org.apache.log4j.Logger;
+
+import gnu.trove.TDoubleArrayList;
+
+import de.intevation.flys.utils.DoubleUtil;
+
+public class Segment
+implements   Serializable
+{
+    private static Logger logger = Logger.getLogger(Segment.class);
+
+    protected double    from;
+    protected double    to;
+    protected double [] values;
+    protected double [] backup;
+    protected double    referencePoint;
+
+    public Segment() {
+    }
+
+    public Segment(double referencePoint) {
+        this.referencePoint = referencePoint;
+    }
+
+    public Segment(double from, double to, double [] values) {
+        this.from   = from;
+        this.to     = to;
+        this.values = values;
+    }
+
+    public boolean isUp() {
+        return from < to;
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder("Segment: [");
+        sb.append("from: ").append(from).append("; to: ")
+          .append(to)
+          .append("; ref: ").append(referencePoint)
+          .append("; values: (");
+        for (int i = 0; i < values.length; ++i) {
+            if (i > 0) sb.append(", ");
+            sb.append(values[i]);
+        }
+        sb.append(")]");
+        return sb.toString();
+    }
+
+    public void setFrom(double from) {
+        this.from = from;
+    }
+
+    public void backup() {
+        backup = (double [])values.clone();
+    }
+
+    public double getFrom() {
+        return from;
+    }
+
+    public void setTo(double to) {
+        this.to = to;
+    }
+
+    public double getTo() {
+        return to;
+    }
+
+    public void setValues(double [] values) {
+        this.values = values;
+    }
+
+    public double [] getValues() {
+        return values;
+    }
+
+    public void setReferencePoint(double referencePoint) {
+        this.referencePoint = referencePoint;
+    }
+
+    public double getReferencePoint() {
+        return referencePoint;
+    }
+
+    public static List<Segment> parseSegments(String input) {
+
+        ArrayList<Segment> segments = new ArrayList<Segment>();
+
+        TDoubleArrayList vs = new TDoubleArrayList();
+
+        for (String segmentStr: input.split(":")) {
+            String [] parts = segmentStr.split(";");
+            if (parts.length < 3) {
+                logger.warn("invalid segment: '" + segmentStr + "'");
+                continue;
+            }
+            try {
+                double from = Double.parseDouble(parts[0].trim());
+                double to   = Double.parseDouble(parts[1].trim());
+
+                vs.clear();
+
+                for (String valueStr: parts[2].split(",")) {
+                    vs.add(DoubleUtil.round(
+                        Double.parseDouble(valueStr.trim())));
+                }
+
+                double [] values = vs.toNativeArray();
+                segments.add(new Segment(from, to, values));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("invalid segment: '" + segmentStr + "'");
+            }
+        }
+
+        return segments;
+    }
+}
+// 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/WQ.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,134 @@
+package de.intevation.flys.artifacts.model;
+
+import gnu.trove.TDoubleArrayList;
+
+import java.util.Random;
+
+public class WQ
+extends      NamedObject
+{
+    protected TDoubleArrayList w;
+
+    protected TDoubleArrayList q;
+
+    public WQ() {
+        this("");
+    }
+
+    public WQ(String name) {
+        w = new TDoubleArrayList();
+        q = new TDoubleArrayList();
+    }
+
+    public WQ(int capacity) {
+        this(capacity, "");
+    }
+
+
+    public WQ(int capacity, String name) {
+        super(name);
+        w = new TDoubleArrayList(capacity);
+        q = new TDoubleArrayList(capacity);
+    }
+
+    public WQ(double [] qs, double [] ws) {
+        this(qs, ws, "");
+    }
+
+    public WQ(double [] qs, double [] ws, String name) {
+        super(name);
+        w = new TDoubleArrayList(ws);
+        q = new TDoubleArrayList(qs);
+    }
+
+    public void add(double w, double q) {
+        this.w.add(w);
+        this.q.add(q);
+    }
+
+    public int size() {
+        return w.size();
+    }
+
+    public double getW(int idx) {
+        return w.getQuick(idx);
+    }
+
+    public double getQ(int idx) {
+        return q.getQuick(idx);
+    }
+
+    public double [] get(int idx) {
+        return get(idx, new double [2]);
+    }
+
+    public double [] get(int idx, double [] dst) {
+        dst[0] = w.getQuick(idx);
+        dst[1] = q.getQuick(idx);
+        return dst;
+    }
+
+    public double [] getWs() {
+        return w.toNativeArray();
+    }
+
+    public double [] getQs() {
+        return q.toNativeArray();
+    }
+
+    public static void removeNaNs(TDoubleArrayList [] arrays) {
+
+        int dest = 0;
+
+        int A = arrays.length;
+        int N = arrays[0].size();
+
+        OUTER: for (int i = 0; i < N; ++i) {
+            for (int j = 0; j < A; ++j) {
+                TDoubleArrayList a = arrays[j];
+                double v = a.getQuick(i);
+                if (Double.isNaN(v)) {
+                    continue OUTER;
+                }
+                a.setQuick(dest, v);
+            }
+            ++dest;
+        }
+
+        if (dest < N) {
+            for (int i = 0; i < A; ++i) {
+                arrays[i].remove(dest, N-dest);
+            }
+        }
+    }
+
+    public void removeNaNs() {
+        removeNaNs(new TDoubleArrayList [] { w, q });
+    }
+
+    public boolean guessWaterIncreasing() {
+        return guessWaterIncreasing(0.05f);
+    }
+
+    public boolean guessWaterIncreasing(float factor) {
+
+        int N = w.size();
+        int samples = (int)(factor*N) + 1;
+
+        int up = 0;
+
+        Random rand = new Random();
+
+        for (int i = 0; i < samples; ++i) {
+            int    pos2 = rand.nextInt(N-1) + 1;
+            int    pos1 = rand.nextInt(pos2);
+            double w1   = w.getQuick(pos1);
+            double w2   = w.getQuick(pos2);
+            if (w2 > w1) ++up;
+        }
+
+        return up > samples/2;
+    }
+
+}
+// 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/WQCKms.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,90 @@
+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() {
+    }
+
+    public WQCKms(WQKms other, double [] cws) {
+        this.w   = other.w;
+        this.q   = other.q;
+        this.kms = other.kms;
+        this.cw  = new TDoubleArrayList(cws);
+    }
+
+
+    public WQCKms(double[] kms, double[] qs, double[] ws, double[] cws) {
+        super(kms, qs, ws);
+
+        this.cw = new TDoubleArrayList(cws);
+    }
+
+    @Override
+    public void removeNaNs() {
+        removeNaNs(new TDoubleArrayList [] { w, q, cw, kms });
+    }
+
+
+    /**
+     * 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.
+     */
+    @Override
+    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.getQuick(idx);
+        }
+
+        return dst;
+    }
+
+    public double getC(int idx) {
+        return cw.getQuick(idx);
+    }
+
+
+    /**
+     * 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:15 2012 +0200
@@ -0,0 +1,67 @@
+package de.intevation.flys.artifacts.model;
+
+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
+extends      WQ
+{
+    protected TIntArrayList days;
+
+    public WQDay() {
+        super("");
+        days = new TIntArrayList();
+    }
+
+    public WQDay(int capacity) {
+        days = new TIntArrayList(capacity);
+    }
+
+    public WQDay(int [] days, double [] ws, double [] qs) {
+        super(qs, ws, "");
+        this.days = new TIntArrayList(days);
+    }
+
+
+    public void add(int day, double w, double q) {
+        super.add(w, q);
+        days.add(day);
+    }
+
+    public int getDay(int idx) {
+        return days.getQuick(idx);
+    }
+
+    @Override
+    public void removeNaNs() {
+
+        int dest = 0;
+        int N = w.size();
+
+        for (int i = 0; i < N; ++i) {
+            double wi = w.getQuick(i);
+            double qi = q.getQuick(i);
+
+            if (Double.isNaN(wi) || Double.isNaN(qi)) {
+                continue;
+            }
+
+            days.setQuick(dest, days.getQuick(i));
+            w.setQuick(dest, wi);
+            q.setQuick(dest, qi);
+            ++dest;
+        }
+
+        if (dest < N) {
+            days.remove(dest, N-dest);
+            w   .remove(dest, N-dest);
+            q   .remove(dest, N-dest);
+        }
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,106 @@
+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      WQ
+{
+    private static Logger logger = Logger.getLogger(WQKms.class);
+
+    /** The array that contains the 'KMs' values.*/
+    protected TDoubleArrayList kms;
+
+
+    public WQKms() {
+        this("");
+    }
+
+
+    public WQKms(String name) {
+        super(name);
+        this.kms = new TDoubleArrayList();
+    }
+
+
+    public WQKms(int capacity) {
+        this(capacity, "");
+    }
+
+
+    public WQKms(int capacity, String name) {
+        super(name);
+        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(qs, ws, name);
+        this.kms = new TDoubleArrayList(kms);
+    }
+
+    @Override
+    public void removeNaNs() {
+        removeNaNs(new TDoubleArrayList [] { w, q, 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 km) {
+        super.add(w, q);
+        kms.add(km);
+    }
+
+    /**
+     * 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.
+     */
+    @Override
+    public double[] get(int idx, double [] dst) {
+        dst[0] = w  .getQuick(idx);
+        dst[1] = q  .getQuick(idx);
+        dst[2] = kms.getQuick(idx);
+        return dst;
+    }
+
+    public double getKms(int idx) {
+        return kms.getQuick(idx);
+    }
+
+    public double[] getKms() {
+        return kms.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 from + " - " + to;
+    }
+}
+// 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/WaterlevelFacet.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,54 @@
+package de.intevation.flys.artifacts.model;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.DefaultFacet;
+
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.states.DefaultState.ComputeType;
+
+public class WaterlevelFacet extends DefaultFacet {
+
+    private static Logger logger = Logger.getLogger(WaterlevelFacet.class);
+
+    protected ComputeType type;
+    protected String      stateID;
+    protected String      hash;
+
+    public WaterlevelFacet(int index, String name, String description) {
+        this(index, name, description, ComputeType.ADVANCE, null, null);
+    }
+
+    public WaterlevelFacet(
+        int         index,
+        String      name,
+        String      description,
+        ComputeType type,
+        String      stateID,
+        String      hash
+
+    ) {
+        super(index, name, description);
+        this.type    = type;
+        this.stateID = stateID;
+        this.hash    = hash;
+    }
+
+    public Object getData(Artifact artifact, CallContext context) {
+        logger.debug("Get data for waterlevels at index: " + index);
+
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult res = (CalculationResult)
+            winfo.compute(context, stateID, hash, type, false);
+
+        WQKms [] wqkms = (WQKms [])res.getData();
+
+        return wqkms[index];
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,48 @@
+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 {
+
+    public static final int DEFAULT_KIND = 0;
+
+    /** 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) {
+        return getWst(river, DEFAULT_KIND);
+    }
+
+    public static Wst getWst(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();
+
+        return wsts.isEmpty() ? null : wsts.get(0);
+    }
+}
+// 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:15 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:15 2012 +0200
@@ -0,0 +1,631 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+import de.intevation.flys.artifacts.math.Linear;
+import de.intevation.flys.artifacts.math.Function;
+
+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;
+
+import org.apache.commons.math.exception.MathIllegalArgumentException;
+
+public class WstValueTable
+implements   Serializable
+{
+    private static Logger log = Logger.getLogger(WstValueTable.class);
+
+    public static final int DEFAULT_Q_STEPS = 500;
+
+    public static final class Column
+    implements                Serializable
+    {
+        protected String name;
+
+        protected QRangeTree qRangeTree;
+
+        public Column() {
+        }
+
+        public Column(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public QRangeTree getQRangeTree() {
+            return qRangeTree;
+        }
+
+        public void setQRangeTree(QRangeTree qRangeTree) {
+            this.qRangeTree = qRangeTree;
+        }
+    } // class Column
+
+    public static final class QPosition {
+
+        protected int    index;
+        protected double weight;
+
+        public QPosition() {
+        }
+
+        public QPosition(int index, double weight) {
+            this.index  = index;
+            this.weight = weight;
+        }
+
+        public QPosition set(int index, double weight) {
+            this.index  = index;
+            this.weight = weight;
+            return this;
+        }
+
+    } // class Position
+
+    public static final class Row
+    implements                Serializable, Comparable<Row>
+    {
+        double    km;
+        double [] ws;
+
+        public Row() {
+        }
+
+        public Row(double km) {
+            this.km = km;
+        }
+
+        public Row(double km, double [] ws) {
+            this(km);
+            this.ws = ws;
+        }
+
+        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 void interpolateW(
+            Row           other,
+            double        km,
+            double []     iqs,
+            double []     ows,
+            WstValueTable table,
+            Calculation   errors
+        ) {
+            double kmWeight = Linear.factor(km, this.km, other.km);
+
+            QPosition qPosition = new QPosition();
+
+            for (int i = 0; i < iqs.length; ++i) {
+                if (table.getQPosition(km, iqs[i], qPosition) != null) {
+                    double wt =       getW(qPosition);
+                    double wo = other.getW(qPosition);
+                    if (Double.isNaN(wt) || Double.isNaN(wo)) {
+                        if (errors != null) {
+                            // TODO: I18N
+                            errors.addProblem(
+                                km, "cannot find w for q = " + iqs[i]);
+                        }
+                        ows[i] = Double.NaN;
+                    }
+                    else {
+                        ows[i] = Linear.weight(kmWeight, wt, wo);
+                    }
+                }
+                else {
+                    if (errors != null) {
+                        // TODO: I18N
+                        errors.addProblem(km, "cannot find q = " + iqs[i]);
+                    }
+                    ows[i] = Double.NaN;
+                }
+            }
+        }
+
+        public double [][] interpolateWQ(
+            Row           other,
+            double        km,
+            int           steps,
+            WstValueTable table,
+            Calculation   errors
+        ) {
+            int W = Math.min(ws.length, other.ws.length);
+
+            if (W < 1) {
+                if (errors != null) {
+                    // TODO: I18N
+                    errors.addProblem("no ws found");
+                }
+                return new double[2][0];
+            }
+
+            double factor = Linear.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 = Linear.weight(factor, ws[i], other.ws[i]);
+                double wqs = Linear.weight(
+                    factor,
+                    table.getQIndex(i, km),
+                    table.getQIndex(i, other.km));
+
+                if (Double.isNaN(wws) || Double.isNaN(wqs)) {
+                    if (errors != null) {
+                        // TODO: I18N
+                        errors.addProblem(km, "cannot find w or q");
+                    }
+                }
+                else {
+                    if (wqs < minQ) minQ = wqs;
+                    if (wqs > maxQ) maxQ = wqs;
+                }
+
+                splineW[i] = wws;
+                splineQ[i] = wqs;
+            }
+
+            double stepWidth = (maxQ - minQ)/steps;
+
+            SplineInterpolator interpolator = new SplineInterpolator();
+            PolynomialSplineFunction spline;
+
+            try {
+                spline = interpolator.interpolate(splineQ, splineW);
+            }
+            catch (MathIllegalArgumentException miae) {
+                if (errors != null) {
+                    // TODO: I18N
+                    errors.addProblem(km, "spline creation failed");
+                }
+                log.error("spline creation failed");
+                return new double[2][0];
+            }
+
+            double [] outWs = new double[steps];
+            double [] outQs = new double[steps];
+
+            Arrays.fill(outWs, Double.NaN);
+            Arrays.fill(outQs, Double.NaN);
+
+            try {
+                double q = minQ;
+                for (int i = 0; i < outWs.length; ++i, q += stepWidth) {
+                    outWs[i] = spline.value(outQs[i] = q);
+                }
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                if (errors != null) {
+                    // TODO: I18N
+                    errors.addProblem(km, "spline interpolation failed");
+                }
+                log.error("spline interpolation failed", aode);
+            }
+
+            return new double [][] { outWs, outQs };
+        }
+
+        public double [][] interpolateWQ(
+            int           steps,
+            WstValueTable table,
+            Calculation   errors
+        ) {
+            int W = ws.length;
+
+            if (W < 1) {
+                if (errors != null) {
+                    // TODO: I18N
+                    errors.addProblem(km, "no ws found");
+                }
+                return new double[2][0];
+            }
+
+            double [] splineQ = new double[W];
+
+            double minQ =  Double.MAX_VALUE;
+            double maxQ = -Double.MAX_VALUE;
+
+            for (int i = 0; i < W; ++i) {
+                double sq = table.getQIndex(i, km);
+                if (Double.isNaN(sq)) {
+                    if (errors != null) {
+                        // TODO: I18N
+                        errors.addProblem(
+                            km, "no q found in " + (i+1) + " column");
+                    }
+                }
+                else {
+                    if (sq < minQ) minQ = sq;
+                    if (sq > maxQ) maxQ = sq;
+                }
+                splineQ[i] = sq;
+            }
+
+            double stepWidth = (maxQ - minQ)/steps;
+
+            SplineInterpolator interpolator = new SplineInterpolator();
+
+            PolynomialSplineFunction spline;
+
+            try {
+                spline = interpolator.interpolate(splineQ, ws);
+            }
+            catch (MathIllegalArgumentException miae) {
+                if (errors != null) {
+                    // TODO: I18N
+                    errors.addProblem(km, "spline creation failed");
+                }
+                log.error("spline creation failed");
+                return new double[2][0];
+            }
+
+            double [] outWs = new double[steps];
+            double [] outQs = new double[steps];
+
+            Arrays.fill(outWs, Double.NaN);
+            Arrays.fill(outQs, Double.NaN);
+
+            try {
+                double q = minQ;
+                for (int i = 0; i < outWs.length; ++i, q += stepWidth) {
+                    outWs[i] = spline.value(outQs[i] = q);
+                }
+            }
+            catch (ArgumentOutsideDomainException aode) {
+                if (errors != null) {
+                    // TODO: I18N
+                    errors.addProblem(km, "spline interpolation failed");
+                }
+                log.error("spline interpolation failed.", aode);
+            }
+
+            return new double [][] { outWs, outQs };
+        }
+
+
+        public double getW(QPosition qPosition) {
+            int    index  = qPosition.index;
+            double weight = qPosition.weight;
+
+            return weight == 1.0
+                ? ws[index]
+                : Linear.weight(weight, ws[index-1], ws[index]);
+        }
+
+        public double getW(
+            Row       other,
+            double    km,
+            QPosition qPosition
+        ) {
+            double kmWeight = Linear.factor(km, this.km, other.km);
+
+            int    index  = qPosition.index;
+            double weight = qPosition.weight;
+
+            double tw, ow;
+
+            if (weight == 1.0) {
+                tw = ws[index];
+                ow = other.ws[index];
+            }
+            else {
+                tw = Linear.weight(weight, ws[index-1], ws[index]);
+                ow = Linear.weight(weight, other.ws[index-1], other.ws[index]);
+            }
+
+            return Linear.weight(kmWeight, tw, ow);
+        }
+    } // class Row
+
+    protected List<Row> rows;
+
+    protected Column [] columns;
+
+    public WstValueTable() {
+        rows = new ArrayList<Row>();
+    }
+
+    public WstValueTable(Column [] columns) {
+        this();
+        this.columns = columns;
+    }
+
+    public WstValueTable(Column [] columns, List<Row> rows) {
+        this.columns = columns;
+        this.rows    = rows;
+    }
+
+    public void sortRows() {
+        Collections.sort(rows);
+    }
+
+    public double [] interpolateW(double km, double [] qs, double [] ws) {
+        return interpolateW(km, qs, ws, null);
+    }
+
+    public double [] interpolateW(
+        double      km,
+        double []   qs,
+        double []   ws,
+        Calculation errors
+    ) {
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        QPosition qPosition = new QPosition();
+
+        if (rowIndex >= 0) { // direct row match
+            Row row = rows.get(rowIndex);
+            for (int i = 0; i < qs.length; ++i) {
+                if (getQPosition(km, qs[i], qPosition) == null) {
+                    if (errors != null) {
+                        // TODO: I18N
+                        errors.addProblem(km, "cannot find q = " + qs[i]);
+                    }
+                    ws[i] = Double.NaN;
+                }
+                else {
+                    if (Double.isNaN(ws[i] = row.getW(qPosition))
+                    && errors != null) {
+                        // TODO: I18N
+                        errors.addProblem(
+                            km, "cannot find w for q = " + qs[i]);
+                    }
+                }
+            }
+        }
+        else { // needs bilinear interpolation
+            rowIndex = -rowIndex -1;
+
+            if (rowIndex < 1 || rowIndex >= rows.size()) {
+                // do not extrapolate
+                Arrays.fill(ws, Double.NaN);
+                if (errors != null) {
+                    // TODO: I18N
+                    errors.addProblem(km, "km not found");
+                }
+            }
+            else {
+                Row r1 = rows.get(rowIndex-1);
+                Row r2 = rows.get(rowIndex);
+                r1.interpolateW(r2, km, qs, ws, this, errors);
+            }
+        }
+
+        return ws;
+    }
+
+    public double [][] interpolateWQ(double km) {
+        return interpolateWQ(km, null);
+    }
+
+    public double [][] interpolateWQ(double km, Calculation errors) {
+        return interpolateWQ(km, DEFAULT_Q_STEPS, errors);
+    }
+
+    public double [][] interpolateWQ(double km, int steps, Calculation errors) {
+
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex >= 0) { // direct row match
+            Row row = rows.get(rowIndex);
+            return row.interpolateWQ(steps, this, errors);
+        }
+
+        rowIndex = -rowIndex -1;
+
+        if (rowIndex < 1 || rowIndex >= rows.size()) {
+            // do not extrapolate
+            if (errors != null) {
+                // TODO: I18N
+                errors.addProblem(km, "km not found");
+            }
+            return new double[2][0];
+        }
+
+        Row r1 = rows.get(rowIndex-1);
+        Row r2 = rows.get(rowIndex);
+
+        return r1.interpolateWQ(r2, km, steps, this, errors);
+    }
+
+    public boolean interpolate(
+        double    km,
+        double [] out,
+        QPosition qPosition,
+        Function  qFunction
+    ) {
+        int R1 = rows.size()-1;
+
+        out[1] = qFunction.value(getQ(qPosition, km));
+
+        if (Double.isNaN(out[1])) {
+            return false;
+        }
+
+        QPosition nPosition = new QPosition();
+        if (getQPosition(km, out[1], nPosition) == null) {
+            return false;
+        }
+
+        int rowIndex = Collections.binarySearch(rows, new Row(km));
+
+        if (rowIndex >= 0) {
+            // direct row match
+            out[0] = rows.get(rowIndex).getW(nPosition);
+            return !Double.isNaN(out[0]);
+        }
+
+        rowIndex = -rowIndex -1;
+
+        if (rowIndex < 1 || rowIndex >= R1) {
+            // do not extrapolate
+            return false;
+        }
+
+        Row r1 = rows.get(rowIndex-1);
+        Row r2 = rows.get(rowIndex);
+        out[0] = r1.getW(r2, km, nPosition);
+
+        return !Double.isNaN(out[0]);
+    }
+
+    public QPosition interpolate(
+        double      q,
+        double      referenceKm,
+        double []   kms,
+        double []   ws,
+        double []   qs,
+        Calculation errors
+    ) {
+        return interpolate(
+            q, referenceKm, kms, ws, qs, 0, kms.length, errors);
+    }
+
+    public QPosition interpolate(
+        double      q,
+        double      referenceKm,
+        double []   kms,
+        double []   ws,
+        double []   qs,
+        int         startIndex,
+        int         length,
+        Calculation errors
+    ) {
+        QPosition qPosition = getQPosition(referenceKm, q);
+
+        if (qPosition == null) {
+            // we cannot locate q at km
+            Arrays.fill(ws, Double.NaN);
+            Arrays.fill(qs, Double.NaN);
+            if (errors != null) {
+                errors.addProblem(referenceKm, "cannot find q");
+            }
+            return null;
+        }
+
+        Row kmKey = new Row();
+
+        int R1 = rows.size()-1;
+
+        for (int i = startIndex, end = startIndex+length; i < end; ++i) {
+
+            if (Double.isNaN(qs[i] = getQ(qPosition, kms[i]))) {
+                if (errors != null) {
+                    errors.addProblem(kms[i], "cannot find q");
+                }
+                ws[i] = Double.NaN;
+                continue;
+            }
+
+            kmKey.km = kms[i];
+            int rowIndex = Collections.binarySearch(rows, kmKey);
+
+            if (rowIndex >= 0) {
+                // direct row match
+                if (Double.isNaN(ws[i] = rows.get(rowIndex).getW(qPosition))
+                && errors != null) {
+                    errors.addProblem(kms[i], "cannot find w");
+                }
+                continue;
+            }
+
+            rowIndex = -rowIndex -1;
+
+            if (rowIndex < 1 || rowIndex >= R1) {
+                // do not extrapolate
+                if (errors != null) {
+                    errors.addProblem(kms[i], "cannot find km");
+                }
+                ws[i] =  Double.NaN;
+                continue;
+            }
+            Row r1 = rows.get(rowIndex-1);
+            Row r2 = rows.get(rowIndex);
+
+            if (Double.isNaN(ws[i] = r1.getW(r2, kms[i], qPosition))
+            && errors != null) {
+                errors.addProblem(kms[i], "cannot find w");
+            }
+        }
+
+        return qPosition;
+    }
+
+    public QPosition getQPosition(double km, double q) {
+        return getQPosition(km, q, new QPosition());
+    }
+
+    public QPosition getQPosition(double km, double q, QPosition qPosition) {
+
+        if (columns.length == 0) {
+            return null;
+        }
+
+        double qLast = columns[0].getQRangeTree().findQ(km);
+
+        if (Math.abs(qLast - q) < 0.00001) {
+            return qPosition.set(0, 1d);
+        }
+
+        for (int i = 1; i < columns.length; ++i) {
+            double qCurrent = columns[i].getQRangeTree().findQ(km);
+            if (Math.abs(qCurrent - q) < 0.00001) {
+                return qPosition.set(i, 1d);
+            }
+
+            double qMin, qMax;
+            if (qLast < qCurrent) { qMin = qLast; qMax = qCurrent; }
+            else                  { qMin = qCurrent; qMax = qLast; }
+
+            if (q > qMin && q < qMax) {
+                double weight = Linear.factor(q, qLast, qCurrent);
+                return qPosition.set(i, weight);
+            }
+            qLast = qCurrent;
+        }
+
+        return null;
+    }
+
+    public double getQIndex(int index, double km) {
+        return columns[index].getQRangeTree().findQ(km);
+    }
+
+    public double getQ(QPosition qPosition, double km) {
+        int    index  = qPosition.index;
+        double weight = qPosition.weight;
+
+        if (weight == 1d) {
+            return columns[index].getQRangeTree().findQ(km);
+        }
+        double q1 = columns[index-1].getQRangeTree().findQ(km);
+        double q2 = columns[index  ].getQRangeTree().findQ(km);
+        return Linear.weight(weight, q1, q2);
+    }
+
+}
+// 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/WstValueTableCacheKey.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,30 @@
+package de.intevation.flys.artifacts.model;
+
+import java.io.Serializable;
+
+public final class WstValueTableCacheKey
+implements         Serializable
+{
+    public static final String CACHE_NAME = "wst-value-table";
+
+    private int riverId;
+    private int kind;
+
+    public WstValueTableCacheKey(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 WstValueTableCacheKey)) {
+            return false;
+        }
+        WstValueTableCacheKey o = (WstValueTableCacheKey)other;
+        return riverId == o.riverId && kind == o.kind;
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,244 @@
+package de.intevation.flys.artifacts.model;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import de.intevation.flys.backend.SessionHolder;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.flys.model.River;
+import de.intevation.flys.model.Wst;
+
+import org.hibernate.Session;
+import org.hibernate.Query;
+import org.hibernate.SQLQuery;
+
+import org.hibernate.type.StandardBasicTypes;
+
+public class WstValueTableFactory
+{
+    private static Logger log = Logger.getLogger(WstValueTableFactory.class);
+
+    public static final int DEFAULT_KIND = 0;
+
+    // TODO: put this into a property file
+
+    public static final String HQL_WST =
+        "from Wst where river=:river and kind=:kind";
+
+    public static final String SQL_SELECT_NAMES_POS =
+        "SELECT position, name FROM wst_columns " +
+        "WHERE wst_id = :wst_id ORDER BY position";
+
+    public static final String SQL_SELECT_QS =
+        "SELECT column_pos, q, a, b FROM wst_q_values " +
+        "WHERE wst_id = :wst_id";
+
+    public static final String SQL_SELECT_WS =
+        "SELECT km, w, column_pos FROM wst_w_values " +
+        "WHERE wst_id = :wst_id";
+
+    private WstValueTableFactory() {
+    }
+
+    public static WstValueTable getTable(River river) {
+        return getTable(river, DEFAULT_KIND);
+    }
+
+    public static WstValueTable getTable(River river, int kind) {
+
+        Cache cache = CacheFactory.getCache(WstValueTableCacheKey.CACHE_NAME);
+
+        WstValueTableCacheKey cacheKey;
+
+        if (cache != null) {
+            cacheKey = new WstValueTableCacheKey(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 (valueTable != null && 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) {
+        return getTableUncached(river, DEFAULT_KIND);
+    }
+
+    public static WstValueTable getTableUncached(River river, int kind) {
+
+        Session session = SessionHolder.HOLDER.get();
+
+        Wst wst = loadWst(session, river, kind);
+
+        if (wst == null) {
+            return null;
+        }
+
+        WstValueTable.Column [] columns = loadColumns(session, wst);
+
+        loadQRanges(session, columns, wst);
+
+        List<WstValueTable.Row> rows = loadRows(session, wst, columns.length);
+
+        return new WstValueTable(columns, rows);
+    }
+
+    protected static Wst loadWst(Session session, River river, int kind) {
+        Query query = session.createQuery(HQL_WST);
+        query.setParameter("river", river);
+        query.setInteger("kind",    kind);
+
+        List<Wst> wsts = query.list();
+
+        return wsts.isEmpty() ? null : wsts.get(0);
+    }
+
+    protected static List<WstValueTable.Row> loadRows(
+        Session session,
+        Wst     wst,
+        int     numColumns
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_WS)
+            .addScalar("km",         StandardBasicTypes.DOUBLE)
+            .addScalar("w",          StandardBasicTypes.DOUBLE)
+            .addScalar("column_pos", StandardBasicTypes.INTEGER);
+
+        sqlQuery.setInteger("wst_id", wst.getId());
+
+        List<Object []> results = sqlQuery.list();
+
+        int lastColumn = Integer.MAX_VALUE;
+        double [] ws = null;
+
+        ArrayList<WstValueTable.Row> rows = new ArrayList<WstValueTable.Row>();
+
+        for (Object [] result: results) {
+            int column = (Integer)result[2];
+            if (column < lastColumn) {
+                ws = new double[numColumns];
+                WstValueTable.Row row =
+                    new WstValueTable.Row((Double)result[0], ws);
+                rows.add(row);
+            }
+            Double w = (Double)result[1];
+            ws[column] = w != null ? w : Double.NaN;
+            lastColumn = column;
+        }
+
+        rows.trimToSize();
+        return rows;
+    }
+
+    protected static WstValueTable.Column [] loadColumns(
+        Session session,
+        Wst     wst
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_NAMES_POS)
+            .addScalar("position",   StandardBasicTypes.INTEGER)
+            .addScalar("name",       StandardBasicTypes.STRING);
+
+        sqlQuery.setInteger("wst_id", wst.getId());
+
+        List<Object []> columnNames = sqlQuery.list();
+
+        WstValueTable.Column [] columns =
+            new WstValueTable.Column[columnNames.size()];
+
+        for (int i = 0; i < columns.length; ++i) {
+            columns[i] = new WstValueTable.Column(
+                (String)columnNames.get(i)[1]);
+        }
+        return columns;
+    }
+
+    protected static void loadQRanges(
+        Session                 session,
+        WstValueTable.Column [] columns,
+        Wst                     wst
+    ) {
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_QS)
+            .addScalar("column_pos", StandardBasicTypes.INTEGER)
+            .addScalar("q",          StandardBasicTypes.DOUBLE)
+            .addScalar("a",          StandardBasicTypes.DOUBLE)
+            .addScalar("b",          StandardBasicTypes.DOUBLE);
+
+        sqlQuery.setInteger("wst_id", wst.getId());
+
+        List<Object []> qRanges = sqlQuery.list();
+
+        int     start      = -1;
+        int     Q          = qRanges.size();
+        Integer lastColumn = null;
+
+        for (int i = 0; i < Q; ++i) {
+            Object [] qRange = qRanges.get(i);
+            Integer columnId = (Integer)qRange[0];
+            if (lastColumn == null) {
+                lastColumn = columnId;
+                start = i;
+            }
+            else if (!lastColumn.equals(columnId)) {
+                QRangeTree qRangeTree = new QRangeTree(qRanges, start, i);
+                columns[lastColumn].setQRangeTree(qRangeTree);
+                lastColumn = columnId;
+                start = i;
+            }
+        }
+
+        if (start != -1) {
+            QRangeTree qRangeTree = new QRangeTree(qRanges, start, Q);
+            columns[lastColumn].setQRangeTree(qRangeTree);
+        }
+
+        /* This is debug code to visualize the q ranges trees
+
+        java.io.PrintWriter out = null;
+        try {
+            out = new java.io.PrintWriter(
+                new java.io.FileWriter(
+                    "/tmp/qranges" + System.currentTimeMillis() + ".dot"));
+
+            out.println("graph \"Q ranges trees\" {");
+
+            for (int i = 0; i < columns.length; ++i) {
+                QRangeTree tree = columns[i].getQRangeTree();
+                out.println(tree.toGraph());
+            }
+
+            out.println("}");
+
+            out.flush();
+        }
+        catch (java.io.IOException ioe) {
+            log.error(ioe);
+        }
+        finally {
+            if (out != null) {
+                out.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/artifacts/resources/Resources.java	Fri Sep 28 12:14:15 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:15 2012 +0200
@@ -0,0 +1,175 @@
+package de.intevation.flys.artifacts.services;
+
+import java.math.BigDecimal;
+import java.util.Iterator;
+
+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.Annotation;
+import de.intevation.flys.model.Attribute;
+import de.intevation.flys.model.Position;
+import de.intevation.flys.model.Range;
+import de.intevation.flys.model.Edge;
+
+import de.intevation.flys.artifacts.model.AnnotationsFactory;
+
+import org.hibernate.Session;
+
+import de.intevation.flys.artifacts.cache.CacheFactory;
+
+import net.sf.ehcache.Cache;
+
+/**
+ * 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);
+
+    public static final String CACHE_NAME = "service-distanceinfo";
+
+    public static final String RIVER_XPATH = "/art:river/text()";
+
+
+    /**
+     * The default constructor.
+     */
+    public DistanceInfoService() {
+    }
+
+
+    public Document process(
+        Document data,
+        Object   globalContext,
+        CallMeta callMeta)
+    {
+        logger.debug("DistanceInfoService.process");
+
+        String river = XMLUtils.xpathString(
+            data, RIVER_XPATH, ArtifactNamespaceContext.INSTANCE);
+
+        if (river == null || (river = river.trim()).length() == 0) {
+            logger.warn("No river specified. Cannot return distance info!");
+            return XMLUtils.newDocument();
+        }
+
+        logger.debug("Search distances for river: " + river);
+
+        Cache cache = CacheFactory.getCache(CACHE_NAME);
+
+        if (cache == null) {
+            logger.debug("no cache configured for distance info");
+            return getUncached(river);
+        }
+
+        net.sf.ehcache.Element element = cache.get(river);
+
+        if (element != null) {
+            logger.debug("distance info found in cache");
+            return (Document)element.getValue();
+        }
+
+        Document result = getUncached(river);
+
+        element = new net.sf.ehcache.Element(river, result);
+
+        logger.debug("store distance info found into cache");
+
+        cache.put(element);
+
+        return result;
+    }
+
+    protected Document getUncached(String river) {
+
+        Document result = XMLUtils.newDocument();
+
+        Session session = SessionHolder.acquire();
+
+        try {
+            Iterator<Annotation> iter =
+                AnnotationsFactory.getAnnotationsIterator(river);
+
+            Element all = result.createElement("distances");
+
+            while (iter.hasNext()) {
+                Annotation a = iter.next();
+                Element distance = buildDistanceNode(result, a);
+                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 static Element buildDistanceNode(
+        Document   document,
+        Annotation anno
+    ) {
+        Position   pos   = anno.getPosition();
+        Range      range = anno.getRange();
+        Attribute  attr  = anno.getAttribute();
+        Edge       edge  = anno.getEdge();
+        BigDecimal a     = range.getA();
+        BigDecimal b     = range.getB();
+
+        Element distance = document.createElement("distance");
+
+        distance.setAttribute("description", pos.getValue());
+
+        String riverSide = attr.getValue();
+
+        if (riverSide != null && riverSide.length() > 0) {
+            distance.setAttribute("riverside", riverSide);
+        }
+
+        if (a != null) {
+            distance.setAttribute("from", a.toString());
+        }
+        if (b != null) {
+            distance.setAttribute("to", b.toString());
+        }
+        if (edge != null) {
+            BigDecimal bottom = edge.getBottom();
+            BigDecimal top    = edge.getTop();
+            if (bottom != null) {
+                distance.setAttribute("bottom", bottom.toString());
+            }
+            if (top != null) {
+                distance.setAttribute("top", top.toString());
+            }
+        }
+
+        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:15 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:15 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:15 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:15 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:15 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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,140 @@
+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.CallMeta;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.ProtocolUtils;
+import de.intevation.artifactdatabase.data.StateData;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+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);
+
+
+
+    public static final String FIELD_MODE = "calculation_mode";
+
+    /** 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";
+
+
+    public CalculationSelect() {
+    }
+
+
+    @Override
+    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");
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData data = getData(flys, FIELD_MODE);
+        String    calc = data != null ? (String) data.getValue() : null;
+
+        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:15 2012 +0200
@@ -0,0 +1,86 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.WaterlevelFacet;
+import de.intevation.flys.artifacts.model.DataFacet;
+import de.intevation.flys.artifacts.model.ReportFacet;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.artifacts.model.CalculationResult;
+
+import de.intevation.flys.artifacts.resources.Resources;
+
+
+/**
+ * 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
+implements   FacetTypes
+{
+    /** The logger that is used in this state.*/
+    private static Logger logger =
+        Logger.getLogger(ComputedDischargeCurveState.class);
+
+    public ComputedDischargeCurveState() {
+    }
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult)old
+            : winfo.getComputedDischargeCurveData();
+
+        WQKms [] wqkms = (WQKms [])res.getData();
+
+        if (facets != null && wqkms.length > 0) {
+            for (int i = 0; i < wqkms.length; ++i) {
+
+                Object[] args = new Object[] {
+                    winfo.getRiver().getName(),
+                    wqkms[i].getName()
+                };
+
+                String name = Resources.getMsg(
+                    context.getMeta(),
+                    "chart.computed.discharge.curve.curve.label",
+                    "",
+                    args);
+
+                facets.add(new WaterlevelFacet(i, COMPUTED_DISCHARGE_Q, name));
+                facets.add(new WaterlevelFacet(i, AT, "AT data"));
+            }
+
+            facets.add(new DataFacet(CSV, "CSV data"));
+
+            if (res.getReport().hasProblems()) {
+                facets.add(new ReportFacet());
+            }
+        }
+
+        return res;
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,287 @@
+package de.intevation.flys.artifacts.states;
+
+import java.text.NumberFormat;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.List;
+
+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.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+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 static enum ComputeType {
+        FEED, ADVANCE
+    }
+
+    protected StateData getData(FLYSArtifact artifact,  String name) {
+        return artifact.getData(name);
+    }
+
+
+    public Element describeStatic(
+        Artifact    artifact,
+        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;
+        }
+
+        Iterator<String> iter = theData.keySet().iterator();
+        FLYSArtifact     flys = (FLYSArtifact) artifact;
+
+        while (iter.hasNext()) {
+            String    name = iter.next();
+            StateData data = getData(flys, name);
+
+            String value = data != null ? (String) data.getValue() : null;
+
+            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);
+
+            String attrValue = "";
+            try {
+                // XXX A better way to format the output would be to use the
+                // 'type' value if the data objects.
+                double doubleVal = Double.valueOf(value);
+                Locale         l = Resources.getLocale(meta);
+                NumberFormat  nf = NumberFormat.getInstance(l);
+
+                attrValue = nf.format(doubleVal);
+            }
+            catch (NumberFormatException nfe) {
+                attrValue = Resources.getMsg(meta, value, value);
+            }
+
+            creator.addAttr(itemElement, "label", attrValue, 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;
+        }
+
+        Iterator<String> iter = theData.keySet().iterator();
+        FLYSArtifact     flys = (FLYSArtifact) artifact;
+
+        while (iter.hasNext()) {
+            String    name = iter.next();
+            StateData data = getData(flys, name);
+
+            data = data != null ? data : getData(name);
+
+            Element select = createData(creator, artifact, data, context);
+
+            String defValue = (String) data.getValue();
+            String defDesc  = null;
+
+            if (defValue != null && defValue.length() > 0) {
+                defDesc = Resources.getMsg(
+                    context.getMeta(),
+                    defValue,
+                    defValue);
+            }
+
+            if (defValue != null && defDesc != null) {
+                creator.addAttr(select, "defaultValue", defValue, true);
+                creator.addAttr(select, "defaultLabel", defDesc, true);
+            }
+
+            Element choices = ProtocolUtils.createArtNode(
+            creator, "choices", null, null);
+
+            select.appendChild(choices);
+            ui.appendChild(select);
+
+            Element[] items = createItems(creator, artifact, name, context);
+            if (items != null) {
+                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 Element[] createItems(
+        XMLUtils.ElementCreator cr,
+        Artifact    artifact,
+        String      name,
+        CallContext context
+    ) {
+        return null;
+    }
+
+
+    /**
+     * 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;
+    }
+
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        return null;
+    }
+
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        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:15 2012 +0200
@@ -0,0 +1,99 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.ReportFacet;
+import de.intevation.flys.artifacts.model.WaterlevelFacet;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.artifacts.model.WQCKms;
+import de.intevation.flys.artifacts.model.CalculationResult;
+
+import de.intevation.flys.artifacts.model.DataFacet;
+
+public class DischargeLongitudinalSection
+extends      DefaultState
+implements   FacetTypes
+{
+    private static Logger logger =
+        Logger.getLogger(DischargeLongitudinalSection.class);
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult)old
+            : winfo.getDischargeLongitudinalSectionData();
+
+        if (facets == null) {
+            return res;
+        }
+
+        WQKms [] wqkms = (WQKms [])res.getData();
+
+        for (int i = 0; i < wqkms.length; i++) {
+            String nameW = null;
+            String nameQ = null;
+
+            if (winfo.isQ()) {
+                nameQ = wqkms[i].getName();
+                nameW = "W(" + nameQ + ")";
+            }
+            else {
+                nameW = wqkms[i].getName();
+                nameQ = "Q(" + nameQ + ")";
+            }
+
+            Facet w = new WaterlevelFacet(
+                i, DISCHARGE_LONGITUDINAL_W, nameW);
+
+            Facet q = new WaterlevelFacet(
+                i, DISCHARGE_LONGITUDINAL_Q, nameQ);
+
+            facets.add(w);
+            facets.add(q);
+
+            if (wqkms[i] instanceof WQCKms) {
+                // TODO DO i18n
+
+                String nameC = nameW.replace(
+                    "Benutzerdefiniert",
+                    "Benutzerdefiniert [korrigiert]");
+
+                Facet c = new WaterlevelFacet(
+                    i, DISCHARGE_LONGITUDINAL_C, nameC);
+
+                facets.add(c);
+            }
+        }
+
+        if (wqkms.length > 0) {
+            facets.add(new DataFacet(CSV, "CSV data"));
+            facets.add(new DataFacet(WST, "WST data"));
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet());
+        }
+
+        return res;
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,96 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.model.DurationCurveFacet;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.WQDay;
+
+import de.intevation.flys.artifacts.model.DataFacet;
+import de.intevation.flys.artifacts.model.ReportFacet;
+import de.intevation.flys.artifacts.model.CalculationResult;
+
+import de.intevation.flys.artifacts.resources.Resources;
+
+
+/**
+ * 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
+implements   FacetTypes
+{
+    /** The logger that is used in this state.*/
+    private static Logger logger = Logger.getLogger(DurationCurveState.class);
+
+    public DurationCurveState() {
+    }
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult res;
+
+        if (old instanceof CalculationResult) {
+            res = (CalculationResult)old;
+        }
+        else {
+            res = winfo.getDurationCurveData();
+        }
+
+        WQDay wqday = (WQDay)res.getData();
+
+        if (wqday != null && facets != null) {
+            Object[] args = new Object[] {
+                winfo.getRiver().getName()
+            };
+
+            String nameW = Resources.getMsg(
+                context.getMeta(),
+                "chart.duration.curve.curve.w",
+                "",
+                args);
+
+            String nameQ = Resources.getMsg(
+                context.getMeta(),
+                "chart.duration.curve.curve.q",
+                "",
+                args);
+
+            Facet w = new DurationCurveFacet(DURATION_W, nameW);
+            Facet q = new DurationCurveFacet(DURATION_Q, nameQ);
+
+            facets.add(w);
+            facets.add(q);
+
+            facets.add(new DataFacet(CSV, "CSV data"));
+
+            if (res.getReport().hasProblems()) {
+                facets.add(new ReportFacet());
+            }
+        }
+
+        return res;
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,345 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Element;
+
+import gnu.trove.TDoubleArrayList;
+
+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.artifactdatabase.state.Facet;
+
+import de.intevation.flys.model.River;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.WINFOArtifact;
+
+import de.intevation.flys.artifacts.model.RiverFactory;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.artifacts.model.CalculationResult;
+import de.intevation.flys.artifacts.model.WaterlevelFacet;
+import de.intevation.flys.artifacts.model.FacetTypes;
+
+import de.intevation.flys.artifacts.resources.Resources;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LocationDistanceSelect
+extends      RangeState
+implements   FacetTypes
+{
+
+    /** 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 'mode' field. */
+    public static final String MODE = "ld_mode";
+
+    /** The name of the 'locations' field.*/
+    public static final String LOCATIONS = "ld_locations";
+
+    /** 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;
+    }
+
+
+    @Override
+    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;
+    }
+
+
+    @Override
+    protected String getUIProvider() {
+        return "location_distance_panel";
+    }
+
+
+    protected double[] getMinMaxDistance(Artifact artifact) {
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+        StateData    data         = getData(flysArtifact, "river");
+
+        String name = data != null ? (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");
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        if (flys.isRange()) {
+            return validateRange(flys, context);
+        }
+        else {
+            return validateLocations(flys, context);
+        }
+    }
+
+
+    protected boolean validateLocations(FLYSArtifact flys, CallContext context)
+    throws    IllegalArgumentException
+    {
+        StateData dValues = getData(flys, LOCATIONS);
+        String    values  = dValues != null ? (String)dValues.getValue() : null;
+
+        if (values == null || values.length() == 0) {
+            throw new IllegalArgumentException("error_empty_state");
+        }
+
+        double[] absMinMax = getMinMaxDistance(flys);
+        double[] relMinMax = getMinMaxFromString(values);
+
+        if (relMinMax[0] < absMinMax[0] || relMinMax[0] > absMinMax[1]) {
+            throw new IllegalArgumentException("error_feed_from_out_of_range");
+        }
+
+        if (relMinMax[1] > absMinMax[1] || relMinMax[1] < absMinMax[0]) {
+            throw new IllegalArgumentException("error_feed_to_out_of_range");
+        }
+
+        return true;
+    }
+
+
+    protected boolean validateRange(FLYSArtifact flys, CallContext context)
+    throws    IllegalArgumentException
+    {
+        StateData dFrom = getData(flys, FROM);
+        StateData dTo   = getData(flys, TO);
+        StateData dStep = getData(flys, STEP);
+
+        String fromStr = dFrom != null ? (String) dFrom.getValue() : null;
+        String toStr   = dTo != null ? (String) dTo.getValue() : null;
+        String stepStr = dStep != null ? (String) dStep.getValue() : null;
+
+        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(flys);
+
+            return validateBounds(minmaxDist[0], minmaxDist[1], from, to, step);
+        }
+        catch (NumberFormatException nfe) {
+            throw new IllegalArgumentException("error_invalid_double_value");
+        }
+    }
+
+
+    /**
+     * Extracts the min/max values from String <i>s</i>. An
+     * IllegalArgumentException is thrown if there is a value that throws a
+     * NumberFormatException.
+     *
+     * @param s String that contains whitespace separated double values.
+     *
+     * @return a 2dmin array [min,max].
+     */
+    public static double[] getMinMaxFromString(String s)
+    throws IllegalArgumentException
+    {
+        String[] values = s.split(" ");
+
+        double[] minmax = new double[] {
+            Double.MAX_VALUE,
+            -Double.MAX_VALUE };
+
+        for (String v: values) {
+            try {
+                double value = Double.valueOf(v);
+
+                minmax[0] = minmax[0] < value ? minmax[0] : value;
+                minmax[1] = minmax[1] > value ? minmax[1] : value;
+            }
+            catch (NumberFormatException nfe) {
+                throw new IllegalArgumentException(
+                    "error_invalid_double_value");
+            }
+        }
+
+        return minmax;
+    }
+
+
+    public static double[] getLocations(FLYSArtifact flys) {
+        StateData data  = flys.getData("ld_locations");
+        String    value = data != null ? (String) data.getValue() : null;
+
+        if (value == null || value.length() == 0) {
+            logger.warn("No location data given.");
+            return null;
+        }
+
+        String[]         splitted = value.split(" ");
+        TDoubleArrayList values   = new TDoubleArrayList();
+
+        for (String split: splitted) {
+            try {
+                values.add(Double.valueOf(split));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        return values.toNativeArray();
+    }
+
+
+    @Override
+    public Object computeFeed(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        logger.debug("computeFeed");
+
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult)old
+            : winfo.getDischargeCurveData();
+
+        if (facets == null) {
+            logger.debug("generate no facets");
+            return res;
+        }
+
+        WQKms [] wqkms = (WQKms [])res.getData();
+
+        logger.debug("generate " + wqkms.length + " facets.");
+
+        String stateID = winfo.getCurrentStateId();
+
+        for (int i = 0; i < wqkms.length; ++i) {
+            String name = wqkms[i].getName();
+            facets.add(new WaterlevelFacet(
+                i, DISCHARGE_CURVE, name, ComputeType.FEED, stateID, hash));
+        }
+
+
+        return res;
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,134 @@
+package de.intevation.flys.artifacts.states;
+
+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.data.StateData;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+
+/**
+ * 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
+    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(FIELD_LOCATIONS)) {
+            Element min = createItem(
+                cr,
+                new String[] {"min", new Double(minVal).toString()});
+
+            Element max = createItem(
+                cr,
+                new String[] {"max", new Double(maxVal).toString()});
+
+            return new Element[] { min, max };
+        }
+
+        return null;
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        logger.debug("LocationSelect.validate");
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        StateData    data = getData(flys, FIELD_LOCATIONS);
+
+        String locationStr = data != null ? (String) data.getValue() : null;
+
+        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:15 2012 +0200
@@ -0,0 +1,56 @@
+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:15 2012 +0200
@@ -0,0 +1,163 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+
+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.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 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;
+    }
+
+
+    @Override
+    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");
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData dRiver = getData(flys, "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:15 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:15 2012 +0200
@@ -0,0 +1,454 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Comparator;
+import java.util.Collections;
+
+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";
+
+    public static final class GaugeOrder implements Comparator<Gauge> {
+        private int order;
+
+        public GaugeOrder(boolean up) {
+            order = up ? 1 : -1;
+        }
+
+        public int compare(Gauge a, Gauge b) {
+            return order * a.getRange().getA().compareTo(b.getRange().getA());
+        }
+    } // class GaugeOrder
+
+    public static final GaugeOrder GAUGE_UP   = new GaugeOrder(true);
+    public static final GaugeOrder GAUGE_DOWN = new GaugeOrder(false);
+
+    public WQAdapted() {
+    }
+
+    /**
+     * 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.
+     */
+    @Override
+    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();
+        River       river  = flysArtifact.getRiver();
+        Wst         wst    = WstFactory.getWst(river);
+        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];
+
+        double rangeFrom = dist[0];
+        double rangeTo   = dist[1];
+
+        int idx = 0;
+
+        if (rangeFrom < rangeTo) {
+            Collections.sort(gauges, GAUGE_UP);
+            for (Gauge gauge: gauges) {
+                Range range = gauge.getRange();
+                double lower = range.getA().doubleValue();
+                double upper = range.getB().doubleValue();
+
+                double from = lower < rangeFrom ? rangeFrom : lower;
+                double to   = upper > rangeTo   ? rangeTo   : upper;
+
+                double[] mmQ = determineMinMaxQ(gauge, wst);
+                double[] mmW = gauge.determineMinMaxW();
+
+                elements[idx++] = createItem(
+                    cr, new String[] { from + ";" + to, ""}, mmQ, mmW);
+            }
+        }
+        else {
+            Collections.sort(gauges, GAUGE_DOWN);
+            rangeFrom = dist[1];
+            rangeTo   = dist[0];
+            for (Gauge gauge: gauges) {
+                Range range = gauge.getRange();
+                double lower = range.getA().doubleValue();
+                double upper = range.getB().doubleValue();
+
+                double from = lower < rangeFrom ? rangeFrom : lower;
+                double to   = upper > rangeTo   ? rangeTo   : upper;
+
+                double[] mmQ = determineMinMaxQ(gauge, wst);
+                double[] mmW = gauge.determineMinMaxW();
+
+                elements[idx++] = createItem(
+                    cr, new String[] { to + ";" + from, ""}, mmQ, mmW);
+            }
+        }
+
+        return elements;
+    }
+
+
+    protected Element createItem(XMLUtils.ElementCreator cr, Object obj) {
+        return createItem(cr, obj, null, null);
+    }
+
+
+    protected Element createItem(
+        XMLUtils.ElementCreator cr,
+        Object   obj,
+        double[] q,
+        double[] w)
+    {
+        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);
+
+        if (q != null) {
+            Element qRange = createRangeElement(cr, q, "Q");
+            item.appendChild(qRange);
+        }
+
+        if (w != null) {
+            Element wRange = createRangeElement(cr, w, "W");
+            item.appendChild(wRange);
+        }
+
+        return item;
+    }
+
+
+    protected Element createRangeElement(
+        XMLUtils.ElementCreator cr,
+        double[] mm,
+        String   type)
+    {
+        Element range = ProtocolUtils.createArtNode(
+            cr, "range",
+            new String[] {"type"},
+            new String[] {type});
+
+        Element min = ProtocolUtils.createArtNode(cr, "min", null, null);
+        min.setTextContent(String.valueOf(mm[0]));
+
+        Element max = ProtocolUtils.createArtNode(cr, "max", null, null);
+        max.setTextContent(String.valueOf(mm[1]));
+
+        range.appendChild(min);
+        range.appendChild(max);
+
+        return range;
+    }
+
+
+    /**
+     * Determines the min and max Q value for the given gauge. If no min and
+     * max values could be determined, this method will return
+     * [Double.MIN_VALUE, Double.MAX_VALUE].
+     *
+     * @param gauge
+     * @param wst
+     *
+     * @return the min and max Q values for the given gauge.
+     */
+    protected double[] determineMinMaxQ(Gauge gauge, Wst wst) {
+        logger.debug("WQAdapted.determineMinMaxQ");
+
+        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
+    protected String getUIProvider() {
+        return "wq_panel_adapted";
+    }
+
+
+    @Override
+    public boolean validate(Artifact artifact, CallContext context)
+    throws IllegalArgumentException
+    {
+        logger.debug("WQAdapted.validate");
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        StateData    data = getData(flys, FIELD_WQ_MODE);
+
+        String mode = data != null ? (String) data.getValue() : null;
+
+        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");
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values"));
+
+        if (rwvs == null) {
+            throw new IllegalArgumentException("error_missing_wq_data");
+        }
+
+        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");
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        RangeWithValues[] rwvs = extractInput(getData(flys, "wq_values"));
+
+        if (rwvs == null) {
+            throw new IllegalArgumentException("error_missing_wq_data");
+        }
+
+        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) {
+        if (data == null) {
+            return null;
+        }
+
+        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:15 2012 +0200
@@ -0,0 +1,375 @@
+package de.intevation.flys.artifacts.states;
+
+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;
+    }
+
+
+    @Override
+    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;
+    }
+
+
+    @Override
+    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");
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData data       = getData(flys, WQ_SELECTION);
+        String selectionMode = data != null ? (String) data.getValue() : null;
+
+        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");
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        StateData    data = getData(flys, WQ_SINGLE);
+
+        String tmp = data != null ? (String) data.getValue() : null;
+
+        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();
+
+        StateData dMode = getData(flys, WQ_MODE);
+        String    mode  = dMode != null ? (String) data.getValue() : null;
+
+        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");
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+
+        StateData data = flys.getData(WQ_MODE);
+        String    mode = data != null ? (String) data.getValue() : null;
+        logger.debug("WQ Mode: " + mode);
+
+        if (mode == null || mode.length() == 0) {
+            throw new IllegalArgumentException("error_feed_invalid_wq_mode");
+        }
+
+        StateData dFrom = flys.getData(WQ_FROM);
+        StateData dTo   = flys.getData(WQ_TO);
+        StateData dStep = flys.getData(WQ_STEP);
+
+        String fromStr = dFrom != null ? (String) dFrom.getValue() : null;
+        String toStr   = dTo != null ? (String) dTo.getValue() : null;
+        String stepStr = dStep != null ? (String) dStep.getValue() : null;
+
+        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);
+
+            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/states/WaterlevelState.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,88 @@
+package de.intevation.flys.artifacts.states;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.WINFOArtifact;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.ReportFacet;
+import de.intevation.flys.artifacts.model.WaterlevelFacet;
+import de.intevation.flys.artifacts.model.WQKms;
+
+import de.intevation.flys.artifacts.model.DataFacet;
+import de.intevation.flys.artifacts.model.CalculationResult;
+
+
+public class WaterlevelState
+extends      DefaultState
+implements   FacetTypes
+{
+    /** The logger that is used in this state.*/
+    private static Logger logger = Logger.getLogger(WaterlevelState.class);
+
+
+    @Override
+    public Object computeAdvance(
+        FLYSArtifact artifact,
+        String       hash,
+        CallContext  context,
+        List<Facet>  facets,
+        Object       old
+    ) {
+        WINFOArtifact winfo = (WINFOArtifact)artifact;
+
+        CalculationResult res = old instanceof CalculationResult
+            ? (CalculationResult)old
+            : winfo.getWaterlevelData();
+
+        if (facets == null) {
+            return res;
+        }
+
+        WQKms [] wqkms = (WQKms [])res.getData();
+
+        for (int i = 0; i < wqkms.length; i++) {
+            String nameW = null;
+            String nameQ = null;
+
+            if (winfo.isQ()) {
+                nameQ = wqkms[i].getName();
+                nameW = "W(" + nameQ + ")";
+            }
+            else {
+                nameW = wqkms[i].getName();
+                nameQ = "Q(" + nameQ + ")";
+            }
+
+            logger.debug("Create facet: " + nameW);
+            logger.debug("Create facet: " + nameQ);
+
+            Facet w = new WaterlevelFacet(i, LONGITUDINAL_W, nameW);
+            Facet q = new WaterlevelFacet(i, LONGITUDINAL_Q, nameQ);
+
+            facets.add(w);
+            facets.add(q);
+        }
+
+        if (wqkms.length > 0) {
+            Facet wst = new DataFacet(WST, "WST data");
+            Facet csv = new DataFacet(CSV, "CSV data");
+
+            facets.add(wst);
+            facets.add(csv);
+        }
+
+        if (res.getReport().hasProblems()) {
+            facets.add(new ReportFacet());
+        }
+
+        return res;
+    }
+}
+// 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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,144 @@
+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.Element;
+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:attribute/art:outputs/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.");
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        for (int i = 0; i < num; i++) {
+            Element theme = (Element) themes.item(i);
+
+            String name  = theme.getAttributeNS(uri, "facet");
+            if (name == null || name.length() == 0) {
+                continue;
+            }
+
+            String uuid  = theme.getAttributeNS(uri, "artifact");
+            if (uuid == null || uuid.length() == 0) {
+                continue;
+            }
+
+            String pos  = theme.getAttributeNS(uri, "pos");
+            if (pos == null || pos.length() == 0) {
+                continue;
+            }
+
+            String index = theme.getAttributeNS(uri, "index");
+            if (index == null || index.length() == 0) {
+                continue;
+            }
+
+            String active = theme.getAttributeNS(uri, "active");
+            if (active == null || active.length() == 0) {
+                continue;
+            }
+
+            String description = theme.getAttributeNS(uri, "description");
+
+            ManagedFacet item = new ManagedFacet(
+                name, Integer.parseInt(index), description, 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:15 2012 +0200
@@ -0,0 +1,150 @@
+package de.intevation.flys.collections;
+
+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 attribute = cr.create("attribute");
+        Element outs      = cr.create("outputs");
+
+        attribute.appendChild(outs);
+        doc.appendChild(attribute);
+
+        for (String outName: newAttr.keySet()) {
+
+            Output a = newAttr.get(outName);
+            Output b = oldAttr.get(outName);
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
+            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() + a.getIndex();
+
+        if (list == null) {
+            logger.debug("No old facets found.");
+            return false;
+        }
+
+        for (Facet facet: list) {
+            if (name.equals(facet.getName() + facet.getIndex())) {
+                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);
+        cr.addAttr(theme, "index", Integer.toString(f.getIndex()), true);
+        cr.addAttr(theme, "description", f.getDescription(), true);
+
+        output.appendChild(theme);
+    }
+}
+// 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/collections/FLYSArtifactCollection.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,778 @@
+package de.intevation.flys.collections;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Date;
+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.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");
+
+        ec.addAttr(collection, "name", getName(), true);
+        ec.addAttr(collection, "uuid", identifier(), true);
+        ec.addAttr(collection, "creation", creation,  true);
+        ec.addAttr(collection, "ttl", String.valueOf(getTTL()), true);
+
+        collection.appendChild(artifacts);
+        doc.appendChild(collection);
+
+        ArtifactDatabase db = context.getDatabase();
+
+        try {
+            String[] artifactUUIDs = getArtifactUUIDs(context);
+
+            Document oldAttrs = getAttribute();
+
+            Document attrs = buildAttributes(
+                db, context,
+                oldAttrs,
+                artifactUUIDs);
+
+            collection.appendChild(doc.importNode(attrs.getFirstChild(), true));
+
+            // save the merged document into database
+            db.setCollectionAttribute(identifier(), context.getMeta(), attrs);
+
+            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(
+        String       type,
+        Document     format,
+        OutputStream out,
+        CallContext  context)
+    throws IOException
+    {
+        log.info("FLYSArtifactCollection.out");
+
+        String name = XMLUtils.xpathString(
+            format, XPATH_OUT_NAME, ArtifactNamespaceContext.INSTANCE);
+
+        String subtype = XMLUtils.xpathString(
+            format, XPATH_OUT_TYPE, ArtifactNamespaceContext.INSTANCE);
+
+        log.info("-> Output name = " + name);
+        log.info("-> Output type = " + type);
+        log.info("-> Output subtype = " + subtype);
+
+        OutGenerator generator = null;
+        if (type != null && type.length() > 0) {
+            if (type.indexOf("chartinfo") > 0) {
+                generator = getOutGenerator(context, type, subtype);
+            }
+            else {
+                generator = getOutGenerator(context, name, subtype);
+            }
+        }
+        else {
+            generator = getOutGenerator(context, name, subtype);
+        }
+
+        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 {
+            Document attr = getAttribute(context, name);
+            doOut(generator, name, subtype, attr, 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.info("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);
+
+                if (theme.getActive() == 0) {
+                    continue;
+                }
+
+                generator.doOut(
+                    artifact,
+                    theme,
+                    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:attribute/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 Map<Integer, ManagedFacet> themes;
+
+        public ThemeList(Document output) {
+            themes = new HashMap<Integer, 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;
+            }
+
+            String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+            for (int i = 0; i < num; i++) {
+                Element theme = (Element) themes.item(i);
+
+                String name   = theme.getAttributeNS(uri, "facet");
+                String uuid   = theme.getAttributeNS(uri, "artifact");
+                String pos    = theme.getAttributeNS(uri, "pos");
+                String active = theme.getAttributeNS(uri, "active");
+                String idx    = theme.getAttributeNS(uri, "index");
+                String desc   = theme.getAttributeNS(uri, "description");
+
+                addTheme(uuid, name, idx, pos, active, desc);
+            }
+        }
+
+        protected void addTheme(
+            String uuid,
+            String name,
+            String index,
+            String position,
+            String active,
+            String description)
+        {
+            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);
+                int idx = Integer.parseInt(index);
+
+                themes.put(
+                    new Integer(pos-1),
+                    new ManagedFacet(name, idx, description, uuid, pos, act));
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn(nfe, nfe);
+            }
+        }
+
+        public ManagedFacet get(int idx) {
+            return themes.get(new Integer(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:15 2012 +0200
@@ -0,0 +1,148 @@
+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.Element;
+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++) {
+            Element out = (Element)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, Element out) {
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        String name = out.getAttributeNS(uri, "name");
+
+        if (outs.get(name) == null) {
+            logger.debug("Create new output: " + name);
+            newOutput(out, name);
+        }
+
+        parseItems(uuid, out, name);
+    }
+
+
+    protected void newOutput(Element out, String name) {
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        String desc     = out.getAttributeNS(uri, "description");
+        String mimetype = out.getAttributeNS(uri, "mime-type");
+
+        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;
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        logger.debug("Output has " + num + " facets.");
+
+        for (int i = 0; i < num; i++) {
+            Element facet = (Element) facets.item(i);
+
+            String name  = facet.getAttributeNS(uri, "name");
+            String desc  = facet.getAttributeNS(uri, "description");
+            String index = facet.getAttributeNS(uri, "index");
+
+            ManagedFacet item = new ManagedFacet(
+                name, Integer.parseInt(index), desc, 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/ATExporter.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,78 @@
+package de.intevation.flys.exports;
+
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.model.WQ;
+
+public class ATExporter
+implements   OutGenerator
+{
+    private static Logger logger = Logger.getLogger(ATExporter.class);
+
+    public static final String DEFAULT_ENCODING = "UTF-8";
+
+    protected WQ           data;
+    protected CallContext  context;
+    protected OutputStream out;
+
+    public ATExporter() {
+    }
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        this.context = context;
+        this.out     = out;
+    }
+
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        // not needed
+    }
+
+    @Override
+    public void doOut(Artifact artifact, Facet facet, Document attr) {
+
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        if ((facet = flys.getNativeFacet(facet)) == null) {
+            logger.debug("native facet not found.");
+            return;
+        }
+
+        data = (WQ)facet.getData(flys, context);
+    }
+
+    @Override
+    public void generate() throws IOException {
+
+        if (data == null) {
+            logger.debug("no W/Q data");
+            return;
+        }
+
+        ATWriter at;
+        try {
+            at = new ATWriter(data);
+        }
+        catch (IllegalArgumentException iae) {
+            logger.error("creating ATWriter failed", iae);
+            throw new IOException(iae);
+        }
+
+        at.write(new OutputStreamWriter(out, DEFAULT_ENCODING));
+    }
+}
+// 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/ATWriter.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,156 @@
+package de.intevation.flys.exports;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.io.PrintWriter;
+
+import java.util.Locale;
+
+import de.intevation.flys.artifacts.model.WQ;
+
+import org.apache.commons.math.analysis.UnivariateRealFunction;
+
+import org.apache.commons.math.analysis.interpolation.SplineInterpolator;
+import org.apache.commons.math.analysis.interpolation.LinearInterpolator;
+
+import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
+
+import org.apache.commons.math.FunctionEvaluationException;
+
+import org.apache.log4j.Logger;
+
+public class ATWriter
+{
+    private static Logger logger = Logger.getLogger(ATWriter.class);
+
+    public static final int COLUMNS = 10;
+
+    public static final String EMPTY = "         ";
+
+    protected double minW;
+    protected double maxW;
+    protected double minQ;
+    protected double maxQ;
+
+    protected UnivariateRealFunction qFunc;
+
+    public ATWriter() {
+    }
+
+    public ATWriter(WQ wq) throws IllegalArgumentException {
+
+        int maxIndex = maxIncreasingWIndex(wq);
+
+        if (maxIndex < 1) { // Only first w can be written out.
+            minW = maxW = wq.getW(0);
+            minQ = maxQ = wq.getQ(0);
+            // constant function
+            qFunc = new PolynomialFunction(new double [] { minQ });
+            return;
+        }
+
+        double [] ws = new double[maxIndex];
+        double [] qs = new double[ws.length];
+
+        for (int i = 0; i < ws.length; ++i) {
+            ws[i] = wq.getW(i);
+            qs[i] = wq.getQ(i);
+        }
+
+        qFunc = ws.length < 3
+            ? new LinearInterpolator().interpolate(ws, qs)
+            : new SplineInterpolator().interpolate(ws, qs);
+
+        minW = wq.getW(0);
+        maxW = wq.getW(maxIndex);
+        minQ = wq.getQ(0);
+        maxQ = wq.getQ(maxIndex);
+    }
+
+    public static int maxIncreasingWIndex(WQ wq) {
+
+        int N = wq.size();
+
+        if (N < 2) {
+            return N-1;
+        }
+
+        double last = wq.getW(0);
+
+        for (int i = 1; i < N; ++i) {
+            double current = wq.getW(i);
+            if (current <= last) {
+                return i-1;
+            }
+        }
+
+        return N-1;
+    }
+
+    public double getQ(double w) {
+
+        try {
+            return qFunc.value(w);
+        }
+        catch (FunctionEvaluationException aode) {
+            // should not happen
+            logger.warn("spline interpolation failed", aode);
+            return w <= minW ? minQ : maxQ;
+        }
+    }
+
+    protected static void printQ(PrintWriter out, double q) {
+        String format;
+             if (q <   1d) format = " % 8.3f";
+        else if (q <  10d) format = " % 8.2f";
+        else if (q < 100d) format = " % 8.1f";
+        else {
+            format = " % 8.0f";
+            if (q > 1000d) q = Math.rint(q/10d)*10d;
+        }
+        out.printf(Locale.US, format, q);
+    }
+
+    public void write(Writer writer) throws IOException {
+
+        PrintWriter out = new PrintWriter(writer);
+
+        double rest = Math.abs(minW % COLUMNS);
+
+        double startW = Math.round(minW*10.0)/10.0;
+        if (rest >= 1d) {
+            startW -= 0.1;
+            out.printf(Locale.US, "%8d", (int)Math.round(startW*100.0));
+            int columns = (int)Math.floor(rest);
+            for (int i = columns; i < COLUMNS; ++i) {
+                out.print(EMPTY);
+            }
+            for (int i = COLUMNS-columns; i < COLUMNS; ++i) {
+                printQ(out, getQ(startW + i*0.01));
+            }
+            out.println();
+            startW += 0.1;
+        }
+
+        int col = 0;
+        for (double w = startW; w <= maxW; w += 0.01) {
+            if (col == 0) {
+                out.printf(Locale.US, "%8d", (int)Math.round(w*100.0));
+            }
+
+            printQ(out, getQ(w));
+
+            if (++col >= COLUMNS) {
+                out.println();
+                col = 0;
+            }
+        }
+
+        if (col > 0) {
+            out.println();
+        }
+
+        out.flush();
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,227 @@
+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.artifactdatabase.state.Facet;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.flys.artifacts.resources.Resources;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+
+/**
+ * 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(Object data);
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("AbstractExporter.init");
+
+        this.request = request;
+        this.out     = out;
+        this.context = context;
+    }
+
+
+    @Override
+    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.
+     */
+    @Override
+    public void doOut(Artifact artifact, Facet facet, Document attr) {
+        String name = facet.getName();
+
+        logger.debug("AbstractExporter.doOut: " + name);
+
+        if (!isFacetValid(name)) {
+            logger.warn("Facet '" + name + "' not valid. No output created!");
+            return;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact)artifact;
+
+        Facet nativeFacet = flys.getNativeFacet(facet);
+
+        if (nativeFacet != null) {
+            addData(nativeFacet.getData(flys, context));
+        }
+    }
+
+
+    /**
+     * Generates an export based on a specified facet.
+     */
+    @Override
+    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:15 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;
+
+        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:15 2012 +0200
@@ -0,0 +1,249 @@
+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.Element;
+
+import org.jfree.data.Range;
+
+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.artifactdatabase.state.Facet;
+
+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";
+
+    public static final String XPATH_CHART_X_RANGE =
+        "/art:action/art:attributes/art:xrange";
+
+    public static final String XPATH_CHART_Y_RANGE =
+        "/art:action/art:attributes/art:yrange";
+
+
+    /** 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];
+
+        Element sizeEl = (Element)XMLUtils.xpath(
+            request,
+            XPATH_CHART_SIZE,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (sizeEl != null) {
+            String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+            String w = sizeEl.getAttributeNS(uri, "width");
+            String h = sizeEl.getAttributeNS(uri, "height");
+
+            if (w.length() > 0 && 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();
+    }
+
+
+    protected Range getDomainAxisRange() {
+        Element xrange = (Element)XMLUtils.xpath(
+            request,
+            XPATH_CHART_X_RANGE,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (xrange == null) {
+            return null;
+        }
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        String lower = xrange.getAttributeNS(uri, "from");
+        String upper = xrange.getAttributeNS(uri, "to");
+
+        logger.debug("FOUND X RANGE: " + lower + " -> " + upper);
+
+        if (lower.length() > 0 && upper.length() > 0) {
+            try {
+                double from = Double.parseDouble(lower);
+                double to   = Double.parseDouble(upper);
+
+                if (from == 0 && to == 0) {
+                    logger.debug("No range specified. Lower and upper X == 0");
+                    return null;
+                }
+
+                if (from > to) {
+                    double tmp = to;
+                    to         = from;
+                    from       = tmp;
+                }
+
+                return new Range(from, to);
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("Wrong values for domain axis range.");
+            }
+        }
+
+        return null;
+    }
+
+
+    protected Range getValueAxisRange() {
+        Element yrange = (Element)XMLUtils.xpath(
+            request,
+            XPATH_CHART_Y_RANGE,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (yrange == null) {
+            return null;
+        }
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        String lower = yrange.getAttributeNS(uri, "from");
+        String upper = yrange.getAttributeNS(uri, "to");
+
+        if (lower.length() > 0 && upper.length() > 0) {
+            try {
+                double from = Double.parseDouble(lower);
+                double to   = Double.parseDouble(upper);
+
+                if (from == 0 && to == 0) {
+                    logger.debug("No range specified. Lower and upper Y == 0");
+                    return null;
+                }
+
+                if (from > to) {
+                    double tmp = to;
+                    to         = from;
+                    from       = tmp;
+                }
+
+                return new Range(from, to);
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("Wrong values for value axis range.");
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * 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, Facet 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/ChartInfoGenerator.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,112 @@
+package de.intevation.flys.exports;
+
+import java.awt.Transparency;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.ChartRenderingInfo;
+import org.jfree.chart.JFreeChart;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+
+/**
+ * An OutGenerator that generates meta information for charts. A concrete
+ * ChartInfoGenerator need to instantiate a concrete ChartGenerator and dispatch
+ * the methods to that instance. The only thing this ChartInfoGenerator needs
+ * to, is to overrite the generate() method which doesn't write the chart image
+ * to the OutputStream but a Document that contains some meta information of the
+ * created chart.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public abstract class ChartInfoGenerator implements OutGenerator {
+
+    /** The logger used in this generator.*/
+    private static Logger logger =
+        Logger.getLogger(ChartInfoGenerator.class);
+
+
+    /** The OutGenerator that creates the charts.*/
+    protected XYChartGenerator generator;
+
+    protected OutputStream out;
+
+
+
+    public ChartInfoGenerator(XYChartGenerator generator) {
+        this.generator = generator;
+    }
+
+
+    /**
+     * Dispatches the operation to the instantiated generator.
+     *
+     * @param request
+     * @param out
+     * @param context
+     */
+    public void init(Document request, OutputStream out, CallContext context) {
+        this.out = out;
+
+        generator.init(request, out, context);
+    }
+
+
+    /**
+     * Dispatches the operation to the instantiated generator.
+     *
+     * @param master
+     */
+    public void setMasterArtifact(Artifact master) {
+        generator.setMasterArtifact(master);
+    }
+
+
+    /**
+     * Dispatches the operation to the instantiated generator.
+     *
+     * @param artifacts
+     * @param facet
+     * @param attr
+     */
+    public void doOut(Artifact artifact, Facet facet, Document attr) {
+        generator.doOut(artifact, facet, attr);
+    }
+
+
+    /**
+     * This method generates the chart using a concrete ChartGenerator but
+     * doesn't write the chart itself to the OutputStream but a Document that
+     * contains meta information of the created chart.
+     */
+    @Override
+    public void generate()
+    throws IOException
+    {
+        logger.debug("ChartInfoGenerator.generate");
+
+        JFreeChart chart = generator.generateChart();
+
+        int[] size = generator.getSize();
+
+        ChartRenderingInfo info = new ChartRenderingInfo();
+
+        chart.createBufferedImage(size[0], size[1], Transparency.BITMASK, info);
+
+        InfoGeneratorHelper helper = new InfoGeneratorHelper(generator);
+        Document doc = helper.createInfoDocument(chart, info);
+
+        XMLUtils.toStream(doc, out);
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,120 @@
+package de.intevation.flys.exports;
+
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.w3c.dom.Document;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.flys.artifacts.model.CalculationResult;
+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\u00b3/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>();
+    }
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof WQKms []) {
+                data.addAll(Arrays.asList((WQKms [])d));
+            }
+        }
+    }
+
+
+    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:15 2012 +0200
@@ -0,0 +1,120 @@
+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.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.FacetTypes;
+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
+implements   FacetTypes
+{
+    /** 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, Facet facet, Document attr) {
+        String name = facet != null ? facet.getName() : null;
+
+        logger.debug("ComputedDischargeCurveGenerator.doOut: " + name);
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        Facet        f    = flys.getNativeFacet(facet);
+
+        if (name != null && name.equals(COMPUTED_DISCHARGE_Q)) {
+            doQOut((WQKms) f.getData(artifact, context));
+        }
+        else {
+            logger.warn("Unknown facet type for computed discharge: " + name);
+            return;
+        }
+    }
+
+
+    protected void doQOut(WQKms wqkms) {
+        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 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/ComputedDischargeCurveInfoGenerator.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,16 @@
+package de.intevation.flys.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific computed
+ * discharge curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ComputedDischargeCurveInfoGenerator extends ChartInfoGenerator {
+
+    public ComputedDischargeCurveInfoGenerator() {
+        super(new ComputedDischargeCurveGenerator());
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,188 @@
+package de.intevation.flys.exports;
+
+import java.util.Date;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+
+import org.w3c.dom.Document;
+
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.Range;
+import org.jfree.data.xy.DefaultXYDataset;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+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.WQKms;
+
+/**
+ * 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\u00b3/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);
+    }
+
+
+    @Override
+    protected boolean zoomX(XYPlot plot, ValueAxis axis, Range range, Range x) {
+        boolean zoomin = super.zoom(plot, axis, range, x);
+
+        if (!zoomin) {
+            axis.setLowerBound(0d);
+        }
+
+        return zoomin;
+    }
+
+
+    protected void addDatasets(JFreeChart chart) {
+        XYPlot plot = (XYPlot) chart.getPlot();
+        plot.setDataset(0, dataset);
+    }
+
+
+    public void doOut(Artifact artifact, Facet facet, Document attr) {
+        logger.debug("DischargeCurveGenerator.doOut: " + facet.getName());
+
+        if (!(artifact instanceof FLYSArtifact)) {
+            logger.debug("Artifact is no instance of FLYSArtifact.");
+            return;
+        }
+
+        FLYSArtifact flysArtifact = (FLYSArtifact) artifact;
+
+        facet = flysArtifact.getNativeFacet(facet);
+
+        if (facet == null) {
+            logger.debug("no facet found");
+            return;
+        }
+
+        WQKms wqkms = (WQKms)facet.getData(flysArtifact, context);
+
+        String gaugeName = wqkms.getName();
+
+        River river = flysArtifact.getRiver();
+
+        if (river == null) {
+            logger.debug("no river found");
+            return;
+        }
+
+        Gauge gauge = river.determineGaugeByName(gaugeName);
+
+        if (gauge == null) {
+            logger.debug("no gauge found");
+            return;
+        }
+
+        String seriesName = getSeriesName(gauge);
+
+        double [][] values = new double [][] {
+            wqkms.getQs(), wqkms.getWs() };
+
+        dataset.addSeries(seriesName, values);
+    }
+
+
+    protected String getSeriesName(Gauge gauge) {
+        // XXX The following code stops the artifact server accepting new HTTP
+        // requests. It needs more analysis! Is loading the discharge table a
+        // memory problem? Or is the time interval the problem?
+
+        //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/DischargeCurveInfoGenerator.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,16 @@
+package de.intevation.flys.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific discharge
+ * curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DischargeCurveInfoGenerator extends ChartInfoGenerator {
+
+    public DischargeCurveInfoGenerator() {
+        super(new DischargeCurveGenerator());
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,121 @@
+package de.intevation.flys.exports;
+
+import java.text.NumberFormat;
+
+import org.apache.log4j.Logger;
+
+import au.com.bytecode.opencsv.CSVWriter;
+
+import de.intevation.flys.artifacts.model.WQCKms;
+import de.intevation.flys.artifacts.model.WQKms;
+import de.intevation.flys.artifacts.model.CalculationResult;
+
+/**
+ * @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\u00b3/s]";
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof WQKms []) {
+                data.add((WQKms [])d);
+            }
+        }
+    }
+
+
+    @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:15 2012 +0200
@@ -0,0 +1,161 @@
+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.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+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 storage for the corrected W series to be drawn in this chart.*/
+    protected XYSeriesCollection cw;
+
+
+    public DischargeLongitudinalSectionGenerator() {
+        super();
+
+        this.cw = new XYSeriesCollection();
+    }
+
+
+    @Override
+    public void addDatasets(JFreeChart chart) {
+        super.addDatasets(chart);
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+
+        plot.setDataset(2, cw);
+    }
+
+
+    @Override
+    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);
+    }
+
+
+    protected void adjustAxes(XYPlot plot) {
+        super.adjustAxes(plot);
+
+        plot.mapDatasetToRangeAxis(2, 0);
+    }
+
+
+
+    @Override
+    public void doOut(Artifact artifact, Facet facet, Document attr) {
+        logger.debug("DischargeLongitudinalSectionGenerator.doOut");
+
+        if (facet == null) {
+            return;
+        }
+
+        String name = facet.getName();
+
+        if (name == null) {
+            return;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        Facet        f    = flys.getNativeFacet(facet);
+
+        if (name.equals(DISCHARGE_LONGITUDINAL_W)) {
+            doWOut((WQKms) f.getData(artifact, context));
+        }
+        else if (name.equals(DISCHARGE_LONGITUDINAL_Q)) {
+            doQOut((WQKms) f.getData(artifact, context));
+        }
+        else if (name.equals(DISCHARGE_LONGITUDINAL_C)) {
+            doCorrectedWOut((WQCKms) f.getData(artifact, context));
+        }
+        else {
+            logger.warn("Unknown facet name: " + name);
+        }
+    }
+
+
+    /**
+     * 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");
+
+        int size = wqckms.size();
+
+        if (size > 0) {
+            XYSeries series = new XYSeries(getSeriesNameForCorrected(wqckms, "W"));
+            for (int i = 0; i < size; i++) {
+                series.add(wqckms.getKms(i), wqckms.getC(i));
+            }
+            cw.addSeries(series);
+        }
+
+        if (wqckms.guessWaterIncreasing()) {
+            setInverted(true);
+        }
+    }
+
+
+    protected String getSeriesNameForCorrected(WQKms wqkms, String mode) {
+        String name = wqkms.getName();
+
+        name = name.replace(
+            "benutzerdefiniert",
+            "benutzerdefiniert [korrigiert]");
+
+        String prefix = 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/DischargeLongitudinalSectionInfoGenerator.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,17 @@
+package de.intevation.flys.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific discharge
+ * longitudinal section curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DischargeLongitudinalSectionInfoGenerator
+extends      ChartInfoGenerator
+{
+    public DischargeLongitudinalSectionInfoGenerator() {
+        super(new DischargeLongitudinalSectionGenerator());
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,138 @@
+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.CallContext;
+
+import de.intevation.flys.artifacts.model.WQDay;
+import de.intevation.flys.artifacts.model.CalculationResult;
+
+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\u00b3/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>();
+    }
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof WQDay) {
+                data.add((WQDay)d);
+            }
+        }
+    }
+
+
+    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:15 2012 +0200
@@ -0,0 +1,277 @@
+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.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.Range;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import de.intevation.artifacts.Artifact;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+import de.intevation.flys.artifacts.model.FacetTypes;
+import de.intevation.flys.artifacts.model.WQDay;
+import de.intevation.flys.artifacts.resources.Resources;
+
+
+/**
+ * An OutGenerator that generates duration curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DurationCurveGenerator
+extends      XYChartGenerator
+implements   FacetTypes
+{
+    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 I18N_DURATION_W =
+        "chart.duration.curve.curve.w";
+
+    public static final String I18N_DURATION_Q =
+        "chart.duration.curve.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);
+    }
+
+
+    @Override
+    protected boolean zoomX(XYPlot plot, ValueAxis axis, Range range, Range x) {
+        boolean zoomin = super.zoom(plot, axis, range, x);
+
+        if (!zoomin) {
+            axis.setLowerBound(0d);
+        }
+
+        return zoomin;
+    }
+
+
+    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\u00b3/s]");
+
+        plot.setRangeAxis(1, qAxis);
+        plot.mapDatasetToRangeAxis(1, 1);
+    }
+
+
+    @Override
+    public void doOut(Artifact artifact, Facet facet, Document attr) {
+        String name = facet != null ? facet.getName() : null;
+
+        logger.debug("DurationCurveGenerator.doOut: " + name);
+
+        if (name == null || name.length() == 0) {
+            logger.error("No facet given. Cannot create dataset.");
+            return;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        Facet        f    = flys.getNativeFacet(facet);
+
+        if (name.equals(DURATION_W)) {
+            doWOut((WQDay) f.getData(artifact, context));
+        }
+        else if (name.equals(DURATION_Q)) {
+            doQOut((WQDay) f.getData(artifact, context));
+        }
+        else {
+            logger.warn("Unknown facet name: " + name);
+            return;
+        }
+    }
+
+
+    /**
+     * Creates the series for a duration curve's W facet.
+     *
+     * @param wqdays The WQDay store that contains the Ws.
+     * @param river The name of the river.
+     */
+    protected void doWOut(WQDay wqdays) {
+        logger.debug("DurationCurveGenerator.doWOut");
+
+        // TODO find the correct series name
+        XYSeries series = new XYSeries(
+            getSeriesName(getRiverName(), DURATION_W));
+
+        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.
+     * @param river The name of the river.
+     */
+    protected void doQOut(WQDay wqdays) {
+        logger.debug("DurationCurveGenerator.doQOut");
+
+        // TODO find the correct series name
+        XYSeries series = new XYSeries(
+            getSeriesName(getRiverName(), DURATION_Q));
+
+        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);
+    }
+
+
+    protected String getSeriesName(String river, String type) {
+        Object[] args = new Object[] { river };
+
+        if (type == null || type.length() == 0) {
+            logger.warn("No duration curve type given.");
+            return "n/a";
+        }
+        else if (type.equals(DURATION_W)) {
+            return Resources.getMsg(
+                context.getMeta(),
+                I18N_DURATION_W,
+                "W",
+                args);
+        }
+        else if (type.equals(DURATION_Q)) {
+            return Resources.getMsg(
+                context.getMeta(),
+                I18N_DURATION_Q,
+                "W",
+                args);
+        }
+
+        logger.warn("Could not determine chart curve type: " + type);
+        return type;
+    }
+}
+// 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/DurationCurveInfoGenerator.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,17 @@
+package de.intevation.flys.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific duration
+ * curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DurationCurveInfoGenerator
+extends      ChartInfoGenerator
+{
+    public DurationCurveInfoGenerator() {
+        super(new DurationCurveGenerator());
+    }
+}
+// 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/InfoGeneratorHelper.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,315 @@
+package de.intevation.flys.exports;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Rectangle2D;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.ChartRenderingInfo;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.Range;
+import org.jfree.data.xy.XYDataset;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.XMLUtils.ElementCreator;
+
+
+/**
+ * This class helps generating chart info documents.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class InfoGeneratorHelper {
+
+    private static final Logger logger =
+        Logger.getLogger(InfoGeneratorHelper.class);
+
+
+    protected XYChartGenerator generator;
+
+
+    public InfoGeneratorHelper(XYChartGenerator generator) {
+        this.generator = generator;
+    }
+
+
+    /**
+     * Triggers the creation of the chart info document.
+     *
+     * @param chart The JFreeChart chart.
+     * @param info An info object that has been created while chart creation.
+     *
+     * @return the info document.
+     */
+    public Document createInfoDocument(
+        JFreeChart         chart,
+        ChartRenderingInfo info)
+    {
+        logger.debug("InfoGeneratorHelper.createInfoDocument");
+
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element chartinfo = cr.create("chartinfo");
+
+        chartinfo.appendChild(createAxesElements(cr, chart));
+        chartinfo.appendChild(createTransformationElements(cr, chart, info));
+
+        doc.appendChild(chartinfo);
+
+        return doc;
+    }
+
+
+    /**
+     * This method create a axes element that contains all domain and range
+     * axes of the given chart.
+     *
+     * @param cr The ElementCreator.
+     * @param chart The chart that provides range information of its axes.
+     *
+     * @return an element with axes information.
+     */
+    protected Element createAxesElements(
+        ElementCreator     cr,
+        JFreeChart         chart)
+    {
+        logger.debug("InfoGeneratorHelper.createRangeElements");
+
+        Element axes = cr.create("axes");
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+
+        int dAxisCount = plot.getDomainAxisCount();
+        for (int i = 0; i < dAxisCount; i++) {
+            ValueAxis axis = plot.getDomainAxis(i);
+            XYDataset data = plot.getDataset(i);
+
+            if (axis != null) {
+                Element e = createAxisElement(cr, axis, data, "domain", i);
+                axes.appendChild(e);
+            }
+        }
+
+        int rAxisCount = plot.getRangeAxisCount();
+        for (int i = 0; i < rAxisCount; i++) {
+            ValueAxis axis = plot.getRangeAxis(i);
+            XYDataset data = plot.getDataset(i);
+
+            if (axis == null || data == null) {
+                logger.warn("Axis or dataset is empty at pos: " + i);
+                continue;
+            }
+
+            Element e = createAxisElement(cr, axis, data, "range", i);
+            axes.appendChild(e);
+        }
+
+        return axes;
+    }
+
+
+    /**
+     * This method create a axis element for a given <i>axis</i> and
+     * <i>type</i>. Type can be one of 'domain' or 'range'.
+     *
+     * @param cr The ElementCreator
+     * @param axis The axis that provides range information.
+     * @param dataset The dataset for min/max determination.
+     * @param type The axis type ('domain' or 'range').
+     * @param pos The position in the chart.
+     *
+     * @return An element that contains range information of a given axis.
+     */
+    protected Element createAxisElement(
+        ElementCreator cr,
+        ValueAxis      axis,
+        XYDataset      dataset,
+        String         type,
+        int            pos)
+    {
+        Range range = axis.getRange();
+
+        Element e = cr.create(type);
+        cr.addAttr(e, "pos", String.valueOf(pos), true);
+        cr.addAttr(e, "from", String.valueOf(range.getLowerBound()), true);
+        cr.addAttr(e, "to", String.valueOf(range.getUpperBound()), true);
+
+        Range[] rs = generator.getRangesForDataset(dataset);
+        Range   r  = null;
+
+        if (type.equals("range")) {
+            r = rs[1];
+        }
+        else {
+            r = rs[0];
+        }
+
+        cr.addAttr(e, "min", String.valueOf(r.getLowerBound()), true);
+        cr.addAttr(e, "max", String.valueOf(r.getUpperBound()), true);
+
+        return e;
+    }
+
+
+    /**
+     * This method appends the values of a transformation matrix to transform
+     * image pixel coordinates into chart coordinates.
+     *
+     * @param cr The ElementCreator.
+     * @param chart The chart object.
+     * @param info The ChartRenderingInfo that is filled while chart creation.
+     *
+     * @return an element that contains one or more transformation matrix.
+     */
+    protected Element createTransformationElements(
+        ElementCreator     cr,
+        JFreeChart         chart,
+        ChartRenderingInfo info)
+    {
+        logger.debug("InfoGeneratorHelper.createTransformationElements");
+
+        Element tf = cr.create("transformation-matrix");
+
+        Rectangle2D dataArea = info.getPlotInfo().getDataArea();
+
+        XYPlot    plot  = (XYPlot) chart.getPlot();
+        ValueAxis xAxis = plot.getDomainAxis();
+
+        if (xAxis == null) {
+            logger.error("There is no x axis in the chart!");
+            return null;
+        }
+
+        for (int i  = 0, num = plot.getRangeAxisCount(); i < num; i++) {
+            ValueAxis yAxis = plot.getRangeAxis(i);
+
+            if (yAxis == null) {
+                logger.warn("No y axis at pos " + i + " existing.");
+                continue;
+            }
+
+            Element matrix = createTransformationElement(
+                cr, xAxis, yAxis, dataArea, i);
+
+            tf.appendChild(matrix);
+        }
+
+        return tf;
+    }
+
+
+    /**
+     * Creates an element that contains values used to transform coordinates
+     * of a coordinate system A into a coordinate system B.
+     *
+     * @param cr The ElementCreator.
+     * @param xAxis The x axis of the target coordinate system.
+     * @param yAxis The y axis of the target coordinate system.
+     * @param dataArea The pixel coordinates of the chart image.
+     * @param pos The dataset position.
+     *
+     * @return an element that contains transformation matrix values.
+     */
+    protected Element createTransformationElement(
+        ElementCreator cr,
+        ValueAxis      xAxis,
+        ValueAxis      yAxis,
+        Rectangle2D    dataArea,
+        int            pos)
+    {
+        double[] tm = createTransformationMatrix(dataArea, xAxis, yAxis);
+
+        Element matrix = cr.create("matrix");
+
+        cr.addAttr(matrix, "pos", String.valueOf(pos), true);
+        cr.addAttr(matrix, "sx", String.valueOf(tm[0]), true);
+        cr.addAttr(matrix, "sy", String.valueOf(tm[1]), true);
+        cr.addAttr(matrix, "tx", String.valueOf(tm[2]), true);
+        cr.addAttr(matrix, "ty", String.valueOf(tm[3]), true);
+
+        return matrix;
+    }
+
+
+    /**
+     * This method determines a transformation matrix to transform pixel
+     * coordinates of the chart image into chart coordinates.
+     *
+     * @param dataArea The rectangle that contains the data points of the chart.
+     * @param xRange The x axis range.
+     * @param yRange The y axis range.
+     *
+     * @return a double array as follows: [sx, sy, tx, ty].
+     */
+    protected static double[] createTransformationMatrix(
+        Rectangle2D dataArea,
+        ValueAxis   xAxis,
+        ValueAxis   yAxis)
+    {
+        logger.debug("InfoGeneratorHelper.createTransformationMatrix");
+
+        double offsetX = dataArea.getX();
+        double width   = dataArea.getWidth();
+        double offsetY = dataArea.getY();
+        double height  = dataArea.getHeight();
+
+        Range xRange = xAxis.getRange();
+        Range yRange = yAxis.getRange();
+
+        double lowerX  = xRange.getLowerBound();
+        double upperX  = xRange.getUpperBound();
+        double lowerY  = yRange.getLowerBound();
+        double upperY  = yRange.getUpperBound();
+
+        if (xAxis.isInverted()) {
+            logger.info("X-Axis is inverted!");
+
+            double tmp = upperX;
+            upperX = lowerX;
+            lowerX = tmp;
+        }
+
+        double dMoveX = upperX - lowerX;
+        double fMoveX = width * lowerX;
+        double dMoveY = lowerY - upperY;
+        double fMoveY = height * upperY;
+
+        AffineTransform t1 = AffineTransform.getTranslateInstance(
+                offsetX - ( fMoveX / dMoveX ),
+                offsetY - ( fMoveY / dMoveY ) );
+
+        AffineTransform t2 = AffineTransform.getScaleInstance(
+                width / (upperX - lowerX),
+                height / (lowerY - upperY));
+
+        t1.concatenate(t2);
+
+        try {
+            t1.invert();
+
+            double[] c = new double[6];
+            t1.getMatrix(c);
+
+            return new double[] { c[0], c[3], c[4], c[5] };
+        }
+        catch (NoninvertibleTransformException e) {
+            // do nothing
+            logger.warn("Matrix is not invertible.");
+        }
+
+        return new double[] { 1d, 1d, 0d, 0d };
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,305 @@
+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.Range;
+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.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+import de.intevation.flys.artifacts.model.FacetTypes;
+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
+implements   FacetTypes
+{
+    /** The logger that is used in this generator.*/
+    private static Logger logger =
+        Logger.getLogger(LongitudinalSectionGenerator.class);
+
+    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\u00e4ngsschnitt";
+    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\u00b3/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;
+
+    protected boolean inverted;
+
+
+    public LongitudinalSectionGenerator() {
+        super();
+
+        this.w = new XYSeriesCollection();
+        this.q = new XYSeriesCollection();
+    }
+
+
+    protected String getChartTitle() {
+        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
+    }
+
+    public boolean isInverted() {
+        return inverted;
+    }
+
+    public void setInverted(boolean inverted) {
+        this.inverted = inverted;
+    }
+
+    @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);
+    }
+
+
+    @Override
+    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(1, qAxis);
+        plot.mapDatasetToRangeAxis(1, 1);
+
+        invertXAxis(plot.getDomainAxis());
+    }
+
+
+    /**
+     * This method overrides the XYChartGenerators zoomY method to include the 0
+     * value on the Q axis.
+     */
+    @Override
+    protected boolean zoomY(XYPlot plot, ValueAxis axis, Range range, Range x) {
+        if (plot.getRangeAxisIndex(axis) == 1) {
+            // we want the Q axis to start at 0 if no zooming has been done
+            range = new Range(0d, range.getUpperBound());
+        }
+
+        return super.zoomY(plot, axis, range, x);
+    }
+
+
+    /**
+     * 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) {
+
+        if (inverted) {
+            logger.debug("Invert X-Axis.");
+            xaxis.setInverted(true);
+        }
+    }
+
+
+    public void doOut(Artifact artifact, Facet facet, Document attr) {
+        String name = facet.getName();
+
+        logger.debug("LongitudinalSectionGenerator.doOut: " + name);
+
+        if (name == null) {
+            logger.error("No facet name for doOut(). No output generated!");
+            return;
+        }
+
+        FLYSArtifact flys = (FLYSArtifact) artifact;
+        Facet        f    = flys.getNativeFacet(facet);
+
+        if (f == null) {
+            return;
+        }
+
+        if (name.equals(LONGITUDINAL_W)) {
+            doWOut((WQKms) f.getData(artifact, context));
+        }
+        else if (name.equals(LONGITUDINAL_Q)) {
+            doQOut((WQKms) f.getData(artifact, context));
+        }
+        else {
+            logger.warn("Unknown facet name: " + name);
+            return;
+        }
+    }
+
+
+    /**
+     * 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");
+
+        XYSeries series = new XYSeries(getSeriesName(wqkms, "W"));
+
+        int size = wqkms.size();
+
+        if (logger.isDebugEnabled()) {
+            if (wqkms.size() > 0) {
+                logger.debug("Generate series: " + series.getKey());
+                logger.debug("Start km: " + wqkms.getKms(0));
+                logger.debug("End   km: " + wqkms.getKms(size-1));
+                logger.debug("Values  : " + size);
+            }
+        }
+
+        for (int i = 0; i < size; i++) {
+            series.add(wqkms.getKms(i), wqkms.getW(i));
+        }
+
+        w.addSeries(series);
+
+        if (wqkms.guessWaterIncreasing()) {
+            setInverted(true);
+        }
+    }
+
+
+    /**
+     * 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");
+
+        XYSeries series = new XYSeries(getSeriesName(wqkms, "Q"));
+
+        int size = wqkms.size();
+
+        if (logger.isDebugEnabled()) {
+            if (wqkms.size() > 0) {
+                logger.debug("Generate series: " + series.getKey());
+                logger.debug("Start km: " + wqkms.getKms(0));
+                logger.debug("End   km: " + wqkms.getKms(size-1));
+                logger.debug("Values  : " + size);
+            }
+        }
+
+        for (int i = 0; i < size; i++) {
+            series.add(wqkms.getKms(i), wqkms.getQ(i));
+        }
+
+        q.addSeries(series);
+
+        if (wqkms.guessWaterIncreasing()) {
+            setInverted(true);
+        }
+    }
+
+
+    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/LongitudinalSectionInfoGenerator.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,17 @@
+package de.intevation.flys.exports;
+
+
+/**
+ * A ChartInfoGenerator that generates meta information for specific
+ * longitudinal section curves.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class LongitudinalSectionInfoGenerator
+extends      ChartInfoGenerator
+{
+    public LongitudinalSectionInfoGenerator() {
+        super(new LongitudinalSectionGenerator());
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,58 @@
+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;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+
+/**
+ * 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, Facet 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/ReportGenerator.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,62 @@
+package de.intevation.flys.exports;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.log4j.Logger;
+
+import de.intevation.artifacts.Artifact;
+import de.intevation.artifacts.CallContext;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+
+import de.intevation.artifactdatabase.state.Facet;
+
+import de.intevation.flys.artifacts.FLYSArtifact;
+
+import de.intevation.flys.artifacts.model.Calculation;
+
+import org.w3c.dom.Document;
+
+public class ReportGenerator
+implements   OutGenerator
+{
+    private static Logger logger = Logger.getLogger(ReportGenerator.class);
+
+    protected Document     result;
+    protected OutputStream out;
+    protected CallContext  context;
+
+    public ReportGenerator() {
+    }
+
+    @Override
+    public void init(Document request, OutputStream out, CallContext context) {
+        logger.debug("init");
+        this.out     = out;
+        this.context = context;
+        result = XMLUtils.newDocument();
+    }
+
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        // not needed
+    }
+
+    @Override
+    public void doOut(Artifact artifact, Facet facet, Document attr) {
+        logger.debug("doOut");
+        facet = ((FLYSArtifact)artifact).getNativeFacet(facet);
+        if (facet != null) {
+            Calculation report = (Calculation)facet.getData(artifact, context);
+            report.toXML(result, context.getMeta());
+        }
+    }
+
+    @Override
+    public void generate() throws IOException {
+        logger.debug("generate");
+        XMLUtils.toStream(result, out);
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,221 @@
+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.CallContext;
+
+import de.intevation.flys.artifacts.model.CalculationResult;
+import de.intevation.flys.artifacts.model.WQCKms;
+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\u00b3/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");
+        }
+    }
+
+
+    @Override
+    protected void addData(Object d) {
+        if (d instanceof CalculationResult) {
+            d = ((CalculationResult)d).getData();
+            if (d instanceof WQKms []) {
+                data.add((WQKms [])d);
+            }
+        }
+    }
+
+
+    @Override
+    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[4];
+
+        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);
+                }
+
+                if (wqkms instanceof WQCKms) {
+                    addWSTColumn(writer, wqkms);
+
+                    for (int c = 0; c < size; c++) {
+                        result = wqkms.get(c, result);
+
+                        writer.addCorrected(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:15 2012 +0200
@@ -0,0 +1,237 @@
+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 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)));
+
+        this.qs = new double[cols];
+
+        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]);
+    }
+
+
+    public void addCorrected(double[] wqckms) {
+        Double km = wqckms[2];
+
+        WstLine line = lines.get(km);
+
+        if (line == null) {
+            line = new WstLine(km.doubleValue());
+            lines.put(km, line);
+        }
+
+        line.add(wqckms[3], wqckms[1]);
+    }
+
+
+    /**
+     * Adds a further column name.
+     *
+     * @param name The name of the new column.
+     */
+    public void addColumn(String name) {
+        if (name != null) {
+            cols++;
+
+            String basename = name;
+
+            int i = 0;
+            while (columnNames.contains(name)) {
+                name = basename + "_" + i++;
+
+                if (name.length() > 9) {
+                    name = name.substring(name.length() - 9);
+                }
+            }
+
+            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 < qs.length; 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:15 2012 +0200
@@ -0,0 +1,271 @@
+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.axis.ValueAxis;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.Range;
+import org.jfree.data.xy.XYDataset;
+
+import org.jfree.ui.RectangleInsets;
+
+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(XYChartGenerator.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 = generateChart();
+
+        int[] size = getSize();
+
+        ChartExportHelper.exportImage(
+            out,
+            chart,
+            "png",
+            size[0], size[1]);
+    }
+
+
+    public JFreeChart generateChart() {
+        logger.debug("XYChartGenerator.generateChart");
+
+        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);
+        autoZoom(plot);
+
+        return chart;
+    }
+
+
+    /**
+     * This method zooms the plot to the specified ranges in the attribute
+     * document or to the ranges specified by the min/max values in the
+     * datasets. <b>Note:</b> We determine the range manually if no zoom ranges
+     * are given, because JFreeCharts auto-zoom adds a margin to the left and
+     * right of the data area.
+     *
+     * @param plot The XYPlot.
+     */
+    protected void autoZoom(XYPlot plot) {
+        logger.debug("Zoom to specified ranges.");
+
+        Range xrange = getDomainAxisRange();
+        Range yrange = getValueAxisRange();
+
+        for (int i = 0, num = plot.getDatasetCount(); i < num; i++) {
+            XYDataset dataset = plot.getDataset(i);
+            Range[]   ranges  = getRangesForDataset(dataset);
+
+            if (i == 0) {
+                ValueAxis xaxis = plot.getDomainAxis();
+                zoomX(plot, xaxis, ranges[0], xrange);
+            }
+
+            ValueAxis yaxis = plot.getRangeAxis(i);
+
+            if (yaxis == null) {
+                continue;
+            }
+
+            zoomY(plot, yaxis, ranges[1], yrange);
+        }
+    }
+
+
+    protected boolean zoomX(XYPlot plot, ValueAxis axis, Range range, Range x) {
+        return zoom(plot, axis, range, x);
+    }
+
+
+    protected boolean zoomY(XYPlot plot, ValueAxis axis, Range range, Range x) {
+        return zoom(plot, axis, range, x);
+    }
+
+
+    /**
+     * Zooms the x axis to the range specified in the attribute document.
+     *
+     * @param plot The XYPlot.
+     * @param axis The axis the shoud be modified.
+     * @param range The whole range specified by a dataset.
+     * @param x A user defined range (null permitted).
+     *
+     * @return true, if a zoom range was specified, otherwise false.
+     */
+    protected boolean zoom(XYPlot plot, ValueAxis axis, Range range, Range x) {
+        if (x != null) {
+            double min  = range.getLowerBound();
+            double max  = range.getUpperBound();
+            double diff = max > min ? max - min : min - max;
+
+            Range computed = new Range(
+                min + x.getLowerBound() * diff,
+                min + x.getUpperBound() * diff);
+
+            axis.setRangeWithMargins(computed);
+
+            logger.debug("Zoom axis to: " + computed);
+
+            return true;
+        }
+
+        axis.setRangeWithMargins(range);
+        return false;
+    }
+
+
+    /**
+     * This method extracts the minimum and maximum values for x and y axes.
+     *
+     * @param dataset The dataset that should be observed.
+     *
+     * @return a Range[] as follows: [x-Range, y-Range].
+     */
+    public static Range[] getRangesForDataset(XYDataset dataset) {
+        double[] xr = new double[] { Double.MAX_VALUE, -Double.MAX_VALUE };
+        double[] yr = new double[] { Double.MAX_VALUE, -Double.MAX_VALUE };
+
+        int sCount = dataset.getSeriesCount();
+
+        for (int i = 0; i < sCount; i++) {
+            int iCount = dataset.getItemCount(i);
+
+            for (int j = 0; j < iCount; j++) {
+                double x = dataset.getX(i, j).doubleValue();
+                double y = dataset.getY(i, j).doubleValue();
+
+                if (!Double.isNaN(x)) {
+                    xr[0] = xr[0] < x ? xr[0] : x;
+                    xr[1] = xr[1] > x ? xr[1] : x;
+                }
+
+                if (!Double.isNaN(y)) {
+                    yr[0] = yr[0] < y ? yr[0] : y;
+                    yr[1] = yr[1] > y ? yr[1] : y;
+                }
+            }
+        }
+
+        // this is only required, if there are no items in the dataset.
+        xr[0] = xr[0] < xr[1] ? xr[0] : xr[1];
+        xr[1] = xr[1] > xr[0] ? xr[1] : xr[0];
+        yr[0] = yr[0] < yr[1] ? yr[0] : yr[1];
+        yr[1] = yr[1] > yr[0] ? yr[1] : yr[0];
+
+        return new Range[] {new Range(xr[0], xr[1]), new Range(yr[0], yr[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);
+
+        plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d));
+    }
+
+
+    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:15 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:15 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:15 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:15 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:15 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/DoubleUtil.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,48 @@
+package de.intevation.flys.utils;
+
+public class DoubleUtil
+{
+    public static final double DEFAULT_STEP_PRECISION = 1e6;
+
+    private DoubleUtil() {
+    }
+
+    public static final double [] explode(double from, double to, double step) {
+        return explode(from, to, step, DEFAULT_STEP_PRECISION);
+    }
+
+    public static final double round(double x, double precision) {
+        return Math.round(x * precision)/precision;
+    }
+
+    public static final double round(double x) {
+        return Math.round(x * DEFAULT_STEP_PRECISION)/DEFAULT_STEP_PRECISION;
+    }
+
+    public static final double [] explode(
+        double from,
+        double to,
+        double step,
+        double precision
+    ) {
+        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];
+
+        if (from > to) {
+            step = -step;
+        }
+
+        for (int idx = 0; idx < num; idx++) {
+            values[idx] = round(lower, precision);
+            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/utils/Formatter.java	Fri Sep 28 12:14:15 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:15 2012 +0200
@@ -0,0 +1,50 @@
+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]
+chart.duration.curve.curve.w = Waterlevel duration curve for {0}
+chart.duration.curve.curve.q = Discharge duration curve for {0}
+
+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:15 2012 +0200
@@ -0,0 +1,50 @@
+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 ungleichwertigem 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]
+chart.duration.curve.curve.w = Wasserstandsdauerline f\u00fcr {0}
+chart.duration.curve.curve.q = Abflussdauerline f\u00fcr {0}
+
+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:15 2012 +0200
@@ -0,0 +1,50 @@
+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 ungleichwertigem 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]
+chart.duration.curve.curve.w = Wasserstandsdauerline f\u00fcr {0}
+chart.duration.curve.curve.q = Abflussdauerline f\u00fcr {0}
+
+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:15 2012 +0200
@@ -0,0 +1,50 @@
+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]
+chart.duration.curve.curve.w = Waterlevel duration curve for {0}
+chart.duration.curve.curve.q = Discharge duration curve for {0}
+
+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:15 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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,3410 @@
+2011-06-28  Ingo Weinzierl <ingo@intevation.de>
+
+	Tagged RELEASE 2.4
+
+2011-06-27  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue182 (WINFO: Vorbelegung Q bei "ungleichwertigem Abflusslängsschnitt")
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQAdaptedInputPanel.java:
+	  Set initial mode to Q.
+
+2011-06-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* Changes: Prepared changes for the upcoming release.
+
+2011-06-26	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/**/*.java:
+	  Removed trailing whitespace.
+
+2011-06-26	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/UserCollectionsServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/shared/model/CollectionRecord.java,
+	  src/main/java/de/intevation/flys/client/client/ui/chart/PanControl.java:
+	  Removed superfluous imports.
+
+2011-06-24  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue165 (Projektliste: Einige Auffälligkeiten nach Neuimplementierung)
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Set the maximal length of project names to 50 characters. If the name,
+	  entered by the user, is longer than this max value, it is not saved to
+	  artifact server and the old name is displayed again.
+
+	* 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 warning message for too long project names.
+
+2011-06-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  The "zoom-to-max-extent" button will have the effect that no zoom values
+	  are transfered to the server.
+
+2011-06-24  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue21 (Gleiches Projekt kann mehr als einmal gleichzeitig geöffnet werden)
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Implements
+	  CollectionChangeHandler to lock new projects. In addition, there is a
+	  new method to close projects.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  If this view contains a Collection, FLYS is used to close this window.
+	  FLYS needs to unlock the project before the view is destroyed.
+	  Otherwise - if no Collection has been created yet - this view destroys
+	  itself.
+
+2011-06-23  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue170 (Diagramm: Initiales Zoomen zeigt verschobenen Bereich)
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  The chart image is no longer removed from DOM but it's source is set to
+	  a new value after the panel's size changes. This makes the zoom actions
+	  much smoother and there the initial zoom values are no longer broken.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/PanControl.java:
+	  Panning will now move the image itself but not its parent element.
+
+	* src/main/java/de/intevation/flys/client/shared/model/ChartInfo.java,
+	  src/main/java/de/intevation/flys/client/shared/Transform2D.java: Added
+	  methods to dump the objects to GWT log.
+
+2011-06-23	Sascha L. Teichmann	<sascha.teichmann@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:
+	  Prepare for AT export.
+
+2011-06-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Removed the formatter that cuts out a piece of the middle of project
+	  names.
+
+2011-06-23  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/CollectionRecord.java:
+	  Store Html escaped strings as names.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Display the project's name stored at the CollectionRecord which is Html
+	  escaped.
+
+2011-06-23  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue165 (Projektliste: Einige Auffälligkeiten nach Neuimplementierung)
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Added tooltips and table headers.
+
+2011-06-23  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 more i18n strings for the project list and error messages.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Added a
+	  handler that catches uncaught exceptions. Those exceptions are displayed
+	  using a new method showWarning(Throwable t). This method brings up a
+	  Window that displays the stacktrace of the exceptions.
+
+2011-06-23	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* pom.xml: Added dependency to Apache Commons Lang 2.6 http://commons.apache.org/lang/
+	  Used for proper HTML string escaping.
+
+	* src/main/java/de/intevation/flys/client/server/ReportServiceImpl.java: Generate
+	  error reports as HTML lists.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java: Set report
+	  in corresponding panel.
+
+2011-06-22	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/ReportService.java,
+	  src/main/java/de/intevation/flys/client/server/ReportServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/ReportServiceAsync.java:
+	  Stub for report service.
+
+	* src/main/webapp/WEB-INF/web.xml: registered new service.
+
+2011-06-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added a new error string.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQAdaptedInputPanel.java:
+	  Display a warning if there are no information of W and Q ranges that
+	  might be used to validate user input.
+
+2011-06-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Register the CollectionView when a new collection is created. This is
+	  necessary to avoid an inconsistent view that would otherwise display
+	  projects that might have been deleted.
+
+2011-06-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  If an open project is removed (via context menu) the related
+	  CollectionView is removed from screen.
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Added a method
+	  that returns the current FLYSWorkspace.
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSWorkspace.java:
+	  Added a method that destroys a CollectionView if it is open.
+
+2011-06-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DoCollectionAction.java:
+	  New. This base RPC service implementation handles collecion specific
+	  actions. It has a doAction() method that takes a Collection, the action
+	  document and a server url.
+
+	* src/main/java/de/intevation/flys/client/server/SetCollectionNameServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/DeleteCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/SetCollectionTTLServiceImpl.java:
+	  Trigger the collection actions on the server using the
+	  DoCollectionAction class which handles the Http stuff.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Bugfix: If no user collections returned, we will clear the project list.
+
+2011-06-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/SetCollectionNameServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/SetCollectionNameService.java,
+	  src/main/java/de/intevation/flys/client/server/SetCollectionNameServiceImpl.java:
+	  New. This service is used to set the name of a collection. Note, that
+	  this is currently just a stub. The ServiceImpl currently throws a "NOT
+	  IMPLEMENTED" exception.
+
+	* src/main/java/de/intevation/flys/client/client/services/SetCollectionTTLServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/SetCollectionTTLService.java,
+	  src/main/java/de/intevation/flys/client/server/SetCollectionTTLServiceImpl.java:
+	  New. This service is used to set the time-to-live of a collection. Note,
+	  that this is currently just a stub. The ServiceImpl currently throws a
+	  "NOT IMPLEMENTED" exception.
+
+	* src/main/java/de/intevation/flys/client/client/services/DeleteCollectionServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/DeleteCollectionService.java,
+	  src/main/java/de/intevation/flys/client/server/DeleteCollectionServiceImpl.java:
+	  New. This service is used to delete a collection. Note, that this is
+	  currently just a stub. The ServiceImpl currently throws a "NOT
+	  IMPLEMENTED" exception.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Calls the services SetCollectionTTLService, SetCollectionNameService and
+	  DeleteCollectionService and display a warning if an error occured.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the new services.
+
+2011-06-22  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:
+	  There are setter methods for 'name' and 'ttl' now. The project list
+	  requires this to set those attribute to a new value.
+
+	* 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 project list's context menu item "rename project".
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Added a context menu item to rename the selected project and listeners
+	  to trigger setting 'name' and 'ttl' to a new value.
+
+	  TODO: We still need to write RPC services to update those values!
+
+2011-06-22  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Renamed the german translation of wq mode for "Q at gauge".
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Adapted the layout to fit the (long) german wq mode string.
+
+2011-06-22  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue104 (W-INFO: Wasserspiegellagenberechnung / Strecke)
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  The old/static panel will display the selected wq mode now, because it's
+	  no longer possible to recognize the selected q mode based on the
+	  displayed units (there are two different q modes now).
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  Initialize the combobox correctly.
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue156 (WINFO: Platzierung des Berechnungsberichtes)
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Do not display report modes as tabs.
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/SelectProvider.java:
+	  The combobox is no longer editable now.
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue21 (Gleiches Projekt kann mehr als einmal gleichzeitig geöffnet werden)
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSWorkspace.java:
+	  Open projects that are opened again via ProjectList are brought up to
+	  front and their size is restored.
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java:
+	  Take care on empty fields when adding a new double value.
+
+2011-06-21	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/ReportMode.java:
+	  New. Mode to detect out of type 'report'.
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java:
+	  Build ReportModes for 'report' outs.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Filter reports and dispatch them to updateReports().
+	  
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue132 (WINFO: Streckenauswahl per default setzen)
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Set the initial mode to "distance".
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/UserCollectionsServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/CreateCollectionServiceImpl.java:
+	  Read the time-to-live from collection's describe document.
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultCollection.java,
+	  src/main/java/de/intevation/flys/client/shared/model/Collection.java:
+	  Adapted the constructor calls. They require a time-to-live attribute
+	  now. This value is retrievable via getTTL().
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  The project list has now a column that displays an icon that shows the
+	  favorite state of the project. A gold star means, the project is marked
+	  as favorite, a silver one means that the project is no favorite project.
+
+	* src/main/java/de/intevation/flys/client/shared/model/CollectionRecord.java:
+	  The 'ttl' attribute returns the filename of the icon that should be
+	  displayed in the project list.
+
+	* src/main/webapp/images/star_silver.png,
+	  src/main/webapp/images/star_gold.png: New. These icons - based on icons
+	  copied from famfamfam - are used to mark a project as favorite or no
+	  favorite.
+
+2011-06-21	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/config.xml:
+	  Set project list update interval to 5 minutes. That is enough
+	  for long living projects.
+
+2011-06-21  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue22 (Abgelaufene Projekte/Collections werden noch in der Projektliste angezeigt.)
+
+	* src/main/java/de/intevation/flys/client/client/Config.java,
+	  src/main/java/de/intevation/flys/client/client/config.xml:
+	  Added a config option to specify the update interval of the project
+	  list. This value is retrievable using the method
+	  Config.getProjectListUpdateInterval().
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Added a timer to update the user's collections frequently. The interval
+	  is determined by the constant UPDATE_INTERVAL which is currently 30
+	  seconds or a config.xml option that needs to be configured at
+	  "/config/projectlist/update-interval/text()".
+
+2011-06-21  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 i18n strings for the new output "report" for each calculation
+	  mode.
+
+2011-06-20	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java: Added report panel.
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/server/DistanceInfoServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/chart/MousePositionPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Removed superfluous imports.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java,
+	  src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  'boolean ? true : false' <=> 'boolean'
+	
+2011-06-20	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java:
+	  Removed more expensive XPath.
+	  
+2011-06-20  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue140 (WINFO: WQ-Tabelle in Tabs aufsplitten)
+	  flys/issue122 (W-INFO: Wasserspiegellagenberechnung / Auswahlunterstützung Abfluss (W-Werte sind nicht auswählbar))
+
+	* src/main/java/de/intevation/flys/client/client/ui/wq/QDTable.java:
+	  New. This table is used to show Q and D values for the WQInputPanel. The
+	  first two rows are selectable icons.
+
+	* src/main/java/de/intevation/flys/client/client/ui/wq/WTable.java: New.
+	  This table is used to show W values for the WQInputPanel. The rows in
+	  this table are not selectable.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleRangePanel.java:
+	  Added methods to set the lower and upper value directly.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java:
+	  Added a method to add a further double value to the current list.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Removed the old WQD tables and replaced them with a QDTable and a
+	  WTable. The WQD values are sorted into the two tables when they are
+	  initialized. The values in the WTable are not selectable. The QDTable
+	  has two selectable columns. Based on the current mode (single values or
+	  range mode), the selected values are filled into the array panel or into
+	  the range panel.
+
+2011-06-17  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue140 PART 1 (WINFO: WQ-Tabelle in Tabs aufsplitten)
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Splitted the code to determine min/max kilometer range and rivername
+	  from code to fetch WQ data via RPC.
+
+2011-06-17  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue118 (W-INFO: Eingabe Q aus Auswahlunterstützung und Manuell)
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Added the "wq_free" parameter to the feed data even if the single value
+	  mode is selected.
+
+2011-06-17  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue118 (W-INFO: Eingabe Q aus Auswahlunterstützung und Manuell)
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Added a further radio button to select a "Q at Gauge". In addition, this
+	  panel returns a new value "wq_free" that depends on the selected Q mode.
+	  It is "true" if Q is selected, otherwise "false".
+
+	* 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 radio button.
+
+2011-06-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java:
+	  Write "index" and "description" into the attribute document. This is
+	  necessary for the ChartThemePanel.
+
+	* src/main/java/de/intevation/flys/client/shared/model/Theme.java: Added a
+	  getIndex() method.
+
+2011-06-17  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java:
+	  Read "index" and "description" of facets from collection's describe
+	  document. In addition, I replaced the expensive XPath search for
+	  attributes with DOM methods.
+
+	* src/main/java/de/intevation/flys/client/shared/model/Theme.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultTheme.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultFacet.java:
+	  Themes and facets have now indices and descriptions.
+
+	* src/main/java/de/intevation/flys/client/shared/model/FacetRecord.java:
+	  Display the description of a theme in the theme panel instead of the
+	  name (which represents the facet type).
+
+2011-06-16	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DatacageWindow.java:
+	  Alloc TreeNode array with the right size because its known.
+
+2011-06-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java:
+	  Be more tolerant with entered whitespaces.
+
+2011-06-15  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Set height of the parameter panel that shows the "old" values to "1" if
+	  no "old" value is existing. This avoids GWT throwing warning messages.
+
+2011-06-15  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue117 (W-INFO: Wasserspiegellagenberechnung / Q-Eingabe)
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java:
+	  This panel will return a list of unique values.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleRangePanel.java:
+	  Improved parsing of double values.
+
+2011-06-14	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	  flys/issue88 (WINFO/Wasserspiegellage: Namensänderung im Eingabebereich
+	  von Orten und Strecken)
+
+	* 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:
+	  Modified strings for the location/distance panel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Modified strings for the radio button and panel titles.
+
+2011-06-14	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DistanceInfoServiceImpl.java:
+	  Got rid of namespace. Use DOM instead of XPath.
+
+2011-06-14	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DistanceInfoRecord.java:
+	  Adjusted table model.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  Bring 'Oberkante' and 'Unterkante' into UI.
+
+	* 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_de.properties:
+	  i18n. Repaired a few wrong encoded strings, too.
+
+2011-06-14	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DistanceInfoServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DistanceInfoObjectImpl.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DistanceInfoObject.java:
+	  Loop through 'Unterkante' 'Oberkante' from service.
+
+2011-06-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties:
+	  Adapted the german error message that is displayed if the user entered a
+	  value which is not in the valid range.
+
+2011-06-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/WQDataItem.java:
+	  New. This DataItem is used to save min/max W/Q values. This enables the
+	  UIProvider to validate the entered values. Currently, this is used in the
+	  WQAdaptedInputPanel only.
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Read the W/Q ranges from DESCRIBE document and create WQDataItems if
+	  they are existing.
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQAdaptedInputPanel.java:
+	  Introduced a client side input validation for the entered W/Q values
+	  with respect on their format and min/max range.
+
+2011-06-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java: The
+	  values entered by the user are validated on client side now taking
+	  account on the format and the river's W/Q ranges.
+
+2011-06-14  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleArrayPanel.java:
+	  Added a method to validate the form field. If a value is not a valid
+	  double value, it returns false.
+
+	* src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java:
+	  The values entered in the text field are validated regarding to the
+	  river's range.
+
+	* 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
+	  error messages displayed if we were not able to read min/max values from
+	  DESCRIBE document.
+
+2011-06-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,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_de.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java:
+	  Added error strings for the range range/location panel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/AbstractUIProvider.java:
+	  Added a validate() method that is called before the event to step
+	  forward is fired. If the method returns a list with errors, the event is
+	  not fired. In that case, an error dialog is displayed.
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Override the validate() method of AbstractUIProvider.
+
+	* src/main/java/de/intevation/flys/client/client/ui/DoubleRangePanel.java:
+	  Added a method that validates the form.
+
+2011-06-09  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ChartInfoServiceImpl.java:
+	  Parse min/max values for axes and all transformation matrix elements.
+
+	* src/main/java/de/intevation/flys/client/shared/model/Axis.java: An axis
+	  has a min and max value now.
+
+	* src/main/java/de/intevation/flys/client/shared/model/ChartInfo.java:
+	  The ChartInfo object provides a Transform2D object for each y axis
+	  contained in the chart.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Refactored the zoom and pan actions. The values used to select a
+	  concrete clip of the chart are computed based on the min/max range of an
+	  axis. The chart request uses percentual values for axes ranges now.
+
+2011-06-09  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue139 (WINFO: NPE bei Step-Back bei ungleichwertiger Berechnungsart)
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQAdaptedInputPanel.java:
+	  Fixed NPE while initialising user values.
+
+2011-06-08  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue103 PART 2 (WINFO: Wasserspiegellagenberechnung / Layout-Inkonsistenz)
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQInputPanel.java:
+	  Append the unit of W or Q to the values in the single mode.
+
+2011-06-08  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue43 (i18n: Einige Verbesserungen)
+
+	* src/main/java/de/intevation/flys/client/client/FLYSConstants.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants_en.properties:
+	  Adjusted the english text that is displayed before switching to another
+	  language.
+
+2011-06-08  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue21 (Gleiches Projekt kann mehr als einmal gleichzeitig
+	  geöffnet werden)
+
+	* src/main/java/de/intevation/flys/client/client/FLYS.java: Introduced a
+	  lock mechanism for projects. When a project is opened, it becomes
+	  locked. Further clicks on that project in the project list will have no
+	  effect except it brings up the project window.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Implements the CloseClickHandler interface now. The operations triggered
+	  by the "x" in the upper right corner of the window will close and
+	  destroy the window. This has been done automatically before.
+
+	* src/main/java/de/intevation/flys/client/client/ui/FLYSWorkspace.java:
+	  Added a new method that brings up a project window.
+
+2011-06-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ChartInfoServiceImpl.java:
+	  Skip axes that lay out of valid bounds. E.g. there are 2 axes: if one of
+	  those axes position is > 1 it is skipped.
+
+2011-06-08	Sascha L. Teichmann	<sascha.teichmann@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Don't crash if there is no transformer.
+
+2011-06-08  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/ProjectList.java:
+	  Added a dirty hack to accept project names shorter than 30 characters
+	  without cutting a part from the middle.
+
+2011-06-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/PanControl.java:
+	  Implemented the onMouseOut() method. If this event is catched, the
+	  current pan operation is finished (not canceled).
+
+2011-06-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/Axis.java: New.
+	  This class should represent an axis in a chart.
+
+	* src/main/java/de/intevation/flys/client/shared/model/ChartInfo.java:
+	  New. This class is used by the ChartInfoService to retrieve more
+	  information about a Chart that just the Transform2D object to reproject
+	  points. It currently stores x and y axes and the Transform2D object of a
+	  chart.
+
+	* src/main/java/de/intevation/flys/client/client/services/ChartInfoServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/server/ChartInfoServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/services/ChartInfoService.java:
+	  Changed the return value of this service from Transform2D to ChartInfo.
+	  The Transform2D object is included in this new ChartInfo.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Implements the PanHandler interface. If a PanEvent is fired by the
+	  PanControl, a new chart range is computed using the axes information
+	  provided by the ChartInfo object (that has been retrieved by the
+	  ChartInfoService).
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Added the PanControl to the toolbar.
+
+2011-06-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/event/HasPanHandlers.java,
+	  src/main/java/de/intevation/flys/client/client/event/PanEvent.java,
+	  src/main/java/de/intevation/flys/client/client/event/PanHandler.java:
+	  New. Classes and interfaces necessary for a pan listeners mechanism.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/PanControl.java:
+	  New. A control that enables the user to pan a chart.
+
+	* src/main/webapp/images/pan.png,
+	  src/main/webapp/images/pan_Selected.png: Icons for the PanControl.
+
+	* 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
+	  links to the pan icons.
+
+2011-06-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/CSVExportServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/CSVExportService.java,
+	  src/main/java/de/intevation/flys/client/server/CSVExportServiceImpl.java:
+	  Changed the return value of this service from List to List<String[]>.
+	  This helps GWT creating JavaScript objects.
+
+	* src/main/java/de/intevation/flys/client/client/ui/TableDataPanel.java:
+	  Adjusted the return value of the CSVExportService and added a warning
+	  dialog if an error occured while fetching those data.
+
+2011-06-07  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ChartServiceHelper.java:
+	  New. This class helps generating the attribute document that is used for
+	  generating charts.
+
+	* src/main/java/de/intevation/flys/client/client/services/ChartInfoServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/ChartInfoService.java,
+	  src/main/java/de/intevation/flys/client/server/ChartInfoServiceImpl.java,
+	* src/main/java/de/intevation/flys/client/server/ChartOutputServiceImpl.java:
+	  Removed the code to create the chart attribute document. This work is
+	  done in the ChartServiceHelper.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Added a method that returns all attribute that should be used for the
+	  chart creation. Currently, there are width, height, x and y ranges.
+
+2011-06-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Repaired broken zoom-out control. We will now zoom out for 10% of the
+	  axis range. E.g. range before = 2 - 10 , range after = 1.2 - 10.8
+
+2011-06-06  Ingo Weinzierl <ingo@intevation.de>
+
+	  flys/issue131 (Anzeige der Position bei großen Zahlen ungenau)
+
+	* src/main/java/de/intevation/flys/client/server/ChartInfoServiceImpl.java:
+	  Repaired a broken XPath expression for the chart attributes.
+
+2011-06-06  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ZoomboxControl.java:
+	  Added a handler that listens to mouse out events. If the mouse moves out
+	  of the chart area, the current zoom operation is canceled. The zoombox
+	  and the coordinates are reset.
+
+2011-06-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Added a method that allows zooming out for a specified factor (between
+	  0-100).
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Added a button to zoom out 10%.
+
+	* 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:
+
+	* src/main/webapp/images/zoom-out.png: New. The icon for the zoom-out
+	  control.
+
+2011-06-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  New method to reset the zoom.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Added a button that resets the zoom and triggers an update of the chart.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ZoomboxControl.java:
+	  This control inherits from ImgButton now. The icon displayed for this
+	  control is the same as in the desktop version of this software.
+
+	* 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 links to zoom button images.
+
+	* src/main/webapp/images/zoom-in_Selected.png,
+	  src/main/webapp/images/zoom-1.png,
+	  src/main/webapp/images/zoom-in.png: Images used for the zoom buttons.
+
+2011-06-03  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ChartOutputServiceImpl.java:
+	  Append the selected min and max ranges for x and y axes to the attribute
+	  document. This enables the server to zoom the chart to the specified
+	  view.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  The x and y ranges specified after zooming are stored as instance
+	  variables and used while creating the chart imgage url. The
+	  ChartOutputService will put those ranges into the attribute document for
+	  the chart creation.
+
+2011-06-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Implements the ZoomHandler interface now. Currently, the values are just
+	  received from ZoomboxControl. We still need to refresh the chart with
+	  those zoom values.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Registered the ChartOutputTab as ZoomHandler at the ZoomboxControl.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ZoomboxControl.java:
+	  Bugfix: fire the ZoomEvent when mouse up events occur. The events was
+	  not fired anytime.
+
+2011-06-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/event/HasZoomHandlers.java,
+	  src/main/java/de/intevation/flys/client/client/event/ZoomHandler.java,
+	  src/main/java/de/intevation/flys/client/client/event/ZoomEvent.java:
+	  New. Necessary interfaces and classes for a listener mechanism for zoom
+	  events.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ZoomboxControl.java:
+	  New. A zoombox control that draws - if activated - an semi opaque
+	  rectangle on the top of the observed panel. This control uses mouse
+	  up/down/move events to draw the rectangle. A mouse down event starts the
+	  zoom process, a mouse up event determines the coordinates for the zoom
+	  and fires a ZoomEvent.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Added the ZoomboxControl.
+
+2011-06-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Moved the mouse position panel to the right border of the toolbar panel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/MousePositionPanel.java:
+	  Improved the layout of this HLayout panel. It has a fixed width of 195px
+	  now. The components inside this panel have also fixed width values.
+
+2011-06-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  After the chart panel is resized, we will now fetch a chart info
+	  document using the ChartInfoService. This service lets us update the
+	  current Transform2D - which is used to display mouse position in chart
+	  coordinates and determine mouse clicks for zooming.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Changed the constructor call of the MousePositionPanel. It needs to
+	  ChartOutputTab now.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/MousePositionPanel.java:
+	  Changed the way to compute the chart coordinates. We will now use the
+	  Transform2D object that is returned by the ChartInfoService.
+
+2011-06-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Made the collection object accessible.
+
+2011-06-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ChartInfoServiceImpl.java:
+	  Removed a debug statement.
+
+2011-06-01  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/Transform2D.java: New.
+	  This class transforms coordinates into an other coordinate system based
+	  on specified scale and translation factors.
+
+	* src/main/java/de/intevation/flys/client/client/services/ChartInfoServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/client/services/ChartInfoService.java,
+	  src/main/java/de/intevation/flys/client/server/ChartInfoServiceImpl.java:
+	  New. A service that fetches the chart info document provided by the
+	  artifact server and returns (currently) a Transform2D object.
+
+	* 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_en.properties,
+	  src/main/java/de/intevation/flys/client/client/FLYSConstants.java: Added
+	  an error string that should be displayed if the chart info service
+	  failed.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the chart info service.
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Added a public method that returns the chart panel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/MousePositionPanel.java:
+	  New. A panel that displays the mouse position relative to a element.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  Added the MousePositionPanel.
+
+	  NOTE: The MousePositionPanel does NOT show the correct position and it
+	  does NOT show the x|y coordinates in chart dimension yet!
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Moved the toolbar code into an own class.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartToolbar.java:
+	  New. The toolbar used for chart panels.
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java:
+	  Replaced buttons displayed as labels with real buttons.
+
+	* src/main/java/de/intevation/flys/client/client/ui/MainMenu.java,
+	  src/main/java/de/intevation/flys/client/client/ui/chart/ThemeNavigationPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Replaced label buttons with real buttons.
+
+	* src/main/webapp/images/theme_top.png,
+	  src/main/webapp/images/theme_up.png,
+	  src/main/webapp/images/theme_bottom.png,
+	  src/main/webapp/images/theme_down.png: New. Images used in the
+	  ThemeNavigationPanel for buttons to move themes.
+
+	* 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 string for the databasket and pathes for the icons used in the
+	  ThemeNavigationPanel.
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/event/HasOutputParameterChangeHandlers.java,
+	  src/main/java/de/intevation/flys/client/client/event/OutputParameterChangeHandler.java,
+	  src/main/java/de/intevation/flys/client/client/event/OutputParameterChangeEvent.java:
+	  Interface and class to realize a listener mechanism for output parameter
+	  changes.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  Implements the OutputParameterChangeHandler interface. After the output
+	  parameter of the chart have changed, the chart is refreshed.
+
+	  NOTE: The panel is disabled right before the RPC call to replace the
+	  collection's attribute is executed. It is enabled after the RPC call has
+	  finished.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Implements the HasOutputParameterChangeHanders interface. The event is
+	  fired if there are changes in the chart theme control panel.
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/CollectionAttributeServiceImpl.java:
+	  Connected this service with the artifact server. Modifications of a
+	  Collection will be stored in the artifact database now.
+
+	* src/main/java/de/intevation/flys/client/server/CollectionHelper.java:
+	  New. This helper is currently used to create the attribute document of a
+	  collection based on a Collection itself. This helper now takes the
+	  attributes of the Collection and transforms those values into XML.
+
+2011-05-27  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/Theme.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultTheme.java:
+	  Added a setter method to set the 'active' attribute if a theme.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  After editing themes in the theme control panel will now modify the
+	  themes stored at the collection as well.
+
+2011-05-26  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/Theme.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultTheme.java:
+	  Added further methods to set the position of a theme to a new value and
+	  to compare two theme objects.
+
+	* src/main/java/de/intevation/flys/client/shared/model/ThemeList.java:
+	  Added a method to set the position of a theme to a new value. Based on
+	  the new position, all other themes in this list are moved up or down.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  The actions in the navigation panel have now an effect on the order of
+	  the themes in the theme list. The selected theme/s is/are moved to
+	  top/bottom or just a single field up/down.
+
+	  NOTE: The order still have no effect on the chart rendering process.
+
+2011-05-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/event/OnMoveHandler.java,
+	  src/main/java/de/intevation/flys/client/client/event/OnMoveEvent.java:
+	  New. Necessary classes and interfaces for a listener mechanism that is
+	  used to inform handlers about a movement. E.g. the movement of a theme.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ThemeNavigationPanel.java:
+	  New. A panel that consist of four buttons that throw OnMoveEvents after
+	  pushing it.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  In addition, it implements the OnMoveHandler interface to listen to move
+	  events.
+
+	  NOTE: We need to implement the code that moves themes up/down.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Set the minimum width of the theme panel to 200px.
+
+2011-05-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/services/CollectionAttributeService.java,
+	  src/main/java/de/intevation/flys/client/client/services/CollectionAttributeServiceAsync.java,
+	  src/main/java/de/intevation/flys/client/server/CollectionAttributeServiceImpl.java:
+	  New. This service should be used to modify/update the attribute of a
+	  collection.
+
+	  NOTE: The update process needs to be implemented! Currently, the only
+	  thing this service does is, that is waits 5sec and fetches a new
+	  description right after this ;-)
+
+	* src/main/java/de/intevation/flys/client/shared/model/ThemeList.java:
+	  Added methods to remove and add themes.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  Changes in the theme panel will now trigger the update process of the
+	  collection's attribute using the CollectionAttributeService.
+
+	* src/main/webapp/WEB-INF/web.xml: Registered the new service.
+
+2011-05-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/DefaultTheme.java:
+	  Removed a bug that caused that the artifact of a default theme is never
+	  set.
+
+2011-05-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/DescribeCollectionServiceImpl.java:
+	  Parse the theme lists - contained in the attribute of the collection -
+	  and add those lists to the collection. The lists are retrievable using
+	  the method getThemeList(String outName).
+
+	* src/main/java/de/intevation/flys/client/shared/model/Theme.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultTheme.java:
+	  The interface and its default implementation of a theme. A theme
+	  represents a curve in a chart or a layer in a map. The default
+	  implementation knows about the facet, the position, the status and the
+	  artifact this theme belongs to.
+
+	* src/main/java/de/intevation/flys/client/shared/model/ThemeList.java:
+	  New. A ThemeList is a wrapper for themes and should provide some
+	  functions to process regularly used operations on a list of themes.
+
+	* 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 constructor that takes a ThemeList as well and a method to
+	  retrieve a ThemeList based on the name of the output.
+
+	* src/main/java/de/intevation/flys/client/shared/model/FacetRecord.java:
+	  This record no longer stores facets but themes.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  Modified the process of adding new themes to the theme grid.
+
+2011-05-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/FacetRecord.java:
+	  New. This ListRecord is used to display facets in grids.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartThemePanel.java:
+	  New. This panel contains elements to control the themes of a chart.
+
+	* 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 ChartThemePanel.
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  The left side of this panel is filled with the ChartThemePanel now.
+
+2011-05-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/meta/Converter.java,
+	  src/main/java/de/intevation/flys/client/server/MetaDataServiceImpl.java,
+	  src/main/java/de/intevation/flys/client/client/ui/MapSelection.java,
+	  src/main/java/de/intevation/flys/client/client/ui/SingleLocationPanel.java,
+	  src/main/java/de/intevation/flys/client/client/ui/ModuleSelection.java:
+	  Removed needless imports.
+
+2011-05-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/chart: A new package
+	  for all chart relevant classes.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ChartOutputTab.java,
+	  src/main/java/de/intevation/flys/client/client/ui/chart/ChartOutputTab.java:
+	  Moved the ChartOutputTab to the chart package.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java:
+	  Adapted imports - added ChartOutputTab that has been moved to chart
+	  subpackage.
+
+2011-05-25  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Bugfix: We return the inserted values only in getData(). We do not
+	  return Data objects with no value. This prevents NullPointerExceptions
+	  that occur if no location has been inserted - e.g. the case if we
+	  selected a range.
+
+2011-05-24  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-91
+	  ISSUE-40 (part II/II)
+
+	* src/main/java/de/intevation/flys/client/client/ui/WQAdaptedInputPanel.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/SingleLocationPanel.java:
+	  Read default values in those panels and fill the form items with that
+	  data.
+	  
+2011-05-24  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:
+	  Added i18n error messages.
+
+2011-05-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Take care on empty Data objects while initializing default values.
+
+2011-05-24  Ingo Weinzierl <ingo@intevation.de>
+
+	ISSUE-85 (part II/III)
+
+	* src/main/java/de/intevation/flys/client/client/ui/LocationDistancePanel.java:
+	  Repaired this broken panel. Now, we distinguish between the two modes
+	  'range' and 'locations'.
+
+2011-05-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/server/ArtifactDescriptionFactory.java:
+	  Read the default values for Data objects from DESCRIBE.
+
+2011-05-24  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/shared/model/Data.java,
+	  src/main/java/de/intevation/flys/client/shared/model/DefaultData.java:
+	  A data item can now have a default value and a default label.
+
+2011-05-23  Ingo Weinzierl <ingo@intevation.de>
+
+	  ISSUE-62 (part I/II)
+
+	* src/main/java/de/intevation/flys/client/client/event/OutputModesChangeEvent.java,
+	  src/main/java/de/intevation/flys/client/client/event/HasOutputModesChangeHandlers.java,
+	  src/main/java/de/intevation/flys/client/client/event/OutputModesChangeHandler.java:
+	  Classes and interfaces that are necessary to build up a listener
+	  mechanism for output mode changes.
+
+	* src/main/java/de/intevation/flys/client/client/ui/CollectionView.java::
+	  Implements the HasOutputModesChangeHandlers and OutputModesChangeHandler
+	  interfaces.
+
+	* src/main/java/de/intevation/flys/client/client/ui/ParameterList.java:
+	  Implements the OutputModesChangeHandler interface which enables us to
+	  update the export panel even after the outs of an artifact have changed.
+
+
+
+2011-05-20  Ingo Weinzierl <ingo@intevation.de>
+
+	* src/main/java/de/intevation/flys/client/client/config.xml:
+	  Reverted to default config; unintentional change in previous commit
+
+2011-05-20  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 missing i18n strings for duration curve export.
+
+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>
+
+	* 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:15 2012 +0200
@@ -0,0 +1,158 @@
+2011-06-27  RELEASE 2.4
+
+    NEW:
+
+        * Introduced:
+          - a theme panel on the left of the chart. It allows to change
+            the state and order of the themes in the chart.
+          - a panel that displays the current mouse position in chart
+            coordinates.
+          - a Zoombox-Control that allows zooming in charts via zoombox.
+          - a control to zoom out for 10%.
+          - a control to zoom to the max extent of the chart.
+          - a control to pan charts.
+          - a panel that displays reports generated by calculations.
+          - a client side validation of user input.
+
+        * Improved the project list. The user can now rename projects and set
+          favorite projects that will never be deleted.
+
+        * Improved the chart output service to support zoom values.
+
+        * Added new services to:
+          - set the Collection's attribute to a new value
+          - set the name of a project
+          - set the time-to-live of a project
+          - delete a project
+          - retrieve meta information of the current chart
+            This service returns a ChartInfo object that stores information about
+            the axes ranges, data ranges and a transformation matrix to transform
+            image coordinates into chart coordinates
+          - fetch calculation reports
+
+        * flys/issue40 Input fields will show former selected values.
+
+    FIXES:
+
+        * flys/issue21 Projects cannot be opened twice but if a projects is
+          already open, it is brought to foreground.
+
+        * flys/issue22 Introduced a polling for user projects. Dead projects are
+          automatically removed.
+
+        * flys/issue43 I18N adjustments.
+
+        * flys/issue85 Location/Distance panel now distinguishes between these
+          two modes. Two (or more) locations will no longer end up in a distance.
+
+        * flys/issue88 I18N adjustments in the location/distance panel.
+
+        * flys/issue91 The WQ panel's initial mode is Q.
+
+        * flys/issue103 Append units in WQ panel.
+
+        * flys/issue104 The selected WQ mode is displayed in the static panel
+          now.
+
+        * flys/issue117 DoubleArrayPanel and DoubleRangePanel will no longer
+          return duplicate values.
+
+        * flys/issue118 The WQ panel has three modes now: W at gauge, Q at
+          gauge and Q free.
+
+        * flys/issue122 The values in the table displaying W values are no
+          longer selectable.
+
+        * flys/issue132 The initial location/distance mode is now distance.
+
+        * flys/issue139 Fixed NPE while initialising user values in the adapted
+          WQ panel.
+
+        * flys/issue140 Splitted the table - displaying W, Q and D values - up
+          in two tables for W (no longer selectable) and for QD.
+
+        * flys/issue156 The report panel is not displayed as tab besides the
+          chart tabs.
+
+        * flys/issue165 Limit the length of new project names to 50 characters.
+
+        * flys/issue170 Fixed broken initial chart info.
+
+
+
+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:15 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:15 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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,172 @@
+<?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>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+      <version>2.6</version>
+    </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:15 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:15 2012 +0200
@@ -0,0 +1,110 @@
+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();
+    }
+
+
+    /**
+     * Returns the integer configured at
+     * <i>/config/projectlist/update-interval/text()</i> or -1 if an error
+     * occured or no such option is defined.
+     *
+     * @return the update interval of the project list.
+     */
+    public int getProjectListUpdateInterval() {
+        Node projectlist = config.getElementsByTagName("projectlist").item(0);
+
+        if (projectlist == null) {
+            return -1;
+        }
+
+        Node interval = config.getElementsByTagName("update-interval").item(0);
+
+        if (interval == null) {
+            return -1;
+        }
+
+        String value = interval.getFirstChild().getNodeValue();
+
+        return value != null ? Integer.valueOf(value) : -1;
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,444 @@
+package de.intevation.flys.client.client;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import com.google.gwt.core.client.EntryPoint;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.event.shared.UmbrellaException;
+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.Window;
+import com.smartgwt.client.widgets.HTMLPane;
+import com.smartgwt.client.widgets.layout.VLayout;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.events.CloseClickHandler;
+import com.smartgwt.client.widgets.events.CloseClientEvent;
+
+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.event.CollectionChangeEvent;
+import de.intevation.flys.client.client.event.CollectionChangeHandler;
+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, CollectionChangeHandler {
+
+    /** 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 list is used to track the opened projects.*/
+    protected List<String> openProjects;
+
+
+    /**
+     * This is the entry point method.
+     */
+    public void onModuleLoad() {
+        openProjects = new ArrayList<String>();
+
+        GWT.setUncaughtExceptionHandler(new GWT.UncaughtExceptionHandler() {
+            public void onUncaughtException(Throwable e) {
+                showWarning(e);
+            }
+        });
+
+        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();
+            }
+        });
+    }
+
+
+    public void showWarning(Throwable e) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("<tt>");
+
+        if (e instanceof UmbrellaException) {
+            UmbrellaException u = (UmbrellaException) e;
+            Set<Throwable> throwables = u.getCauses();
+
+            for (Throwable t: throwables) {
+                sb.append(t.getLocalizedMessage());
+                sb.append("<br>");
+            }
+        }
+        else {
+            sb.append(e.getLocalizedMessage());
+        }
+
+        sb.append("</tt>");
+
+        Window w = new Window();
+        w.setTitle(MSG.unexpected_exception());
+        w.setWidth(550);
+        w.setHeight(300);
+        w.centerInPage();
+        w.setCanDragResize(true);
+
+        HTMLPane p = new HTMLPane();
+        p.setContents(sb.toString());
+
+        w.addItem(p);
+        w.show();
+    }
+
+
+    /**
+     * 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 the projects workspace that contains all project windows.
+     *
+     * @return the FLYSWorkspace.
+     */
+    public FLYSWorkspace getWorkspace() {
+        return workspace;
+    }
+
+
+    /**
+     * 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("new-project", view);
+
+        view.addCollectionChangeHandler(getProjectList());
+    }
+
+
+    protected void lockProject(String uuid) {
+        if (isProjectLocked(uuid)) {
+            return;
+        }
+
+        openProjects.add(uuid);
+    }
+
+
+    protected void unlockProject(String uuid) {
+        openProjects.remove(uuid);
+    }
+
+
+    protected boolean isProjectLocked(String uuid) {
+        return openProjects.contains(uuid);
+    }
+
+
+    public void openProject(final String collectionID) {
+        if (collectionID == null) {
+            return;
+        }
+
+        if (isProjectLocked(collectionID)) {
+            workspace.bringUp(collectionID);
+            return;
+        }
+
+        lockProject(collectionID);
+
+        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) {
+                                unlockProject(collectionID);
+                                SC.warn(MSG.getString(caught.getMessage()));
+                            }
+
+                            public void onSuccess(Artifact artifact) {
+                                CollectionView view = new CollectionView(
+                                    FLYS.this, collection, artifact);
+
+                                view.addCollectionChangeHandler(
+                                    getProjectList());
+                                view.addCloseClickHandler(
+                                    new CloseCollectionViewHandler(
+                                        FLYS.this, collectionID));
+
+                                workspace.addView(collectionID, view);
+                            }
+                    });
+
+                }
+        });
+    }
+
+
+    public void closeProject(String uuid) {
+        unlockProject(uuid);
+        workspace.destroyProject(uuid);
+    }
+
+
+    /**
+     * 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.");
+                }
+        });
+    }
+
+
+    public void onCollectionChange(CollectionChangeEvent event) {
+        Collection oldC = event.getOldValue();
+
+        if (oldC == null) {
+            Collection newC = event.getNewValue();
+            lockProject(newC.identifier());
+        }
+    }
+
+
+
+    /**
+     * This CloseClickHandler is used to remove lock on a specific Collection so
+     * that is might be opened again.
+     */
+    public class CloseCollectionViewHandler implements CloseClickHandler {
+        protected FLYS   flys;
+        protected String uuid;
+
+        public CloseCollectionViewHandler(FLYS flys, String uuid) {
+            this.flys = flys;
+            this.uuid = uuid;
+        }
+
+        public void onCloseClick(CloseClientEvent event) {
+            flys.closeProject(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-client/src/main/java/de/intevation/flys/client/client/FLYSConstants.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,293 @@
+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 unexpected_exception();
+
+    String title();
+
+    String fullname();
+
+    String user();
+
+    String guest();
+
+    String projects();
+
+    String open_project();
+
+    String delete_project();
+
+    String rename_project();
+
+    String manage_projects();
+
+    String favorite_tooltip();
+
+    String favorite_tooltip_meaning();
+
+    String projectlist_creationTime();
+
+    String projectlist_title();
+
+    String projectlist_favorite();
+
+    String really_delete();
+
+    String project_name_too_long();
+
+    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 databasket();
+
+    String theme_top();
+
+    String theme_up();
+
+    String theme_down();
+
+    String theme_bottom();
+
+    String zoom_all();
+
+    String zoom_in();
+
+    String zoom_out();
+
+    String pan();
+
+    String fix();
+
+    String next();
+
+    String river_km();
+
+    String location_distance_state();
+
+    String location();
+
+    String locations();
+
+    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 wqQGauge();
+
+    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 waterlevel_report();
+
+    String computed_dischargecurve_at_export();
+
+    String discharge_longitudinal_section_export();
+
+    String discharge_longitudinal_section_report();
+
+    String computed_dischargecurve_export();
+
+    String computed_dischargecurve_report();
+
+    String durationcurve_export();
+
+    String durationcurve_report();
+
+    String dataexport();
+
+    String csv();
+
+    String wst();
+
+    String at();
+
+    String chart_themepanel_header_themes();
+
+    // ERRORS
+
+    String error_read_minmax_values();
+
+    String error_validate_range();
+
+    String error_validate_lower_range();
+
+    String error_validate_upper_range();
+
+    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();
+
+    String error_chart_info_service();
+
+    String error_invalid_double_value();
+
+    String bottom_edge();
+
+    String top_edge();
+}
+// 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:15 2012 +0200
@@ -0,0 +1,146 @@
+title = FLYS-3.0
+fullname = Flusshydrologische Software
+user = User:
+guest = guest
+logout = Logout
+projects = Projects
+open_project = Open Project
+delete_project = Delete Project
+rename_project = Rename Project
+manage_projects = Manage Projects
+favorite_tooltip = Permanently store?
+favorite_tooltip_meaning = Golden star: stored
+projectlist_creationTime = Creation time
+projectlist_title = Title
+projectlist_favorite = Permanent
+really_delete = Do you really want to delete this project?
+project_name_too_long = The entered project name is too long. Max length is $LEN characters.
+switch_language = German
+info = Info
+warning = Attention
+warning_language = Choosing a new language starts a new session. All 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
+databasket = Databasket
+fix = TODO FIX ANALYSE TODO
+next = Next
+location_distance_state = Choose calculation location(s) / range [km]
+location = Location(s)
+locations = Locations
+distance = Range
+unitFrom = km
+unitTo = km a
+unitWidth = m
+unitLocation = km
+wrongFormat = Wrong format
+description = Description
+from = From
+to = To
+riverside = Riverside
+bottom_edge = Bottom edge
+top_edge = Top edge
+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\u00b3/s]
+wqQGauge = Q [m\u00b3/s] at Gauge
+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
+theme_top = images/theme_top.png
+theme_up = images/theme_up.png
+theme_down = images/theme_down.png
+theme_bottom = images/theme_bottom.png
+zoom_all = images/zoom-1.png
+zoom_in = images/zoom-in.png
+zoom_out = images/zoom-out.png
+pan = images/pan.png
+
+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
+waterlevel_report = Waterlevel Report
+computed_dischargecurve_export = Discharge Curve Export
+computed_dischargecurve_report = Discharge Curve Report
+discharge_longitudinal_section_export = Discharge Longitudinal Section Export
+discharge_longitudinal_section_report = Discharge Longitudinal Section Report
+durationcurve_export = Duration Curve Export
+durationcurve_report = Duration Curve Report
+dataexport = Datenexport
+csv = CSV
+wst = WST
+at = AT
+computed_dischargecurve_at_export = AT Export
+chart_themepanel_header_themes = Theme
+
+unexpected_exception = There occured an unexpected exception
+error_read_minmax_values = Error while reading min/max values for the location input.
+error_validate_range = The value $1 needs to be smaller than $3 and bigger than $2.
+error_validate_lower_range = The lower value $1 needs to be bigger than $2.
+error_validate_upper_range = The upper value $1 needs to be smaller than $2.
+error_empty_state = Required inputs are missing.
+error_invalid_double_value = Wrong format for a floating point number.
+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_chart_info_service = Could not fetch meta information of the chart.
+
+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:15 2012 +0200
@@ -0,0 +1,146 @@
+title = FLYS-3.0
+fullname = Flusshydrologische Software
+user = Benutzer:
+guest = Gast
+logout = Abmelden
+projects = Projekte
+open_project = Projekt \u00f6ffnen
+delete_project = Projekt l\u00f6schen
+rename_project = Projekt umbenennen
+manage_projects = Projekte verwalten
+favorite_tooltip = Dauerhaft speichern?
+favorite_tooltip_meaning = Goldener Stern: gespeichert
+projectlist_creationTime = Anlegezeitpunkt
+projectlist_title = Titel
+projectlist_favorite = Dauerhaft
+really_delete = Wollen Sie dieses Projekt wirklich l\u00f6schen?
+project_name_too_long = Der eingegebene Projektname ist zu lang. Die maximale L\u00e4nge betr\u00e4gt $LEN Zeichen.
+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\u00e4sser
+winfo = WINFO
+minfo = MINFO
+map = Neue Karte
+chart = Neues Diagramm
+databasket = Datenkorb
+fix = Fixierungsanalyse
+next = Weiter
+location_distance_state = Berechnungsort(e) / strecke w\u00e4hlen [km]
+location = Ort(e)
+locations = Orte
+distance = Strecke
+unitFrom = km&nbsp;-
+unitTo = km a
+unitWidth = m
+unitLocation = km
+wrongFormat = Falsches Format
+description = Beschreibung
+from = von
+to = bis
+bottom_edge = Unterkante
+top_edge = Oberkante
+riverside = Flussseite
+name = Name
+type = Typ
+wq_value = W/Q/D
+river_km = Fluss-Km
+
+wqTitle = Eingabe f\u00fcr W/Q Daten
+wqadaptedTitle = Eingabe f\u00fcr W/Q Daten
+calcTableTitle = Berechnungsausgabe
+wqW = W am Pegel [cm]
+wqQ = Q [m\u00b3/s]
+wqQGauge = Q [m\u00b3/s] am Pegel (Kennzeichnender Abfluss)
+wqSingle = Einzelwerte
+wqRange = Wertebereich
+unitWNN = W [NN+m]
+unitWSingle = cm
+unitWFrom = cm&nbsp;-
+unitWTo = cm&nbsp;a
+unitWStep = cm
+unitQSingle = m\u00b3/s
+unitQFrom = m\u00b3/s&nbsp;-
+unitQTo = m\u00b3/s&nbsp;a
+unitQStep = m\u00b3/s
+footerHome = Home
+footerContact = Kontakt
+footerImpressum = Impressum
+
+buttonNext = \u00dcbernehmen
+imageBack = images/back_de.png
+imageSave = images/save.gif
+theme_top = images/theme_top.png
+theme_up = images/theme_up.png
+theme_down = images/theme_down.png
+theme_bottom = images/theme_bottom.png
+zoom_all = images/zoom-1.png
+zoom_in = images/zoom-in.png
+zoom_out = images/zoom-out.png
+pan = images/pan.png
+
+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
+waterlevel_report = Wasserstand/Wasserspiegellagen Bericht
+computed_dischargecurve_export = Abflusskurven Export
+computed_dischargecurve_report = Abflusskurven Bericht
+discharge_longitudinal_section_export = Abflussl\u00e4ngsschnitt Export
+discharge_longitudinal_section_report = Abflussl\u00e4ngsschnitt Bericht
+durationcurve_export = Dauerzahlen Export
+durationcurve_report = Dauerzahlen Bereicht
+dataexport = Datenexport
+csv = CSV
+wst = WST
+at = AT
+computed_dischargecurve_at_export = AT Export
+chart_themepanel_header_themes = Thema
+
+unexpected_exception = Ein unerwarteter Fehler ist aufgetreten
+error_read_minmax_values = Fehler beim Lesen der min/max Werte. Es kann keine Validierung der eingegebenen Strecke durchgef\u00fchrt werden.
+error_validate_range = Der Wert $1 muss kleiner als $3 und gr\u00f6\u00dfer als $2 sein.
+error_validate_lower_range = Der untere Wert $1 muss gr\u00f6\u00dfer sein als $2.
+error_validate_upper_range = Der obere Wert $1 muss kleiner sein als $2.
+error_empty_state = Es wurden nicht alle ben\u00d6tigten Daten eingegeben.
+error_invalid_double_value = Falsches Format f\u00fc eine Flie\u00dfkommazahl.
+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_chart_info_service = Meta Informationen des Diagramms konnten nicht geladen werden.
+
+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:15 2012 +0200
@@ -0,0 +1,144 @@
+title = FLYS-3.0
+fullname = Flusshydrologische Software
+user = User:
+guest = guest
+logout = Logout
+projects = Projects
+open_project = Open Project
+delete_project = Delete Project
+rename_project = Rename Project
+manage_projects = Manage Projects
+favorite_tooltip = Permanently store?
+favorite_tooltip_meaning = Golden star: stored
+projectlist_creationTime = Creation time
+projectlist_title = Title
+projectlist_favorite = Permanent
+really_delete = Do you really want to delete this project?
+project_name_too_long = The entered project name is too long. Max length is $LEN characters.
+switch_language = German
+info = Info
+warning = Attention
+warning_language = Choosing a new language starts a new session. All 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
+databasket = Databasket
+fix = TODO FIX ANALYSE TODO
+next = Next
+location_distance_state = Choose calculation location(s) / range [km]
+location = Location(s)
+locations = Locations
+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]
+wqQGauge = Q [m³/s] at Gauge
+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
+theme_top = images/theme_top.png
+theme_up = images/theme_up.png
+theme_down = images/theme_down.png
+theme_bottom = images/theme_bottom.png
+zoom_all = images/zoom-1.png
+zoom_in = images/zoom-in.png
+zoom_out = images/zoom-out.png
+pan = images/pan.png
+
+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
+waterlevel_report = Waterlevel Report
+computed_dischargecurve_export = Discharge Curve Export
+computed_dischargecurve_report = Discharge Curve Report
+discharge_longitudinal_section_export = Discharge Longitudinal Section Export
+discharge_longitudinal_section_report = Discharge Longitudinal Section Report
+durationcurve_export = Duration Curve Export
+durationcurve_report = Duration Curve Bericht
+dataexport = Datenexport
+csv = CSV
+wst = WST
+at = AT
+computed_dischargecurve_at_export = AT Export
+chart_themepanel_header_themes = Theme
+
+unexpected_exception = There occured an unexpected exception
+error_read_minmax_values = Error while reading min/max values for the location input.
+error_validate_range = The value $1 needs to be smaller than $3 and bigger than $2.
+error_validate_lower_range = The lower value $1 needs to be bigger than $2.
+error_validate_upper_range = The upper value $1 needs to be smaller than $2.
+error_empty_state = Required inputs are missing.
+error_invalid_double_value = Wrong format for a floating point number.
+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_chart_info_service = Could not fetch meta information of the chart.
+
+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/FLYSImages.java	Fri Sep 28 12:14:15 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:15 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:15 2012 +0200
@@ -0,0 +1,8 @@
+<config>
+    <server>http://localhost:8181</server>
+
+    <projectlist>
+        <!-- The interval to update the user's projects (in ms) -->
+        <update-interval>300000</update-interval>
+    </projectlist>
+</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:15 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:15 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:15 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/HasOutputModesChangeHandlers.java	Fri Sep 28 12:14:15 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 HasOutputModesChangeHandlers {
+
+    /**
+     * Adds a new OutputModesChangeHandler.
+     *
+     * @param handler The new OutputModesChangeHandler.
+     */
+    public void addOutputModesChangeHandler(OutputModesChangeHandler 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/HasOutputParameterChangeHandlers.java	Fri Sep 28 12:14:15 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 HasOutputParameterChangeHandlers {
+
+    /**
+     * Registers a new OutputParameterChangeHandler.
+     *
+     * @param handler The new handler.
+     */
+    void addOutputParameterChangeHandler(OutputParameterChangeHandler 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/HasPanHandlers.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,11 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface HasPanHandlers {
+
+    void addPanHandler(PanHandler 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:15 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:15 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:15 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/HasZoomHandlers.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,11 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface HasZoomHandlers {
+
+    void addZoomHandler(ZoomHandler 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/OnMoveEvent.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,27 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class OnMoveEvent {
+
+    public static final int TOP    = 0;
+    public static final int UP     = 1;
+    public static final int DOWN   = 2;
+    public static final int BOTTOM = 3;
+
+
+    protected int type;
+
+
+    public OnMoveEvent(int type) {
+        this.type = type;
+    }
+
+
+    public int getType() {
+        return type;
+    }
+}
+// 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/OnMoveHandler.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,11 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface OnMoveHandler {
+
+    void onMove(OnMoveEvent 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/OutputModesChangeEvent.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,26 @@
+package de.intevation.flys.client.client.event;
+
+import de.intevation.flys.client.shared.model.OutputMode;
+
+
+/**
+ * An event that is thrown if the output modes of a collection or an artifact
+ * have changed.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class OutputModesChangeEvent {
+
+    protected OutputMode[] outs;
+
+
+    public OutputModesChangeEvent(OutputMode[] outs) {
+        this.outs = outs;
+    }
+
+
+    public OutputMode[] getOutputModes() {
+        return outs;
+    }
+}
+// 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/OutputModesChangeHandler.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,18 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * Handler interface for OutputModesChangeEvent events.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface OutputModesChangeHandler {
+
+    /**
+     * Called when a OutputModesChangeEvent is fired.
+     *
+     * @param event The OutputModesChangeEvent.
+     */
+    public void onOutputModesChange(OutputModesChangeEvent 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/OutputParameterChangeEvent.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,12 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class OutputParameterChangeEvent {
+
+    public OutputParameterChangeEvent() {
+    }
+}
+// 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/OutputParameterChangeHandler.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,17 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface OutputParameterChangeHandler {
+
+    /**
+     * Called when the parameter of an output have changed. E.g. when the state
+     * of themes in a theme panel changed.
+     *
+     * @param event The event that is fired.
+     */
+    void onOutputParameterChanged(OutputParameterChangeEvent 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/PanEvent.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,28 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class PanEvent {
+
+    protected int[] startPos;
+    protected int[] endPos;
+
+
+    public PanEvent(int[] startPos, int[] endPos) {
+        this.startPos = startPos;
+        this.endPos   = endPos;
+    }
+
+
+    public int[] getStartPos() {
+        return startPos;
+    }
+
+
+    public int[] getEndPos() {
+        return endPos;
+    }
+}
+// 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/PanHandler.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,11 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface PanHandler {
+
+    void onPan(PanEvent 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/ParameterChangeEvent.java	Fri Sep 28 12:14:15 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:15 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:15 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:15 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:15 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:15 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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/flys-client/src/main/java/de/intevation/flys/client/client/event/ZoomEvent.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,43 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ZoomEvent {
+
+    protected int xmin;
+    protected int ymin;
+
+    protected int xmax;
+    protected int ymax;
+
+
+    public ZoomEvent(int xmin, int ymin, int xmax, int ymax) {
+        this.xmin = xmin;
+        this.xmax = xmax;
+        this.ymin = ymin;
+        this.ymax = ymax;
+    }
+
+
+    public int getStartX() {
+        return xmin;
+    }
+
+
+    public int getEndX() {
+        return xmax;
+    }
+
+
+    public int getStartY() {
+        return ymin;
+    }
+
+
+    public int getEndY() {
+        return ymax;
+    }
+}
+// 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/ZoomHandler.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,11 @@
+package de.intevation.flys.client.client.event;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface ZoomHandler {
+
+    void onZoom(ZoomEvent 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:15 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:15 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:15 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:15 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:15 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:15 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:15 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<String[]> 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:15 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<String[]>> 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/ChartInfoService.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,26 @@
+package de.intevation.flys.client.client.services;
+
+import java.util.Map;
+
+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.ChartInfo;
+import de.intevation.flys.client.shared.model.Collection;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("chart-info")
+public interface ChartInfoService extends RemoteService {
+
+    ChartInfo getChartInfo(
+        Collection          collection,
+        String              url,
+        String              locale,
+        String              type,
+        Map<String, String> attr)
+    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/ChartInfoServiceAsync.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,24 @@
+package de.intevation.flys.client.client.services;
+
+import java.util.Map;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import de.intevation.flys.client.shared.model.ChartInfo;
+import de.intevation.flys.client.shared.model.Collection;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface ChartInfoServiceAsync {
+
+    public void getChartInfo(
+        Collection          collection,
+        String              url,
+        String              locale,
+        String              type,
+        Map<String, String> attr,
+        AsyncCallback<ChartInfo> 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/CollectionAttributeService.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,19 @@
+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;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("collection-attribute")
+public interface CollectionAttributeService extends RemoteService {
+
+    Collection update(Collection collection, 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/CollectionAttributeServiceAsync.java	Fri Sep 28 12:14:15 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 CollectionAttributeServiceAsync {
+
+    void update(
+        Collection collection,
+        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/CreateCollectionService.java	Fri Sep 28 12:14:15 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:15 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/DeleteCollectionService.java	Fri Sep 28 12:14:15 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.ServerException;
+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("delete-collection")
+public interface DeleteCollectionService extends RemoteService {
+
+    /**
+     * Deletes a collection.
+     *
+     * @param collection The Collection that should be deleted.
+     * @param url        The url of the artifact server.
+     */
+    void delete(Collection collection, String url)
+    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/DeleteCollectionServiceAsync.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,18 @@
+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 DeleteCollectionServiceAsync {
+
+    public void delete(
+        Collection          collection,
+        String              url,
+        AsyncCallback<Void> 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:15 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:15 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:15 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:15 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:15 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:15 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:15 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:15 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/ReportService.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,16 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.RemoteService;
+import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
+
+@RemoteServiceRelativePath("report")
+public interface ReportService
+extends          RemoteService
+{
+    String report(
+        String collectionId,
+        String url,
+        String locale,
+        String out);
+}
+// 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/ReportServiceAsync.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,14 @@
+package de.intevation.flys.client.client.services;
+
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+public interface ReportServiceAsync
+{
+    void report(
+        String collectionId,
+        String url,
+        String locale,
+        String out,
+        AsyncCallback<String> 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/RiverService.java	Fri Sep 28 12:14:15 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:15 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/SetCollectionNameService.java	Fri Sep 28 12:14:15 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.ServerException;
+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("set-collectionname")
+public interface SetCollectionNameService extends RemoteService {
+
+    /**
+     * Set the name of a collection.
+     *
+     * @param collection The Collection that should be extended.
+     * @param url        The url of the artifact server.
+     */
+    void setName(Collection collection, String url)
+    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/SetCollectionNameServiceAsync.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,18 @@
+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 SetCollectionNameServiceAsync {
+
+    public void setName(
+        Collection          collection,
+        String              url,
+        AsyncCallback<Void> 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/SetCollectionTTLService.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,19 @@
+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;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+@RemoteServiceRelativePath("set-collectionttl")
+public interface SetCollectionTTLService extends RemoteService {
+
+    void setTTL(Collection c, String url)
+    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/SetCollectionTTLServiceAsync.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,18 @@
+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 SetCollectionTTLServiceAsync {
+
+    public void setTTL(
+        Collection          collection,
+        String              url,
+        AsyncCallback<Void> 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:15 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:15 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:15 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:15 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:15 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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,256 @@
+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.util.SC;
+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) {
+        List<String> errors = validate();
+        if (errors == null || errors.isEmpty()) {
+            Data[] data = getData();
+            fireStepForwardEvent(new StepForwardEvent(data));
+        }
+        else {
+            StringBuilder sb = new StringBuilder();
+
+            for (String error: errors) {
+                sb.append(error);
+                sb.append("<br>");
+            }
+
+            SC.warn(sb.toString());
+        }
+    }
+
+
+    /**
+     * 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;
+    }
+
+
+    public List<String> validate() {
+        return new ArrayList<String>();
+    }
+
+
+    /**
+     * 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/CollectionView.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,507 @@
+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.events.CloseClientEvent;
+import com.smartgwt.client.widgets.events.CloseClickHandler;
+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.ReportMode;
+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.HasOutputModesChangeHandlers;
+import de.intevation.flys.client.client.event.CollectionChangeEvent;
+import de.intevation.flys.client.client.event.CollectionChangeHandler;
+import de.intevation.flys.client.client.event.OutputModesChangeEvent;
+import de.intevation.flys.client.client.event.OutputModesChangeHandler;
+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;
+import de.intevation.flys.client.client.ui.chart.ChartOutputTab;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CollectionView
+extends      Window
+implements   CollectionChangeHandler, HasCollectionChangeHandlers,
+             OutputModesChangeHandler, HasOutputModesChangeHandlers,
+             ParameterChangeHandler, CloseClickHandler
+{
+    /** 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 list of ValueChangeHandlers.*/
+    protected List<OutputModesChangeHandler> outHandlers;
+
+    /** 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.outHandlers   = new ArrayList<OutputModesChangeHandler>();
+        this.layout        = new VLayout();
+        this.parameterList = new ParameterList(
+            flys, this, messages.new_project());
+
+        addCollectionChangeHandler(this);
+        addCollectionChangeHandler(parameterList);
+        addCollectionChangeHandler(flys);
+        addOutputModesChangeHandler(this);
+        addOutputModesChangeHandler(parameterList);
+        addCloseClickHandler(this);
+
+        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.outHandlers   = new ArrayList<OutputModesChangeHandler>();
+        this.layout        = new VLayout();
+        this.parameterList = new ParameterList(
+            flys,
+            this,
+            messages.getString(artifact.getName()),
+            artifact);
+
+        addCollectionChangeHandler(this);
+        addCollectionChangeHandler(parameterList);
+        addCollectionChangeHandler(flys);
+        addOutputModesChangeHandler(this);
+        addOutputModesChangeHandler(parameterList);
+        addCloseClickHandler(this);
+
+        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 registers a new OutputModesChangeHandler.
+     *
+     * @param handler The new OutputModesChangeHandler.
+     */
+    public void addOutputModesChangeHandler(OutputModesChangeHandler handler) {
+        if (handler != null) {
+            outHandlers.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));
+        }
+    }
+
+
+    protected void fireOutputModesChangeEvent(OutputMode[] outputs) {
+        if (collection == null) {
+            return;
+        }
+
+        for (OutputModesChangeHandler handler: outHandlers) {
+            handler.onOutputModesChange(new OutputModesChangeEvent(outputs));
+        }
+    }
+
+
+    /**
+     * 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;
+    }
+
+
+    /**
+     * Returns the artifact that is used for the parameterization.
+     *
+     * @return the artifact that is used for the parameterization.
+     */
+    public Artifact getArtifact() {
+        return artifact;
+    }
+
+
+    /**
+     * Set the current artifact that is the master of the parameterization.
+     *
+     * @param artifact The new artifact.
+     */
+    public 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();
+
+        fireOutputModesChangeEvent(outs);
+
+        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.
+     */
+    public Collection getCollection() {
+        return collection;
+    }
+
+
+    /**
+     * Set the current collection.
+     *
+     * @param collection The new collection.
+     */
+    protected void setCollection(Collection collection) {
+        if (collection != null && this.collection == null) {
+            flys.getWorkspace().addView(collection.identifier(), this);
+        }
+
+        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();
+        OutputMode[] prepared = new OutputMode[outs.size()];
+
+        int idx = 0;
+        for (String outname: keys) {
+            prepared[idx++] = outs.get(outname);
+        }
+
+        fireOutputModesChangeEvent(prepared);
+
+        updateView();
+    }
+
+
+    public void onOutputModesChange(OutputModesChangeEvent event) {
+        OutputMode[] outs = event.getOutputModes();
+
+        if (outs == null) {
+            return;
+        }
+
+        for (OutputMode out: outs) {
+            addOutputTab(out.getName(), out);
+        }
+    }
+
+
+    /**
+     * 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;
+        }
+
+        if (out instanceof ReportMode) {
+            // we don't want to display report modes at all
+            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));
+            }
+        }
+    }
+
+
+    public void onCloseClick(CloseClientEvent event) {
+        if (collection != null) {
+            flys.closeProject(collection.identifier());
+        }
+        else {
+            hide();
+            destroy();
+        }
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,174 @@
+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.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(convertTree(tree));
+    }
+
+    protected static TreeNode convert(DataCageNode node) {
+        List<DataCageNode> children = node.getChildren();
+
+        TreeNode [] cs;
+
+        if (children != null) {
+            cs = new TreeNode[children.size()];
+
+            for (int i = 0, N = children.size(); i < N; ++i) {
+                cs[i] = convert(children.get(i));
+            }
+        }
+        else {
+            cs = new TreeNode[0];
+        }
+
+        return new TreeNode(node.getName(), cs);
+    }
+
+    protected static Tree convertTree(DataCageTree tree) {
+        DataCageNode root = tree.getRoot();
+
+        List<DataCageNode> children = root.getChildren();
+
+        TreeNode [] cs;
+
+        if (children != null) {
+            cs = new TreeNode[children.size()];
+
+            for (int i = 0, N = children.size(); i < N; ++i) {
+                cs[i] = convert(children.get(i));
+            }
+        }
+        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:15 2012 +0200
@@ -0,0 +1,261 @@
+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 appends a double value to the current list of values.
+     *
+     * @param value A new value.
+     */
+    public void addValue(double value) {
+        NumberFormat f = NumberFormat.getDecimalFormat();
+
+        String current = ti.getValueAsString();
+
+        if (current == null || current.length() == 0) {
+            current = f.format(value);
+        }
+        else {
+            current += " " + f.format(value);
+        }
+
+        ti.setValue(current);
+    }
+
+
+    protected boolean validateForm() {
+        return validateForm(ti);
+    }
+
+
+    /**
+     * 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("\\s+");
+
+        if (parts == null) {
+            return valid;
+        }
+
+        NumberFormat f = NumberFormat.getDecimalFormat();
+        Map errors     = getErrors();
+
+        try {
+            for (String part: parts) {
+
+                if (part.length() == 0) {
+                    continue;
+                }
+
+                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("\\s+");
+
+        if (parts == null) {
+            return null;
+        }
+
+        NumberFormat f = NumberFormat.getDecimalFormat();
+
+        double[] values = new double[parts.length];
+
+        int i = 0;
+        OUTER: for (String part: parts) {
+            if (part.length() == 0) {
+                continue;
+            }
+
+            try {
+                double x = f.parse(part);
+                for (int j = 0; j < i; ++j) {
+                    if (values[j] == x) {
+                        continue OUTER;
+                    }
+                }
+                values[i++] = x;
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing
+            }
+        }
+
+        double [] out = new double[i];
+        System.arraycopy(values, 0, out, 0, i);
+
+        return out;
+    }
+
+
+    /**
+     * 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:15 2012 +0200
@@ -0,0 +1,254 @@
+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));
+    }
+
+
+    public boolean validateForm() {
+        return
+            validateForm(fromItem) &&
+            validateForm(toItem) &&
+            validateForm(stepItem);
+    }
+
+    /**
+     * 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 {
+            if (v == null) {
+                throw new NumberFormatException("empty");
+            }
+
+            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();
+
+        String[] splitted = value.split(" ");
+
+        return f.parse(splitted[0]);
+    }
+
+
+    /**
+     * 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);
+    }
+
+
+    /**
+     * Sets the value of the field with name <i>fieldname</i>.
+     *
+     * @param value The new value.
+     * @param fieldname The name of the field.
+     */
+    public void setDoubleValue(double value, String fieldname) {
+        NumberFormat f = NumberFormat.getDecimalFormat();
+        setValue(fieldname, f.format(value));
+    }
+
+
+    /**
+     * Sets a new start value.
+     *
+     * @param value The new start value.
+     */
+    public void setFrom(double value) {
+        setDoubleValue(value, FIELD_FROM);
+    }
+
+
+    /**
+     * Sets a new end value.
+     *
+     * @param value The new end value.
+     */
+    public void setTo(double value) {
+        setDoubleValue(value, FIELD_TO);
+    }
+}
+// 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:15 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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,80 @@
+package de.intevation.flys.client.client.ui;
+
+import com.smartgwt.client.widgets.Canvas;
+
+import java.util.HashMap;
+import java.util.Map;
+
+
+/**
+ * @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 Map<String, CollectionView> views;
+
+
+    /**
+     * The default constructor creates an empty FLYSWorkspace with no
+     * CollectionViews opened.
+     */
+    public FLYSWorkspace() {
+        views = new HashMap<String, 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(String uuid, CollectionView collectionView) {
+        int num    = views != null ? views.size() : 0;
+        int factor = num % MAX_WINDOWS;
+
+        collectionView.moveTo(factor * WINDOW_OFFSET, factor * WINDOW_OFFSET);
+
+        views.put(uuid, collectionView);
+        addChild(collectionView);
+    }
+
+
+    public void removeProject(String uuid) {
+        views.remove(uuid);
+    }
+
+
+    public void bringUp(String uuid) {
+        CollectionView view = views.get(uuid);
+
+        view.show();
+        view.restore();
+    }
+
+
+    /**
+     * Removes a project from workspace (view) and clears its reference from
+     * hash map.
+     *
+     * @param uuid The project's uuid.
+     */
+    public void destroyProject(String uuid) {
+        CollectionView project = views.get(uuid);
+
+        if (project != null) {
+            removeProject(uuid);
+            project.destroy();
+        }
+    }
+}
+// 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:15 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:15 2012 +0200
@@ -0,0 +1,1150 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.NumberFormat;
+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);
+
+    public static final String FIELD_MODE = "mode";
+
+    /** 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 locations.*/
+    public static final String FIELD_VALUE_LOCATION = "location";
+
+    /** The constant name of the input field to enter distance.*/
+    public static final String FIELD_VALUE_DISTANCE = "distance";
+
+    /** 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);
+
+        Label label   = new Label(MESSAGES.location_distance_state());
+        Canvas widget = createWidget(data);
+        Canvas submit = getNextButton();
+        createDistanceInputPanel();
+
+        initDefaults(data);
+
+        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 (20);
+        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(40);
+        ListGridField to = new ListGridField("to", MESSAGES.to());
+        to.setType(ListGridFieldType.TEXT);
+        to.setWidth(40);
+        ListGridField dside = new ListGridField("riverside",
+                MESSAGES.riverside());
+        dside.setType(ListGridFieldType.TEXT);
+        dside.setWidth(40);
+
+        ListGridField bottom =
+            new ListGridField("bottom", MESSAGES.bottom_edge());
+        bottom.setType(ListGridFieldType.TEXT);
+        bottom.setWidth(30);
+
+        ListGridField top =
+            new ListGridField("top", MESSAGES.top_edge());
+        top.setType(ListGridFieldType.TEXT);
+        top.setWidth(30);
+
+        distanceTable.setFields(
+            addDistance, ddescr, from, to, dside, bottom, top);
+    }
+
+
+    /**
+     * 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 (20);
+
+        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(40);
+        ListGridField loc = new ListGridField("from", MESSAGES.locations());
+        loc.setType(ListGridFieldType.TEXT);
+        loc.setWidth(40);
+
+        ListGridField bottom =
+            new ListGridField("bottom", MESSAGES.bottom_edge());
+        bottom.setType(ListGridFieldType.TEXT);
+        bottom.setWidth(30);
+
+        ListGridField top =
+            new ListGridField("top", MESSAGES.top_edge());
+        top.setType(ListGridFieldType.TEXT);
+        top.setWidth(30);
+
+        locationsTable.setFields(addLocation, ldescr, loc, lside, bottom, top);
+    }
+
+
+    /**
+     * 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 (20);
+        addfrom.setCellIcon (IMAGES.markerGreen ().getURL ());
+
+        ListGridField addto2 = new ListGridField ("", "");
+        addto2.setType (ListGridFieldType.ICON);
+        addto2.setWidth (20);
+        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(40);
+        ListGridField loc = new ListGridField("from", MESSAGES.locations());
+        loc.setType(ListGridFieldType.TEXT);
+        loc.setWidth(40);
+        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);
+                }
+            }
+        });
+        ListGridField bottom =
+            new ListGridField("bottom", MESSAGES.bottom_edge());
+        bottom.setType(ListGridFieldType.TEXT);
+        bottom.setWidth(30);
+
+        ListGridField top =
+            new ListGridField("top", MESSAGES.top_edge());
+        top.setType(ListGridFieldType.TEXT);
+        top.setWidth(30);
+
+        locationDistanceTable.setFields(
+            addfrom, addto2, ldescr, loc, lside, bottom, top);
+    }
+
+
+    public Canvas createOld(DataList dataList) {
+        List<Data> items = dataList.getAll();
+
+        Data       dMode  = getData(items, "ld_mode");
+        DataItem[] dItems = dMode.getItems();
+
+        boolean rangeMode = true;
+        if (dItems != null && dItems[0] != null) {
+            rangeMode = FIELD_VALUE_DISTANCE.equals(dItems[0].getStringValue());
+        }
+
+        HLayout layout = new HLayout();
+        layout.setWidth("400px");
+
+        Label   label  = new Label(dataList.getLabel());
+        label.setWidth("200px");
+
+        Canvas back = getBackButton(dataList.getState());
+
+        layout.addMember(label);
+
+        if (rangeMode) {
+            layout.addMember(getOldRangeSelection(dataList));
+        }
+        else {
+            layout.addMember(getOldLocationSelection(dataList));
+        }
+
+        layout.addMember(back);
+
+        return layout;
+    }
+
+
+    /**
+     * Creates a label for the selected range.
+     *
+     * @param dataList The DataList containing all values for this state.
+     *
+     * @return A label displaying the selected values.
+     */
+    protected Label getOldRangeSelection(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();
+
+        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());
+
+        Label selected = new Label(sb.toString());
+        selected.setWidth("130px");
+
+        return selected;
+    }
+
+
+    /**
+     * Creates a label for the selected locations.
+     *
+     * @param dataList The DataList containing all values for this state.
+     *
+     * @return A label displaying the selected values.
+     */
+    protected Label getOldLocationSelection(DataList dataList) {
+        List<Data> items = dataList.getAll();
+
+        Data       dLocations = getData(items, "ld_locations");
+        DataItem[] lItems     = dLocations.getItems();
+
+        String value = lItems[0].getStringValue();
+
+        Label selected = new Label(value);
+        selected.setWidth(130);
+
+        return selected;
+    }
+
+
+    /**
+     * 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 m = getData(list.getAll(), "ld_mode");
+        Data l = getData(list.getAll(), "ld_locations");
+        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());
+
+        DataItem   mDef   = m.getDefault();
+        DataItem   lDef   = l.getDefault();
+        DataItem   fDef   = f.getDefault();
+        DataItem   tDef   = t.getDefault();
+        DataItem   sDef   = s.getDefault();
+
+        String mDefValue = mDef != null ? mDef.getStringValue() : null;
+        String theMode = mDefValue != null && mDefValue.length() > 0
+            ? mDef.getStringValue()
+            : FIELD_VALUE_DISTANCE;
+
+        mode.setValue(FIELD_MODE, theMode);
+
+        String fDefValue = fDef != null ? fDef.getStringValue() : null;
+        setFrom(fDefValue != null && fDefValue.length() > 0
+            ? Double.valueOf(fDef.getStringValue())
+            : min);
+
+        String tDefValue = tDef != null ? tDef.getStringValue() : null;
+        setTo(tDefValue != null && tDefValue.length() > 0
+            ? Double.valueOf(tDef.getStringValue())
+            : max);
+
+        String sDefValue = sDef != null ? sDef.getStringValue() : null;
+        setStep(sDefValue != null && sDefValue.length() > 0
+            ? Double.valueOf(sDef.getStringValue())
+            : step);
+
+        if (lDef != null) {
+            String lDefValue = lDef != null ? lDef.getStringValue() : null;
+
+            if (lDefValue != null && lDefValue.length() > 0) {
+                setLocationValues(lDef.getStringValue());
+            }
+        }
+
+        if (theMode.equals(FIELD_VALUE_DISTANCE)) {
+            enableDistancePanel();
+            inputTables.selectTab(1);
+        }
+    }
+
+
+    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.locations());
+        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;
+    }
+
+
+    @Override
+    public List<String> validate() {
+        if (isLocationMode()) {
+            return validateLocations();
+        }
+        else {
+            return validateRange();
+        }
+    }
+
+
+    protected List<String> validateLocations() {
+        List<String> errors = new ArrayList<String>();
+        NumberFormat nf     = NumberFormat.getDecimalFormat();
+
+        try {
+            saveLocationValues(locationPanel);
+        }
+        catch (Exception e) {
+            errors.add(MESSAGES.wrongFormat());
+        }
+
+        double[] values = getLocationValues();
+        double[] good   = new double[values.length];
+        int      idx    = 0;
+
+        for (double value: values) {
+            if (value < min || value > max) {
+                String tmp = MESSAGES.error_validate_range();
+                tmp = tmp.replace("$1", nf.format(value));
+                tmp = tmp.replace("$2", nf.format(min));
+                tmp = tmp.replace("$3", nf.format(max));
+                errors.add(tmp);
+            }
+            else {
+                good[idx++] = value;
+            }
+        }
+
+        double[] justGood = new double[idx];
+        for (int i = 0; i < justGood.length; i++) {
+            justGood[i] = good[i];
+        }
+
+        if (!errors.isEmpty()) {
+            locationPanel.setValues(justGood);
+        }
+
+        return errors;
+    }
+
+
+    protected List<String> validateRange() {
+        List<String> errors = new ArrayList<String>();
+        NumberFormat nf     = NumberFormat.getDecimalFormat();
+
+        try {
+            saveDistanceValues(distancePanel);
+        }
+        catch (Exception e) {
+            errors.add(MESSAGES.wrongFormat());
+        }
+
+        double from = getFrom();
+        double to   = getTo();
+        double step = getStep();
+
+        if (from < min || from > max) {
+            String tmp = MESSAGES.error_validate_lower_range();
+            tmp = tmp.replace("$1", nf.format(from));
+            tmp = tmp.replace("$2", nf.format(min));
+            errors.add(tmp);
+            from = min;
+        }
+
+        if (to < min || to > max) {
+            String tmp = MESSAGES.error_validate_upper_range();
+            tmp = tmp.replace("$1", nf.format(to));
+            tmp = tmp.replace("$2", nf.format(max));
+            errors.add(tmp);
+            to = max;
+        }
+
+        if (!errors.isEmpty()) {
+            distancePanel.setValues(from, to, step);
+        }
+
+        return errors;
+    }
+
+
+    /**
+     * 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);
+            }
+        }
+
+        Data dMode      = getDataMode();
+        Data dLocations = getDataLocations();
+        Data dFrom      = getDataFrom();
+        Data dTo        = getDataTo();
+        Data dStep      = getDataStep();
+
+        List<Data> data = new ArrayList<Data>();
+
+        if (dMode != null) {
+            data.add(dMode);
+        }
+
+        if (dLocations != null) {
+            data.add(dLocations);
+        }
+
+        if (dFrom != null) {
+            data.add(dFrom);
+        }
+
+        if (dTo != null) {
+            data.add(dTo);
+        }
+
+        if (dStep != null) {
+            data.add(dStep);
+        }
+
+        return (Data[]) data.toArray(new Data[data.size()]);
+    }
+
+
+    /**
+     * Returns the Data object for the 'mode' attribute.
+     *
+     * @return the Data object for the 'mode' attribute.
+     */
+    protected Data getDataMode() {
+        String   value = mode.getValueAsString(FIELD_MODE);
+        DataItem item  = new DefaultDataItem("ld_mode", "ld_mode", value);
+        return new DefaultData("ld_mode", null, null, new DataItem[] { item });
+    }
+
+
+    protected Data getDataLocations() {
+        double[] locations = getLocationValues();
+        boolean  first     = true;
+
+        if (locations == null) {
+            return null;
+        }
+
+        StringBuilder sb = new StringBuilder();
+
+        for (double l: locations) {
+            if (!first) {
+                sb.append(" ");
+            }
+
+            sb.append(l);
+
+            first = false;
+        }
+
+        DataItem item = new DefaultDataItem(
+            "ld_locations",
+            "ld_locations",
+            sb.toString());
+
+        return new DefaultData(
+            "ld_locations",
+            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(getFrom()).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(getTo()).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(getStep()).toString();
+        DataItem item = new DefaultDataItem("ld_step","ld_step", value);
+        return new DefaultData(
+            "ld_step", null, null, new DataItem[] { item });
+    }
+
+
+    /**
+     * Determines the current input mode.
+     *
+     * @return true, if 'location' is the current input mode, otherwise false.
+     */
+    public boolean isLocationMode() {
+        String inputMode = mode.getValueAsString(FIELD_MODE);
+
+        return inputMode.equals(FIELD_VALUE_LOCATION) ? true : false;
+    }
+
+
+    /**
+     * Activates the location panel.
+     */
+    protected void enableLocationPanel() {
+        locationPanel = new DoubleArrayPanel(
+            MESSAGES.unitLocation(),
+            getLocationValues(),
+            this);
+
+        container.removeMembers(container.getMembers());
+        container.addMember(locationPanel);
+    }
+
+
+    /**
+     * Activates the distance panel.
+     */
+    protected void enableDistancePanel() {
+        distancePanel = new DoubleRangePanel(
+            MESSAGES.unitFrom(), MESSAGES.unitTo(), MESSAGES.unitWidth(),
+            getFrom(), getTo(), getStep(),
+            250,
+            this);
+
+        container.removeMembers(container.getMembers());
+        container.addMember(distancePanel);
+    }
+
+
+    /**
+     * 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_VALUE_LOCATION)) {
+            enableLocationPanel();
+
+            while (inputTables.getNumTabs() > 0) {
+                inputTables.removeTab(0);
+            }
+            Tab t1 = new Tab (MESSAGES.locations());
+            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 {
+            enableDistancePanel();
+
+            while (inputTables.getNumTabs () > 0) {
+                inputTables.removeTab(0);
+            }
+            Tab t1 = new Tab(MESSAGES.locations());
+            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(FIELD_MODE);
+        radio.setShowTitle(false);
+        radio.setVertical(false);
+
+        LinkedHashMap values = new LinkedHashMap();
+        values.put(FIELD_VALUE_LOCATION, MESSAGES.location());
+        values.put(FIELD_VALUE_DISTANCE, MESSAGES.distance());
+
+        LinkedHashMap initial = new LinkedHashMap();
+        initial.put(FIELD_MODE, FIELD_VALUE_DISTANCE);
+
+        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 setLocationValues(String values) {
+        String[] vs = values.split(" ");
+
+        if (vs == null) {
+            return;
+        }
+
+        double[] ds  = new double[vs.length];
+        int      idx = 0;
+
+        for (String s: vs) {
+            try {
+                ds[idx++] = Double.valueOf(s);
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing
+            }
+        }
+
+        setLocationValues(ds);
+    }
+
+
+    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:15 2012 +0200
@@ -0,0 +1,265 @@
+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.Button;
+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 Button logout;
+
+    /** The button to add new projects.*/
+    protected Button newProject;
+
+    /** The button to open the project list.*/
+    protected Button projectList;
+
+    /** The button to switch between the english and german version.*/
+    protected Button language;
+
+    /** The button to open an info panel.*/
+    protected Button 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 Button(messages.new_project());
+        projectList = new Button(messages.manage_projects());
+        logout      = new Button(messages.logout());
+        language    = new Button(messages.switch_language());
+        info        = new Button(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);
+        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:15 2012 +0200
@@ -0,0 +1,78 @@
+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.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:15 2012 +0200
@@ -0,0 +1,149 @@
+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.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:15 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:15 2012 +0200
@@ -0,0 +1,652 @@
+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.ReportMode;
+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.OutputModesChangeEvent;
+import de.intevation.flys.client.client.event.OutputModesChangeHandler;
+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.ReportService;
+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;
+import de.intevation.flys.client.client.services.ReportServiceAsync;
+
+
+public class ParameterList
+extends      Tab
+implements   StepBackHandler, StepForwardHandler, ParameterChangeHandler,
+             HasParameterChangeHandler, CollectionChangeHandler,
+             OutputModesChangeHandler
+{
+    /** 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);
+
+
+    protected ReportServiceAsync reportService =
+        GWT.create(ReportService.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 VLayout report;
+    protected Canvas  helperPanel;
+    protected Canvas  tablePanel;
+    protected Canvas  reportPanel;
+
+    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();
+        report            = 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();
+        report            = 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(1);
+        }
+
+        oldItems.setMembersMargin(10);
+        currentItems.setAlign(VerticalAlignment.TOP);
+
+        left.setMembersMargin(20);
+        left.setWidth(300);
+
+        left.addMember(oldItems);
+        left.addMember(currentItems);
+        left.addMember(exportModes);
+        left.addMember(report);
+
+        reportPanel = new Canvas();
+        reportPanel.setHeight("*");
+        report.addMember(reportPanel);
+
+        // 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();
+
+        OutputMode[] outputs = new OutputMode[outs.size()];
+
+        int idx = 0;
+        for (String outname: keys) {
+            outputs[idx++] = outs.get(outname);
+        }
+
+        updateExportModes(c, getExportModes(outputs));
+        updateReportModes(c, getReportModes(outputs));
+    }
+
+
+    public void onOutputModesChange(OutputModesChangeEvent event) {
+
+        Collection c = cView.getCollection();
+
+        if (c != null) {
+            OutputMode [] outs = event.getOutputModes();
+            updateExportModes(c, getExportModes(outs));
+            updateReportModes(c, getReportModes(outs));
+        }
+    }
+
+
+    protected List<ReportMode> getReportModes(OutputMode [] outs) {
+
+        List<ReportMode> reports = new ArrayList<ReportMode>();
+
+        if (outs == null || outs.length == 0) {
+            return reports;
+        }
+
+        for (OutputMode out: outs) {
+            if (out instanceof ReportMode) {
+                reports.add((ReportMode)out);
+            }
+        }
+
+        return reports;
+    }
+
+
+    protected List<ExportMode> getExportModes(OutputMode[] outs) {
+        List<ExportMode> exports = new ArrayList<ExportMode>();
+
+        if (outs == null || outs.length == 0) {
+            return exports;
+        }
+
+        for (OutputMode out: outs) {
+            if (out instanceof ExportMode) {
+                exports.add((ExportMode) out);
+            }
+        }
+
+        return 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));
+        }
+    }
+
+    protected void updateReportModes(Collection c, List<ReportMode> reports) {
+        int num = reports != null ? reports.size() : 0;
+        GWT.log("Update report modes: " + num);
+
+        if (num == 0) {
+            reportPanel.setContents("");
+            return;
+        }
+
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        String cid = c.identifier();
+
+        for (ReportMode report: reports) {
+            GWT.log("report '" + report.toString() + "'");
+
+            reportService.report(cid, url, locale, report.getName(),
+                new AsyncCallback<String>() {
+                    public void onFailure(Throwable caught) {
+                        SC.warn(caught.getMessage());
+                    }
+
+                    public void onSuccess(String msg) {
+                        setReportMessage(msg);
+                    }
+                });
+        }
+    }
+
+    protected void setReportMessage(String msg) {
+        GWT.log("returned from service: " + msg);
+        if (msg == null) {
+            msg = "";
+        }
+        reportPanel.setContents(msg);
+    }
+
+
+    /**
+     * 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:15 2012 +0200
@@ -0,0 +1,591 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.Date;
+import java.util.Map;
+import java.util.MissingResourceException;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.DateTimeFormat;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.types.Alignment;
+import com.smartgwt.client.types.ListGridEditEvent;
+import com.smartgwt.client.types.ListGridFieldType;
+import com.smartgwt.client.types.SelectionStyle;
+import com.smartgwt.client.types.SortArrow;
+import com.smartgwt.client.types.SortDirection;
+import com.smartgwt.client.util.BooleanCallback;
+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.HoverCustomizer;
+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.CellClickEvent;
+import com.smartgwt.client.widgets.grid.events.CellClickHandler;
+import com.smartgwt.client.widgets.grid.events.CellDoubleClickEvent;
+import com.smartgwt.client.widgets.grid.events.CellDoubleClickHandler;
+import com.smartgwt.client.widgets.grid.events.EditCompleteEvent;
+import com.smartgwt.client.widgets.grid.events.EditCompleteHandler;
+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.DeleteCollectionService;
+import de.intevation.flys.client.client.services.DeleteCollectionServiceAsync;
+import de.intevation.flys.client.client.services.SetCollectionNameService;
+import de.intevation.flys.client.client.services.SetCollectionNameServiceAsync;
+import de.intevation.flys.client.client.services.SetCollectionTTLService;
+import de.intevation.flys.client.client.services.SetCollectionTTLServiceAsync;
+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, EditCompleteHandler
+{
+    /** Interval to refresh the user's projects.*/
+    public static final int UPDATE_INTERVAL = 30000;
+
+    /** Min Interval to refresh the user's projects.*/
+    public static final int MIN_UPDATE_INTERVAL = 5000;
+
+    /** The initial width of this panel.*/
+    public static final int MIN_WIDTH = 300;
+
+    /** The max length for new project names.*/
+    public static final int MAX_NAME_LENGTH = 50;
+
+    public static final String COLUMN_DATE_WIDTH = "100px";
+
+    public static final String COLUMN_TITLE_WIDTH = "*";
+
+    public static final String COLUMN_FAVORITE_WIDTH = "75px";
+
+    /** 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);
+
+    /** The service used to set the name of a project.*/
+    protected SetCollectionNameServiceAsync nameService =
+        GWT.create(SetCollectionNameService.class);
+
+    /** The service used to set the name of a project.*/
+    protected SetCollectionTTLServiceAsync ttlService =
+        GWT.create(SetCollectionTTLService.class);
+
+    /** The service used to set the name of a project.*/
+    protected DeleteCollectionServiceAsync deleteService =
+        GWT.create(DeleteCollectionService.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();
+        initTimer();
+
+        grid.addEditCompleteHandler(this);
+
+        updateUserCollections();
+    }
+
+
+    protected void initGrid() {
+        grid.setWidth100();
+        grid.setHeight100();
+        grid.setAutoFitMaxWidth(500);
+        grid.setEmptyMessage(messages.no_projects());
+        grid.setLoadingDataMessage(messages.load_projects());
+        grid.setCanEdit(false);
+        grid.setEditEvent(ListGridEditEvent.NONE);
+        grid.setShowHeaderContextMenu(false);
+        grid.setShowSortArrow(SortArrow.NONE);
+        grid.setSortDirection(SortDirection.DESCENDING);
+        grid.setSortField(0);
+        grid.setSelectionType(SelectionStyle.SINGLE);
+
+        ListGridField date = buildDateField();
+        ListGridField name = buildNameField();
+        ListGridField fav  = buildFavoriteField();
+
+        grid.setFields(date, name, fav);
+
+        // add a handler to set / unset the favorite state of a project
+        grid.addCellClickHandler(new CellClickHandler() {
+            public void onCellClick(CellClickEvent event) {
+                if (event.getColNum() != 2) {
+                    return;
+                }
+
+                CollectionRecord r = (CollectionRecord) event.getRecord();
+                Collection       c = r.getCollection();
+
+                c.setTTL(c.getTTL() == 0 ? -1 : 0);
+                updateCollectionTTL(c);
+            }
+        });
+
+        // add a handler to open a project
+        grid.addCellDoubleClickHandler(new CellDoubleClickHandler() {
+            public void onCellDoubleClick(CellDoubleClickEvent e) {
+                CollectionRecord record = (CollectionRecord) e.getRecord();
+                String uuid = record != null
+                    ? record.getCollection().identifier()
+                    : "";
+                getFlys().openProject(uuid);
+            }
+        });
+
+        // add a handler to open a context menu
+        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();
+            }
+        });
+    }
+
+
+    /**
+     * Initializes a repeating timer that updates the user's collections. The
+     * interval is specified by the constant <i>UPDATE_INTERVAL</i>.
+     */
+    protected void initTimer() {
+        Config config   = Config.getInstance();
+        int    interval = config.getProjectListUpdateInterval();
+
+        interval = interval > MIN_UPDATE_INTERVAL ? interval : UPDATE_INTERVAL;
+
+        GWT.log("Update project list every " + interval + " milliseconds.");
+
+        Timer t = new Timer() {
+            @Override
+            public void run() {
+                updateUserCollections();
+            }
+        };
+
+        t.scheduleRepeating(interval);
+    }
+
+
+    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.ask(messages.really_delete(), new BooleanCallback() {
+                    public void execute(Boolean value) {
+                        if (value) {
+                            deleteCollection(record.getCollection());
+                        }
+                    }
+                });
+            }
+        });
+
+        MenuItem rename = new MenuItem(messages.rename_project());
+        rename.addClickHandler(new ClickHandler() {
+            public void onClick(MenuItemClickEvent evt) {
+                int row = grid.getRecordIndex(record);
+                grid.startEditing(row, 1, false);
+            }
+        });
+
+        menu.addItem(open);
+        menu.addItem(del);
+        menu.addItem(rename);
+
+        return menu;
+    }
+
+
+    /**
+     * The init() method handles the layout stuff for this widget.
+     */
+    protected void init() {
+        setWidth(MIN_WIDTH);
+        setMinWidth(MIN_WIDTH);
+        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) {
+        if (event.getOldValue() == null) {
+            updateUserCollections();
+        }
+    }
+
+
+    public void onEditComplete(EditCompleteEvent event) {
+        if (event.getColNum() != 1) {
+            return;
+        }
+
+        int row = event.getRowNum();
+
+        CollectionRecord r = (CollectionRecord) grid.getRecord(row);
+        Collection       c = r.getCollection();
+
+        Map newValues = event.getNewValues();
+        String name   = (String) newValues.get("name");
+
+        int maxLength = getMaxNameLength();
+        int length    = name != null ? name.length() : 0;
+
+        if (length <= 0 || length > maxLength) {
+            String msg = messages.project_name_too_long();
+            msg        = msg.replace("$LEN", String.valueOf(maxLength));
+            SC.warn(msg);
+
+            ListGridRecord[] rs = grid.getRecords();
+            rs[row] = (ListGridRecord) event.getOldRecord();
+            grid.setRecords(rs);
+
+            return;
+        }
+
+        c.setName(name);
+        updateCollectionName(c);
+    }
+
+
+    /**
+     * Set the name of the collection <i>c</i> to a new value. If the update
+     * process succeeded, the project list is refreshed.
+     *
+     * @param c The Collection with a new name.
+     */
+    protected void updateCollectionName(Collection c) {
+        if (c == null) {
+            return;
+        }
+
+        GWT.log("Update Collection name: " + c.identifier());
+        GWT.log("=> New name = " + c.getName());
+
+        Config config = Config.getInstance();
+        nameService.setName(c, config.getServerUrl(), new AsyncCallback<Void>(){
+            public void onFailure(Throwable caught) {
+                String msg = caught.getMessage();
+
+                try {
+                    SC.warn(messages.getString(msg));
+                }
+                catch (MissingResourceException mre) {
+                    SC.warn(msg);
+                }
+            }
+
+            public void onSuccess(Void v) {
+                updateUserCollections();
+            }
+        });
+    }
+
+
+    /**
+     * Set the ttl of the collection <i>c</i> to a new value. If the update
+     * process succeeded, the project list is refreshed.
+     *
+     * @param c The Collection with a new ttl.
+     */
+    protected void updateCollectionTTL(Collection c) {
+        if (c == null) {
+            return;
+        }
+
+        GWT.log("Update Collection TTL: " + c.identifier());
+        GWT.log("=> New ttl = " + c.getTTL());
+
+        Config config = Config.getInstance();
+        ttlService.setTTL(c, config.getServerUrl(), new AsyncCallback<Void>() {
+            public void onFailure(Throwable caught) {
+                String msg = caught.getMessage();
+
+                try {
+                    SC.warn(messages.getString(msg));
+                }
+                catch (MissingResourceException mre) {
+                    SC.warn(msg);
+                }
+            }
+
+            public void onSuccess(Void v) {
+                updateUserCollections();
+            }
+        });
+    }
+
+
+    /**
+     * Delete the collection <i>c</i>.
+     *
+     * @param c The Collection that should be deleted.
+     */
+    protected void deleteCollection(final Collection c) {
+        if (c == null) {
+            return;
+        }
+
+        GWT.log("Delete Collection: " + c.identifier());
+
+        Config config = Config.getInstance();
+        deleteService.delete(c, config.getServerUrl(), new AsyncCallback<Void>(){
+            public void onFailure(Throwable caught) {
+                String msg = caught.getMessage();
+
+                try {
+                    SC.warn(messages.getString(msg));
+                }
+                catch (MissingResourceException mre) {
+                    SC.warn(msg);
+                }
+            }
+
+            public void onSuccess(Void v) {
+                flys.getWorkspace().destroyProject(c.identifier());
+                updateUserCollections();
+            }
+        });
+    }
+
+
+    protected void updateUserCollections() {
+        GWT.log("==> ProjectList updates user collections!");
+
+        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) {
+                    String msg = caught.getMessage();
+
+                    try {
+                        SC.warn(messages.getString(msg));
+                    }
+                    catch (MissingResourceException mre) {
+                        SC.warn(msg);
+                    }
+                }
+
+                public void onSuccess(Collection[] collections) {
+                    int num = collections != null ? collections.length : 0;
+
+                    GWT.log("Received " + num + " user collections.");
+
+                    updateGrid(collections);
+                }
+            }
+        );
+    }
+
+
+    protected void clearGrid() {
+        ListGridRecord[] records = grid.getRecords();
+
+        for (ListGridRecord record: records) {
+            grid.removeData(record);
+        }
+    }
+
+
+    protected void updateGrid(Collection[] collections) {
+        clearGrid();
+
+        if (collections == null || collections.length == 0) {
+            return;
+        }
+
+        for (Collection c: collections) {
+            grid.addData(new CollectionRecord(c));
+        }
+    }
+
+
+    public int getMaxNameLength() {
+        return MAX_NAME_LENGTH;
+    }
+
+
+    /**
+     * 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", messages.projectlist_creationTime());
+
+        date.setType(ListGridFieldType.DATE);
+        date.setCanEdit(false);
+
+        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(COLUMN_DATE_WIDTH);
+        date.setAlign(Alignment.CENTER);
+
+        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", messages.projectlist_title());
+
+        name.setType(ListGridFieldType.TEXT);
+        name.setShowHover(true);
+        name.setHoverCustomizer(new HoverCustomizer() {
+            public String hoverHTML(
+                Object         value,
+                ListGridRecord record,
+                int            row,
+                int            col)
+            {
+                CollectionRecord r = (CollectionRecord) record;
+                Collection       c = r.getCollection();
+
+                String name = r.getName();
+
+                return name != null && name.length() > 0
+                    ? name
+                    : c.identifier();
+            }
+        });
+
+        name.setWidth(COLUMN_TITLE_WIDTH);
+        name.setAlign(Alignment.LEFT);
+
+        return name;
+    }
+
+
+    protected ListGridField buildFavoriteField() {
+        ListGridField fav = new ListGridField(
+            "ttl", messages.projectlist_favorite());
+
+        fav.setType(ListGridFieldType.IMAGE);
+        String base = GWT.getHostPageBaseURL();
+        fav.setImageURLPrefix(base + "images/");
+        fav.setImageURLSuffix(".png");
+        fav.setWidth(COLUMN_FAVORITE_WIDTH);
+        fav.setAlign(Alignment.CENTER);
+        fav.setCanEdit(false);
+
+        return fav;
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,187 @@
+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.SelectItem;
+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);
+
+            SelectItem combobox = new SelectItem(d.getLabel());
+            combobox.setWidth(250);
+
+            LinkedHashMap<String, String> it = new LinkedHashMap<String, String>();
+
+            boolean  defaultSet = false;
+            boolean  first      = true;
+
+            DataItem def      = d.getDefault();
+            String   defValue = def != null ? def.getStringValue() : null;
+
+            if (defValue != null && defValue.length() > 0) {
+                initial.put(d.getLabel(), def.getStringValue());
+                defaultSet = true;
+            }
+
+            for (DataItem item: d.getItems()) {
+                if (!defaultSet && first) {
+                    initial.put(d.getLabel(), item.getStringValue());
+                    first = 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:15 2012 +0200
@@ -0,0 +1,445 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.NumberFormat;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.util.SC;
+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 minimal value that the user is allowed to enter.*/
+    protected double min;
+
+    /** The maximal value that the user is allowed to enter.*/
+    protected double max;
+
+    /** 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);
+
+        Label label   = new Label(MESSAGES.location ());
+        Canvas widget = createWidget(data);
+        Canvas submit = getNextButton();
+
+        initDefaults(data);
+
+        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() {
+        GWT.log("---------- I WAS HERE ---------");
+        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 (20);
+        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(40);
+
+        ListGridField loc = new ListGridField("from", MESSAGES.location());
+        loc.setType(ListGridFieldType.TEXT);
+        loc.setWidth(40);
+
+        ListGridField bottom =
+            new ListGridField("bottom", MESSAGES.bottom_edge());
+        bottom.setType(ListGridFieldType.TEXT);
+        bottom.setWidth(30);
+
+        ListGridField top =
+            new ListGridField("top", MESSAGES.top_edge());
+        top.setType(ListGridFieldType.TEXT);
+        top.setWidth(30);
+
+        locationTable.setFields(
+            addLocation, ldescr, loc, lside, bottom, top);
+    }
+
+
+    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) {
+        Data data = list.get(0);
+
+        if (data == null) {
+            return;
+        }
+
+        DataItem[] items = data.getItems();
+        DataItem   iMin  = getDataItem(items, "min");
+        DataItem   iMax  = getDataItem(items, "max");
+
+        try {
+            min = Double.parseDouble(iMin.getStringValue());
+            max = Double.parseDouble(iMax.getStringValue());
+        }
+        catch (NumberFormatException nfe) {
+            SC.warn(MESSAGES.error_read_minmax_values());
+            min = -Double.MAX_VALUE;
+            max = Double.MAX_VALUE;
+        }
+
+        DataItem def   = data.getDefault();
+        String   value = def.getStringValue();
+
+        try {
+            double d = Double.parseDouble(value);
+            setLocationValues(new double[] { d } );
+        }
+        catch (NumberFormatException nfe) {
+            // could not parse, dont know what to do else
+        }
+    }
+
+
+    /**
+     * 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;
+    }
+
+
+    @Override
+    public List<String> validate() {
+        List<String> errors = new ArrayList<String>();
+        NumberFormat nf     = NumberFormat.getDecimalFormat();
+
+        saveLocationValues(locationPanel);
+
+        if (!locationPanel.validateForm()) {
+            errors.add(MESSAGES.wrongFormat());
+            return errors;
+        }
+
+        double[] values = getLocationValues();
+        double[] good   = new double[values.length];
+        int      idx    = 0;
+
+        for (double value: values) {
+            if (value < min || value > max) {
+                String tmp = MESSAGES.error_validate_range();
+                tmp = tmp.replace("$1", nf.format(value));
+                tmp = tmp.replace("$2", nf.format(min));
+                tmp = tmp.replace("$3", nf.format(max));
+                errors.add(tmp);
+            }
+            else {
+                good[idx++] = value;
+            }
+        }
+
+        double[] justGood = new double[idx];
+        for (int i = 0; i < justGood.length; i++) {
+            justGood[i] = good[i];
+        }
+
+        if (!errors.isEmpty()) {
+            locationPanel.setValues(justGood);
+        }
+
+        return errors;
+    }
+
+
+    /**
+     * 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:15 2012 +0200
@@ -0,0 +1,160 @@
+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.util.SC;
+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<String[]>>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not recieve csv.");
+                    SC.warn(caught.getMessage());
+                }
+
+                public void onSuccess(List<String[]> 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,565 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+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.google.gwt.i18n.client.NumberFormat;
+
+import com.smartgwt.client.types.TitleOrientation;
+import com.smartgwt.client.types.VerticalAlignment;
+import com.smartgwt.client.util.SC;
+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.shared.model.WQDataItem;
+
+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;
+
+    /** Stores the min/max values for each q range.*/
+    protected Map<String, double[]> qranges;
+
+    /** Stores the min/max values for each w range.*/
+    protected Map<String, double[]> wranges;
+
+    /** The RadioGroupItem that determines the w/q input mode.*/
+    protected DynamicForm modes;
+
+
+
+    public WQAdaptedInputPanel() {
+        wqranges = new HashMap<String, DoubleArrayPanel>();
+        qranges  = new HashMap<String, double[]>();
+        wranges  = new HashMap<String, double[]>();
+    }
+
+
+    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);
+
+        initUserDefaults(dataList);
+
+        return layout;
+    }
+
+
+    @Override
+    public List<String> validate() {
+        if (isWMode()) {
+            return validateW();
+        }
+        else {
+            return validateQ();
+        }
+    }
+
+
+    protected List<String> validateW() {
+        List<String> errors = new ArrayList<String>();
+        NumberFormat nf     = NumberFormat.getDecimalFormat();
+
+        Iterator<String> iter = wqranges.keySet().iterator();
+
+        while (iter.hasNext()) {
+            List<String> tmpErrors = new ArrayList<String>();
+
+            String           key = iter.next();
+            DoubleArrayPanel dap = wqranges.get(key);
+
+            if (!dap.validateForm()) {
+                errors.add(MSG.error_invalid_double_value());
+                return errors;
+            }
+
+            double[] mm  = wranges.get(key);
+            if (mm == null) {
+                SC.warn(MSG.error_read_minmax_values());
+                continue;
+            }
+
+            double[] values = dap.getInputValues();
+            double[] good   = new double[values.length];
+
+            int idx = 0;
+
+            for (double value: values) {
+                if (value < mm[0] || value > mm[1]) {
+                    String tmp = MSG.error_validate_range();
+                    tmp = tmp.replace("$1", nf.format(value));
+                    tmp = tmp.replace("$2", nf.format(mm[0]));
+                    tmp = tmp.replace("$3", nf.format(mm[1]));
+                    tmpErrors.add(tmp);
+                }
+                else {
+                    good[idx++] = value;
+                }
+            }
+
+            double[] justGood = new double[idx];
+            for (int i = 0; i < justGood.length; i++) {
+                justGood[i] = good[i];
+            }
+
+            if (!tmpErrors.isEmpty()) {
+                dap.setValues(justGood);
+
+                errors.addAll(tmpErrors);
+            }
+        }
+
+        return errors;
+    }
+
+
+    protected List<String> validateQ() {
+        List<String> errors = new ArrayList<String>();
+        NumberFormat nf     = NumberFormat.getDecimalFormat();
+
+        Iterator<String> iter = wqranges.keySet().iterator();
+
+        while (iter.hasNext()) {
+            List<String> tmpErrors = new ArrayList<String>();
+
+            String           key = iter.next();
+            DoubleArrayPanel dap = wqranges.get(key);
+
+            if (!dap.validateForm()) {
+                errors.add(MSG.error_invalid_double_value());
+                return errors;
+            }
+
+            double[] mm = qranges.get(key);
+            if (mm == null) {
+                SC.warn(MSG.error_read_minmax_values());
+                continue;
+            }
+
+            double[] values = dap.getInputValues();
+            double[] good   = new double[values.length];
+
+            int idx = 0;
+
+            for (double value: values) {
+                if (value < mm[0] || value > mm[1]) {
+                    String tmp = MSG.error_validate_range();
+                    tmp = tmp.replace("$1", nf.format(value));
+                    tmp = tmp.replace("$2", nf.format(mm[0]));
+                    tmp = tmp.replace("$3", nf.format(mm[1]));
+                    tmpErrors.add(tmp);
+                }
+                else {
+                    good[idx++] = value;
+                }
+            }
+
+            double[] justGood = new double[idx];
+            for (int i = 0; i < justGood.length; i++) {
+                justGood[i] = good[i];
+            }
+
+            if (!tmpErrors.isEmpty()) {
+                dap.setValues(justGood);
+
+                errors.addAll(tmpErrors);
+            }
+        }
+
+        return errors;
+    }
+
+
+    protected void initUserDefaults(DataList dataList) {
+
+        initUserWQValues(dataList);
+        initUserWQMode(dataList);
+    }
+
+
+    protected void initUserWQMode(DataList dataList) {
+        List<Data> allData = dataList.getAll();
+
+        Data     dDef  = getData(allData, "wq_mode");
+        DataItem def   = dDef != null ? dDef.getDefault() : null;
+        String   value = def != null ? def.getStringValue() : null;
+
+        if (value != null && value.equals(FIELD_WQ_W)) {
+            modes.setValue(FIELD_WQ_MODE, FIELD_WQ_W);
+        }
+        else {
+            modes.setValue(FIELD_WQ_MODE, FIELD_WQ_Q);
+        }
+    }
+
+
+    protected void initUserWQValues(DataList dataList) {
+        List<Data> allData = dataList.getAll();
+
+        Data     dDef  = getData(allData, "wq_values");
+        DataItem def   = dDef != null ? dDef.getDefault() : null;
+        String   value = def != null ? def.getStringValue() : null;
+
+        if (value == null || value.length() == 0) {
+            return;
+        }
+
+        String[] lines = value.split(GAUGE_SEPARATOR);
+
+        if (lines == null || lines.length == 0) {
+            return;
+        }
+
+        for (String line: lines) {
+            String[] cols  = line.split(GAUGE_PART_SEPARATOR);
+            String   title = createLineTitle(line);
+
+            if (cols == null || cols.length < 3) {
+                continue;
+            }
+
+            String[] strValues = cols[2].split(VALUE_SEPARATOR);
+            double[] values    = new double[strValues.length];
+
+            int idx = 0;
+
+            for (String strValue: strValues) {
+                try {
+                    values[idx++] = Double.valueOf(strValue);
+                }
+                catch (NumberFormatException nfe) {
+                    // do nothing
+                }
+            }
+
+            String           key = cols[0] + GAUGE_PART_SEPARATOR + cols[1];
+            DoubleArrayPanel dap = wqranges.get(key);
+
+            if (dap == null) {
+                continue;
+            }
+
+            dap.setValues(values);
+        }
+    }
+
+
+    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);
+
+            if (item instanceof WQDataItem) {
+                WQDataItem wq = (WQDataItem) item;
+                double[] mmQ = wq.getQRange();
+                double[] mmW = wq.getWRange();
+
+                GWT.log(title + " Q: " + mmQ[0] + " - " + mmQ[1]);
+                GWT.log(title + " W: " + mmW[0] + " - " + mmW[1]);
+
+                qranges.put(title, mmQ);
+                wranges.put(title, mmW);
+            }
+
+            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_Q);
+        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 };
+    }
+
+
+    public boolean isWMode() {
+        String mode = (String) modes.getValue(FIELD_WQ_MODE);
+
+        return FIELD_WQ_W.equals(mode);
+    }
+
+
+    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:15 2012 +0200
@@ -0,0 +1,1400 @@
+package de.intevation.flys.client.client.ui;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.i18n.client.NumberFormat;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.data.Record;
+
+import com.smartgwt.client.util.SC;
+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.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 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 de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.FLYSImages;
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.services.WQInfoService;
+import de.intevation.flys.client.client.services.WQInfoServiceAsync;
+import de.intevation.flys.client.client.ui.wq.WTable;
+import de.intevation.flys.client.client.ui.wq.QDTable;
+
+
+/**
+ * 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 value for Q input mode.*/
+    public static final String FIELD_WQ_Q_GAUGE = "Q_GAUGE";
+
+    /** 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_UPPER = 400;
+
+    public static final int WIDTH_LEFT_LOWER = 223;
+
+
+    /** 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;
+
+    protected QDTable qdTable;
+
+    protected WTable wTable;
+
+    protected TabSet tabs;
+
+    /**
+     * Creates a new WQInputPanel instance.
+     */
+    public WQInputPanel() {
+        qdTable      = new QDTable();
+        wTable       = new WTable();
+
+        initTableListeners();
+    }
+
+
+    /**
+     * Initializes the listeners of the WQD tables.
+     */
+    protected void initTableListeners() {
+        CellClickHandler handler = new CellClickHandler() {
+            public void onCellClick(CellClickEvent e) {
+
+                if (isWMode()) {
+                    return;
+                }
+
+                int    idx = e.getColNum();
+                Record r   = e.getRecord ();
+                double val = r.getAttributeAsDouble("value");
+
+                if (idx == 0) {
+                    if (isRangeMode()) {
+                        qRangePanel.setFrom(val);
+                    }
+                    else {
+                        qArrayPanel.addValue(val);
+                    }
+                }
+                else if (idx == 1) {
+                    if (isRangeMode()) {
+                        qRangePanel.setTo(val);
+                    }
+                    else {
+                        qArrayPanel.addValue(val);
+                    }
+                }
+            }
+        };
+
+        qdTable.addCellClickHandler(handler);
+    }
+
+
+    /**
+     * 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);
+
+        initHelperPanel();
+        initUserDefaults(data);
+
+        return layout;
+    }
+
+
+    protected void initHelperPanel() {
+        tabs = new TabSet();
+        tabs.setWidth100();
+        tabs.setHeight100();
+
+        // TODO i18n
+        Tab wTab = new Tab("W");
+        Tab qTab = new Tab("Q / D");
+
+        wTab.setPane(wTable);
+        qTab.setPane(qdTable);
+
+        tabs.addTab(wTab, 0);
+        tabs.addTab(qTab, 1);
+
+        helperContainer.addChild(tabs);
+
+        fetchWQData();
+    }
+
+
+    public Canvas createOld(DataList dataList) {
+        List<Data> items = dataList.getAll();
+
+        Data dMode      = getData(items, "wq_mode");
+        Data dFree      = getData(items, "wq_free");
+        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);
+
+        DataItem[] free = dFree.getItems();
+        String  strFree = free[0].getStringValue();
+        boolean isFree  = Boolean.valueOf(strFree);
+
+        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]);
+        }
+
+        VLayout selectedLayout = new VLayout();
+        String  wqMode         = null;
+
+        if (wMode) {
+            wqMode = MESSAGE.wqW();
+        }
+        else {
+            wqMode = isFree ? MESSAGE.wqQ() : MESSAGE.wqQGauge();
+        }
+
+        Label mLabel = new Label(wqMode);
+        Label vLabel = new Label(text);
+        mLabel.setWidth(175);
+        mLabel.setHeight(20);
+        vLabel.setWidth(175);
+        vLabel.setHeight(20);
+
+        selectedLayout.addMember(mLabel);
+        selectedLayout.addMember(vLabel);
+        selectedLayout.setHeight(40);
+
+        Canvas back = getBackButton(dataList.getState());
+
+        layout.addMember(label);
+        layout.addMember(selectedLayout);
+        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(), "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;
+    }
+
+
+    /**
+     * Initializes the form items with former inserted user data.
+     *
+     * @param list The DataList that contains the user data.
+     */
+    protected void initUserDefaults(DataList list) {
+        List<Data> allData = list.getAll();
+
+        Data     m        = getData(allData, "wq_mode");
+        DataItem modeItem = m != null ? m.getDefault() : null;
+        String   theMode  = modeItem != null
+            ? modeItem.getStringValue()
+            : "";
+
+        Data     f        = getData(allData, "wq_free");
+        DataItem freeItem = f != null ? f.getDefault() : null;
+        String   theFree  = freeItem != null
+            ? freeItem.getStringValue()
+            : null;
+
+        Data     s            = getData(allData, "wq_selection");
+        DataItem sI           = s != null ? s.getDefault() : null;
+        String   theSelection = sI != null ? sI.getStringValue() : null;
+
+        if (theMode == null || theMode.length() == 0) {
+            return;
+        }
+
+        boolean isW    = theMode.equals(FIELD_WQ_W);
+        boolean isFree = Boolean.valueOf(theFree);
+
+        initUserSingleValues(list, theMode);
+        initUserRangeValues(list, theMode);
+
+        if (isW) {
+            modes.setValue(FIELD_WQ, theMode);
+        }
+        else {
+            modes.setValue(FIELD_WQ, isFree ? FIELD_WQ_Q : FIELD_WQ_Q_GAUGE);
+        }
+
+        if (theSelection != null || theSelection.length() > 0) {
+            modes.setValue(FIELD_MODE, theSelection);
+            updatePanels(theMode, theSelection);
+        }
+    }
+
+
+    /**
+     * Initializes the single values of W or Q from DataList.
+     *
+     * @param list The DataList that contains the 'wq_single' object.
+     * @param theMode The W or Q mode.
+     */
+    protected void initUserSingleValues(DataList list, String theMode) {
+        List<Data> allData = list.getAll();
+
+        Data     s = getData(allData, "wq_single");
+        DataItem i = s != null ? s.getDefault() : null;
+
+        if (i != null) {
+            String   value = i.getStringValue();
+            String[] split = value.split(" ");
+
+            int num = split != null ? split.length : 0;
+
+            double[] values = new double[num];
+
+            for (int j = 0; j < num; j++) {
+                try {
+                    values[j] = Double.valueOf(split[j]);
+                }
+                catch (NumberFormatException nfe) {
+                    // nothing to do
+                }
+            }
+
+            if (theMode.equals("W")) {
+                setSingleW(values);
+            }
+            else {
+                setSingleQ(values);
+            }
+        }
+    }
+
+
+    /**
+     * Initializes the range values of W or Q from DataList.
+     *
+     * @param list The DataList that contains the 'wq_single' object.
+     * @param theMode The W or Q mode.
+     */
+    protected void initUserRangeValues(DataList list, String theMode) {
+        List<Data> allData = list.getAll();
+
+        // init range mode values
+        Data f = getData(allData, "wq_from");
+        Data t = getData(allData, "wq_to");
+        Data s = getData(allData, "wq_step");
+
+        if (f != null && t != null && s != null) {
+            DataItem dF = f.getDefault();
+            DataItem dT = t.getDefault();
+            DataItem dS = s.getDefault();
+
+            String fS = dF != null ? dF.getStringValue() : null;
+            String tS = dT != null ? dT.getStringValue() : null;
+            String sS = dS != null ? dS.getStringValue() : null;
+
+            try {
+                double from = Double.valueOf(fS);
+                double to   = Double.valueOf(tS);
+                double step = Double.valueOf(sS);
+
+                if (theMode.equals("W")) {
+                    setWRangeValues(from, to, step);
+                }
+                else {
+                    setQRangeValues(from, to, step);
+                }
+            }
+            catch (NumberFormatException nfe) {
+                // do nothing
+            }
+        }
+    }
+
+
+    protected void setQRangeValues(double f, double t, double s) {
+        setFromQ(f);
+        setToQ(t);
+        setStepQ(s);
+    }
+
+
+    protected void setWRangeValues(double f, double t, double s) {
+        setFromW(f);
+        setToW(t);
+        setStepW(s);
+    }
+
+
+    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().replace(" ", " " + MESSAGE.unitWSingle() + " ");
+    }
+
+
+    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().replace(" ", " " + MESSAGE.unitQSingle() + " ");
+    }
+
+
+    /**
+     * 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);
+
+        // the initial panel is the Single-W panel.
+        double[] values = getSingleQ();
+        qArrayPanel = new DoubleArrayPanel(
+            MESSAGE.unitQSingle(), values, this);
+        container.addMember(qArrayPanel);
+
+        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_UPPER);
+        wq.setWrap(false);
+
+        RadioGroupItem mode = new RadioGroupItem(FIELD_MODE);
+        mode.setShowTitle(false);
+        mode.setVertical(false);
+        mode.setWidth(WIDTH_LEFT_LOWER);
+
+        LinkedHashMap wqValues = new LinkedHashMap();
+        wqValues.put(FIELD_WQ_W, MESSAGE.wqW());
+        wqValues.put(FIELD_WQ_Q, MESSAGE.wqQ());
+        wqValues.put(FIELD_WQ_Q_GAUGE, MESSAGE.wqQGauge());
+
+        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_UPPER);
+        modes.setNumCols(1);
+
+        LinkedHashMap initial = new LinkedHashMap();
+        initial.put(FIELD_WQ, FIELD_WQ_Q_GAUGE);
+        initial.put(FIELD_MODE, FIELD_MODE_SINGLE);
+        modes.setValues(initial);
+
+        return modes;
+    }
+
+
+    @Override
+    public List<String> validate() {
+        if (isRangeMode()) {
+            return validateRangeValues();
+        }
+        else {
+            return validateSingleValues();
+        }
+    }
+
+
+    protected List<String> validateRangeValues() {
+        if (isWMode()) {
+            return validateRange(wRangePanel, minW, maxW);
+        }
+        else {
+            return validateRange(qRangePanel, minQ, maxQ);
+        }
+    }
+
+
+    protected List<String> validateSingleValues() {
+        if (isWMode()) {
+            return validateSingle(wArrayPanel, minW, maxW);
+        }
+        else {
+            return validateSingle(qArrayPanel, minQ, maxQ);
+        }
+    }
+
+
+    protected List<String> validateRange(
+        DoubleRangePanel panel,
+        double min, double max)
+    {
+        List<String> errors = new ArrayList<String>();
+        NumberFormat nf     = NumberFormat.getDecimalFormat();
+
+        if (!panel.validateForm()) {
+            errors.add(MESSAGE.wrongFormat());
+        }
+
+        double from = panel.getFrom();
+        double to   = panel.getTo();
+
+        if (from < min || from > max) {
+            String tmp = MESSAGE.error_validate_lower_range();
+            tmp = tmp.replace("$1", nf.format(from));
+            tmp = tmp.replace("$2", nf.format(min));
+            errors.add(tmp);
+            from = min;
+        }
+
+        if (to < min || to > max) {
+            String tmp = MESSAGE.error_validate_upper_range();
+            tmp = tmp.replace("$1", nf.format(to));
+            tmp = tmp.replace("$2", nf.format(max));
+            errors.add(tmp);
+            to = max;
+        }
+
+        return errors;
+    }
+
+
+    protected List<String> validateSingle(
+        DoubleArrayPanel panel,
+        double min, double max)
+    {
+        List<String> errors = new ArrayList<String>();
+        NumberFormat nf     = NumberFormat.getDecimalFormat();
+
+        if (!panel.validateForm()) {
+            errors.add(MESSAGE.wrongFormat());
+        }
+
+        double[] values = panel.getInputValues();
+        double[] good   = new double[values.length];
+        int      idx    = 0;
+
+        for (double value: values) {
+            if (value < min || value > max) {
+                String tmp = MESSAGE.error_validate_range();
+                tmp = tmp.replace("$1", nf.format(value));
+                tmp = tmp.replace("$2", nf.format(min));
+                tmp = tmp.replace("$3", nf.format(max));
+                errors.add(tmp);
+            }
+            else {
+                good[idx++] = value;
+            }
+        }
+
+        double[] justGood = new double[idx];
+        for (int i = 0; i < justGood.length; i++) {
+            justGood[i] = good[i];
+        }
+
+        if (!errors.isEmpty()) {
+            panel.setValues(justGood);
+        }
+
+        return errors;
+    }
+
+
+    /**
+     * 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(),
+                getQMode(),
+                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(),
+                getQMode(),
+                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);
+
+        String value = null;
+        if (wqMode.equals(FIELD_WQ_Q) || wqMode.equals(FIELD_WQ_Q_GAUGE)) {
+            value = FIELD_WQ_Q;
+        }
+        else {
+            value = FIELD_WQ_W;
+        }
+
+        DataItem item = new DefaultDataItem("wq_mode", "wq_mode", value);
+        return new DefaultData(
+            "wq_mode", null, null, new DataItem[] { item });
+    }
+
+
+    /**
+     * Returns the Q mode. The Q mode can be "true" or "false". True means, the
+     * calculation is not based on a gauge, false means the calculation should
+     * be based on a gauge.
+     *
+     * @return the Data object for the 'wq_free' attribute.
+     */
+    protected Data getQMode() {
+        String value = isQFree() ? "true" : "false";
+
+        DataItem item = new DefaultDataItem("wq_free", "wq_free", value);
+        return new DefaultData(
+            "wq_free", 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);
+    }
+
+
+    /**
+     * 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);
+    }
+
+
+    public boolean isQFree() {
+        String wqMode = modes.getValueAsString(FIELD_WQ);
+        return wqMode.equals(FIELD_WQ_Q);
+    }
+
+
+    /**
+     * 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());
+
+        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);
+            }
+            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);
+            }
+
+            tabs.selectTab(0);
+        }
+        else {
+            if (inputMode.equals(FIELD_MODE_SINGLE)) {
+                // Single Q mode
+                double[] values = getSingleQ();
+
+                qArrayPanel = new DoubleArrayPanel(
+                    MESSAGE.unitQSingle(), values, this);
+                container.addMember(qArrayPanel);
+            }
+            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);
+            }
+
+            tabs.selectTab(1);
+        }
+    }
+
+    /**
+     * 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;
+    }
+
+
+    /**
+     * Determines the min and max kilometer value selected in a former state. A
+     * bit silly, but we need to run over each value of the "old data" to find
+     * such values because it is not available here.
+     *
+     * @param data The DataList which contains the whole data inserted for the
+     * current artifact.
+     *
+     * @return a double array with [min, max].
+     */
+    protected double[] getMinMaxKM(DataList[] data) {
+        int     num = data != null ? data.length : 0;
+        double[] mm = new double[] { Double.MAX_VALUE, -Double.MAX_VALUE };
+
+        for (int i = 0; i < num; i++) {
+            DataList dl = data[i];
+
+            if (dl.getState().equals("state.winfo.location_distance")) {
+                for (int j = 0, n = dl.size(); j < n; j++) {
+                    Data d = dl.get(j);
+
+                    String label = d.getLabel();
+                    if (label.equals("ld_step") || label.equals("ld_mode")) {
+                        continue;
+                    }
+
+                    for (DataItem item: d.getItems()) {
+                        String itemValue = item.getStringValue();
+
+                        try {
+                            double v = Double.valueOf(itemValue);
+
+                            mm[0] = mm[0] < v ? mm[0] : v;
+                            mm[1] = mm[1] > v ? mm[1] : v;
+                        }
+                        catch (NumberFormatException nfe) {
+                            // do nothing
+                        }
+                    }
+                }
+            }
+        }
+
+        return mm;
+    }
+
+
+    /**
+     * Returns the name of the selected river.
+     *
+     * @param data The DataList with all data.
+     *
+     * @return the name of the current river.
+     */
+    protected String getRiverName(DataList[] 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) {
+                           return d.getItems()[0].getStringValue();
+                        }
+                    }
+                }
+            }
+        }
+
+        return null;
+    }
+
+
+    protected void fetchWQData() {
+        Config config    = Config.getInstance();
+        String url       = config.getServerUrl();
+        String locale    = config.getLocale ();
+
+        ArtifactDescription adescr = artifact.getArtifactDescription();
+        DataList[] data = adescr.getOldData();
+
+        double[]  mm = getMinMaxKM(data);
+        String river = getRiverName(data);
+
+        wqInfoService.getWQInfo(url, locale, river, mm[0], mm[1],
+            new AsyncCallback<WQInfoObject[]>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("Could not recieve wq informations.");
+                    SC.warn(caught.getMessage());
+                }
+
+                public void onSuccess(WQInfoObject[] wqi) {
+                    int num = wqi != null ? wqi.length :0;
+                    GWT.log("Recieved " + num + " wq informations.");
+
+                    if (num == 0) {
+                        return;
+                    }
+
+                    addWQInfo(wqi);
+
+                    String wq = (String) modes.getValue(FIELD_WQ);
+                    String sr = (String) modes.getValue(FIELD_MODE);
+                    updatePanels(wq, sr);
+                }
+            }
+        );
+    }
+
+
+    protected void addWQInfo (WQInfoObject[] wqi) {
+        for(WQInfoObject wi: wqi) {
+            WQInfoRecord rec = new WQInfoRecord(wi);
+
+            if (wi.getType().equals("W")) {
+                wTable.addData(rec);
+            }
+            else {
+                qdTable.addData(rec);
+            }
+        }
+    }
+}
+// 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/chart/ChartOutputTab.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,559 @@
+package de.intevation.flys.client.client.ui.chart;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.types.Overflow;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Img;
+
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.layout.VLayout;
+
+import com.smartgwt.client.widgets.events.ResizedEvent;
+import com.smartgwt.client.widgets.events.ResizedHandler;
+
+import de.intevation.flys.client.shared.Transform2D;
+import de.intevation.flys.client.shared.model.Axis;
+import de.intevation.flys.client.shared.model.ChartInfo;
+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.client.event.OutputParameterChangeEvent;
+import de.intevation.flys.client.client.event.OutputParameterChangeHandler;
+import de.intevation.flys.client.client.event.PanEvent;
+import de.intevation.flys.client.client.event.PanHandler;
+import de.intevation.flys.client.client.event.ZoomEvent;
+import de.intevation.flys.client.client.event.ZoomHandler;
+import de.intevation.flys.client.client.services.ChartInfoService;
+import de.intevation.flys.client.client.services.ChartInfoServiceAsync;
+import de.intevation.flys.client.client.ui.CollectionView;
+import de.intevation.flys.client.client.ui.OutputTab;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartOutputTab
+extends      OutputTab
+implements   ResizedHandler, OutputParameterChangeHandler, ZoomHandler,
+             PanHandler
+{
+    public static final int DEFAULT_CHART_WIDTH  = 600;
+    public static final int DEFAULT_CHART_HEIGHT = 500;
+
+    public static final int THEMEPANEL_MIN_WIDTH = 200;
+
+
+    /** The service that is used to fetch chart information.*/
+    protected ChartInfoServiceAsync info = GWT.create(ChartInfoService.class);
+
+
+    /** The ChartInfo object that provides information about the current
+     * chart.*/
+    protected ChartInfo chartInfo;
+
+    /** The transformer used to transform image pixels into chart coordinates.*/
+    protected Transform2D[] transformer;
+
+    /** The collection view.*/
+    protected CollectionView view;
+
+
+    /** 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;
+
+    protected Img chart;
+
+
+    /** Chart zoom options.*/
+    protected int[] xrange;
+    protected int[] yrange;
+
+    protected double[] zoom;
+
+
+    /**
+     * 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);
+
+        view      = collectionView;
+        left      = new Canvas();
+        right     = new Canvas();
+        tbarPanel = new ChartToolbar(collectionView, this);
+        xrange    = new int[2];
+        yrange    = new int[2];
+        zoom      = new double[4];
+
+        left.setBorder("1px solid black");
+        left.setWidth(THEMEPANEL_MIN_WIDTH);
+        left.setMinWidth(THEMEPANEL_MIN_WIDTH);
+        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);
+
+        ChartThemePanel ctp = new ChartThemePanel(collection, mode);
+        ctp.addOutputParameterChangeHandler(this);
+
+        chart = createChartImg();
+        right.addChild(chart);
+        right.setOverflow(Overflow.HIDDEN);
+        //right.addChild(createChartPanel());
+        left.addChild(ctp);
+
+        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) {
+        updateChartPanel();
+        updateChartInfo();
+    }
+
+
+    /**
+     * Listens to change event in the chart them panel and updates chart after
+     * receiving such an event.
+     *
+     * @param event The OutputParameterChangeEvent.
+     */
+    public void onOutputParameterChanged(OutputParameterChangeEvent event) {
+        updateChartPanel();
+    }
+
+
+    /**
+     * Listens to zoom events and refreshes the current chart in such case.
+     *
+     * @param evt The ZoomEvent that stores the coordinates for zooming.
+     */
+    public void onZoom(ZoomEvent evt) {
+        xrange[0] = evt.getStartX();
+        xrange[1] = evt.getEndX();
+        yrange[0] = evt.getStartY();
+        yrange[1] = evt.getEndY();
+
+        xrange[0] = xrange[0] < xrange[1] ? xrange[0] : xrange[1];
+        yrange[0] = yrange[0] < yrange[1] ? yrange[0] : yrange[1];
+
+        translateCoordinates();
+
+        updateChartInfo();
+        updateChartPanel();
+    }
+
+
+    protected double[] translateCoordinates() {
+        if (xrange == null || (xrange[0] == 0 && xrange[1] == 0)) {
+            zoom[0] = 0d;
+            zoom[1] = 1d;
+        }
+        else {
+            translateXCoordinates();
+        }
+
+        if (yrange == null || (yrange[0] == 0 && yrange[1] == 0)) {
+            zoom[2] = 0d;
+            zoom[3] = 1d;
+        }
+        else {
+            translateYCoordinates();
+        }
+
+        return zoom;
+    }
+
+
+    protected void translateXCoordinates() {
+        Axis xAxis = chartInfo.getXAxis(0);
+
+        double xmin   = xAxis.getMin();
+        double xmax   = xAxis.getMax();
+        double xRange = xmax - xmin;
+
+        Transform2D transformer = getTransformer(0);
+
+        double[] start = transformer.transform(xrange[0], yrange[0]);
+        double[] end   = transformer.transform(xrange[1], yrange[1]);
+
+        zoom[0] = (start[0] - xmin) / xRange;
+        zoom[1] = (end[0] - xmin) / xRange;
+    }
+
+
+    protected void translateYCoordinates() {
+        Axis yAxis = chartInfo.getYAxis(0);
+
+        double ymin   = yAxis.getMin();
+        double ymax   = yAxis.getMax();
+        double yRange = ymax - ymin;
+
+        Transform2D transformer = getTransformer(0);
+
+        double[] start = transformer.transform(xrange[0], yrange[0]);
+        double[] end   = transformer.transform(xrange[1], yrange[1]);
+
+        zoom[2] = (start[1] - ymin) / yRange;
+        zoom[3] = (end[1] - ymin) / yRange;
+    }
+
+
+    public void onPan(PanEvent event) {
+        if (chartInfo == null) {
+            return;
+        }
+
+        int[] start = event.getStartPos();
+        int[] end   = event.getEndPos();
+
+        Transform2D t = getTransformer();
+
+        double[] ts = t.transform(start[0], start[1]);
+        double[] tt = t.transform(end[0], end[1]);
+
+        double diffX = ts[0] - tt[0];
+        double diffY = ts[1] - tt[1];
+
+        Axis xAxis = chartInfo.getXAxis(0);
+        Axis yAxis = chartInfo.getYAxis(0);
+
+        double[] x = panAxis(xAxis, diffX);
+        double[] y = panAxis(yAxis, diffY);
+
+        zoom[0] = x[0];
+        zoom[1] = x[1];
+        zoom[2] = y[0];
+        zoom[3] = y[1];
+
+        updateChartInfo();
+        updateChartPanel();
+    }
+
+
+    protected double[] panAxis(Axis axis, double diff) {
+        double min = axis.getFrom();
+        double max = axis.getTo();
+
+        min += diff;
+        max += diff;
+
+        return computeZoom(axis, min, max);
+    }
+
+
+    public void resetRanges() {
+        zoom[0] = 0d;
+        zoom[1] = 1d;
+        zoom[2] = 0d;
+        zoom[3] = 1d;
+
+        updateChartInfo();
+        updateChartPanel();
+    }
+
+
+    /**
+     * This method is used to zoom out.
+     *
+     * @param factor The factor should be between 0-100.
+     */
+    public void zoomOut(int factor) {
+        if (factor < 0 || factor > 100 || chartInfo == null) {
+            return;
+        }
+
+        Axis xAxis = chartInfo.getXAxis(0);
+        Axis yAxis = chartInfo.getYAxis(0);
+
+        GWT.log("ZOOM X");
+        double[] x = zoomAxis(xAxis, factor);
+
+        GWT.log("ZOOM Y");
+        double[] y = zoomAxis(yAxis, factor);
+
+        zoom[0] = x[0];
+        zoom[1] = x[1];
+        zoom[2] = x[0];
+        zoom[3] = y[1];
+
+        updateChartInfo();
+        updateChartPanel();
+    }
+
+
+    public double[] zoomAxis(Axis axis, int factor) {
+        double min  = axis.getFrom();
+        double max  = axis.getTo();
+
+        GWT.log("ZOOM CURRENT: " + min + " - " + max);
+
+        double add = (max - min) / 100 * factor;
+        add = add < 0 ? (-1) * add : add;
+
+        min -= add;
+        max += add;
+
+        GWT.log("ZOOM TO: " + min + " - " + max);
+
+        return computeZoom(axis, min, max);
+    }
+
+
+    public static double[] computeZoom(Axis axis, double min, double max) {
+        double[] zoom = new double[2];
+
+        double absMin = axis.getMin();
+        double absMax = axis.getMax();
+        double diff   = absMax > absMin ? absMax - absMin : absMin - absMax;
+
+        zoom[0] = (min - absMin) / diff;
+        zoom[1] = (max - absMin) / diff;
+
+        return zoom;
+    }
+
+
+    /**
+     * Updates the Transform2D object using the chart info service.
+     */
+    public void updateChartInfo() {
+        Config config = Config.getInstance();
+        String url    = config.getServerUrl();
+        String locale = config.getLocale();
+
+        info.getChartInfo(
+            view.getCollection(),
+            url,
+            locale,
+            mode.getName(),
+            getChartAttributes(),
+            new AsyncCallback<ChartInfo>() {
+                public void onFailure(Throwable caught) {
+                    GWT.log("ChartInfo ERROR: " + caught.getMessage());
+                }
+
+                public void onSuccess(ChartInfo chartInfo) {
+                    setChartInfo(chartInfo);
+                }
+            });
+    }
+
+
+    public void updateChartPanel() {
+        int w = right.getWidth();
+        int h = right.getHeight();
+
+        chart.setSrc(getImgUrl(w, h));
+    }
+
+
+    /**
+     * Returns the existing chart panel.
+     *
+     * @return the existing chart panel.
+     */
+    public Canvas getChartPanel() {
+        return right;
+    }
+
+
+    public Canvas getChartImg() {
+        return chart;
+    }
+
+
+    public ChartInfo getChartInfo() {
+        return chartInfo;
+    }
+
+
+    protected void setChartInfo(ChartInfo chartInfo) {
+        this.chartInfo = chartInfo;
+    }
+
+
+    public Transform2D getTransformer() {
+        if (chartInfo == null) {
+            return null;
+        }
+
+        return chartInfo.getTransformer(0);
+    }
+
+
+    /**
+     * Returns the Transform2D object used to transform image coordinates into
+     * chart coordinates.
+     *
+     * @param pos The index of a specific transformer.
+     *
+     * @return the Transform2D object.
+     */
+    public Transform2D getTransformer(int pos) {
+        if (chartInfo == null) {
+            return null;
+        }
+
+        return chartInfo.getTransformer(pos);
+    }
+
+
+    /**
+     * Creates a new chart panel with default size.
+     *
+     * @return the created chart panel.
+     */
+    protected Img createChartImg() {
+        return createChartImg(DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT);
+    }
+
+
+    /**
+     * Creates a new chart panel with specified width and height.
+     *
+     * @param width The width for the chart panel.
+     * @param height The height for the chart panel.
+     *
+     * @return the created chart panel.
+     */
+    protected Img createChartImg(int width, int height) {
+        Img chart  = getChartImg(width, height);
+        chart.setWidth100();
+        chart.setHeight100();
+
+        return chart;
+    }
+
+
+    /**
+     * 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.
+     * @param xr Optional x range (used for zooming).
+     * @param yr Optional y range (used for zooming).
+     *
+     * @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);
+
+        double[] zoom = getZoomValues();
+
+        if (zoom != null) {
+            if (zoom[0] != 0 || zoom[1] != 1) {
+                // a zoom range of 0-1 means displaying the whole range. In such
+                // case we don't need to zoom.
+                imgUrl += "&minx=" + Double.toString(zoom[0]);
+                imgUrl += "&maxx=" + Double.toString(zoom[1]);
+            }
+            if (zoom[2] != 0 || zoom[3] != 1) {
+                // a zoom range of 0-1 means displaying the whole range. In such
+                // case we don't need to zoom.
+                imgUrl += "&miny=" + Double.toString(zoom[2]);
+                imgUrl += "&maxy=" + Double.toString(zoom[3]);
+            }
+        }
+
+        return imgUrl;
+    }
+
+
+    public Map getChartAttributes() {
+        Map<String, String> attr = new HashMap<String, String>();
+
+        Canvas chart = getChartPanel();
+        attr.put("width", chart.getWidth().toString());
+        attr.put("height", chart.getHeight().toString());
+
+        double[] zoom = getZoomValues();
+
+        if (zoom != null) {
+            if (zoom[0] != 0 || zoom[1] != 1) {
+                // a zoom range of 0-1 means displaying the whole range. In such
+                // case we don't need to zoom.
+                attr.put("minx", Double.toString(zoom[0]));
+                attr.put("maxx", Double.toString(zoom[1]));
+            }
+            if (zoom[2] != 0 || zoom[3] != 1) {
+                // a zoom range of 0-1 means displaying the whole range. In such
+                // case we don't need to zoom.
+                attr.put("miny", Double.toString(zoom[2]));
+                attr.put("maxy", Double.toString(zoom[3]));
+            }
+        }
+
+        return attr;
+    }
+
+
+    protected double[] getZoomValues() {
+        return zoom;
+    }
+}
+// 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/chart/ChartThemePanel.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,425 @@
+package de.intevation.flys.client.client.ui.chart;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.user.client.rpc.AsyncCallback;
+
+import com.smartgwt.client.types.ListGridFieldType;
+import com.smartgwt.client.util.SC;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.grid.events.EditCompleteEvent;
+import com.smartgwt.client.widgets.grid.events.EditCompleteHandler;
+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.layout.VLayout;
+
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.FacetRecord;
+import de.intevation.flys.client.shared.model.OutputMode;
+import de.intevation.flys.client.shared.model.Theme;
+import de.intevation.flys.client.shared.model.ThemeList;
+
+import de.intevation.flys.client.client.Config;
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.event.HasOutputParameterChangeHandlers;
+import de.intevation.flys.client.client.event.OnMoveEvent;
+import de.intevation.flys.client.client.event.OnMoveHandler;
+import de.intevation.flys.client.client.event.OutputParameterChangeEvent;
+import de.intevation.flys.client.client.event.OutputParameterChangeHandler;
+import de.intevation.flys.client.client.services.CollectionAttributeService;
+import de.intevation.flys.client.client.services.CollectionAttributeServiceAsync;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartThemePanel
+extends      Canvas
+implements   EditCompleteHandler, OnMoveHandler,
+             HasOutputParameterChangeHandlers
+{
+    /** The interface that provides i18n messages. */
+    private FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+
+    /** The service that is used to modify collection attributes.*/
+    protected CollectionAttributeServiceAsync updater =
+        GWT.create(CollectionAttributeService.class);
+
+
+    public static final String GRID_FIELD_ACTIVE = "active";
+    public static final String GRID_FIELD_NAME   = "name";
+
+
+    protected Collection collection;
+
+    protected OutputMode mode;
+
+    protected List<OutputParameterChangeHandler> outHandlers;
+
+    protected ListGrid list;
+
+    protected ChartOutputTab chartOut;
+
+    protected ThemeNavigationPanel navigation;
+
+
+
+    public ChartThemePanel(Collection collection, OutputMode mode) {
+        this.collection  = collection;
+        this.mode        = mode;
+        this.outHandlers = new ArrayList<OutputParameterChangeHandler>();
+        this.chartOut    = chartOut;
+        this.list        = new ListGrid();
+        this.navigation  = new ThemeNavigationPanel();
+        this.navigation.addOnMoveHandler(this);
+
+        initGrid();
+        initLayout();
+
+        updateGrid();
+    }
+
+
+    /**
+     * Initializes the layout of this panel.
+     */
+    protected void initLayout() {
+        setWidth100();
+        setHeight100();
+
+        VLayout layout = new VLayout();
+        layout.setWidth100();
+        layout.setHeight100();
+
+        layout.addMember(list);
+        layout.addMember(navigation);
+
+        addChild(layout);
+    }
+
+
+    /**
+     * Initializes the components (columns) of the theme grid.
+     */
+    protected void initGrid() {
+        list.setCanEdit(true);
+        list.setCanSort(false);
+        list.setShowRecordComponents(false);
+        list.setShowRecordComponentsByCell(true);
+        list.setShowHeader(true);
+        list.setShowHeaderContextMenu(false);
+        list.setWidth100();
+        list.setHeight100();
+
+        list.addEditCompleteHandler(this);
+
+        ListGridField active = new ListGridField(GRID_FIELD_ACTIVE, " ", 20);
+        active.setType(ListGridFieldType.BOOLEAN);
+
+        ListGridField name = new ListGridField(
+            GRID_FIELD_NAME, MSG.chart_themepanel_header_themes());
+        name.setType(ListGridFieldType.TEXT);
+
+        list.setFields(active, name);
+    }
+
+
+    /**
+     * Replace the current collection with a new one. <b>NOTE: this operation
+     * triggers updateGrid() which modifies the themes in the grid.</b>
+     *
+     * @param collection The new collection object.
+     */
+    protected void setCollection(Collection collection) {
+        this.collection = collection;
+
+        updateGrid();
+    }
+
+
+    /**
+     * Registers a new OutputParameterChangeHandler.
+     *
+     * @param h The new handler.
+     */
+    public void addOutputParameterChangeHandler(OutputParameterChangeHandler h){
+        if (h != null) {
+            outHandlers.add(h);
+        }
+    }
+
+
+    /**
+     * Returns the ThemeList of the current collection and output mode.
+     *
+     * @return the current ThemeList.
+     */
+    public ThemeList getThemeList() {
+        return collection.getThemeList(mode.getName());
+    }
+
+
+    /**
+     * A method that removes all records from theme grid.
+     */
+    protected void clearGrid() {
+        ListGridRecord[] records = list.getRecords();
+
+        if (records == null || records.length == 0) {
+            return;
+        }
+
+        for (ListGridRecord record: records) {
+            list.removeData(record);
+        }
+    }
+
+
+    /**
+     * This method is used to clear the current theme grid and add new updated
+     * data.
+     */
+    protected void updateGrid() {
+        GWT.log("ChartThemePanel.updateGrid");
+
+        clearGrid();
+
+        ThemeList themeList = getThemeList();
+
+        if (themeList == null) {
+            GWT.log("ERROR: No theme list.");
+            return;
+        }
+
+        int count = themeList.getThemeCount();
+
+        for (int i = 1; i <= count; i++) {
+            Theme theme = themeList.getThemeAt(i);
+
+            if (theme == null) {
+                continue;
+            }
+
+            list.addData(new FacetRecord(theme));
+        }
+
+        fireOutputParameterChanged();
+    }
+
+
+    /**
+     * Called when the attribution of a chart changed. It informs the registered
+     * handlers about the changes.
+     */
+    protected void fireOutputParameterChanged() {
+        OutputParameterChangeEvent evt = new OutputParameterChangeEvent();
+
+        for (OutputParameterChangeHandler handler: outHandlers) {
+            handler.onOutputParameterChanged(evt);
+        }
+    }
+
+
+    /**
+     * This method is called after a cell in the theme grid has been modified.
+     *
+     * @param event The event that stores information about the modified record.
+     */
+    public void onEditComplete(EditCompleteEvent event) {
+        GWT.log("Edited record.");
+
+        int         row = event.getRowNum();
+        FacetRecord rec = (FacetRecord) list.getRecord(row);
+
+        Theme theme = rec.getTheme();
+        theme.setActive(rec.getActive());
+
+        updateThemeList(rec.getTheme());
+    }
+
+
+    /**
+     * Update the theme list of the current collection with a modified theme. If
+     * a theme is really modified, the CollectionAttributeService is triggered
+     * to save the changes to the artifact server.
+     *
+     * @param theme The modified theme.
+     */
+    protected void updateThemeList(Theme theme) {
+        GWT.log("Update theme: " + theme.getFacet());
+
+        ThemeList themeList = getThemeList();
+
+        String a = theme.getArtifact();
+        String f = theme.getFacet();
+
+        int num = themeList != null ? themeList.getThemeCount() : 0;
+
+        boolean updateRequired = false;
+
+        for (int i = 1; i <= num; i++) {
+            Theme old = themeList.getThemeAt(i);
+
+            if (f.equals(old.getFacet()) && a.equals(old.getArtifact())) {
+                themeList.removeTheme(old);
+                themeList.addTheme(theme);
+
+                updateRequired = true;
+
+                break;
+            }
+        }
+
+        if (updateRequired) {
+            updateCollection();
+        }
+    }
+
+
+    /**
+     * This method triggers the CollectionAttributeService. Based on the current
+     * collectin settings, the attribute of the collection is modified or not.
+     * But in every case, we will get a new collection object - which might be
+     * the same as the current one.
+     */
+    public void updateCollection() {
+        final Config config = Config.getInstance();
+        final String url    = config.getServerUrl();
+        final String loc    = config.getLocale();
+
+        GWT.log("ChartThemePanel.updateCollection via RPC now");
+
+        // don't forget to enable the panel after the request has finished!
+        disable();
+
+        updater.update(collection, url, loc, new AsyncCallback<Collection>() {
+            public void onFailure(Throwable caught) {
+                GWT.log("Could not update collection attributes.");
+                SC.warn(MSG.getString(caught.getMessage()));
+
+                enable();
+            }
+
+
+            public void onSuccess(Collection collection) {
+                setCollection(collection);
+
+                enable();
+            }
+        });
+    }
+
+
+    public void onMove(OnMoveEvent event) {
+        int type = event.getType();
+
+        GWT.log("ChartThemePanel.onMove: " + type);
+
+        ListGridRecord[] records = list.getSelection();
+
+        if (records == null || records.length == 0) {
+            GWT.log("ChartThemePanel.onMove: No records selected.");
+            return;
+        }
+
+        switch (type) {
+            case 0: moveRecordsTop(records); break;
+            case 1: moveRecordsUp(records); break;
+            case 2: moveRecordsDown(records); break;
+            case 3: moveRecordsBottom(records); break;
+        }
+
+        updateCollection();
+    }
+
+
+    /**
+     * Moves the selected grid records (themes) to the top of the grid.
+     *
+     * @param records The selected themes in the list. Null not permitted.
+     */
+    protected void moveRecordsTop(ListGridRecord[] records) {
+        ThemeList themeList = getThemeList();
+
+        int idx = 1;
+
+        for (ListGridRecord record: records) {
+            Theme theme = ((FacetRecord) record).getTheme();
+            themeList.setThemePosition(theme, idx++);
+        }
+
+        updateGrid();
+    }
+
+
+    /**
+     * Moves the selected grid records (themes) one step up.
+     *
+     * @param records The selected themes in the list. Null not permitted.
+     */
+    protected void moveRecordsUp(ListGridRecord[] records) {
+        ThemeList themeList = getThemeList();
+
+        int[] newPos = new int[records.length];
+
+        for (int i = 0; i < records.length ; i++) {
+            Theme theme = ((FacetRecord) records[i]).getTheme();
+            newPos[i]   = theme.getPosition() - 1;
+        }
+
+        for (int i = 0; i < records.length ; i++) {
+            Theme theme = ((FacetRecord) records[i]).getTheme();
+            themeList.setThemePosition(theme, newPos[i]);
+        }
+
+        updateGrid();
+    }
+
+
+    /**
+     * Moves the selected grid records (themes) one step down.
+     *
+     * @param records The selected themes in the list. Null not permitted.
+     */
+    protected void moveRecordsDown(ListGridRecord[] records) {
+        ThemeList themeList = getThemeList();
+
+        int[] newPos = new int[records.length];
+
+        for (int i = records.length-1; i >= 0; i--) {
+            Theme theme = ((FacetRecord) records[i]).getTheme();
+            newPos[i] = theme.getPosition()+1;
+        }
+
+        for (int i = records.length-1; i >= 0; i--) {
+            Theme theme = ((FacetRecord) records[i]).getTheme();
+            themeList.setThemePosition(theme, newPos[i]);
+        }
+
+        updateGrid();
+    }
+
+
+    /**
+     * Moves the selected grid records (themes) to the bottom of the grid.
+     *
+     * @param records The selected themes in the list. Null not permitted.
+     */
+    protected void moveRecordsBottom(ListGridRecord[] records) {
+        ThemeList themeList = getThemeList();
+
+        int idx = themeList.getThemeCount();
+
+        for (int i = records.length-1; i >= 0; i--) {
+            Theme theme = ((FacetRecord) records[i]).getTheme();
+            themeList.setThemePosition(theme, idx--);
+        }
+
+        updateGrid();
+    }
+}
+// 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/chart/ChartToolbar.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,162 @@
+package de.intevation.flys.client.client.ui.chart;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.widgets.Button;
+import com.smartgwt.client.widgets.ImgButton;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.layout.HLayout;
+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 de.intevation.flys.client.shared.model.Artifact;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.ui.CollectionView;
+import de.intevation.flys.client.client.ui.DatacageWindow;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartToolbar extends HLayout {
+
+    protected static FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+
+    public static final int PANEL_HEIGHT = 30;
+
+
+    protected CollectionView view;
+
+    protected ChartOutputTab chartTab;
+
+
+    protected Button datacage;
+
+    protected MousePositionPanel position;
+
+    protected ZoomboxControl zoombox;
+
+    protected ImgButton zoomToMaxExtent;
+
+    protected ImgButton zoomOut;
+
+    protected PanControl panControl;
+
+
+
+    public ChartToolbar(CollectionView view, ChartOutputTab chartTab) {
+        super();
+
+        this.view       = view;
+        this.chartTab   = chartTab;
+
+        datacage        = new Button(MSG.databasket());
+        position        = new MousePositionPanel(chartTab);
+        zoombox         = new ZoomboxControl(chartTab, MSG.zoom_in());
+        zoomToMaxExtent = new ImgButton();
+        zoomOut         = new ImgButton();
+        panControl      = new PanControl(chartTab, MSG.pan());
+
+        datacage.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                GWT.log("Clicked 'datacage' button.");
+                openDatacageWindow(getArtifact());
+            }
+        });
+
+        String baseUrl = GWT.getHostPageBaseURL();
+        zoomToMaxExtent.setSrc(baseUrl + MSG.zoom_all());
+        zoomToMaxExtent.setWidth(20);
+        zoomToMaxExtent.setHeight(20);
+        zoomToMaxExtent.setShowDown(false);
+        zoomToMaxExtent.setShowRollOver(false);
+        zoomToMaxExtent.setShowDisabled(false);
+        zoomToMaxExtent.setShowDisabledIcon(true);
+        zoomToMaxExtent.setShowDownIcon(false);
+        zoomToMaxExtent.setShowFocusedIcon(false);
+        zoomToMaxExtent.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                getChartOutputTab().resetRanges();
+            }
+        });
+
+        zoomOut.setSrc(baseUrl + MSG.zoom_out());
+        zoomOut.setWidth(20);
+        zoomOut.setHeight(20);
+        zoomOut.setShowDown(false);
+        zoomOut.setShowRollOver(false);
+        zoomOut.setShowDisabled(false);
+        zoomOut.setShowDisabledIcon(true);
+        zoomOut.setShowDownIcon(false);
+        zoomOut.setShowFocusedIcon(false);
+        zoomOut.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                getChartOutputTab().zoomOut(10);
+            }
+        });
+
+        zoombox.addZoomHandler(chartTab);
+        zoombox.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                panControl.deselect();
+            }
+        });
+
+        panControl.addPanHandler(chartTab);
+        panControl.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                zoombox.deselect();
+            }
+        });
+
+        initLayout();
+    }
+
+
+    public Artifact getArtifact() {
+        return view.getArtifact();
+    }
+
+
+    public ChartOutputTab getChartOutputTab() {
+        return chartTab;
+    }
+
+
+    protected void initLayout() {
+        setWidth100();
+        setHeight(PANEL_HEIGHT);
+        setMembersMargin(10);
+        setPadding(5);
+        setBorder("1px solid black");
+
+        Label spacer = new Label();
+        spacer.setWidth("*");
+        datacage.setWidth("95px");
+        position.setWidth("200px");
+
+        addMember(datacage);
+        addMember(zoomToMaxExtent);
+        addMember(zoomOut);
+        addMember(zoombox);
+        addMember(panControl);
+        addMember(spacer);
+        addMember(position);
+    }
+
+
+    protected void openDatacageWindow(Artifact artifact) {
+        final DatacageWindow dc = new DatacageWindow(artifact);
+        dc.addCloseClickHandler(new CloseClickHandler() {
+            public void onCloseClick(CloseClientEvent event) {
+                dc.destroy();
+            }
+        });
+        dc.show();
+    }
+}
+// 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/chart/MousePositionPanel.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,130 @@
+package de.intevation.flys.client.client.ui.chart;
+
+import com.google.gwt.i18n.client.NumberFormat;
+
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Label;
+import com.smartgwt.client.widgets.layout.HLayout;
+import com.smartgwt.client.widgets.events.MouseMoveEvent;
+import com.smartgwt.client.widgets.events.MouseMoveHandler;
+
+import de.intevation.flys.client.shared.Transform2D;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class MousePositionPanel extends HLayout implements MouseMoveHandler {
+
+    protected ChartOutputTab chartTab;
+
+    protected NumberFormat nf;
+
+    protected Label x;
+    protected Label y;
+
+
+    public MousePositionPanel(ChartOutputTab chartTab) {
+        super();
+
+        this.chartTab = chartTab;
+
+        x = new Label();
+        y = new Label();
+
+        nf = NumberFormat.getDecimalFormat();
+
+        chartTab.getChartPanel().addMouseMoveHandler(this);
+
+        initLayout();
+    }
+
+
+    /**
+     * Initializes the layout of this component. <b>Note:</b> This layout has a
+     * fixed width of 195px plus a margin of 5px.
+     */
+    protected void initLayout() {
+        setMembersMargin(5);
+
+        Label xDesc = new Label("Position: X = ");
+        Label yDesc  = new Label("Y = ");
+
+        HLayout xLayout = new HLayout();
+        xLayout.setWidth(125);
+        xLayout.addMember(xDesc);
+        xLayout.addMember(x);
+
+        HLayout yLayout = new HLayout();
+        yLayout.setWidth(70);
+        yLayout.addMember(yDesc);
+        yLayout.addMember(y);
+
+        xDesc.setWidth(70);
+        x.setWidth(55);
+        yDesc.setWidth(20);
+        y.setWidth(50);
+
+        addMember(xLayout);
+        addMember(yLayout);
+    }
+
+
+    /**
+     * /Updates the X value displayed in the <i>x</i> label.
+     *
+     * @param x the new x value.
+     */
+    public void setX(double x) {
+        this.x.setContents(nf.format(x));
+    }
+
+
+    /**
+     * /Updates the Y value displayed in the <i>y</i> label.
+     *
+     * @param y the new y value.
+     */
+    public void setY(double y) {
+        this.y.setContents(nf.format(y));
+    }
+
+
+    /**
+     * Listens to mouse move events to refresh the xy position.
+     *
+     * @param event The move event.
+     */
+    public void onMouseMove(MouseMoveEvent event) {
+        updateMousePosition(event.getX(), event.getY());
+    }
+
+
+    /**
+     * This method takes pixel coordinates, transforms those values into chart
+     * coordinates using the Transform2D class and updates the mouse position.
+     *
+     * @param x The x part of the pixel.
+     * @param y The y part of the pixel.
+     */
+    public void updateMousePosition(double x, double y) {
+        Transform2D transformer = chartTab.getTransformer();
+
+        if (transformer == null) {
+            return;
+        }
+
+        Canvas chart = chartTab.getChartPanel();
+        int xOffset = chart.getPageLeft();
+        int yOffset = chart.getPageTop();
+
+        x = x - xOffset;
+        y = y - yOffset;
+
+        double[] xy = transformer.transform(x,y);
+
+        setX(xy[0]);
+        setY(xy[1]);
+    }
+}
+// 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/chart/PanControl.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,210 @@
+package de.intevation.flys.client.client.ui.chart;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.SelectionType;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.Img;
+import com.smartgwt.client.widgets.ImgButton;
+import com.smartgwt.client.widgets.events.MouseDownEvent;
+import com.smartgwt.client.widgets.events.MouseDownHandler;
+import com.smartgwt.client.widgets.events.MouseMoveEvent;
+import com.smartgwt.client.widgets.events.MouseMoveHandler;
+import com.smartgwt.client.widgets.events.MouseOutEvent;
+import com.smartgwt.client.widgets.events.MouseOutHandler;
+import com.smartgwt.client.widgets.events.MouseUpEvent;
+import com.smartgwt.client.widgets.events.MouseUpHandler;
+
+import de.intevation.flys.client.client.event.HasPanHandlers;
+import de.intevation.flys.client.client.event.PanEvent;
+import de.intevation.flys.client.client.event.PanHandler;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class PanControl
+extends      ImgButton
+implements   MouseDownHandler, MouseMoveHandler, MouseUpHandler,
+             MouseOutHandler, HasPanHandlers
+{
+    protected ChartOutputTab chartTab;
+
+    protected List<PanHandler> handlers;
+
+    protected int[] start;
+    protected int[] end;
+
+
+    public PanControl(ChartOutputTab chartTab, String imageUrl) {
+        super();
+
+        this.chartTab = chartTab;
+        this.handlers = new ArrayList<PanHandler>();
+        this.start    = new int[] { -1, -1 };
+        this.end      = new int[] { -1, -1 };
+
+        String baseUrl = GWT.getHostPageBaseURL();
+        setSrc(baseUrl + imageUrl);
+        setActionType(SelectionType.CHECKBOX);
+        setSize(20);
+        setShowRollOver(false);
+        setSelected(false);
+
+        chartTab.getChartPanel().addMouseDownHandler(this);
+        chartTab.getChartPanel().addMouseMoveHandler(this);
+        chartTab.getChartPanel().addMouseUpHandler(this);
+        chartTab.getChartPanel().addMouseOutHandler(this);
+    }
+
+
+    /**
+     * Method used to register a new PanHandler.
+     *
+     * @param handler A new PanHandler.
+     */
+    public void addPanHandler(PanHandler handler) {
+        if (handler != null) {
+            handlers.add(handler);
+        }
+    }
+
+
+    /**
+     * This event starts the dragging operation if the control is activated.
+     *
+     * @param event The mouse down event which contains the start coordinates.
+     */
+    public void onMouseDown(MouseDownEvent event) {
+        if (!isSelected()) {
+            return;
+        }
+
+        start[0] = event.getX();
+        start[1] = event.getY();
+
+        end[0] = start[0];
+        end[1] = start[1];
+    }
+
+
+    /**
+     * This event is used to reposition the chart image based on the current
+     * drag operation.
+     *
+     * @param event The move event which contains the new coordinates to update
+     * the chart image position.
+     */
+    public void onMouseMove(MouseMoveEvent event) {
+        if (!isSelected() || start[0] == -1 || start[1] == -1) {
+            return;
+        }
+
+        int x = event.getX() - end[0];
+        int y = event.getY() - end[1];
+
+        end[0] = end[0] + x;
+        end[1] = end[1] + y;
+
+        Canvas c = chartTab.getChartImg();
+        c.moveBy(x, y);
+    }
+
+
+    /**
+     * This event stops the dragging operation and fires a DragEnd event to the
+     * registered listeners.
+     *
+     * @param event The mouse up event which contains the end coordinates.
+     */
+    public void onMouseUp(MouseUpEvent event) {
+        if (!isSelected()) {
+            return;
+        }
+
+        end[0] = event.getX();
+        end[1] = event.getY();
+
+        Canvas c = chartTab.getChartImg();
+        c.setLeft(0);
+        c.setTop(0);
+
+        fireOnPan();
+
+        start[0] = -1;
+        start[1] = -1;
+    }
+
+
+    /**
+     * This event is used to cancel the current dragging operation.
+     *
+     * @param event The mouse out event.
+     */
+    public void onMouseOut(MouseOutEvent event) {
+        int x = event.getX();
+        int y = event.getY();
+
+        if (!isSelected() || !isMouseOut(x, y) || start[0] == -1) {
+            return;
+        }
+
+        Canvas c = chartTab.getChartImg();
+        c.setLeft(0);
+        c.setTop(0);
+
+        fireOnPan();
+
+        start[0] = -1;
+        start[1] = -1;
+    }
+
+
+    /**
+     * This method is required to check manually if the mouse pointer really
+     * moves out the chart area. The MouseOutEvent is also fired if the mouse
+     * goes down which doesn't seem to be correct. So, we gonna check this
+     * manually.
+     *
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     *
+     * @return true, if the mouse is really out of the chart area, otherwise
+     * false.
+     */
+    protected boolean isMouseOut(int x, int y) {
+        Canvas chart = chartTab.getChartImg();
+
+        if (chart instanceof Img) {
+            chart = chart.getParentElement();
+        }
+
+        int left   = chart.getPageLeft();
+        int right  = chart.getPageRight();
+        int top    = chart.getPageTop();
+        int bottom = chart.getPageBottom();
+
+        if (x <= left || x >= right || y <= top || y >= bottom) {
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * A pan event is fired to inform the registered listeners about a pan
+     * operation has finished.
+     */
+    protected void fireOnPan() {
+        PanEvent event = new PanEvent(start, end);
+
+        for (PanHandler handler: handlers) {
+            handler.onPan(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/ui/chart/ThemeNavigationPanel.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,103 @@
+package de.intevation.flys.client.client.ui.chart;
+
+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.ImgButton;
+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.FLYSConstants;
+import de.intevation.flys.client.client.event.OnMoveEvent;
+import de.intevation.flys.client.client.event.OnMoveHandler;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ThemeNavigationPanel extends Canvas {
+
+    public static final int PANEL_MARGIN  = 5;
+    public static final int BUTTON_HEIGHT = 25;
+    public static final int BUTTON_MARGIN = 5;
+
+
+    protected FLYSConstants MSG = GWT.create(FLYSConstants.class);
+
+
+    protected List<OnMoveHandler> handlers;
+
+
+    public ThemeNavigationPanel() {
+        this.handlers = new ArrayList<OnMoveHandler>();
+
+        setWidth100();
+        setHeight(BUTTON_HEIGHT);
+        setMargin(PANEL_MARGIN);
+
+        HLayout layout = new HLayout();
+        layout.setWidth100();
+        layout.setHeight(BUTTON_HEIGHT);
+        layout.setMembersMargin(BUTTON_MARGIN);
+
+        Canvas cu = createButton(MSG.theme_top(), OnMoveEvent.TOP);
+        Canvas u  = createButton(MSG.theme_up(), OnMoveEvent.UP);
+        Canvas d  = createButton(MSG.theme_down(), OnMoveEvent.DOWN);
+        Canvas cd = createButton(MSG.theme_bottom(), OnMoveEvent.BOTTOM);
+
+        layout.addMember(cu);
+        layout.addMember(u);
+        layout.addMember(d);
+        layout.addMember(cd);
+
+        addChild(layout);
+    }
+
+
+    protected Canvas createButton(final String title, final int moveType) {
+        String url = GWT.getHostPageBaseURL() + title;
+
+        ImgButton b = new ImgButton();
+        b.setSrc(url);
+        b.setHeight(BUTTON_HEIGHT);
+        b.setWidth(40);
+        b.setIconHeight(BUTTON_HEIGHT-10);
+        b.setShowDown(false);
+        b.setShowRollOver(false);
+        b.setShowDisabled(false);
+        b.setShowDisabledIcon(true);
+        b.setShowDownIcon(false);
+        b.setShowFocusedIcon(false);
+        b.setBackgroundColor("f2f2f2");
+        b.setBorder("1px solid #A6ABB4");
+
+        b.addClickHandler(new ClickHandler() {
+            public void onClick(ClickEvent event) {
+                fireOnMoveEvent(moveType);
+            }
+        });
+
+        return b;
+    }
+
+
+    protected void addOnMoveHandler(OnMoveHandler handler) {
+        if (handler != null) {
+            handlers.add(handler);
+        }
+    }
+
+
+    protected void fireOnMoveEvent(int type) {
+        OnMoveEvent event = new OnMoveEvent(type);
+
+        for (OnMoveHandler handler: handlers) {
+            handler.onMove(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/ui/chart/ZoomboxControl.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,307 @@
+package de.intevation.flys.client.client.ui.chart;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.Positioning;
+import com.smartgwt.client.types.SelectionType;
+import com.smartgwt.client.widgets.ImgButton;
+import com.smartgwt.client.widgets.Canvas;
+import com.smartgwt.client.widgets.events.MouseDownEvent;
+import com.smartgwt.client.widgets.events.MouseDownHandler;
+import com.smartgwt.client.widgets.events.MouseMoveEvent;
+import com.smartgwt.client.widgets.events.MouseMoveHandler;
+import com.smartgwt.client.widgets.events.MouseOutEvent;
+import com.smartgwt.client.widgets.events.MouseOutHandler;
+import com.smartgwt.client.widgets.events.MouseUpEvent;
+import com.smartgwt.client.widgets.events.MouseUpHandler;
+
+import de.intevation.flys.client.client.event.HasZoomHandlers;
+import de.intevation.flys.client.client.event.ZoomEvent;
+import de.intevation.flys.client.client.event.ZoomHandler;
+
+
+/**
+ * This control observes that panel retrieved by ChartOutputTab.getChartPanel().
+ * If activated, a zoombox is drawn. One of the two edges is the position of the
+ * mouse down event on the observed panel. The other edge is specified by the
+ * current mouse position. If the mouse up event occurs, start and end point
+ * relative to the left and upper border of the observed panel is determined and
+ * a ZoomEvent is fired.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ZoomboxControl
+extends      ImgButton
+implements   MouseDownHandler, MouseUpHandler, MouseMoveHandler, HasZoomHandlers,
+             MouseOutHandler
+{
+    protected List<ZoomHandler> handlers;
+
+    protected ChartOutputTab chartTab;
+
+    protected Canvas zoombox;
+
+    protected int[] start;
+    protected int[] end;
+
+
+    public ZoomboxControl(ChartOutputTab chartTab, String imageUrl) {
+        super();
+
+        this.handlers = new ArrayList<ZoomHandler>();
+        this.chartTab = chartTab;
+        this.start    = new int[2];
+        this.end      = new int[2];
+        this.zoombox  = new Canvas();
+
+        initZoombox();
+
+        String baseUrl = GWT.getHostPageBaseURL();
+        setSrc(baseUrl + imageUrl);
+        setActionType(SelectionType.CHECKBOX);
+        setSize(20);
+        setShowRollOver(false);
+        setSelected(false);
+
+        chartTab.getChartPanel().addMouseDownHandler(this);
+        chartTab.getChartPanel().addMouseOutHandler(this);
+        chartTab.getChartPanel().addMouseMoveHandler(this);
+        chartTab.getChartPanel().addMouseUpHandler(this);
+    }
+
+
+    /**
+     * Initializes the zoombox that is displayed over the observed area. The
+     * zoombox has an opaque background. Its height/width and x/y values are
+     * determined by the start point (mouse down) and the current mouse
+     * position.
+     */
+    protected void initZoombox() {
+        zoombox.setPosition(Positioning.ABSOLUTE);
+        zoombox.setBorder("2px solid black");
+        zoombox.setOpacity(50);
+        zoombox.setWidth(0);
+        zoombox.setHeight(0);
+    }
+
+
+    /**
+     * Registers a new ZoomHandler that wants to listen to ZoomEvents.
+     *
+     * @param handler A new ZoomHandler.
+     */
+    public void addZoomHandler(ZoomHandler handler) {
+        if (handler != null) {
+            handlers.add(handler);
+        }
+    }
+
+
+    /**
+     * A mouse down event on the specified area will set the start point for the
+     * zoombox.
+     *
+     * @param event The mouse down event which contains the xy coordinates of
+     * the observed area.
+     */
+    public void onMouseDown(MouseDownEvent event) {
+        if (!isSelected()) {
+            return;
+        }
+
+        start[0] = getRelativeX(event.getX());
+        start[1] = getRelativeY(event.getY());
+
+        end[0] = start[0];
+        end[1] = start[1];
+
+        Canvas c = getChartPanel();
+        c.addChild(zoombox);
+
+        positionZoombox();
+    }
+
+
+    /**
+     * A mouse move event on the specified area will set the end point for the
+     * zoombox. If the end point differs from the start point, an opaque box is
+     * displayed.
+     *
+     * @param event The mouse move event which contains the xy coordinates of
+     * the observed area.
+     */
+    public void onMouseMove(MouseMoveEvent event) {
+        if (!isSelected()) {
+            return;
+        }
+
+        end[0] = getRelativeX(event.getX());
+        end[1] = getRelativeY(event.getY());
+
+        positionZoombox();
+    }
+
+
+    /**
+     * The mouse up event finalizes the zoom operation. It sets the end point
+     * for this operation, clears the zoombox and fires a ZoomEvent.
+     *
+     * @param event The mouse up event which contains the xy coordinates of the
+     * observed area.
+     */
+    public void onMouseUp(MouseUpEvent event) {
+        if (!isSelected()) {
+            return;
+        }
+
+        end[0] = getRelativeX(event.getX());
+        end[1] = getRelativeY(event.getY());
+
+        fireZoomEvent();
+
+        clearZoombox();
+    }
+
+
+    /**
+     * The mouse out event is used to cancel an active zoom operation.
+     *
+     * @param event The mouse out event.
+     */
+    public void onMouseOut(MouseOutEvent event) {
+        if (!isSelected() || !isMouseOut(event.getX(), event.getY())) {
+            return;
+        }
+
+        start[0] = 0;
+        start[1] = 0;
+        end[0] = 0;
+        end[1] = 0;
+
+        clearZoombox();
+    }
+
+
+    /**
+     * Returns the chart panel.
+     *
+     * @return the chart panel.
+     */
+    protected Canvas getChartPanel() {
+        return chartTab.getChartPanel();
+    }
+
+
+    /**
+     * This method is required to check manually if the mouse pointer really
+     * moves out the chart area. The MouseOutEvent is also fired if the mouse
+     * goes down which doesn't seem to be correct. So, we gonna check this
+     * manually.
+     *
+     * @param x The x coordinate.
+     * @param y The y coordinate.
+     *
+     * @return true, if the mouse is really out of the chart area, otherwise
+     * false.
+     */
+    protected boolean isMouseOut(int x, int y) {
+        Canvas chart = getChartPanel();
+
+        int left   = chart.getPageLeft();
+        int right  = chart.getPageRight();
+        int top    = chart.getPageTop();
+        int bottom = chart.getPageBottom();
+
+        if (x <= left || x >= right || y <= top || y >= bottom) {
+            return true;
+        }
+
+        return false;
+    }
+
+
+    /**
+     * Returns the X coordinate relative to the left border.
+     *
+     * @param x The X coordinate relative to the window.
+     *
+     * @return the X coordinate relative to the left border.
+     */
+    protected int getRelativeX(int x) {
+        return x - chartTab.getChartPanel().getPageLeft();
+    }
+
+
+    /**
+     * Returns the Y coordinate relative to the top border.
+     *
+     * @param y The Y coordinate relative to the window.
+     *
+     * @return the Y coordinate relative to the top border.
+     */
+    protected int getRelativeY(int y) {
+        return y - chartTab.getChartPanel().getPageTop();
+    }
+
+
+    /**
+     * Returns min and max x/y values based on the stored values in <i>start</i>
+     * and <i>end</i>.
+     *
+     * @return an int[] as follows: [xmin, ymin, xmax, ymax].
+     */
+    protected int[] orderPositions() {
+        int xmin = start[0] < end[0] ? start[0] : end[0];
+        int ymin = start[1] < end[1] ? start[1] : end[1];
+
+        int xmax = start[0] >= end[0] ? start[0] : end[0];
+        int ymax = start[1] >= end[1] ? start[1] : end[1];
+
+        return new int[] { xmin, ymin, xmax, ymax };
+    }
+
+
+    /**
+     * Sets the width, height, x and y values of the zoombox.
+     */
+    protected void positionZoombox() {
+        int[] values = orderPositions();
+
+        zoombox.setLeft(values[0]);
+        zoombox.setTop(values[1]);
+        zoombox.setWidth(values[2] - values[0]);
+        zoombox.setHeight(values[3] - values[1]);
+    }
+
+
+    /**
+     * Clears the zoombox (set position and size to null).
+     */
+    protected void clearZoombox() {
+        zoombox.setLeft(0);
+        zoombox.setTop(0);
+        zoombox.setWidth(0);
+        zoombox.setHeight(0);
+
+        Canvas c = getChartPanel();
+        c.removeChild(zoombox);
+    }
+
+
+    /**
+     * Fires a ZoomEvent to all registered listeners.
+     */
+    protected void fireZoomEvent() {
+        int[] pos = orderPositions();
+
+        ZoomEvent event = new ZoomEvent(pos[0], pos[1], pos[2], pos[3]);
+
+        for (ZoomHandler handler: handlers) {
+            handler.onZoom(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/ui/wq/QDTable.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,61 @@
+package de.intevation.flys.client.client.ui.wq;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.ListGridFieldType;
+import com.smartgwt.client.types.SelectionStyle;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+
+import de.intevation.flys.client.client.FLYSConstants;
+import de.intevation.flys.client.client.FLYSImages;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class QDTable extends ListGrid {
+
+    /** 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);
+
+
+    public QDTable() {
+        setWidth100();
+        setHeight100();
+        setSelectionType(SelectionStyle.SINGLE);
+        setSelectionType(SelectionStyle.SINGLE);
+        setShowHeaderContextMenu(false);
+        setShowRecordComponents(true);
+        setShowRecordComponentsByCell(true);
+        setEmptyMessage(MESSAGE.empty_table());
+
+        ListGridField addMax = new ListGridField("", "");
+        addMax.setType(ListGridFieldType.ICON);
+        addMax.setWidth(30);
+        addMax.setCellIcon(IMAGES.markerRed().getURL());
+
+        ListGridField addMin = new ListGridField("", "");
+        addMin.setType(ListGridFieldType.ICON);
+        addMin.setWidth(30);
+        addMin.setCellIcon(IMAGES.markerGreen().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");
+
+        setFields(addMin, addMax, name, type, 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/client/ui/wq/WTable.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,46 @@
+package de.intevation.flys.client.client.ui.wq;
+
+import com.google.gwt.core.client.GWT;
+
+import com.smartgwt.client.types.ListGridFieldType;
+import com.smartgwt.client.types.SelectionStyle;
+import com.smartgwt.client.widgets.grid.ListGrid;
+import com.smartgwt.client.widgets.grid.ListGridField;
+
+import de.intevation.flys.client.client.FLYSConstants;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WTable extends ListGrid {
+
+    /** The message class that provides i18n strings.*/
+    protected FLYSConstants MESSAGE = GWT.create(FLYSConstants.class);
+
+
+    public WTable() {
+        setWidth100();
+        setHeight100();
+        setSelectionType(SelectionStyle.NONE);
+        setSelectionType(SelectionStyle.NONE);
+        setShowHeaderContextMenu(false);
+        setShowRecordComponents(true);
+        setShowRecordComponentsByCell(true);
+        setEmptyMessage(MESSAGE.empty_table());
+
+        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");
+
+        setFields(name, type, 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/server/AddArtifactServiceImpl.java	Fri Sep 28 12:14:15 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:15 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:15 2012 +0200
@@ -0,0 +1,468 @@
+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;
+import de.intevation.flys.client.shared.model.WQDataItem;
+
+
+/**
+ * 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);
+            DataItem   def       = extractDefaultDataItem(d);
+
+            list.add(new DefaultData(name, label, null, dataItems, def));
+        }
+
+        return list;
+    }
+
+
+    /**
+     * This method extracts the default value of a Data object.
+     *
+     * @param data The data object node.
+     *
+     * @return the default DataItem.
+     */
+    protected static DataItem extractDefaultDataItem(Node data) {
+        System.out.println(
+            "ArtifactDescriptionFactory - extractSelectedDataItem");
+
+        String value = XMLUtils.xpathString(
+            data, "@art:defaultValue", ArtifactNamespaceContext.INSTANCE);
+
+        String label = XMLUtils.xpathString(
+            data, "@art:defaultLabel", ArtifactNamespaceContext.INSTANCE);
+
+        if (value != null && label != null) {
+            return new DefaultDataItem(label, null, value);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * 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);
+
+            double[] mmQ = extractMinMaxQValues(item);
+            double[] mmW = extractMinMaxWValues(item);
+
+            if (mmQ != null || mmW != null) {
+                dataItems.add(new WQDataItem(label, null, value, mmQ, mmW));
+            }
+            else {
+                dataItems.add(new DefaultDataItem(label, null, value));
+            }
+        }
+
+        return (DataItem[]) dataItems.toArray(new DataItem[count]);
+    }
+
+
+    protected static double[] extractMinMaxQValues(Node item) {
+        System.out.println("ArtifactDescriptionFactory - extractMinMaxQValues");
+
+        if (item == null) {
+            System.err.println("This node is empty - no min/max Q values.");
+            return null;
+        }
+
+        Node node = (Node) XMLUtils.xpath(
+            item,
+            "art:range[@art:type='Q']",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (node == null) {
+            System.out.println("No min/max Q values found.");
+            return null;
+        }
+
+        return extractMinMaxValues(node);
+    }
+
+
+    protected static double[] extractMinMaxWValues(Node item) {
+        System.out.println("ArtifactDescriptionFactory - extractMinMaxWValues");
+
+        if (item == null) {
+            System.err.println("This node is empty - no min/max W values.");
+            return null;
+        }
+
+        Node node = (Node) XMLUtils.xpath(
+            item,
+            "art:range[@art:type='W']",
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (node == null) {
+            System.out.println("No min/max W values found.");
+            return null;
+        }
+
+        return extractMinMaxValues(node);
+    }
+
+
+    protected static double[] extractMinMaxValues(Node node) {
+        System.out.println("ArtifactDescriptionFactory.extractMinMaxValues");
+
+        String minStr = XMLUtils.xpathString(
+            node, "art:min/text()", ArtifactNamespaceContext.INSTANCE);
+
+        String maxStr = XMLUtils.xpathString(
+            node, "art:max/text()", ArtifactNamespaceContext.INSTANCE);
+
+        if (maxStr == null || minStr == null) {
+            System.err.println("No min/max values found.");
+            return null;
+        }
+
+        try {
+            double min = Double.valueOf(minStr);
+            double max = Double.valueOf(maxStr);
+
+            return new double[] { min, max };
+        }
+        catch (NumberFormatException nfe) {
+            System.err.println("Error while parsing min/max values.");
+        }
+
+        return null;
+    }
+
+
+    /**
+     * 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:15 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:15 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<String[]> 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 (List<String[]>) 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/ChartInfoServiceImpl.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,254 @@
+package de.intevation.flys.client.server;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+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.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+import de.intevation.flys.client.shared.Transform2D;
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Axis;
+import de.intevation.flys.client.shared.model.ChartInfo;
+import de.intevation.flys.client.shared.model.Collection;
+
+import de.intevation.flys.client.client.services.ChartInfoService;
+
+
+/**
+ * This service fetches a document that contains meta information for a specific
+ * chart.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartInfoServiceImpl
+extends      RemoteServiceServlet
+implements   ChartInfoService
+{
+    public static final String XPATH_TRANSFORM_MATRIX =
+        "/art:chartinfo/art:transformation-matrix/art:matrix";
+
+    public static final String XPATH_X_AXES =
+        "/art:chartinfo/art:axes/art:domain";
+
+    public static final String XPATH_Y_AXES =
+        "/art:chartinfo/art:axes/art:range";
+
+    public static final String EXCEPTION_STRING = "error_chart_info_service";
+
+
+    public ChartInfo getChartInfo(
+        Collection          collection,
+        String              url,
+        String              locale,
+        String              type,
+        Map<String, String> attr)
+    throws ServerException
+    {
+        System.out.println("ChartInfoServiceImpl.getChartInfo");
+
+        Document request = ClientProtocolUtils.newOutCollectionDocument(
+                collection.identifier(),
+                type,
+                type,
+                ChartServiceHelper.getChartAttributes(attr));
+
+        try {
+            HttpClient client = new HttpClientImpl(url, locale);
+            InputStream in    = client.collectionOut(
+                request,
+                collection.identifier(),
+                type + "_chartinfo");
+
+            Document info = XMLUtils.parseDocument(in);
+
+            return parseInfoDocument(info);
+        }
+        catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        System.err.println("Error while fetching chart info.");
+
+        throw new ServerException(EXCEPTION_STRING);
+    }
+
+
+    protected ChartInfo parseInfoDocument(Document doc) {
+        Transform2D[] transformer = parseTransformationMatrix(doc);
+        Axis[]      xAxes         = parseXAxes(doc);
+        Axis[]      yAxes         = parseYAxes(doc);
+
+        return new ChartInfo(xAxes, yAxes, transformer);
+    }
+
+
+    protected Axis[] parseXAxes(Document doc) {
+        System.out.println("ChartInfoServiceImpl.parseXAxes");
+
+        NodeList axes = (NodeList) XMLUtils.xpath(
+            doc,
+            XPATH_X_AXES,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        return parseAxes(axes);
+    }
+
+
+    protected Axis[] parseYAxes(Document doc) {
+        System.out.println("ChartInfoServiceImpl.parseYAxes");
+
+        NodeList axes = (NodeList) XMLUtils.xpath(
+            doc,
+            XPATH_Y_AXES,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        return parseAxes(axes);
+    }
+
+
+    protected Axis[] parseAxes(NodeList axes) {
+        System.out.println("ChartInfoServiceImpl.parseAxes");
+
+        int count = axes != null ? axes.getLength() : 0;
+
+        System.out.println("Chart has " + count + " axes.");
+
+        if (count == 0) {
+            return null;
+        }
+
+        Axis[] result = new Axis[count];
+
+        for (int i = 0; i < count; i++) {
+            Node node = axes.item(i);
+
+            String posStr = XMLUtils.xpathString(
+                node, "@art:pos", ArtifactNamespaceContext.INSTANCE);
+
+            String fromStr = XMLUtils.xpathString(
+                node, "@art:from", ArtifactNamespaceContext.INSTANCE);
+
+            String toStr = XMLUtils.xpathString(
+                node, "@art:to", ArtifactNamespaceContext.INSTANCE);
+
+            String minStr = XMLUtils.xpathString(
+                node, "@art:min", ArtifactNamespaceContext.INSTANCE);
+
+            String maxStr = XMLUtils.xpathString(
+                node, "@art:max", ArtifactNamespaceContext.INSTANCE);
+
+            try {
+                int    pos  = Integer.parseInt(posStr);
+                double from = Double.parseDouble(fromStr);
+                double to   = Double.parseDouble(toStr);
+                double min  = Double.parseDouble(minStr);
+                double max  = Double.parseDouble(maxStr);
+
+                if (pos >= result.length) {
+                    // this should never happen
+                    System.err.println("The axis is out of valid range: " + pos);
+                    continue;
+                }
+
+                result[pos] = new Axis(pos, from, to, min, max);
+            }
+            catch (NumberFormatException nfe) {
+                nfe.printStackTrace();
+            }
+        }
+
+        System.out.println("Parsed " + result.length + " axes");
+
+        return result;
+    }
+
+
+    /**
+     * Parses the chart info document and extract the Transform2D values.
+     *
+     * @param doc The chart info document.
+     *
+     * @return a Transform2D object to transfrom pixel coordinates into chart
+     * coordinates.
+     */
+    protected Transform2D[] parseTransformationMatrix(Document doc) {
+        System.out.println("ChartInfoServiceImpl.parseTransformationMatrix");
+
+        NodeList matrix = (NodeList) XMLUtils.xpath(
+            doc,
+            XPATH_TRANSFORM_MATRIX,
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = matrix != null ? matrix.getLength() : 0;
+
+        List<Transform2D> transformer = new ArrayList<Transform2D>(num);
+
+        for (int i = 0; i < num; i++) {
+            Transform2D t = createTransformer(matrix.item(i));
+
+            if (t == null) {
+                System.err.println("Broken transformation matrix at pos: " + i);
+                continue;
+            }
+
+            transformer.add(t);
+        }
+
+        return (Transform2D[]) transformer.toArray(new Transform2D[num]);
+    }
+
+
+    protected Transform2D createTransformer(Node matrix) {
+        String sx = XMLUtils.xpathString(
+            matrix, "@art:sx", ArtifactNamespaceContext.INSTANCE);
+
+        String sy = XMLUtils.xpathString(
+            matrix, "@art:sy", ArtifactNamespaceContext.INSTANCE);
+
+        String tx = XMLUtils.xpathString(
+            matrix, "@art:tx", ArtifactNamespaceContext.INSTANCE);
+
+        String ty = XMLUtils.xpathString(
+            matrix, "@art:ty", ArtifactNamespaceContext.INSTANCE);
+
+        if (sx != null && sy != null && tx != null && ty != null) {
+            try {
+                return new Transform2D(
+                    Double.parseDouble(sx),
+                    Double.parseDouble(sy),
+                    Double.parseDouble(tx),
+                    Double.parseDouble(ty));
+            }
+            catch (NumberFormatException nfe) {
+                System.err.println("Error while parsing matrix values.");
+            }
+        }
+
+        System.err.println("No matrix values found.");
+
+        return new Transform2D(1d, 1d, 0d, 0d);
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,77 @@
+package de.intevation.flys.client.server;
+
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+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 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
+{
+    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,
+                ChartServiceHelper.getChartAttributes(prepareChartAttributes(req)));
+
+            HttpClient client = new HttpClientImpl(serverUrl, locale);
+            client.collectionOut(request, uuid, "chart", out);
+
+            out.close();
+            out.flush();
+        }
+        catch (IOException ioe) {
+            ioe.printStackTrace();
+            System.err.println(ioe.getMessage());
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+            System.err.println(e.getMessage());
+        }
+    }
+
+
+    protected Map<String, String> prepareChartAttributes(HttpServletRequest req) {
+        Map<String, String> attr = new HashMap<String, String>();
+
+        Map params = req.getParameterMap();
+
+        attr.put("width", req.getParameter("width"));
+        attr.put("height", req.getParameter("height"));
+        attr.put("minx", req.getParameter("minx"));
+        attr.put("maxx", req.getParameter("maxx"));
+        attr.put("miny", req.getParameter("miny"));
+        attr.put("maxy", req.getParameter("maxy"));
+
+        return attr;
+    }
+}
+// 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/ChartServiceHelper.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,151 @@
+package de.intevation.flys.client.server;
+
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import de.intevation.artifacts.common.ArtifactNamespaceContext;
+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 ChartServiceHelper {
+
+    /** 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;
+
+
+    private ChartServiceHelper() {
+    }
+
+    /**
+     * 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 static Document getChartAttributes(Map<String, String> req) {
+        System.out.println("ChartServiceHelper.getChartAttributes");
+
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator ec = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element attributes = ec.create("attributes");
+
+        appendChartSize(req, attributes, ec);
+        appendXRange(req, attributes, ec);
+        appendYRange(req, attributes, ec);
+
+        doc.appendChild(attributes);
+
+        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 attributes The attributes element used to adjust chart settings.
+     * @param ec The ElementCreator that might be used to create new Elements.
+     */
+    protected static void appendChartSize(
+        Map<String, String> req,
+        Element             attributes,
+        ElementCreator      ec)
+    {
+        System.out.println("ChartServiceHelper.appendChartSize");
+
+        Element size = ec.create("size");
+
+        String width  = req.get("width");
+        String height = req.get("height");
+
+        if (width == null || height == null) {
+            width  = String.valueOf(DEFAULT_CHART_WIDTH);
+            height = String.valueOf(DEFAULT_CHART_HEIGHT);
+        }
+
+        ec.addAttr(size, "width", width, true);
+        ec.addAttr(size, "height", height, true);
+
+        attributes.appendChild(size);
+    }
+
+
+    /**
+     * This method extracts the x range for the chart from request object and
+     * appends those range - if it exists - to the attribute document used to
+     * adjust the 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 static void appendXRange(
+        Map<String, String> req,
+        Element             attributes,
+        ElementCreator      ec)
+    {
+        System.out.println("ChartServiceHelper.appendXRange");
+
+        Element range = ec.create("xrange");
+
+        String from = req.get("minx");
+        String to   = req.get("maxx");
+
+        if (from != null && to != null) {
+            ec.addAttr(range, "from", from, true);
+            ec.addAttr(range, "to", to, true);
+
+            attributes.appendChild(range);
+        }
+    }
+
+
+    /**
+     * This method extracts the x range for the chart from request object and
+     * appends those range - if it exists - to the attribute document used to
+     * adjust the 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 static void appendYRange(
+        Map<String, String> req,
+        Element             attributes,
+        ElementCreator      ec)
+    {
+        System.out.println("ChartServiceHelper.appendYRange");
+
+        Element range = ec.create("yrange");
+
+        String from = req.get("miny");
+        String to   = req.get("maxy");
+
+        if (from != null && to != null) {
+            ec.addAttr(range, "from", from, true);
+            ec.addAttr(range, "to", to, true);
+
+            attributes.appendChild(range);
+        }
+    }
+}
+// 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/CollectionAttributeServiceImpl.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,57 @@
+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.Collection;
+
+import de.intevation.flys.client.client.services.CollectionAttributeService;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CollectionAttributeServiceImpl
+extends      DescribeCollectionServiceImpl
+implements   CollectionAttributeService
+{
+    public static final String ERROR_UPDATING_COLLECTION_ATTRIBUTE =
+        "error_update_collection_attribute";
+
+
+    public Collection update(Collection collection, String url, String locale)
+    throws ServerException
+    {
+        System.out.println("CollectionAttributeServiceImpl.update");
+
+        Document attribute = CollectionHelper.createAttribute(collection);
+        Document action    = ClientProtocolUtils.newSetAttributeDocument(
+            collection.identifier(),
+            attribute);
+
+        try {
+            HttpClient http = new HttpClientImpl(url, locale);
+            Document   res  = (Document) http.doCollectionAction(
+                action,
+                collection.identifier(),
+                new DocumentResponseHandler());
+
+            System.out.println("Collection attribute successfully set.");
+
+            return describe(collection.identifier(), url, locale);
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+        }
+
+        throw new ServerException(ERROR_UPDATING_COLLECTION_ATTRIBUTE);
+    }
+}
+// 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/CollectionHelper.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,158 @@
+package de.intevation.flys.client.server;
+
+import java.util.List;
+import java.util.Map;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+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.client.shared.model.Collection;
+import de.intevation.flys.client.shared.model.OutputMode;
+import de.intevation.flys.client.shared.model.Theme;
+import de.intevation.flys.client.shared.model.ThemeList;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class CollectionHelper {
+
+    public static Document createAttribute(Collection collection) {
+        System.out.println("CollectionHelper.createAttribute");
+
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element attr = cr.create("attribute");
+
+        doc.appendChild(attr);
+
+        Map<String, OutputMode> tmpOuts = collection.getOutputModes();
+
+        Element outs = createOutputElements(cr, collection, tmpOuts);
+
+        if (outs != null) {
+            attr.appendChild(outs);
+        }
+
+        return doc;
+    }
+
+
+    /**
+     * Creates a whole block with art:output nodes.
+     *
+     * @param cr The ElementCreator used to create new elements.
+     * @param c  The collection.
+     * @param modes The OutputModes that should be included.
+     *
+     * @return an element with output modes.
+     */
+    protected static Element createOutputElements(
+        ElementCreator          cr,
+        Collection              c,
+        Map<String, OutputMode> mmodes)
+    {
+        System.out.println("CollectionHelper.createOutputElements");
+
+        java.util.Collection<OutputMode> modes = mmodes != null
+            ? mmodes.values()
+            : null;
+
+        if (modes == null || modes.size() == 0) {
+            System.err.println("Collection has no modes: " + c.identifier());
+            return null;
+        }
+
+        Element outs = cr.create("outputs");
+
+        for (OutputMode mode: modes) {
+            Element out = createOutputElement(cr, c, mode);
+
+            if (out != null) {
+                outs.appendChild(out);
+            }
+        }
+
+        return outs;
+    }
+
+
+    /**
+     * Create a node art:output that further consist of art:theme nodes.
+     *
+     * @param cr The ElementCreator used to create new elements.
+     * @param c  The collection.
+     * @param mode The OutputMode.
+     *
+     * @return an element that represents an output mode with its themes.
+     */
+    protected static Element createOutputElement(
+        ElementCreator cr,
+        Collection     collection,
+        OutputMode     mode)
+    {
+        System.out.println("CollectionHelper.createOutputElement");
+
+        Element out = cr.create("output");
+        cr.addAttr(out, "name", mode.getName(), false);
+
+        ThemeList themeList = collection.getThemeList(mode.getName());
+        List<Theme> themes  = themeList != null ? themeList.getThemes() : null;
+
+        if (themes == null || themes.size() == 0) {
+            System.err.println("No themes for output mode: " + mode.getName());
+            return null;
+        }
+
+        for (Theme theme: themes) {
+            Element t = createThemeElement(cr, collection, theme);
+
+            if (t != null) {
+                out.appendChild(t);
+            }
+        }
+
+        return out;
+    }
+
+
+    /**
+     * Creates a theme node art:theme that represents a curve in a chart or map.
+     *
+     * @param cr The ElementCreator used to create new elements.
+     * @param collection The collection.
+     * @param theme The theme whose attributes should be written to an element.
+     *
+     * @return an element that contains the informtion of the given theme.
+     */
+    protected static Element createThemeElement(
+        ElementCreator cr,
+        Collection     collection,
+        Theme          theme)
+    {
+        if (theme == null) {
+            return null;
+        }
+
+        Element t = cr.create("theme");
+
+        cr.addAttr(t, "active", theme.isActive() ? "1" : "0", true);
+        cr.addAttr(t, "artifact", theme.getArtifact(), true);
+        cr.addAttr(t, "facet", theme.getFacet(), true);
+        cr.addAttr(t, "pos", Integer.toString(theme.getPosition()), true);
+        cr.addAttr(t, "index", Integer.toString(theme.getIndex()), true);
+        cr.addAttr(t, "description", theme.getDescription(), true);
+
+        return t;
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,78 @@
+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";
+
+    /** XPath to figure out the ttl of the created collection.*/
+    public static final String XPATH_COLLECTION_TTL =
+        "/art:result/art:artifact-collection/@art:ttl";
+
+    /** 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);
+
+            String ttlStr = XMLUtils.xpathString(
+                doc, XPATH_COLLECTION_TTL, ArtifactNamespaceContext.INSTANCE);
+
+            if (uuid.trim().length() == 0 || ttlStr.length() == 0) {
+                throw new ServerException(ERROR_CREATE_COLLECTION);
+            }
+
+            return new DefaultCollection(uuid, Long.valueOf(ttlStr));
+        }
+        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/DeleteCollectionServiceImpl.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,32 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.client.services.DeleteCollectionService;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DeleteCollectionServiceImpl
+extends      DoCollectionAction
+implements   DeleteCollectionService
+{
+    public static final String XPATH_RESULT      = "/art:result/text()";
+    public static final String OPERATION_FAILURE = "FAILED";
+
+    public void delete(Collection c, String url)
+    throws ServerException
+    {
+        System.out.println("Delete collection: " + c.identifier());
+
+        Document del = ClientProtocolUtils.newDeleteCollectionDocument();
+
+        doAction(c, del, url);
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,388 @@
+package de.intevation.flys.client.server;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+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.DefaultTheme;
+import de.intevation.flys.client.shared.model.ExportMode;
+import de.intevation.flys.client.shared.model.ReportMode;
+import de.intevation.flys.client.shared.model.Facet;
+import de.intevation.flys.client.shared.model.OutputMode;
+import de.intevation.flys.client.shared.model.Theme;
+import de.intevation.flys.client.shared.model.ThemeList;
+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);
+            }
+
+            System.out.println("Collection successfully parsed.");
+
+            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);
+
+        String ttlStr = XMLUtils.xpathString(
+            description,
+            "art:artifact-collection/@art:ttl",
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (uuid.length() == 0 || ttlStr.length() == 0) {
+            System.err.println("Found an invalid Collection!");
+            return null;
+        }
+
+
+        long ttl = -1;
+        try {
+            ttl = Long.valueOf(ttlStr);
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing
+        }
+
+        Map<String, ThemeList> themeList = parseThemeLists(description);
+
+        Collection c = !themeList.isEmpty()
+            ? new DefaultCollection(uuid, ttl, themeList)
+            : new DefaultCollection(uuid, ttl);
+
+        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((Element)items.item(i));
+
+            if (item != null) {
+                c.addItem(item);
+            }
+        }
+
+        System.out.println(
+            "Found " + c.getItemLength() + " collection items " +
+            "for the Collection '" + c.identifier() + "'.");
+
+        return c;
+    }
+
+
+    protected Map<String, ThemeList> parseThemeLists(Document description) {
+        System.out.println("DescribeCollectionServiceImpl.parseThemeLists");
+
+        NodeList lists = (NodeList) XMLUtils.xpath(
+            description,
+            "/art:artifact-collection/art:attribute/art:outputs/art:output",
+            XPathConstants.NODESET,
+            ArtifactNamespaceContext.INSTANCE);
+
+        int num = lists != null ? lists.getLength() : 0;
+
+        Map<String, ThemeList> themeList = new HashMap<String, ThemeList>(num);
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        for (int i = 0; i < num; i++) {
+            Element node = (Element)lists.item(i);
+
+            String outName = node.getAttribute("name");
+
+            if (outName.length() > 0) {
+                ThemeList list = parseThemeList(node);
+                if (list.getThemeCount() > 0) {
+                    themeList.put(outName, list);
+                }
+            }
+        }
+
+        return themeList;
+    }
+
+
+    protected ThemeList parseThemeList(Element node) {
+        System.out.println("DescribeCollectionServiceImpl.parseThemeList");
+
+        NodeList themes = node.getElementsByTagNameNS(
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            "theme");
+
+        int num = themes != null ? themes.getLength() : 0;
+
+        List<Theme> themeList = new ArrayList<Theme>(num);
+
+        for (int i = 0; i < num; i++) {
+            Theme theme = parseTheme((Element)themes.item(i));
+
+            if (theme != null) {
+                themeList.add(theme);
+            }
+        }
+
+        return new ThemeList(themeList);
+    }
+
+
+    protected Theme parseTheme(Element ele) {
+        System.out.println("DescribeCollectionServiceImpl.parseTheme");
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        String strAct = ele.getAttributeNS(uri, "active");
+        String art    = ele.getAttributeNS(uri, "artifact");
+        String fac    = ele.getAttributeNS(uri, "facet");
+        String strPos = ele.getAttributeNS(uri, "pos");
+        String desc   = ele.getAttributeNS(uri, "description");
+        String strIdx = ele.getAttributeNS(uri, "index");
+
+        if (strAct.length() > 0 && art.length() > 0 && fac.length() > 0
+         && strPos.length() > 0 && strIdx.length() > 0)
+        {
+            try {
+                int pos    = Integer.valueOf(strPos);
+                int active = Integer.valueOf(strAct);
+                int idx    = Integer.valueOf(strIdx);
+
+                return new DefaultTheme(pos, idx, active > 0, art, fac, desc);
+            }
+            catch (NumberFormatException nfe) {
+                nfe.printStackTrace();
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * 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(Element node) {
+        System.out.println("AddArtifactServiceImpl.parseCollectionItem");
+
+        if (node == null) {
+            System.err.println("The node for parsing CollectionItem is null!");
+            return null;
+        }
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        String uuid = node.getAttributeNS(uri, "uuid");
+        String hash = node.getAttributeNS(uri, "hash");
+
+        if (uuid == null || uuid.length() == 0) {
+            System.err.println("Found an invalid CollectionItem!");
+            return null;
+        }
+
+        NodeList outputmodes = node.getElementsByTagNameNS(uri, "outputmodes");
+
+        if (outputmodes.getLength() < 1) {
+            return null;
+        }
+
+        Element om = (Element)outputmodes.item(0);
+
+        List<OutputMode> modes = parseOutputModes(om);
+
+        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(Element node) {
+        System.out.println("AddArtifactServiceImpl.parseOutputModes");
+
+        if (node == null) {
+            System.err.println("The node for parsing OutputModes is null!");
+            return null;
+        }
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        NodeList list = node.getElementsByTagNameNS(uri, "output");
+
+        int size = list.getLength();
+
+        if (size == 0) {
+            System.err.println("No outputmode nodes found!");
+            return null;
+        }
+
+        List<OutputMode> modes = new ArrayList<OutputMode>(size);
+
+        for (int i = 0; i < size; i++) {
+            Element tmp = (Element)list.item(i);
+
+            String name = tmp.getAttributeNS(uri, "name");
+            String desc = tmp.getAttributeNS(uri, "description");
+            String mime = tmp.getAttributeNS(uri, "mime-type");
+
+            if (name.length() == 0) {
+                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 if (name.indexOf("report") > -1) {
+                outmode = new ReportMode(name, desc, mime, fs);
+            }
+            else {
+                outmode = new DefaultOutputMode(name, desc, mime, fs);
+            }
+
+            modes.add(outmode);
+        }
+
+        return modes;
+    }
+
+
+    protected static List<Facet> extractFacets(Element 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);
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        for (int i = 0; i < num; i++) {
+            Element facetEl = (Element) facetList.item(i);
+
+            String name  = facetEl.getAttributeNS(uri, "name");
+            String desc  = facetEl.getAttributeNS(uri, "description");
+            String index = facetEl.getAttributeNS(uri, "index");
+
+            if (name != null && name.length() > 0 && index != null) {
+                facets.add(new DefaultFacet(name, Integer.valueOf(index),desc));
+            }
+        }
+
+        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:15 2012 +0200
@@ -0,0 +1,154 @@
+package de.intevation.flys.client.server;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+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 = result.getElementsByTagName("distance");
+
+        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(
+                (Element)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(Element node) {
+
+        String desc      = node.getAttribute("description").trim();
+        String from      = node.getAttribute("from").trim();
+        String to        = node.getAttribute("to").trim();
+        String riverside = node.getAttribute("riverside").trim();
+        String bottom    = node.getAttribute("bottom").trim();
+        String top       = node.getAttribute("top").trim();
+
+        if (desc.length() > 0 && from.length() > 0) {
+            try {
+                Double f  = new Double(from);
+                Double t  = to    .length() > 0 ? new Double(to)     : null;
+                Double b  = bottom.length() > 0 ? new Double(bottom) : null;
+                Double tp = top   .length() > 0 ? new Double(top)    : null;
+
+                return new DistanceInfoObjectImpl(desc, f, t, riverside, b, tp);
+            }
+            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/DoCollectionAction.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,54 @@
+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.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;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DoCollectionAction extends RemoteServiceServlet {
+
+    public static final String XPATH_RESULT      = "/art:result/text()";
+    public static final String OPERATION_FAILURE = "FAILED";
+    public static final String FAILURE_EXCEPTION = "collection_action_failed";
+
+
+    protected void doAction(Collection c, Document action, String url)
+    throws    ServerException
+    {
+        HttpClient client = new HttpClientImpl(url);
+
+        try {
+            Document res  = (Document) client.doCollectionAction(
+                action, c.identifier(),
+                new DocumentResponseHandler());
+
+            String result = XMLUtils.xpathString(
+                res,
+                XPATH_RESULT,
+                ArtifactNamespaceContext.INSTANCE);
+
+            if (result == null || result.equals(OPERATION_FAILURE)) {
+                System.err.println("Operation failed.");
+                throw new ServerException(FAILURE_EXCEPTION);
+            }
+        }
+        catch (ConnectionException ce) {
+            System.err.println(ce.getLocalizedMessage());
+            throw new ServerException(FAILURE_EXCEPTION);
+        }
+    }
+}
--- /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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,63 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+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.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/ReportServiceImpl.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,109 @@
+package de.intevation.flys.client.server;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+
+import org.apache.commons.lang.StringEscapeUtils;
+
+import de.intevation.flys.client.client.services.ReportService;
+
+import de.intevation.artifacts.common.utils.XMLUtils;
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+
+import de.intevation.artifacts.httpclient.http.HttpClient;
+import de.intevation.artifacts.httpclient.http.HttpClientImpl;
+
+public class ReportServiceImpl
+extends      RemoteServiceServlet
+implements   ReportService
+{
+    @Override
+    public String report(
+        String collectionId,
+        String url,
+        String locale,
+        String out
+    ) {
+        System.err.println("report: " + collectionId + " " + out);
+
+        Document request = ClientProtocolUtils.newOutCollectionDocument(
+            collectionId,
+            out,
+            "report");
+
+        InputStream in = null;
+        try {
+            HttpClient client = new HttpClientImpl(url, locale);
+            in = client.collectionOut(request, collectionId, out);
+
+            if (in == null) {
+                System.err.println("report: no report");
+                return null;
+            }
+
+            Document report = XMLUtils.parseDocument(in);
+
+            return buildReport(report);
+        }
+        catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+        finally {
+            if (in != null) {
+                try {
+                    in.close();
+                }
+                catch (IOException ioe) {
+                }
+            }
+        }
+
+        return "error processing error report";
+    }
+
+    protected static String buildReport(Document document) {
+
+        NodeList problems = document.getElementsByTagName("problem");
+
+        StringBuilder global = new StringBuilder();
+        StringBuilder kms    = new StringBuilder();
+
+        for (int i = 0, N = problems.getLength(); i < N; ++i) {
+
+            Element element = (Element)problems.item(i);
+
+            String km  = element.getAttribute("km");
+            String msg = element.getTextContent();
+
+            if (km.length() > 0) {
+                kms.append("<li><strong>")
+                   .append(StringEscapeUtils.escapeHtml(km))
+                   .append("</strong>: ")
+                   .append(StringEscapeUtils.escapeHtml(msg))
+                   .append("</li>");
+            }
+            else {
+                global.append("<li>")
+                      .append(StringEscapeUtils.escapeHtml(msg))
+                      .append("</li>");
+            }
+        }
+
+        StringBuilder sb = new StringBuilder("<ul>")
+            .append(global)
+            .append(kms)
+            .append("</ul>");
+
+        return 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-client/src/main/java/de/intevation/flys/client/server/RiverServiceImpl.java	Fri Sep 28 12:14:15 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/SetCollectionNameServiceImpl.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,30 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.client.services.SetCollectionNameService;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SetCollectionNameServiceImpl
+extends      DoCollectionAction
+implements   SetCollectionNameService
+{
+    public void setName(Collection c, String url)
+    throws ServerException
+    {
+        System.out.println("Set name of collection: " + c.identifier());
+
+        String   name = c.getName();
+        Document set  = ClientProtocolUtils.newSetCollectionNameDocument(name);
+
+        doAction(c, set, url);
+    }
+}
+// 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/SetCollectionTTLServiceImpl.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,45 @@
+package de.intevation.flys.client.server;
+
+import org.w3c.dom.Document;
+
+import de.intevation.artifacts.common.utils.ClientProtocolUtils;
+
+import de.intevation.flys.client.shared.exceptions.ServerException;
+import de.intevation.flys.client.shared.model.Collection;
+import de.intevation.flys.client.client.services.SetCollectionTTLService;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class SetCollectionTTLServiceImpl
+extends      DoCollectionAction
+implements   SetCollectionTTLService
+{
+    public static final String XPATH_RESULT      = "/art:result/text()";
+    public static final String OPERATION_FAILURE = "FAILED";
+
+    public void setTTL(Collection c, String url)
+    throws ServerException
+    {
+        System.out.println("Set ttl of collection: " + c.identifier());
+
+        long   ttl   = c.getTTL();
+        String value = null;
+
+        if (ttl == 0) {
+            value = "INF";
+        }
+        else if (ttl < 0) {
+            value = "DEFAULT";
+        }
+        else {
+            value = String.valueOf(ttl);
+        }
+
+        Document set = ClientProtocolUtils.newSetCollectionTTLDocument(value);
+
+        doAction(c, set, url);
+    }
+}
+// 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:15 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:15 2012 +0200
@@ -0,0 +1,118 @@
+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.Element;
+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((Element) 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(Element node) {
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        String creationStr = node.getAttributeNS(uri, "creation");
+        String name        = node.getAttributeNS(uri, "name");
+        String uuid        = node.getAttributeNS(uri, "uuid");
+        String ttlStr      = node.getAttributeNS(uri, "ttl");
+
+        if (uuid != null && ttlStr != null) {
+            try {
+                long time = Long.parseLong(creationStr);
+                long ttl  = Long.parseLong(ttlStr);
+                return new DefaultCollection(uuid, ttl, name, new Date(time));
+            }
+            catch (NumberFormatException nfe) {
+                System.err.println("Error while parsing collection attributes.");
+                return null;
+            }
+        }
+
+        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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,109 @@
+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.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:15 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/Transform2D.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,62 @@
+package de.intevation.flys.client.shared;
+
+import java.io.Serializable;
+
+import com.google.gwt.core.client.GWT;
+
+
+/**
+ * This object supports a linear transformation to transform xy coordinates into
+ * an other coordinate system based on scale and translation values.
+ *
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class Transform2D implements Serializable {
+
+    protected double sx;
+    protected double sy;
+
+    protected double tx;
+    protected double ty;
+
+
+    public Transform2D() {
+    }
+
+
+    /**
+     * Creates a new transformation with scale and translation factors.
+     *
+     * @param sx The scale factor for the x axis.
+     * @param sy The scale factor for the y axis.
+     * @param tx The translation factor for the x axis.
+     * @param ty The translation factor for the y axis.
+     */
+    public Transform2D(double sx, double sy, double tx, double ty) {
+        this.sx  = sx;
+        this.sy  = sy;
+        this.tx  = tx;
+        this.ty  = ty;
+    }
+
+
+    /**
+     * Transforms the pixel x and y into a new coordinate system based on the
+     * scale and translation values specified in the constructor.
+     */
+    public double[] transform(double x, double y) {
+        double resX = sx * x + tx;
+        double resY = sy * y + ty;
+
+        return new double[] { resX, resY };
+    }
+
+
+    public void dumpGWT() {
+        GWT.log("SX = " + sx);
+        GWT.log("SY = " + sy);
+        GWT.log("TX = " + tx);
+        GWT.log("TY = " + ty);
+    }
+}
+// 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/AuthenticationException.java	Fri Sep 28 12:14:15 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:15 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:15 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:15 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/Axis.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,57 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class Axis implements Serializable {
+
+    protected int pos;
+
+    protected double from;
+    protected double to;
+
+    protected double min;
+    protected double max;
+
+
+    public Axis() {
+    }
+
+
+    public Axis(int pos, double from, double to, double min, double max) {
+        this.pos  = pos;
+        this.from = from;
+        this.to   = to;
+        this.min  = min;
+        this.max  = max;
+    }
+
+
+    public int getPos() {
+        return pos;
+    }
+
+
+    public double getFrom() {
+        return from;
+    }
+
+
+    public double getTo() {
+        return to;
+    }
+
+
+    public double getMin() {
+        return min;
+    }
+
+
+    public double getMax() {
+        return max;
+    }
+}
+// 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/ChartInfo.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,91 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+import com.google.gwt.core.client.GWT;
+
+import de.intevation.flys.client.shared.Transform2D;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ChartInfo implements Serializable {
+
+    protected Axis[] xAxes;
+    protected Axis[] yAxes;
+
+    protected Transform2D[] transformer;
+
+
+    public ChartInfo() {
+    }
+
+
+    public ChartInfo(Axis[] xAxes, Axis[] yAxes, Transform2D[] transformer) {
+        this.xAxes       = xAxes;
+        this.yAxes       = yAxes;
+        this.transformer = transformer;
+    }
+
+
+    public Transform2D getTransformer(int pos) {
+        if (pos >= 0 && pos < transformer.length) {
+            return transformer[pos];
+        }
+
+        return null;
+    }
+
+
+    public int getXAxisCount() {
+        return xAxes.length;
+    }
+
+
+    public int getYAxisCount() {
+        return yAxes.length;
+    }
+
+
+    public Axis getXAxis(int pos) {
+        if (pos >= 0 && pos < xAxes.length) {
+            return xAxes[pos];
+        }
+
+        return null;
+    }
+
+
+    public Axis getYAxis(int pos) {
+        if (pos >= 0 && pos < yAxes.length) {
+            return yAxes[pos];
+        }
+
+        return null;
+    }
+
+
+    public void dumpGWT() {
+        StringBuilder sb = new StringBuilder();
+
+        Axis x = getXAxis(0);
+
+        GWT.log("X axis:");
+        GWT.log("... from " + x.getFrom() + " to " + x.getTo());
+        GWT.log("... min " + x.getMin() + " max " + x.getMax());
+
+        for (int i = 0, count = getYAxisCount(); i < count; i++) {
+            Axis y = getYAxis(i);
+
+            GWT.log("Y axis " + i + ":");
+            GWT.log("... from " + y.getFrom() + " to " + y.getTo());
+            GWT.log("... min " + y.getMin() + " max " + y.getMax());
+        }
+
+        for (Transform2D t: transformer) {
+            t.dumpGWT();
+        }
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,39 @@
+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 void setName(String name);
+
+    public Date getCreationTime();
+
+    public Date getLastAccess();
+
+    public long getTTL();
+
+    public void setTTL(long ttl);
+
+    public void addItem(CollectionItem item);
+
+    public int getItemLength();
+
+    public CollectionItem getItem(int idx);
+
+    public Map<String, OutputMode> getOutputModes();
+
+    public ThemeList getThemeList(String outName);
+}
+// 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:15 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:15 2012 +0200
@@ -0,0 +1,110 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.Date;
+
+import com.google.gwt.safehtml.shared.SafeHtml;
+import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
+
+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());
+
+        setTTL(collection.getTTL());
+    }
+
+
+    /**
+     * 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) {
+        SafeHtmlBuilder b = new SafeHtmlBuilder();
+        b.appendEscaped(name);
+
+        SafeHtml html = b.toSafeHtml();
+
+        setAttribute("name", html.asString());
+    }
+
+
+    /**
+     * 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");
+    }
+
+
+    public void setTTL(long ttl) {
+        if (ttl == 0) {
+            setAttribute("ttl", "star_gold");
+        }
+        else {
+            setAttribute("ttl", "star_silver");
+        }
+    }
+
+
+    public String getTTL() {
+        return getAttribute("ttl");
+    }
+
+
+    /**
+     * 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:15 2012 +0200
@@ -0,0 +1,54 @@
+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();
+
+
+    /**
+     * Returns the default value of this data object.
+     *
+     * @return the default value.
+     */
+    public DataItem getDefault();
+}
+// 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:15 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:15 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:15 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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,178 @@
+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 time to live of the collection. If this value is 0, it will never
+     * die.*/
+    protected long ttl;
+
+    /** The list of artifacts that are managed by this Collection.*/
+    protected List<CollectionItem> items;
+
+    protected Map<String, ThemeList> themeLists;
+
+
+    /**
+     * Constructor without arguments is necessary for GWT.
+     */
+    public DefaultCollection() {
+    }
+
+
+    /**
+     * Creates a new DefaultCollection with a UUID.
+     *
+     * @param uuid The UUID.
+     */
+    public DefaultCollection(String uuid, long ttl) {
+        this.uuid       = uuid;
+        this.ttl        = ttl;
+        this.items      = new ArrayList<CollectionItem>();
+        this.themeLists = new HashMap<String, ThemeList>();
+    }
+
+
+    public DefaultCollection(
+        String uuid,
+        long   ttl,
+        Map<String, ThemeList> themeLists)
+    {
+        this(uuid, ttl);
+        this.themeLists = themeLists;
+    }
+
+
+    /**
+     * 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, long ttl, String name) {
+        this(uuid, ttl);
+
+        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, long ttl, String name, Date creation){
+        this(uuid, ttl, name);
+
+        this.creation = creation;
+    }
+
+
+    public String identifier() {
+        return uuid;
+    }
+
+
+    public Date getCreationTime() {
+        return creation;
+    }
+
+
+    public Date getLastAccess() {
+        return new Date();
+    }
+
+
+    public long getTTL() {
+        return ttl;
+    }
+
+
+    public void setTTL(long ttl) {
+        this.ttl = ttl;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public void setName(String name) {
+        this.name = 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;
+    }
+
+
+    public ThemeList getThemeList(String outName) {
+        if (themeLists != null) {
+            return themeLists.get(outName);
+        }
+
+        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/DefaultCollectionItem.java	Fri Sep 28 12:14:15 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:15 2012 +0200
@@ -0,0 +1,98 @@
+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;
+
+    /** The default DataItem.*/
+    protected DataItem defaultItem;
+
+
+    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, description, type, items, null);
+    }
+
+
+    /**
+     * The constructor to create new DefaultData objects with a default value.
+     *
+     * @param label The label.
+     * @param description The description.
+     * @param type The type.
+     * @param items The DataItems.
+     * @param defaultItem The default DataItem.
+     */
+    public DefaultData(
+        String label,
+        String description,
+        String type,
+        DataItem[] items,
+        DataItem defaultItem)
+    {
+        this.label       = label;
+        this.description = description;
+        this.type        = type;
+        this.items       = items;
+        this.defaultItem = defaultItem;
+    }
+
+
+    public String getLabel() {
+        return label;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public String getType() {
+        return type;
+    }
+
+
+    public DataItem[] getItems() {
+        return items;
+    }
+
+
+    public DataItem getDefault() {
+        return defaultItem;
+    }
+}
+// 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:15 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:15 2012 +0200
@@ -0,0 +1,60 @@
+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;
+
+    /** The description of the facet.*/
+    protected String description;
+
+    /** The index of the facet.*/
+    protected int index;
+
+
+    /**
+     * 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 DefaultFacet(String name, int index, String description) {
+        this(name);
+
+        this.index       = index;
+        this.description = description;
+    }
+
+
+    public String getName() {
+        return name;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public int getIndex() {
+        return index;
+    }
+}
+// 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:15 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:15 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/DefaultTheme.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,117 @@
+package de.intevation.flys.client.shared.model;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class DefaultTheme implements Theme {
+
+    protected int position;
+
+    protected int index;
+
+    protected boolean active;
+
+    protected String artifact;
+
+    protected String facet;
+
+    protected String description;
+
+
+    public DefaultTheme() {
+    }
+
+
+    public DefaultTheme(
+        int     pos,
+        int     index,
+        boolean active,
+        String  art,
+        String  facet,
+        String  description)
+    {
+        this.position    = pos;
+        this.index       = index;
+        this.active      = active;
+        this.artifact    = art;
+        this.facet       = facet;
+        this.description = description;
+    }
+
+
+    public int getPosition() {
+        return position;
+    }
+
+
+    public void setPosition(int pos) {
+        this.position = pos;
+    }
+
+
+    public int getIndex() {
+        return index;
+    }
+
+
+    public boolean isActive() {
+        return active;
+    }
+
+
+    public void setActive(boolean active) {
+        this.active = active;
+    }
+
+
+    public String getArtifact() {
+        return artifact;
+    }
+
+
+    public String getFacet() {
+        return facet;
+    }
+
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public boolean equals(Object o) {
+        if (!(o instanceof DefaultTheme)) {
+            return false;
+        }
+
+        DefaultTheme other = (DefaultTheme) o;
+
+        if (other.position != position) {
+            return false;
+        }
+
+        if (!other.artifact.equals(artifact)) {
+            return false;
+        }
+
+        if (other.active != active) {
+            return false;
+        }
+
+        if (!other.facet.equals(facet)) {
+            return false;
+        }
+
+        if (!other.description.equals(description)) {
+            return false;
+        }
+
+        if (other.index != index) {
+            return false;
+        }
+
+        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-client/src/main/java/de/intevation/flys/client/shared/model/DefaultUser.java	Fri Sep 28 12:14:15 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:15 2012 +0200
@@ -0,0 +1,23 @@
+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();
+
+    Double getBottom();
+
+    Double getTop();
+}
+// 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:15 2012 +0200
@@ -0,0 +1,69 @@
+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;
+
+    protected Double bottom;
+
+    protected Double top;
+
+
+    public DistanceInfoObjectImpl() {
+    }
+
+
+    public DistanceInfoObjectImpl(
+        String description,
+        Double from,
+        Double to,
+        String riverside,
+        Double bottom,
+        Double top
+    ) {
+        this.description = description;
+        this.from        = from;
+        this.to          = to;
+        this.riverside   = riverside;
+        this.bottom      = bottom;
+        this.top         = top;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+
+    public Double getFrom() {
+        return from;
+    }
+
+
+    public Double getTo() {
+        return to;
+    }
+
+
+    public String getRiverside() {
+        return riverside;
+    }
+
+    public Double getBottom() {
+        return bottom;
+    }
+
+    public Double getTop() {
+        return top;
+    }
+}
+// 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:15 2012 +0200
@@ -0,0 +1,107 @@
+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());
+        setBottom(info.getBottom());
+        setTop(info.getTop());
+    }
+
+
+    /**
+     * 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 void setBottom(Double bottom) {
+        setAttribute("bottom", bottom != null ? bottom.toString() : "-");
+    }
+
+    public String getBottom() {
+        return getAttributeAsString("bottom");
+    }
+
+    public void setTop(Double top) {
+        setAttribute("top", top != null ? top.toString() : "-");
+    }
+
+    public String getTop() {
+        return getAttributeAsString("top");
+    }
+
+
+    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:15 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:15 2012 +0200
@@ -0,0 +1,34 @@
+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();
+
+    /**
+     * Returns the index of this facet.
+     *
+     * @return the index.
+     */
+    int getIndex();
+
+    /**
+     * Returns the description of this facet.
+     *
+     * @return the description.
+     */
+    String getDescription();
+}
+// 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/FacetRecord.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,53 @@
+package de.intevation.flys.client.shared.model;
+
+import com.smartgwt.client.widgets.grid.ListGridRecord;
+
+import de.intevation.flys.client.shared.model.Facet;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class FacetRecord extends ListGridRecord {
+
+    protected boolean active;
+
+    protected Theme   theme;
+
+
+    public FacetRecord(Theme theme) {
+        this.theme = theme;
+
+        setActive(theme.isActive());
+        setName(theme.getDescription());
+    }
+
+
+    public Theme getTheme() {
+        return theme;
+    }
+
+
+    public void setName(String description) {
+        // TODO Add a setter method setName() to Facet
+        // facet.setName(name);
+        setAttribute("name", description);
+    }
+
+
+    public String getName() {
+        return getAttribute("name");
+    }
+
+
+    public boolean getActive() {
+        return getAttributeAsBoolean("active");
+    }
+
+
+    public void setActive(boolean active) {
+        this.active = active;
+        setAttribute("active", active);
+    }
+}
+// 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:15 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/ReportMode.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,39 @@
+package de.intevation.flys.client.shared.model;
+
+import java.util.List;
+
+public class ReportMode
+extends      DefaultOutputMode
+{
+    public ReportMode() {
+    }
+
+
+    public ReportMode(String name, String desc, String mimeType) {
+        super(name, desc, mimeType);
+    }
+
+
+    public ReportMode(
+        String      name,
+        String      description,
+        String      mimeType,
+        List<Facet> facets
+    ) {
+        super(name, description, mimeType, facets);
+    }
+
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        boolean first = true;
+        for (Facet f: facets) {
+            if (first) first = false;
+            else       sb.append(", ");
+            sb.append("(name = '").append(f.getName())
+              .append("', index = ").append(f.getIndex())
+              .append(", desc = '").append(f.getDescription()).append("')");
+        }
+        return 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-client/src/main/java/de/intevation/flys/client/shared/model/River.java	Fri Sep 28 12:14:15 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/Theme.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,29 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public interface Theme extends Serializable {
+
+    int getPosition();
+
+    void setPosition(int pos);
+
+    int getIndex();
+
+    boolean isActive();
+
+    void setActive(boolean active);
+
+    String getArtifact();
+
+    String getFacet();
+
+    String getDescription();
+
+    boolean equals(Object o);
+}
+// 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/ThemeList.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,100 @@
+package de.intevation.flys.client.shared.model;
+
+import java.io.Serializable;
+
+import java.util.List;
+
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class ThemeList implements Serializable {
+
+    public List<Theme> themes;
+
+
+    public ThemeList() {
+    }
+
+
+    public ThemeList(List<Theme> themes) {
+        this.themes = themes;
+    }
+
+
+    public List<Theme> getThemes() {
+        return themes;
+    }
+
+
+    public int getThemeCount() {
+        return themes.size();
+    }
+
+
+    /**
+     * Returns a theme at a specific position. <b>NOTE: Themes start at position
+     * 1. So, take care in loops, that might start at index 0!</b>
+     *
+     * @param pos The position of the desired theme.
+     *
+     * @return a theme.
+     */
+    public Theme getThemeAt(int pos) {
+        for (Theme theme: themes) {
+            if (theme.getPosition() == pos) {
+                return theme;
+            }
+        }
+
+        return null;
+    }
+
+
+    public void removeTheme(Theme theme) {
+        if (theme != null) {
+            themes.remove(theme);
+        }
+    }
+
+
+    public void addTheme(Theme theme) {
+        if (theme != null) {
+            themes.add(theme);
+        }
+    }
+
+
+    /**
+     * Modifies the order of themes in this list and the position of the
+     * <i>theme</i> itself.
+     *
+     * @param theme The theme which position has to be modified.
+     * @param newPos The new position.
+     */
+    public void setThemePosition(Theme theme, int newPos) {
+        int count  = getThemeCount();
+        int oldPos = theme.getPosition();
+
+        if (newPos == oldPos || newPos > count || newPos < 1) {
+            return;
+        }
+
+        boolean moveUp = newPos < oldPos;
+
+        for (Theme aTheme: themes) {
+            int tmpPos = aTheme.getPosition();
+
+            if (theme.equals(aTheme)) {
+                theme.setPosition(newPos);
+            }
+            else if (tmpPos >= newPos && tmpPos < oldPos && moveUp) {
+                aTheme.setPosition(tmpPos+1);
+            }
+            else if (tmpPos <= newPos && tmpPos > oldPos && !moveUp) {
+                aTheme.setPosition(tmpPos-1);
+            }
+        }
+    }
+}
+// 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:15 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:15 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/WQDataItem.java	Fri Sep 28 12:14:15 2012 +0200
@@ -0,0 +1,38 @@
+package de.intevation.flys.client.shared.model;
+
+/**
+ * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
+ */
+public class WQDataItem extends DefaultDataItem {
+
+    protected double[] qRange;
+    protected double[] wRange;
+
+    public WQDataItem() {
+    }
+
+
+    public WQDataItem(
+        String   label,
+        String   description,
+        String   value,
+        double[] qRange,
+        double[] wRange)
+    {
+        super(label, description, value);
+
+        this.qRange = qRange;
+        this.wRange = wRange;
+    }
+
+
+    public double[] getQRange() {
+        return qRange;
+    }
+
+
+    public double[] getWRange() {
+        return wRange;
+    }
+}
+// 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:15 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:15 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:15 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:15 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:15 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:15 2012 +0200
@@ -0,0 +1,235 @@
+<?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>
+
+  <servlet>
+    <servlet-name>CollectionAttributeService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.CollectionAttributeServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>CollectionAttributeService</servlet-name>
+    <url-pattern>/flys/collection-attribute</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>ChartInfoService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.ChartInfoServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>ChartInfoService</servlet-name>
+    <url-pattern>/flys/chart-info</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>ReportService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.ReportServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>ReportService</servlet-name>
+    <url-pattern>/flys/report</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>SetCollectionNameService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.SetCollectionNameServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>SetCollectionNameService</servlet-name>
+    <url-pattern>/flys/set-collectionname</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>SetCollectionTTLService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.SetCollectionTTLServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>SetCollectionTTLService</servlet-name>
+    <url-pattern>/flys/set-collectionttl</url-pattern>
+  </servlet-mapping>
+
+  <servlet>
+    <servlet-name>DeleteCollectionService</servlet-name>
+    <servlet-class>de.intevation.flys.client.server.DeleteCollectionServiceImpl</servlet-class>
+  </servlet>
+
+  <servlet-mapping>
+    <servlet-name>DeleteCollectionService</servlet-name>
+    <url-pattern>/flys/delete-collection</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/pan.png has changed
Binary file flys-client/src/main/webapp/images/pan_Selected.png has changed
Binary file flys-client/src/main/webapp/images/save.gif has changed
Binary file flys-client/src/main/webapp/images/star_gold.png has changed
Binary file flys-client/src/main/webapp/images/star_silver.png has changed
Binary file flys-client/src/main/webapp/images/theme_bottom.png has changed
Binary file flys-client/src/main/webapp/images/theme_down.png has changed
Binary file flys-client/src/main/webapp/images/theme_top.png has changed
Binary file flys-client/src/main/webapp/images/theme_up.png has changed
Binary file flys-client/src/main/webapp/images/zoom-1.png has changed
Binary file flys-client/src/main/webapp/images/zoom-in.png has changed
Binary file flys-client/src/main/webapp/images/zoom-in_Selected.png has changed
Binary file flys-client/src/main/webapp/images/zoom-out.png 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:15 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